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