clang-tools 24.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(VarDecl, isDirectInitialization) {
31 return Node.getInitStyle() != VarDecl::InitializationStyle::CInit;
32}
33
34} // namespace
35
36static RewriteRuleWith<std::string> stringviewNullptrCheckImpl() {
37 const auto ConstructionWarning =
38 cat("constructing basic_string_view from null is undefined; replace with "
39 "the default constructor");
40 const auto StaticCastWarning =
41 cat("casting to basic_string_view from null is undefined; replace with "
42 "the empty string");
43 const auto ArgumentConstructionWarning =
44 cat("passing null as basic_string_view is undefined; replace with the "
45 "empty string");
46 const auto AssignmentWarning =
47 cat("assignment to basic_string_view from null is undefined; replace "
48 "with the default constructor");
49 const auto RelativeComparisonWarning =
50 cat("comparing basic_string_view to null is undefined; replace with the "
51 "empty string");
52 const 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(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 const 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 const 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 const auto HandleTemporaryCXXTemporaryObjectExprAndCompoundLiteralExpr =
89 makeRule(
90 cxxTemporaryObjectExpr(cxxConstructExpr(
91 HasBasicStringViewType, argumentCountIs(1),
92 hasAnyArgument(/* `hasArgument` would skip over parens */ anyOf(
93 NullLiteral, NullInitList, EmptyInitList)),
94 has(expr().bind("null_arg_expr")))),
95 remove(node("null_arg_expr")), ConstructionWarning);
96
97 // `(std::string_view) null_arg_expr`
98 const auto HandleTemporaryCStyleCastExpr =
99 makeRule(cStyleCastExpr(hasSourceExpression(
100 BasicStringViewConstructingFromNullExpr)),
101 changeTo(node("null_arg_expr"), cat("{}")), ConstructionWarning);
102
103 // `static_cast<std::string_view>(null_arg_expr)`
104 const auto HandleTemporaryCXXStaticCastExpr =
105 makeRule(cxxStaticCastExpr(hasSourceExpression(
106 BasicStringViewConstructingFromNullExpr)),
107 changeTo(node("null_arg_expr"), cat("\"\"")), StaticCastWarning);
108
109 // `std::string_view sv = null_arg_expr;`
110 const auto HandleStackCopyInitialization =
111 makeRule(varDecl(HasBasicStringViewType,
112 hasInitializer(ignoringImpCasts(cxxConstructExpr(
113 BasicStringViewConstructingFromNullExpr,
114 unless(isListInitialization())))),
115 unless(isDirectInitialization())),
116 changeTo(node("null_arg_expr"), cat("{}")), ConstructionWarning);
117
118 // `std::string_view sv = {null_arg_expr};`
119 const auto HandleStackCopyListInitialization =
120 makeRule(varDecl(HasBasicStringViewType,
121 hasInitializer(cxxConstructExpr(
122 BasicStringViewConstructingFromNullExpr,
123 isListInitialization())),
124 unless(isDirectInitialization())),
125 remove(node("null_arg_expr")), ConstructionWarning);
126
127 // `std::string_view sv(null_arg_expr);`
128 const auto HandleStackDirectInitialization =
129 makeRule(varDecl(HasBasicStringViewType,
130 hasInitializer(cxxConstructExpr(
131 BasicStringViewConstructingFromNullExpr,
132 unless(isListInitialization()))),
133 isDirectInitialization())
134 .bind("var_decl"),
135 changeTo(node("construct_expr"), cat(name("var_decl"))),
136 ConstructionWarning);
137
138 // `std::string_view sv{null_arg_expr};`
139 const auto HandleStackDirectListInitialization =
140 makeRule(varDecl(HasBasicStringViewType,
141 hasInitializer(cxxConstructExpr(
142 BasicStringViewConstructingFromNullExpr,
143 isListInitialization())),
144 isDirectInitialization()),
145 remove(node("null_arg_expr")), ConstructionWarning);
146
147 // `struct S { std::string_view sv = null_arg_expr; };`
148 const auto HandleFieldInClassCopyInitialization = makeRule(
149 fieldDecl(HasBasicStringViewType,
150 hasInClassInitializer(ignoringImpCasts(
151 cxxConstructExpr(BasicStringViewConstructingFromNullExpr,
152 unless(isListInitialization()))))),
153 changeTo(node("null_arg_expr"), cat("{}")), ConstructionWarning);
154
155 // `struct S { std::string_view sv = {null_arg_expr}; };` and
156 // `struct S { std::string_view sv{null_arg_expr}; };`
157 const auto HandleFieldInClassCopyListAndDirectListInitialization = makeRule(
158 fieldDecl(HasBasicStringViewType,
159 hasInClassInitializer(ignoringImpCasts(
160 cxxConstructExpr(BasicStringViewConstructingFromNullExpr,
161 isListInitialization())))),
162 remove(node("null_arg_expr")), ConstructionWarning);
163
164 // `class C { std::string_view sv; C() : sv(null_arg_expr) {} };`
165 const auto HandleConstructorDirectInitialization =
166 makeRule(cxxCtorInitializer(forField(fieldDecl(HasBasicStringViewType)),
167 withInitializer(cxxConstructExpr(
168 BasicStringViewConstructingFromNullExpr,
169 unless(isListInitialization())))),
170 remove(node("null_arg_expr")), ConstructionWarning);
171
172 // `class C { std::string_view sv; C() : sv{null_arg_expr} {} };`
173 const auto HandleConstructorDirectListInitialization =
174 makeRule(cxxCtorInitializer(forField(fieldDecl(HasBasicStringViewType)),
175 withInitializer(cxxConstructExpr(
176 BasicStringViewConstructingFromNullExpr,
177 isListInitialization()))),
178 remove(node("null_arg_expr")), ConstructionWarning);
179
180 // `void f(std::string_view sv = null_arg_expr);`
181 const auto HandleDefaultArgumentCopyInitialization =
182 makeRule(parmVarDecl(HasBasicStringViewType,
183 hasInitializer(ignoringImpCasts(cxxConstructExpr(
184 BasicStringViewConstructingFromNullExpr,
185 unless(isListInitialization()))))),
186 changeTo(node("null_arg_expr"), cat("{}")), ConstructionWarning);
187
188 // `void f(std::string_view sv = {null_arg_expr});`
189 const auto HandleDefaultArgumentCopyListInitialization =
190 makeRule(parmVarDecl(HasBasicStringViewType,
191 hasInitializer(cxxConstructExpr(
192 BasicStringViewConstructingFromNullExpr,
193 isListInitialization()))),
194 remove(node("null_arg_expr")), ConstructionWarning);
195
196 // `new std::string_view(null_arg_expr)`
197 const auto HandleHeapDirectInitialization = makeRule(
198 cxxNewExpr(has(cxxConstructExpr(BasicStringViewConstructingFromNullExpr,
199 unless(isListInitialization()))),
200 unless(isArray()), unless(hasAnyPlacementArg(anything()))),
201 remove(node("null_arg_expr")), ConstructionWarning);
202
203 // `new std::string_view{null_arg_expr}`
204 const auto HandleHeapDirectListInitialization = makeRule(
205 cxxNewExpr(has(cxxConstructExpr(BasicStringViewConstructingFromNullExpr,
206 isListInitialization())),
207 unless(isArray()), unless(hasAnyPlacementArg(anything()))),
208 remove(node("null_arg_expr")), ConstructionWarning);
209
210 // `function(null_arg_expr)`
211 const auto HandleFunctionArgumentInitialization =
212 makeRule(callExpr(hasAnyArgument(ignoringImpCasts(
213 BasicStringViewConstructingFromNullExpr)),
214 unless(cxxOperatorCallExpr())),
215 changeTo(node("construct_expr"), cat("\"\"")),
216 ArgumentConstructionWarning);
217
218 // `sv = null_arg_expr`
219 const auto HandleAssignment = makeRule(
220 cxxOperatorCallExpr(hasOverloadedOperatorName("="),
221 hasRHS(materializeTemporaryExpr(
222 has(BasicStringViewConstructingFromNullExpr)))),
223 changeTo(node("construct_expr"), cat("{}")), AssignmentWarning);
224
225 // `sv < null_arg_expr`
226 const auto HandleRelativeComparison = makeRule(
227 cxxOperatorCallExpr(hasAnyOverloadedOperatorName("<", "<=", ">", ">="),
228 hasEitherOperand(ignoringImpCasts(
229 BasicStringViewConstructingFromNullExpr))),
230 changeTo(node("construct_expr"), cat("\"\"")), RelativeComparisonWarning);
231
232 // `sv == null_arg_expr`
233 const auto HandleEmptyEqualityComparison = makeRule(
234 cxxOperatorCallExpr(
235 hasOverloadedOperatorName("=="),
236 hasOperands(ignoringImpCasts(BasicStringViewConstructingFromNullExpr),
237 traverse(TK_IgnoreUnlessSpelledInSource,
238 expr().bind("instance"))))
239 .bind("root"),
240 changeTo(node("root"), cat(access("instance", cat("empty")), "()")),
241 EqualityComparisonWarning);
242
243 // `sv != null_arg_expr`
244 const auto HandleNonEmptyEqualityComparison = makeRule(
245 cxxOperatorCallExpr(
246 hasOverloadedOperatorName("!="),
247 hasOperands(ignoringImpCasts(BasicStringViewConstructingFromNullExpr),
248 traverse(TK_IgnoreUnlessSpelledInSource,
249 expr().bind("instance"))))
250 .bind("root"),
251 changeTo(node("root"), cat("!", access("instance", cat("empty")), "()")),
252 EqualityComparisonWarning);
253
254 // `return null_arg_expr;`
255 const auto HandleReturnStatement = makeRule(
256 returnStmt(hasReturnValue(
257 ignoringImpCasts(BasicStringViewConstructingFromNullExpr))),
258 changeTo(node("construct_expr"), cat("{}")), ConstructionWarning);
259
260 // `T(null_arg_expr)`
261 const auto HandleConstructorInvocation =
262 makeRule(cxxConstructExpr(
263 hasAnyArgument(/* `hasArgument` would skip over parens */
264 ignoringImpCasts(
265 BasicStringViewConstructingFromNullExpr)),
266 unless(HasBasicStringViewType)),
267 changeTo(node("construct_expr"), cat("\"\"")),
268 ArgumentConstructionWarning);
269
270 return applyFirst(
271 {HandleTemporaryCXXFunctionalCastExpr,
272 HandleTemporaryCXXTemporaryObjectExprAndCompoundLiteralExpr,
273 HandleTemporaryCStyleCastExpr,
274 HandleTemporaryCXXStaticCastExpr,
275 HandleStackCopyInitialization,
276 HandleStackCopyListInitialization,
277 HandleStackDirectInitialization,
278 HandleStackDirectListInitialization,
279 HandleFieldInClassCopyInitialization,
280 HandleFieldInClassCopyListAndDirectListInitialization,
281 HandleConstructorDirectInitialization,
282 HandleConstructorDirectListInitialization,
283 HandleDefaultArgumentCopyInitialization,
284 HandleDefaultArgumentCopyListInitialization,
285 HandleHeapDirectInitialization,
286 HandleHeapDirectListInitialization,
287 HandleFunctionArgumentInitialization,
288 HandleAssignment,
289 HandleRelativeComparison,
290 HandleEmptyEqualityComparison,
291 HandleNonEmptyEqualityComparison,
292 HandleReturnStatement,
293 HandleConstructorInvocation});
294}
295
300
301} // 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)