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