clang-tools  15.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 
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 
30 AST_MATCHER_P(InitListExpr, initCountIs, unsigned, N) {
31  return Node.getNumInits() == N;
32 }
33 
34 AST_MATCHER(clang::VarDecl, isDirectInitialization) {
35  return Node.getInitStyle() != clang::VarDecl::InitializationStyle::CInit;
36 }
37 
38 } // namespace
39 
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 
301  ClangTidyContext *Context)
302  : utils::TransformerClangTidyCheck(StringviewNullptrCheckImpl(), Name,
303  Context) {}
304 
305 } // namespace bugprone
306 } // namespace tidy
307 } // namespace clang
clang::tidy::bugprone::StringviewNullptrCheck::StringviewNullptrCheck
StringviewNullptrCheck(StringRef Name, ClangTidyContext *Context)
Definition: StringviewNullptrCheck.cpp:300
clang::tidy::bugprone::AST_MATCHER
AST_MATCHER(clang::VarDecl, hasConstantDeclaration)
Definition: DynamicStaticInitializersCheck.cpp:19
clang::ast_matchers
Definition: AbseilMatcher.h:14
StringviewNullptrCheck.h
lit.name
name
Definition: test/lit.cfg.py:7
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:66
Name
Token Name
Definition: MacroToEnumCheck.cpp:89
CompletionModelCodegen.node
def node(n, label, next_label)
Definition: CompletionModelCodegen.py:70
clang::tidy::bugprone::StringviewNullptrCheckImpl
RewriteRuleWith< std::string > StringviewNullptrCheckImpl()
Definition: StringviewNullptrCheck.cpp:40
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::bugprone::AST_MATCHER_P
AST_MATCHER_P(FunctionDecl, parameterCountGE, unsigned, N)
Matches functions that have at least the specified amount of parameters.
Definition: EasilySwappableParametersCheck.cpp:1888