xref: /llvm-project/clang-tools-extra/clang-tidy/bugprone/StringviewNullptrCheck.cpp (revision 7d2ea6c422d3f5712b7253407005e1a465a76946)
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::tidy::bugprone {
22 
23 using namespace ::clang::ast_matchers;
24 using namespace ::clang::transformer;
25 
26 namespace {
27 
AST_MATCHER_P(InitListExpr,initCountIs,unsigned,N)28 AST_MATCHER_P(InitListExpr, initCountIs, unsigned, N) {
29   return Node.getNumInits() == N;
30 }
31 
AST_MATCHER(clang::VarDecl,isDirectInitialization)32 AST_MATCHER(clang::VarDecl, isDirectInitialization) {
33   return Node.getInitStyle() != clang::VarDecl::InitializationStyle::CInit;
34 }
35 
36 } // namespace
37 
StringviewNullptrCheckImpl()38 RewriteRuleWith<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 
StringviewNullptrCheck(StringRef Name,ClangTidyContext * Context)298 StringviewNullptrCheck::StringviewNullptrCheck(StringRef Name,
299                                                ClangTidyContext *Context)
300     : utils::TransformerClangTidyCheck(StringviewNullptrCheckImpl(), Name,
301                                        Context) {}
302 
303 } // namespace clang::tidy::bugprone
304