clang-tools 22.0.0git
StringviewNullptrCheck.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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
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("constructing basic_string_view from null is undefined; replace with "
39 "the default constructor");
40 auto StaticCastWarning =
41 cat("casting to basic_string_view from null is undefined; replace with "
42 "the empty string");
43 auto ArgumentConstructionWarning =
44 cat("passing null as basic_string_view is undefined; replace with the "
45 "empty string");
46 auto AssignmentWarning =
47 cat("assignment to basic_string_view from null is undefined; replace "
48 "with the default constructor");
49 auto RelativeComparisonWarning =
50 cat("comparing basic_string_view to null is undefined; replace with the "
51 "empty string");
52 auto EqualityComparisonWarning =
53 cat("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(hasUnqualifiedDesugaredType(recordType(
58 hasDeclaration(cxxRecordDecl(hasName("::std::basic_string_view"))))));
59
60 // Matches `nullptr` and `(nullptr)` binding to a pointer
61 auto NullLiteral = implicitCastExpr(
62 hasCastKind(clang::CK_NullToPointer),
63 hasSourceExpression(ignoringParens(cxxNullPtrLiteralExpr())));
64
65 // Matches `{nullptr}` and `{(nullptr)}` binding to a pointer
66 auto NullInitList = initListExpr(initCountIs(1), hasInit(0, NullLiteral));
67
68 // Matches `{}`
69 auto EmptyInitList = initListExpr(initCountIs(0));
70
71 // Matches null construction without `basic_string_view` type spelling
72 auto BasicStringViewConstructingFromNullExpr =
73 cxxConstructExpr(
74 HasBasicStringViewType, argumentCountIs(1),
75 hasAnyArgument(/* `hasArgument` would skip over parens */ anyOf(
76 NullLiteral, NullInitList, EmptyInitList)),
77 unless(cxxTemporaryObjectExpr(/* filters out type spellings */)),
78 has(expr().bind("null_arg_expr")))
79 .bind("construct_expr");
80
81 // `std::string_view(null_arg_expr)`
82 auto HandleTemporaryCXXFunctionalCastExpr =
83 makeRule(cxxFunctionalCastExpr(hasSourceExpression(
84 BasicStringViewConstructingFromNullExpr)),
85 remove(node("null_arg_expr")), ConstructionWarning);
86
87 // `std::string_view{null_arg_expr}` and `(std::string_view){null_arg_expr}`
88 auto HandleTemporaryCXXTemporaryObjectExprAndCompoundLiteralExpr = makeRule(
89 cxxTemporaryObjectExpr(cxxConstructExpr(
90 HasBasicStringViewType, argumentCountIs(1),
91 hasAnyArgument(/* `hasArgument` would skip over parens */ anyOf(
92 NullLiteral, NullInitList, EmptyInitList)),
93 has(expr().bind("null_arg_expr")))),
94 remove(node("null_arg_expr")), ConstructionWarning);
95
96 // `(std::string_view) null_arg_expr`
97 auto HandleTemporaryCStyleCastExpr =
98 makeRule(cStyleCastExpr(hasSourceExpression(
99 BasicStringViewConstructingFromNullExpr)),
100 changeTo(node("null_arg_expr"), cat("{}")), ConstructionWarning);
101
102 // `static_cast<std::string_view>(null_arg_expr)`
103 auto HandleTemporaryCXXStaticCastExpr =
104 makeRule(cxxStaticCastExpr(hasSourceExpression(
105 BasicStringViewConstructingFromNullExpr)),
106 changeTo(node("null_arg_expr"), cat("\"\"")), StaticCastWarning);
107
108 // `std::string_view sv = null_arg_expr;`
109 auto HandleStackCopyInitialization =
110 makeRule(varDecl(HasBasicStringViewType,
111 hasInitializer(ignoringImpCasts(cxxConstructExpr(
112 BasicStringViewConstructingFromNullExpr,
113 unless(isListInitialization())))),
114 unless(isDirectInitialization())),
115 changeTo(node("null_arg_expr"), cat("{}")), ConstructionWarning);
116
117 // `std::string_view sv = {null_arg_expr};`
118 auto HandleStackCopyListInitialization =
119 makeRule(varDecl(HasBasicStringViewType,
120 hasInitializer(cxxConstructExpr(
121 BasicStringViewConstructingFromNullExpr,
122 isListInitialization())),
123 unless(isDirectInitialization())),
124 remove(node("null_arg_expr")), ConstructionWarning);
125
126 // `std::string_view sv(null_arg_expr);`
127 auto HandleStackDirectInitialization =
128 makeRule(varDecl(HasBasicStringViewType,
129 hasInitializer(cxxConstructExpr(
130 BasicStringViewConstructingFromNullExpr,
131 unless(isListInitialization()))),
132 isDirectInitialization())
133 .bind("var_decl"),
134 changeTo(node("construct_expr"), cat(name("var_decl"))),
135 ConstructionWarning);
136
137 // `std::string_view sv{null_arg_expr};`
138 auto HandleStackDirectListInitialization =
139 makeRule(varDecl(HasBasicStringViewType,
140 hasInitializer(cxxConstructExpr(
141 BasicStringViewConstructingFromNullExpr,
142 isListInitialization())),
143 isDirectInitialization()),
144 remove(node("null_arg_expr")), ConstructionWarning);
145
146 // `struct S { std::string_view sv = null_arg_expr; };`
147 auto HandleFieldInClassCopyInitialization = makeRule(
148 fieldDecl(HasBasicStringViewType,
149 hasInClassInitializer(ignoringImpCasts(
150 cxxConstructExpr(BasicStringViewConstructingFromNullExpr,
151 unless(isListInitialization()))))),
152 changeTo(node("null_arg_expr"), cat("{}")), 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 fieldDecl(HasBasicStringViewType,
158 hasInClassInitializer(ignoringImpCasts(
159 cxxConstructExpr(BasicStringViewConstructingFromNullExpr,
160 isListInitialization())))),
161 remove(node("null_arg_expr")), ConstructionWarning);
162
163 // `class C { std::string_view sv; C() : sv(null_arg_expr) {} };`
164 auto HandleConstructorDirectInitialization =
165 makeRule(cxxCtorInitializer(forField(fieldDecl(HasBasicStringViewType)),
166 withInitializer(cxxConstructExpr(
167 BasicStringViewConstructingFromNullExpr,
168 unless(isListInitialization())))),
169 remove(node("null_arg_expr")), ConstructionWarning);
170
171 // `class C { std::string_view sv; C() : sv{null_arg_expr} {} };`
172 auto HandleConstructorDirectListInitialization =
173 makeRule(cxxCtorInitializer(forField(fieldDecl(HasBasicStringViewType)),
174 withInitializer(cxxConstructExpr(
175 BasicStringViewConstructingFromNullExpr,
176 isListInitialization()))),
177 remove(node("null_arg_expr")), ConstructionWarning);
178
179 // `void f(std::string_view sv = null_arg_expr);`
180 auto HandleDefaultArgumentCopyInitialization =
181 makeRule(parmVarDecl(HasBasicStringViewType,
182 hasInitializer(ignoringImpCasts(cxxConstructExpr(
183 BasicStringViewConstructingFromNullExpr,
184 unless(isListInitialization()))))),
185 changeTo(node("null_arg_expr"), cat("{}")), ConstructionWarning);
186
187 // `void f(std::string_view sv = {null_arg_expr});`
188 auto HandleDefaultArgumentCopyListInitialization =
189 makeRule(parmVarDecl(HasBasicStringViewType,
190 hasInitializer(cxxConstructExpr(
191 BasicStringViewConstructingFromNullExpr,
192 isListInitialization()))),
193 remove(node("null_arg_expr")), ConstructionWarning);
194
195 // `new std::string_view(null_arg_expr)`
196 auto HandleHeapDirectInitialization = makeRule(
197 cxxNewExpr(has(cxxConstructExpr(BasicStringViewConstructingFromNullExpr,
198 unless(isListInitialization()))),
199 unless(isArray()), unless(hasAnyPlacementArg(anything()))),
200 remove(node("null_arg_expr")), ConstructionWarning);
201
202 // `new std::string_view{null_arg_expr}`
203 auto HandleHeapDirectListInitialization = makeRule(
204 cxxNewExpr(has(cxxConstructExpr(BasicStringViewConstructingFromNullExpr,
205 isListInitialization())),
206 unless(isArray()), unless(hasAnyPlacementArg(anything()))),
207 remove(node("null_arg_expr")), ConstructionWarning);
208
209 // `function(null_arg_expr)`
210 auto HandleFunctionArgumentInitialization =
211 makeRule(callExpr(hasAnyArgument(ignoringImpCasts(
212 BasicStringViewConstructingFromNullExpr)),
213 unless(cxxOperatorCallExpr())),
214 changeTo(node("construct_expr"), cat("\"\"")),
215 ArgumentConstructionWarning);
216
217 // `sv = null_arg_expr`
218 auto HandleAssignment = makeRule(
219 cxxOperatorCallExpr(hasOverloadedOperatorName("="),
220 hasRHS(materializeTemporaryExpr(
221 has(BasicStringViewConstructingFromNullExpr)))),
222 changeTo(node("construct_expr"), cat("{}")), AssignmentWarning);
223
224 // `sv < null_arg_expr`
225 auto HandleRelativeComparison = makeRule(
226 cxxOperatorCallExpr(hasAnyOverloadedOperatorName("<", "<=", ">", ">="),
227 hasEitherOperand(ignoringImpCasts(
228 BasicStringViewConstructingFromNullExpr))),
229 changeTo(node("construct_expr"), cat("\"\"")), RelativeComparisonWarning);
230
231 // `sv == null_arg_expr`
232 auto HandleEmptyEqualityComparison = makeRule(
233 cxxOperatorCallExpr(
234 hasOverloadedOperatorName("=="),
235 hasOperands(ignoringImpCasts(BasicStringViewConstructingFromNullExpr),
236 traverse(clang::TK_IgnoreUnlessSpelledInSource,
237 expr().bind("instance"))))
238 .bind("root"),
239 changeTo(node("root"), cat(access("instance", cat("empty")), "()")),
240 EqualityComparisonWarning);
241
242 // `sv != null_arg_expr`
243 auto HandleNonEmptyEqualityComparison = makeRule(
244 cxxOperatorCallExpr(
245 hasOverloadedOperatorName("!="),
246 hasOperands(ignoringImpCasts(BasicStringViewConstructingFromNullExpr),
247 traverse(clang::TK_IgnoreUnlessSpelledInSource,
248 expr().bind("instance"))))
249 .bind("root"),
250 changeTo(node("root"), cat("!", access("instance", cat("empty")), "()")),
251 EqualityComparisonWarning);
252
253 // `return null_arg_expr;`
254 auto HandleReturnStatement = makeRule(
255 returnStmt(hasReturnValue(
256 ignoringImpCasts(BasicStringViewConstructingFromNullExpr))),
257 changeTo(node("construct_expr"), cat("{}")), ConstructionWarning);
258
259 // `T(null_arg_expr)`
260 auto HandleConstructorInvocation =
261 makeRule(cxxConstructExpr(
262 hasAnyArgument(/* `hasArgument` would skip over parens */
263 ignoringImpCasts(
264 BasicStringViewConstructingFromNullExpr)),
265 unless(HasBasicStringViewType)),
266 changeTo(node("construct_expr"), cat("\"\"")),
267 ArgumentConstructionWarning);
268
269 return applyFirst(
270 {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
299
300} // namespace clang::tidy::bugprone
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
StringviewNullptrCheck(StringRef Name, ClangTidyContext *Context)
TransformerClangTidyCheck(StringRef Name, ClangTidyContext *Context)
static RewriteRuleWith< std::string > stringviewNullptrCheckImpl()
AST_MATCHER_P(Stmt, isStatementIdenticalToBoundNode, std::string, ID)
AST_MATCHER(BinaryOperator, isRelationalOperator)