1//===--- StringviewNullptrCheck.cpp - clang-tidy --------------------------===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8
9#include "StringviewNullptrCheck.h"
10#include "../utils/TransformerClangTidyCheck.h"
11#include "clang/AST/Decl.h"
12#include "clang/AST/OperationKinds.h"
13#include "clang/ASTMatchers/ASTMatchers.h"
14#include "clang/Tooling/Transformer/RangeSelector.h"
15#include "clang/Tooling/Transformer/RewriteRule.h"
16#include "clang/Tooling/Transformer/Stencil.h"
17#include "llvm/ADT/StringRef.h"
18
19namespace clang::tidy::bugprone {
20
21using namespace ::clang::ast_matchers;
22using namespace ::clang::transformer;
23
24namespace {
25
26AST_MATCHER_P(InitListExpr, initCountIs, unsigned, N) {
27 return Node.getNumInits() == N;
28}
29
30AST_MATCHER(clang::VarDecl, isDirectInitialization) {
31 return Node.getInitStyle() != clang::VarDecl::InitializationStyle::CInit;
32}
33
34} // namespace
35
36static RewriteRuleWith<std::string> stringviewNullptrCheckImpl() {
37 auto ConstructionWarning =
38 cat(Parts: "constructing basic_string_view from null is undefined; replace with "
39 "the default constructor");
40 auto StaticCastWarning =
41 cat(Parts: "casting to basic_string_view from null is undefined; replace with "
42 "the empty string");
43 auto ArgumentConstructionWarning =
44 cat(Parts: "passing null as basic_string_view is undefined; replace with the "
45 "empty string");
46 auto AssignmentWarning =
47 cat(Parts: "assignment to basic_string_view from null is undefined; replace "
48 "with the default constructor");
49 auto RelativeComparisonWarning =
50 cat(Parts: "comparing basic_string_view to null is undefined; replace with the "
51 "empty string");
52 auto EqualityComparisonWarning =
53 cat(Parts: "comparing basic_string_view to null is undefined; replace with the "
54 "emptiness query");
55
56 // Matches declarations and expressions of type `basic_string_view`
57 auto HasBasicStringViewType = hasType(InnerMatcher: hasUnqualifiedDesugaredType(InnerMatcher: recordType(
58 hasDeclaration(InnerMatcher: cxxRecordDecl(hasName(Name: "::std::basic_string_view"))))));
59
60 // Matches `nullptr` and `(nullptr)` binding to a pointer
61 auto NullLiteral = implicitCastExpr(
62 hasCastKind(Kind: clang::CK_NullToPointer),
63 hasSourceExpression(InnerMatcher: ignoringParens(InnerMatcher: cxxNullPtrLiteralExpr())));
64
65 // Matches `{nullptr}` and `{(nullptr)}` binding to a pointer
66 auto NullInitList = initListExpr(initCountIs(N: 1), hasInit(N: 0, InnerMatcher: NullLiteral));
67
68 // Matches `{}`
69 auto EmptyInitList = initListExpr(initCountIs(N: 0));
70
71 // Matches null construction without `basic_string_view` type spelling
72 auto BasicStringViewConstructingFromNullExpr =
73 cxxConstructExpr(
74 HasBasicStringViewType, argumentCountIs(N: 1),
75 hasAnyArgument(/* `hasArgument` would skip over parens */ InnerMatcher: anyOf(
76 NullLiteral, NullInitList, EmptyInitList)),
77 unless(cxxTemporaryObjectExpr(/* filters out type spellings */)),
78 has(expr().bind(ID: "null_arg_expr")))
79 .bind(ID: "construct_expr");
80
81 // `std::string_view(null_arg_expr)`
82 auto HandleTemporaryCXXFunctionalCastExpr =
83 makeRule(M: cxxFunctionalCastExpr(hasSourceExpression(
84 InnerMatcher: BasicStringViewConstructingFromNullExpr)),
85 Edits: remove(S: node(ID: "null_arg_expr")), Metadata: ConstructionWarning);
86
87 // `std::string_view{null_arg_expr}` and `(std::string_view){null_arg_expr}`
88 auto HandleTemporaryCXXTemporaryObjectExprAndCompoundLiteralExpr = makeRule(
89 M: cxxTemporaryObjectExpr(cxxConstructExpr(
90 HasBasicStringViewType, argumentCountIs(N: 1),
91 hasAnyArgument(/* `hasArgument` would skip over parens */ InnerMatcher: anyOf(
92 NullLiteral, NullInitList, EmptyInitList)),
93 has(expr().bind(ID: "null_arg_expr")))),
94 Edits: remove(S: node(ID: "null_arg_expr")), Metadata: ConstructionWarning);
95
96 // `(std::string_view) null_arg_expr`
97 auto HandleTemporaryCStyleCastExpr =
98 makeRule(M: cStyleCastExpr(hasSourceExpression(
99 InnerMatcher: BasicStringViewConstructingFromNullExpr)),
100 Edits: changeTo(Target: node(ID: "null_arg_expr"), Replacement: cat(Parts: "{}")), Metadata: ConstructionWarning);
101
102 // `static_cast<std::string_view>(null_arg_expr)`
103 auto HandleTemporaryCXXStaticCastExpr =
104 makeRule(M: cxxStaticCastExpr(hasSourceExpression(
105 InnerMatcher: BasicStringViewConstructingFromNullExpr)),
106 Edits: changeTo(Target: node(ID: "null_arg_expr"), Replacement: cat(Parts: "\"\"")), Metadata: StaticCastWarning);
107
108 // `std::string_view sv = null_arg_expr;`
109 auto HandleStackCopyInitialization =
110 makeRule(M: varDecl(HasBasicStringViewType,
111 hasInitializer(InnerMatcher: ignoringImpCasts(InnerMatcher: cxxConstructExpr(
112 BasicStringViewConstructingFromNullExpr,
113 unless(isListInitialization())))),
114 unless(isDirectInitialization())),
115 Edits: changeTo(Target: node(ID: "null_arg_expr"), Replacement: cat(Parts: "{}")), Metadata: ConstructionWarning);
116
117 // `std::string_view sv = {null_arg_expr};`
118 auto HandleStackCopyListInitialization =
119 makeRule(M: varDecl(HasBasicStringViewType,
120 hasInitializer(InnerMatcher: cxxConstructExpr(
121 BasicStringViewConstructingFromNullExpr,
122 isListInitialization())),
123 unless(isDirectInitialization())),
124 Edits: remove(S: node(ID: "null_arg_expr")), Metadata: ConstructionWarning);
125
126 // `std::string_view sv(null_arg_expr);`
127 auto HandleStackDirectInitialization =
128 makeRule(M: varDecl(HasBasicStringViewType,
129 hasInitializer(InnerMatcher: cxxConstructExpr(
130 BasicStringViewConstructingFromNullExpr,
131 unless(isListInitialization()))),
132 isDirectInitialization())
133 .bind(ID: "var_decl"),
134 Edits: changeTo(Target: node(ID: "construct_expr"), Replacement: cat(Parts: name(ID: "var_decl"))),
135 Metadata: ConstructionWarning);
136
137 // `std::string_view sv{null_arg_expr};`
138 auto HandleStackDirectListInitialization =
139 makeRule(M: varDecl(HasBasicStringViewType,
140 hasInitializer(InnerMatcher: cxxConstructExpr(
141 BasicStringViewConstructingFromNullExpr,
142 isListInitialization())),
143 isDirectInitialization()),
144 Edits: remove(S: node(ID: "null_arg_expr")), Metadata: ConstructionWarning);
145
146 // `struct S { std::string_view sv = null_arg_expr; };`
147 auto HandleFieldInClassCopyInitialization = makeRule(
148 M: fieldDecl(HasBasicStringViewType,
149 hasInClassInitializer(InnerMatcher: ignoringImpCasts(
150 InnerMatcher: cxxConstructExpr(BasicStringViewConstructingFromNullExpr,
151 unless(isListInitialization()))))),
152 Edits: changeTo(Target: node(ID: "null_arg_expr"), Replacement: cat(Parts: "{}")), Metadata: ConstructionWarning);
153
154 // `struct S { std::string_view sv = {null_arg_expr}; };` and
155 // `struct S { std::string_view sv{null_arg_expr}; };`
156 auto HandleFieldInClassCopyListAndDirectListInitialization = makeRule(
157 M: fieldDecl(HasBasicStringViewType,
158 hasInClassInitializer(InnerMatcher: ignoringImpCasts(
159 InnerMatcher: cxxConstructExpr(BasicStringViewConstructingFromNullExpr,
160 isListInitialization())))),
161 Edits: remove(S: node(ID: "null_arg_expr")), Metadata: ConstructionWarning);
162
163 // `class C { std::string_view sv; C() : sv(null_arg_expr) {} };`
164 auto HandleConstructorDirectInitialization =
165 makeRule(M: cxxCtorInitializer(forField(InnerMatcher: fieldDecl(HasBasicStringViewType)),
166 withInitializer(InnerMatcher: cxxConstructExpr(
167 BasicStringViewConstructingFromNullExpr,
168 unless(isListInitialization())))),
169 Edits: remove(S: node(ID: "null_arg_expr")), Metadata: ConstructionWarning);
170
171 // `class C { std::string_view sv; C() : sv{null_arg_expr} {} };`
172 auto HandleConstructorDirectListInitialization =
173 makeRule(M: cxxCtorInitializer(forField(InnerMatcher: fieldDecl(HasBasicStringViewType)),
174 withInitializer(InnerMatcher: cxxConstructExpr(
175 BasicStringViewConstructingFromNullExpr,
176 isListInitialization()))),
177 Edits: remove(S: node(ID: "null_arg_expr")), Metadata: ConstructionWarning);
178
179 // `void f(std::string_view sv = null_arg_expr);`
180 auto HandleDefaultArgumentCopyInitialization =
181 makeRule(M: parmVarDecl(HasBasicStringViewType,
182 hasInitializer(InnerMatcher: ignoringImpCasts(InnerMatcher: cxxConstructExpr(
183 BasicStringViewConstructingFromNullExpr,
184 unless(isListInitialization()))))),
185 Edits: changeTo(Target: node(ID: "null_arg_expr"), Replacement: cat(Parts: "{}")), Metadata: ConstructionWarning);
186
187 // `void f(std::string_view sv = {null_arg_expr});`
188 auto HandleDefaultArgumentCopyListInitialization =
189 makeRule(M: parmVarDecl(HasBasicStringViewType,
190 hasInitializer(InnerMatcher: cxxConstructExpr(
191 BasicStringViewConstructingFromNullExpr,
192 isListInitialization()))),
193 Edits: remove(S: node(ID: "null_arg_expr")), Metadata: ConstructionWarning);
194
195 // `new std::string_view(null_arg_expr)`
196 auto HandleHeapDirectInitialization = makeRule(
197 M: cxxNewExpr(has(cxxConstructExpr(BasicStringViewConstructingFromNullExpr,
198 unless(isListInitialization()))),
199 unless(isArray()), unless(hasAnyPlacementArg(InnerMatcher: anything()))),
200 Edits: remove(S: node(ID: "null_arg_expr")), Metadata: ConstructionWarning);
201
202 // `new std::string_view{null_arg_expr}`
203 auto HandleHeapDirectListInitialization = makeRule(
204 M: cxxNewExpr(has(cxxConstructExpr(BasicStringViewConstructingFromNullExpr,
205 isListInitialization())),
206 unless(isArray()), unless(hasAnyPlacementArg(InnerMatcher: anything()))),
207 Edits: remove(S: node(ID: "null_arg_expr")), Metadata: ConstructionWarning);
208
209 // `function(null_arg_expr)`
210 auto HandleFunctionArgumentInitialization =
211 makeRule(M: callExpr(hasAnyArgument(InnerMatcher: ignoringImpCasts(
212 InnerMatcher: BasicStringViewConstructingFromNullExpr)),
213 unless(cxxOperatorCallExpr())),
214 Edits: changeTo(Target: node(ID: "construct_expr"), Replacement: cat(Parts: "\"\"")),
215 Metadata: ArgumentConstructionWarning);
216
217 // `sv = null_arg_expr`
218 auto HandleAssignment = makeRule(
219 M: cxxOperatorCallExpr(hasOverloadedOperatorName(Name: "="),
220 hasRHS(InnerMatcher: materializeTemporaryExpr(
221 has(BasicStringViewConstructingFromNullExpr)))),
222 Edits: changeTo(Target: node(ID: "construct_expr"), Replacement: cat(Parts: "{}")), Metadata: AssignmentWarning);
223
224 // `sv < null_arg_expr`
225 auto HandleRelativeComparison = makeRule(
226 M: cxxOperatorCallExpr(hasAnyOverloadedOperatorName("<", "<=", ">", ">="),
227 hasEitherOperand(InnerMatcher: ignoringImpCasts(
228 InnerMatcher: BasicStringViewConstructingFromNullExpr))),
229 Edits: changeTo(Target: node(ID: "construct_expr"), Replacement: cat(Parts: "\"\"")), Metadata: RelativeComparisonWarning);
230
231 // `sv == null_arg_expr`
232 auto HandleEmptyEqualityComparison = makeRule(
233 M: cxxOperatorCallExpr(
234 hasOverloadedOperatorName(Name: "=="),
235 hasOperands(Matcher1: ignoringImpCasts(InnerMatcher: BasicStringViewConstructingFromNullExpr),
236 Matcher2: traverse(TK: clang::TK_IgnoreUnlessSpelledInSource,
237 InnerMatcher: expr().bind(ID: "instance"))))
238 .bind(ID: "root"),
239 Edits: changeTo(Target: node(ID: "root"), Replacement: cat(Parts: access(BaseId: "instance", Member: cat(Parts: "empty")), Parts: "()")),
240 Metadata: EqualityComparisonWarning);
241
242 // `sv != null_arg_expr`
243 auto HandleNonEmptyEqualityComparison = makeRule(
244 M: cxxOperatorCallExpr(
245 hasOverloadedOperatorName(Name: "!="),
246 hasOperands(Matcher1: ignoringImpCasts(InnerMatcher: BasicStringViewConstructingFromNullExpr),
247 Matcher2: traverse(TK: clang::TK_IgnoreUnlessSpelledInSource,
248 InnerMatcher: expr().bind(ID: "instance"))))
249 .bind(ID: "root"),
250 Edits: changeTo(Target: node(ID: "root"), Replacement: cat(Parts: "!", Parts: access(BaseId: "instance", Member: cat(Parts: "empty")), Parts: "()")),
251 Metadata: EqualityComparisonWarning);
252
253 // `return null_arg_expr;`
254 auto HandleReturnStatement = makeRule(
255 M: returnStmt(hasReturnValue(
256 InnerMatcher: ignoringImpCasts(InnerMatcher: BasicStringViewConstructingFromNullExpr))),
257 Edits: changeTo(Target: node(ID: "construct_expr"), Replacement: cat(Parts: "{}")), Metadata: ConstructionWarning);
258
259 // `T(null_arg_expr)`
260 auto HandleConstructorInvocation =
261 makeRule(M: cxxConstructExpr(
262 hasAnyArgument(/* `hasArgument` would skip over parens */
263 InnerMatcher: ignoringImpCasts(
264 InnerMatcher: BasicStringViewConstructingFromNullExpr)),
265 unless(HasBasicStringViewType)),
266 Edits: changeTo(Target: node(ID: "construct_expr"), Replacement: cat(Parts: "\"\"")),
267 Metadata: ArgumentConstructionWarning);
268
269 return applyFirst(
270 Rules: {HandleTemporaryCXXFunctionalCastExpr,
271 HandleTemporaryCXXTemporaryObjectExprAndCompoundLiteralExpr,
272 HandleTemporaryCStyleCastExpr,
273 HandleTemporaryCXXStaticCastExpr,
274 HandleStackCopyInitialization,
275 HandleStackCopyListInitialization,
276 HandleStackDirectInitialization,
277 HandleStackDirectListInitialization,
278 HandleFieldInClassCopyInitialization,
279 HandleFieldInClassCopyListAndDirectListInitialization,
280 HandleConstructorDirectInitialization,
281 HandleConstructorDirectListInitialization,
282 HandleDefaultArgumentCopyInitialization,
283 HandleDefaultArgumentCopyListInitialization,
284 HandleHeapDirectInitialization,
285 HandleHeapDirectListInitialization,
286 HandleFunctionArgumentInitialization,
287 HandleAssignment,
288 HandleRelativeComparison,
289 HandleEmptyEqualityComparison,
290 HandleNonEmptyEqualityComparison,
291 HandleReturnStatement,
292 HandleConstructorInvocation});
293}
294
295StringviewNullptrCheck::StringviewNullptrCheck(StringRef Name,
296 ClangTidyContext *Context)
297 : utils::TransformerClangTidyCheck(stringviewNullptrCheckImpl(), Name,
298 Context) {}
299
300} // namespace clang::tidy::bugprone
301

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of clang-tools-extra/clang-tidy/bugprone/StringviewNullptrCheck.cpp