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 
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 RewriteRule 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 assignment_warning =
48       cat("assignment to basic_string_view from null is undefined; replace "
49           "with the default constructor");
50   auto relative_comparison_warning =
51       cat("comparing basic_string_view to null is undefined; replace with the "
52           "empty string");
53   auto equality_comparison_warning =
54       cat("comparing basic_string_view to null is undefined; replace with the "
55           "emptiness query");
56   auto constructor_argument_warning =
57       cat("passing null as basic_string_view is undefined; replace with the "
58           "empty string");
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 = makeRule(
215       callExpr(hasAnyArgument(
216                    ignoringImpCasts(BasicStringViewConstructingFromNullExpr)),
217                unless(cxxOperatorCallExpr())),
218       changeTo(node("construct_expr"), cat("{}")), construction_warning);
219 
220   // `sv = null_arg_expr`
221   auto HandleAssignment = makeRule(
222       cxxOperatorCallExpr(hasOverloadedOperatorName("="),
223                           hasRHS(materializeTemporaryExpr(
224                               has(BasicStringViewConstructingFromNullExpr)))),
225       changeTo(node("construct_expr"), cat("{}")), assignment_warning);
226 
227   // `sv < null_arg_expr`
228   auto HandleRelativeComparison = makeRule(
229       cxxOperatorCallExpr(hasAnyOverloadedOperatorName("<", "<=", ">", ">="),
230                           hasEitherOperand(ignoringImpCasts(
231                               BasicStringViewConstructingFromNullExpr))),
232       changeTo(node("construct_expr"), cat("\"\"")),
233       relative_comparison_warning);
234 
235   // `sv == null_arg_expr`
236   auto HandleEmptyEqualityComparison = makeRule(
237       cxxOperatorCallExpr(
238           hasOverloadedOperatorName("=="),
239           hasOperands(ignoringImpCasts(BasicStringViewConstructingFromNullExpr),
240                       traverse(clang::TK_IgnoreUnlessSpelledInSource,
241                                expr().bind("instance"))))
242           .bind("root"),
243       changeTo(node("root"), cat(access("instance", cat("empty")), "()")),
244       equality_comparison_warning);
245 
246   // `sv != null_arg_expr`
247   auto HandleNonEmptyEqualityComparison = makeRule(
248       cxxOperatorCallExpr(
249           hasOverloadedOperatorName("!="),
250           hasOperands(ignoringImpCasts(BasicStringViewConstructingFromNullExpr),
251                       traverse(clang::TK_IgnoreUnlessSpelledInSource,
252                                expr().bind("instance"))))
253           .bind("root"),
254       changeTo(node("root"), cat("!", access("instance", cat("empty")), "()")),
255       equality_comparison_warning);
256 
257   // `return null_arg_expr;`
258   auto HandleReturnStatement = makeRule(
259       returnStmt(hasReturnValue(
260           ignoringImpCasts(BasicStringViewConstructingFromNullExpr))),
261       changeTo(node("construct_expr"), cat("{}")), construction_warning);
262 
263   // `T(null_arg_expr)`
264   auto HandleConstructorInvocation =
265       makeRule(cxxConstructExpr(
266                    hasAnyArgument(/* `hasArgument` would skip over parens */
267                                   ignoringImpCasts(
268                                       BasicStringViewConstructingFromNullExpr)),
269                    unless(HasBasicStringViewType)),
270                changeTo(node("construct_expr"), cat("\"\"")),
271                constructor_argument_warning);
272 
273   return applyFirst(
274       {HandleTemporaryCXXFunctionalCastExpr,
275        HandleTemporaryCXXTemporaryObjectExprAndCompoundLiteralExpr,
276        HandleTemporaryCStyleCastExpr,
277        HandleTemporaryCXXStaticCastExpr,
278        HandleStackCopyInitialization,
279        HandleStackCopyListInitialization,
280        HandleStackDirectInitialization,
281        HandleStackDirectListInitialization,
282        HandleFieldInClassCopyInitialization,
283        HandleFieldInClassCopyListAndDirectListInitialization,
284        HandleConstructorDirectInitialization,
285        HandleConstructorDirectListInitialization,
286        HandleDefaultArgumentCopyInitialization,
287        HandleDefaultArgumentCopyListInitialization,
288        HandleHeapDirectInitialization,
289        HandleHeapDirectListInitialization,
290        HandleFunctionArgumentInitialization,
291        HandleAssignment,
292        HandleRelativeComparison,
293        HandleEmptyEqualityComparison,
294        HandleNonEmptyEqualityComparison,
295        HandleReturnStatement,
296        HandleConstructorInvocation});
297 }
298 
299 StringviewNullptrCheck::StringviewNullptrCheck(StringRef Name,
300                                                ClangTidyContext *Context)
301     : utils::TransformerClangTidyCheck(StringviewNullptrCheckImpl(), Name,
302                                        Context) {}
303 
304 } // namespace bugprone
305 } // namespace tidy
306 } // namespace clang
307