1 //===--- StringviewNullptrCheck.cpp - clang-tidy --------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "StringviewNullptrCheck.h"
10 #include "../utils/TransformerClangTidyCheck.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/AST/Decl.h"
13 #include "clang/AST/OperationKinds.h"
14 #include "clang/ASTMatchers/ASTMatchFinder.h"
15 #include "clang/ASTMatchers/ASTMatchers.h"
16 #include "clang/Tooling/Transformer/RangeSelector.h"
17 #include "clang/Tooling/Transformer/RewriteRule.h"
18 #include "clang/Tooling/Transformer/Stencil.h"
19 #include "llvm/ADT/StringRef.h"
21 namespace clang::tidy::bugprone
{
23 using namespace ::clang::ast_matchers
;
24 using namespace ::clang::transformer
;
28 AST_MATCHER_P(InitListExpr
, initCountIs
, unsigned, N
) {
29 return Node
.getNumInits() == N
;
32 AST_MATCHER(clang::VarDecl
, isDirectInitialization
) {
33 return Node
.getInitStyle() != clang::VarDecl::InitializationStyle::CInit
;
38 RewriteRuleWith
<std::string
> StringviewNullptrCheckImpl() {
39 auto construction_warning
=
40 cat("constructing basic_string_view from null is undefined; replace with "
41 "the default constructor");
42 auto static_cast_warning
=
43 cat("casting to basic_string_view from null is undefined; replace with "
45 auto argument_construction_warning
=
46 cat("passing null as basic_string_view is undefined; replace with the "
48 auto assignment_warning
=
49 cat("assignment to basic_string_view from null is undefined; replace "
50 "with the default constructor");
51 auto relative_comparison_warning
=
52 cat("comparing basic_string_view to null is undefined; replace with the "
54 auto equality_comparison_warning
=
55 cat("comparing basic_string_view to null is undefined; replace with the "
58 // Matches declarations and expressions of type `basic_string_view`
59 auto HasBasicStringViewType
= hasType(hasUnqualifiedDesugaredType(recordType(
60 hasDeclaration(cxxRecordDecl(hasName("::std::basic_string_view"))))));
62 // Matches `nullptr` and `(nullptr)` binding to a pointer
63 auto NullLiteral
= implicitCastExpr(
64 hasCastKind(clang::CK_NullToPointer
),
65 hasSourceExpression(ignoringParens(cxxNullPtrLiteralExpr())));
67 // Matches `{nullptr}` and `{(nullptr)}` binding to a pointer
68 auto NullInitList
= initListExpr(initCountIs(1), hasInit(0, NullLiteral
));
71 auto EmptyInitList
= initListExpr(initCountIs(0));
73 // Matches null construction without `basic_string_view` type spelling
74 auto BasicStringViewConstructingFromNullExpr
=
76 HasBasicStringViewType
, argumentCountIs(1),
77 hasAnyArgument(/* `hasArgument` would skip over parens */ anyOf(
78 NullLiteral
, NullInitList
, EmptyInitList
)),
79 unless(cxxTemporaryObjectExpr(/* filters out type spellings */)),
80 has(expr().bind("null_arg_expr")))
81 .bind("construct_expr");
83 // `std::string_view(null_arg_expr)`
84 auto HandleTemporaryCXXFunctionalCastExpr
=
85 makeRule(cxxFunctionalCastExpr(hasSourceExpression(
86 BasicStringViewConstructingFromNullExpr
)),
87 remove(node("null_arg_expr")), construction_warning
);
89 // `std::string_view{null_arg_expr}` and `(std::string_view){null_arg_expr}`
90 auto HandleTemporaryCXXTemporaryObjectExprAndCompoundLiteralExpr
= makeRule(
91 cxxTemporaryObjectExpr(cxxConstructExpr(
92 HasBasicStringViewType
, argumentCountIs(1),
93 hasAnyArgument(/* `hasArgument` would skip over parens */ anyOf(
94 NullLiteral
, NullInitList
, EmptyInitList
)),
95 has(expr().bind("null_arg_expr")))),
96 remove(node("null_arg_expr")), construction_warning
);
98 // `(std::string_view) null_arg_expr`
99 auto HandleTemporaryCStyleCastExpr
= makeRule(
101 hasSourceExpression(BasicStringViewConstructingFromNullExpr
)),
102 changeTo(node("null_arg_expr"), cat("{}")), construction_warning
);
104 // `static_cast<std::string_view>(null_arg_expr)`
105 auto HandleTemporaryCXXStaticCastExpr
= makeRule(
107 hasSourceExpression(BasicStringViewConstructingFromNullExpr
)),
108 changeTo(node("null_arg_expr"), cat("\"\"")), static_cast_warning
);
110 // `std::string_view sv = null_arg_expr;`
111 auto HandleStackCopyInitialization
= makeRule(
112 varDecl(HasBasicStringViewType
,
113 hasInitializer(ignoringImpCasts(
114 cxxConstructExpr(BasicStringViewConstructingFromNullExpr
,
115 unless(isListInitialization())))),
116 unless(isDirectInitialization())),
117 changeTo(node("null_arg_expr"), cat("{}")), construction_warning
);
119 // `std::string_view sv = {null_arg_expr};`
120 auto HandleStackCopyListInitialization
=
121 makeRule(varDecl(HasBasicStringViewType
,
122 hasInitializer(cxxConstructExpr(
123 BasicStringViewConstructingFromNullExpr
,
124 isListInitialization())),
125 unless(isDirectInitialization())),
126 remove(node("null_arg_expr")), construction_warning
);
128 // `std::string_view sv(null_arg_expr);`
129 auto HandleStackDirectInitialization
=
130 makeRule(varDecl(HasBasicStringViewType
,
131 hasInitializer(cxxConstructExpr(
132 BasicStringViewConstructingFromNullExpr
,
133 unless(isListInitialization()))),
134 isDirectInitialization())
136 changeTo(node("construct_expr"), cat(name("var_decl"))),
137 construction_warning
);
139 // `std::string_view sv{null_arg_expr};`
140 auto HandleStackDirectListInitialization
=
141 makeRule(varDecl(HasBasicStringViewType
,
142 hasInitializer(cxxConstructExpr(
143 BasicStringViewConstructingFromNullExpr
,
144 isListInitialization())),
145 isDirectInitialization()),
146 remove(node("null_arg_expr")), construction_warning
);
148 // `struct S { std::string_view sv = null_arg_expr; };`
149 auto HandleFieldInClassCopyInitialization
= makeRule(
150 fieldDecl(HasBasicStringViewType
,
151 hasInClassInitializer(ignoringImpCasts(
152 cxxConstructExpr(BasicStringViewConstructingFromNullExpr
,
153 unless(isListInitialization()))))),
154 changeTo(node("null_arg_expr"), cat("{}")), construction_warning
);
156 // `struct S { std::string_view sv = {null_arg_expr}; };` and
157 // `struct S { std::string_view sv{null_arg_expr}; };`
158 auto HandleFieldInClassCopyListAndDirectListInitialization
= makeRule(
159 fieldDecl(HasBasicStringViewType
,
160 hasInClassInitializer(ignoringImpCasts(
161 cxxConstructExpr(BasicStringViewConstructingFromNullExpr
,
162 isListInitialization())))),
163 remove(node("null_arg_expr")), construction_warning
);
165 // `class C { std::string_view sv; C() : sv(null_arg_expr) {} };`
166 auto HandleConstructorDirectInitialization
=
167 makeRule(cxxCtorInitializer(forField(fieldDecl(HasBasicStringViewType
)),
168 withInitializer(cxxConstructExpr(
169 BasicStringViewConstructingFromNullExpr
,
170 unless(isListInitialization())))),
171 remove(node("null_arg_expr")), construction_warning
);
173 // `class C { std::string_view sv; C() : sv{null_arg_expr} {} };`
174 auto HandleConstructorDirectListInitialization
=
175 makeRule(cxxCtorInitializer(forField(fieldDecl(HasBasicStringViewType
)),
176 withInitializer(cxxConstructExpr(
177 BasicStringViewConstructingFromNullExpr
,
178 isListInitialization()))),
179 remove(node("null_arg_expr")), construction_warning
);
181 // `void f(std::string_view sv = null_arg_expr);`
182 auto HandleDefaultArgumentCopyInitialization
= makeRule(
183 parmVarDecl(HasBasicStringViewType
,
184 hasInitializer(ignoringImpCasts(
185 cxxConstructExpr(BasicStringViewConstructingFromNullExpr
,
186 unless(isListInitialization()))))),
187 changeTo(node("null_arg_expr"), cat("{}")), construction_warning
);
189 // `void f(std::string_view sv = {null_arg_expr});`
190 auto HandleDefaultArgumentCopyListInitialization
=
191 makeRule(parmVarDecl(HasBasicStringViewType
,
192 hasInitializer(cxxConstructExpr(
193 BasicStringViewConstructingFromNullExpr
,
194 isListInitialization()))),
195 remove(node("null_arg_expr")), construction_warning
);
197 // `new std::string_view(null_arg_expr)`
198 auto HandleHeapDirectInitialization
= makeRule(
199 cxxNewExpr(has(cxxConstructExpr(BasicStringViewConstructingFromNullExpr
,
200 unless(isListInitialization()))),
201 unless(isArray()), unless(hasAnyPlacementArg(anything()))),
202 remove(node("null_arg_expr")), construction_warning
);
204 // `new std::string_view{null_arg_expr}`
205 auto HandleHeapDirectListInitialization
= makeRule(
206 cxxNewExpr(has(cxxConstructExpr(BasicStringViewConstructingFromNullExpr
,
207 isListInitialization())),
208 unless(isArray()), unless(hasAnyPlacementArg(anything()))),
209 remove(node("null_arg_expr")), construction_warning
);
211 // `function(null_arg_expr)`
212 auto HandleFunctionArgumentInitialization
=
213 makeRule(callExpr(hasAnyArgument(ignoringImpCasts(
214 BasicStringViewConstructingFromNullExpr
)),
215 unless(cxxOperatorCallExpr())),
216 changeTo(node("construct_expr"), cat("\"\"")),
217 argument_construction_warning
);
219 // `sv = null_arg_expr`
220 auto HandleAssignment
= makeRule(
221 cxxOperatorCallExpr(hasOverloadedOperatorName("="),
222 hasRHS(materializeTemporaryExpr(
223 has(BasicStringViewConstructingFromNullExpr
)))),
224 changeTo(node("construct_expr"), cat("{}")), assignment_warning
);
226 // `sv < null_arg_expr`
227 auto HandleRelativeComparison
= makeRule(
228 cxxOperatorCallExpr(hasAnyOverloadedOperatorName("<", "<=", ">", ">="),
229 hasEitherOperand(ignoringImpCasts(
230 BasicStringViewConstructingFromNullExpr
))),
231 changeTo(node("construct_expr"), cat("\"\"")),
232 relative_comparison_warning
);
234 // `sv == null_arg_expr`
235 auto HandleEmptyEqualityComparison
= makeRule(
237 hasOverloadedOperatorName("=="),
238 hasOperands(ignoringImpCasts(BasicStringViewConstructingFromNullExpr
),
239 traverse(clang::TK_IgnoreUnlessSpelledInSource
,
240 expr().bind("instance"))))
242 changeTo(node("root"), cat(access("instance", cat("empty")), "()")),
243 equality_comparison_warning
);
245 // `sv != null_arg_expr`
246 auto HandleNonEmptyEqualityComparison
= makeRule(
248 hasOverloadedOperatorName("!="),
249 hasOperands(ignoringImpCasts(BasicStringViewConstructingFromNullExpr
),
250 traverse(clang::TK_IgnoreUnlessSpelledInSource
,
251 expr().bind("instance"))))
253 changeTo(node("root"), cat("!", access("instance", cat("empty")), "()")),
254 equality_comparison_warning
);
256 // `return null_arg_expr;`
257 auto HandleReturnStatement
= makeRule(
258 returnStmt(hasReturnValue(
259 ignoringImpCasts(BasicStringViewConstructingFromNullExpr
))),
260 changeTo(node("construct_expr"), cat("{}")), construction_warning
);
262 // `T(null_arg_expr)`
263 auto HandleConstructorInvocation
=
264 makeRule(cxxConstructExpr(
265 hasAnyArgument(/* `hasArgument` would skip over parens */
267 BasicStringViewConstructingFromNullExpr
)),
268 unless(HasBasicStringViewType
)),
269 changeTo(node("construct_expr"), cat("\"\"")),
270 argument_construction_warning
);
273 {HandleTemporaryCXXFunctionalCastExpr
,
274 HandleTemporaryCXXTemporaryObjectExprAndCompoundLiteralExpr
,
275 HandleTemporaryCStyleCastExpr
,
276 HandleTemporaryCXXStaticCastExpr
,
277 HandleStackCopyInitialization
,
278 HandleStackCopyListInitialization
,
279 HandleStackDirectInitialization
,
280 HandleStackDirectListInitialization
,
281 HandleFieldInClassCopyInitialization
,
282 HandleFieldInClassCopyListAndDirectListInitialization
,
283 HandleConstructorDirectInitialization
,
284 HandleConstructorDirectListInitialization
,
285 HandleDefaultArgumentCopyInitialization
,
286 HandleDefaultArgumentCopyListInitialization
,
287 HandleHeapDirectInitialization
,
288 HandleHeapDirectListInitialization
,
289 HandleFunctionArgumentInitialization
,
291 HandleRelativeComparison
,
292 HandleEmptyEqualityComparison
,
293 HandleNonEmptyEqualityComparison
,
294 HandleReturnStatement
,
295 HandleConstructorInvocation
});
298 StringviewNullptrCheck::StringviewNullptrCheck(StringRef Name
,
299 ClangTidyContext
*Context
)
300 : utils::TransformerClangTidyCheck(StringviewNullptrCheckImpl(), Name
,
303 } // namespace clang::tidy::bugprone