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

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