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