xref: /llvm-project/clang-tools-extra/clang-tidy/bugprone/StringviewNullptrCheck.cpp (revision 7d2ea6c422d3f5712b7253407005e1a465a76946)
16a9487dfSCJ Johnson //===--- StringviewNullptrCheck.cpp - clang-tidy --------------------------===//
26a9487dfSCJ Johnson //
36a9487dfSCJ Johnson // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
46a9487dfSCJ Johnson // See https://llvm.org/LICENSE.txt for license information.
56a9487dfSCJ Johnson // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66a9487dfSCJ Johnson //
76a9487dfSCJ Johnson //===----------------------------------------------------------------------===//
86a9487dfSCJ Johnson 
96a9487dfSCJ Johnson #include "StringviewNullptrCheck.h"
106a9487dfSCJ Johnson #include "../utils/TransformerClangTidyCheck.h"
116a9487dfSCJ Johnson #include "clang/AST/ASTContext.h"
127e29da87SCJ Johnson #include "clang/AST/Decl.h"
137e29da87SCJ Johnson #include "clang/AST/OperationKinds.h"
146a9487dfSCJ Johnson #include "clang/ASTMatchers/ASTMatchFinder.h"
156a9487dfSCJ Johnson #include "clang/ASTMatchers/ASTMatchers.h"
166a9487dfSCJ Johnson #include "clang/Tooling/Transformer/RangeSelector.h"
176a9487dfSCJ Johnson #include "clang/Tooling/Transformer/RewriteRule.h"
186a9487dfSCJ Johnson #include "clang/Tooling/Transformer/Stencil.h"
196a9487dfSCJ Johnson #include "llvm/ADT/StringRef.h"
206a9487dfSCJ Johnson 
21*7d2ea6c4SCarlos Galvez namespace clang::tidy::bugprone {
226a9487dfSCJ Johnson 
236a9487dfSCJ Johnson using namespace ::clang::ast_matchers;
246a9487dfSCJ Johnson using namespace ::clang::transformer;
256a9487dfSCJ Johnson 
266a9487dfSCJ Johnson namespace {
277e29da87SCJ Johnson 
AST_MATCHER_P(InitListExpr,initCountIs,unsigned,N)286a9487dfSCJ Johnson AST_MATCHER_P(InitListExpr, initCountIs, unsigned, N) {
296a9487dfSCJ Johnson   return Node.getNumInits() == N;
306a9487dfSCJ Johnson }
317e29da87SCJ Johnson 
AST_MATCHER(clang::VarDecl,isDirectInitialization)327e29da87SCJ Johnson AST_MATCHER(clang::VarDecl, isDirectInitialization) {
337e29da87SCJ Johnson   return Node.getInitStyle() != clang::VarDecl::InitializationStyle::CInit;
347e29da87SCJ Johnson }
357e29da87SCJ Johnson 
366a9487dfSCJ Johnson } // namespace
376a9487dfSCJ Johnson 
StringviewNullptrCheckImpl()389edeceaeSEric Li RewriteRuleWith<std::string> StringviewNullptrCheckImpl() {
396a9487dfSCJ Johnson   auto construction_warning =
406a9487dfSCJ Johnson       cat("constructing basic_string_view from null is undefined; replace with "
416a9487dfSCJ Johnson           "the default constructor");
427e29da87SCJ Johnson   auto static_cast_warning =
437e29da87SCJ Johnson       cat("casting to basic_string_view from null is undefined; replace with "
447e29da87SCJ Johnson           "the empty string");
45a5684114SCJ Johnson   auto argument_construction_warning =
46a5684114SCJ Johnson       cat("passing null as basic_string_view is undefined; replace with the "
47a5684114SCJ Johnson           "empty string");
486a9487dfSCJ Johnson   auto assignment_warning =
496a9487dfSCJ Johnson       cat("assignment to basic_string_view from null is undefined; replace "
506a9487dfSCJ Johnson           "with the default constructor");
516a9487dfSCJ Johnson   auto relative_comparison_warning =
526a9487dfSCJ Johnson       cat("comparing basic_string_view to null is undefined; replace with the "
536a9487dfSCJ Johnson           "empty string");
546a9487dfSCJ Johnson   auto equality_comparison_warning =
556a9487dfSCJ Johnson       cat("comparing basic_string_view to null is undefined; replace with the "
566a9487dfSCJ Johnson           "emptiness query");
577e29da87SCJ Johnson 
587e29da87SCJ Johnson   // Matches declarations and expressions of type `basic_string_view`
597e29da87SCJ Johnson   auto HasBasicStringViewType = hasType(hasUnqualifiedDesugaredType(recordType(
607e29da87SCJ Johnson       hasDeclaration(cxxRecordDecl(hasName("::std::basic_string_view"))))));
617e29da87SCJ Johnson 
627e29da87SCJ Johnson   // Matches `nullptr` and `(nullptr)` binding to a pointer
637e29da87SCJ Johnson   auto NullLiteral = implicitCastExpr(
647e29da87SCJ Johnson       hasCastKind(clang::CK_NullToPointer),
657e29da87SCJ Johnson       hasSourceExpression(ignoringParens(cxxNullPtrLiteralExpr())));
667e29da87SCJ Johnson 
677e29da87SCJ Johnson   // Matches `{nullptr}` and `{(nullptr)}` binding to a pointer
687e29da87SCJ Johnson   auto NullInitList = initListExpr(initCountIs(1), hasInit(0, NullLiteral));
697e29da87SCJ Johnson 
707e29da87SCJ Johnson   // Matches `{}`
717e29da87SCJ Johnson   auto EmptyInitList = initListExpr(initCountIs(0));
727e29da87SCJ Johnson 
737e29da87SCJ Johnson   // Matches null construction without `basic_string_view` type spelling
747e29da87SCJ Johnson   auto BasicStringViewConstructingFromNullExpr =
756a9487dfSCJ Johnson       cxxConstructExpr(
767e29da87SCJ Johnson           HasBasicStringViewType, argumentCountIs(1),
777e29da87SCJ Johnson           hasAnyArgument(/* `hasArgument` would skip over parens */ anyOf(
787e29da87SCJ Johnson               NullLiteral, NullInitList, EmptyInitList)),
797e29da87SCJ Johnson           unless(cxxTemporaryObjectExpr(/* filters out type spellings */)),
807e29da87SCJ Johnson           has(expr().bind("null_arg_expr")))
816a9487dfSCJ Johnson           .bind("construct_expr");
826a9487dfSCJ Johnson 
837e29da87SCJ Johnson   // `std::string_view(null_arg_expr)`
846a9487dfSCJ Johnson   auto HandleTemporaryCXXFunctionalCastExpr =
857e29da87SCJ Johnson       makeRule(cxxFunctionalCastExpr(hasSourceExpression(
867e29da87SCJ Johnson                    BasicStringViewConstructingFromNullExpr)),
877e29da87SCJ Johnson                remove(node("null_arg_expr")), construction_warning);
886a9487dfSCJ Johnson 
897e29da87SCJ Johnson   // `std::string_view{null_arg_expr}` and `(std::string_view){null_arg_expr}`
907e29da87SCJ Johnson   auto HandleTemporaryCXXTemporaryObjectExprAndCompoundLiteralExpr = makeRule(
917e29da87SCJ Johnson       cxxTemporaryObjectExpr(cxxConstructExpr(
927e29da87SCJ Johnson           HasBasicStringViewType, argumentCountIs(1),
937e29da87SCJ Johnson           hasAnyArgument(/* `hasArgument` would skip over parens */ anyOf(
947e29da87SCJ Johnson               NullLiteral, NullInitList, EmptyInitList)),
957e29da87SCJ Johnson           has(expr().bind("null_arg_expr")))),
967e29da87SCJ Johnson       remove(node("null_arg_expr")), construction_warning);
976a9487dfSCJ Johnson 
987e29da87SCJ Johnson   // `(std::string_view) null_arg_expr`
996a9487dfSCJ Johnson   auto HandleTemporaryCStyleCastExpr = makeRule(
1007e29da87SCJ Johnson       cStyleCastExpr(
1017e29da87SCJ Johnson           hasSourceExpression(BasicStringViewConstructingFromNullExpr)),
1027e29da87SCJ Johnson       changeTo(node("null_arg_expr"), cat("{}")), construction_warning);
1036a9487dfSCJ Johnson 
1047e29da87SCJ Johnson   // `static_cast<std::string_view>(null_arg_expr)`
1056a9487dfSCJ Johnson   auto HandleTemporaryCXXStaticCastExpr = makeRule(
1066a9487dfSCJ Johnson       cxxStaticCastExpr(
1077e29da87SCJ Johnson           hasSourceExpression(BasicStringViewConstructingFromNullExpr)),
1087e29da87SCJ Johnson       changeTo(node("null_arg_expr"), cat("\"\"")), static_cast_warning);
1096a9487dfSCJ Johnson 
1107e29da87SCJ Johnson   // `std::string_view sv = null_arg_expr;`
1116a9487dfSCJ Johnson   auto HandleStackCopyInitialization = makeRule(
1127e29da87SCJ Johnson       varDecl(HasBasicStringViewType,
1137e29da87SCJ Johnson               hasInitializer(ignoringImpCasts(
1147e29da87SCJ Johnson                   cxxConstructExpr(BasicStringViewConstructingFromNullExpr,
1157e29da87SCJ Johnson                                    unless(isListInitialization())))),
1167e29da87SCJ Johnson               unless(isDirectInitialization())),
1177e29da87SCJ Johnson       changeTo(node("null_arg_expr"), cat("{}")), construction_warning);
1186a9487dfSCJ Johnson 
1197e29da87SCJ Johnson   // `std::string_view sv = {null_arg_expr};`
1207e29da87SCJ Johnson   auto HandleStackCopyListInitialization =
1217e29da87SCJ Johnson       makeRule(varDecl(HasBasicStringViewType,
1227e29da87SCJ Johnson                        hasInitializer(cxxConstructExpr(
1237e29da87SCJ Johnson                            BasicStringViewConstructingFromNullExpr,
1247e29da87SCJ Johnson                            isListInitialization())),
1257e29da87SCJ Johnson                        unless(isDirectInitialization())),
1267e29da87SCJ Johnson                remove(node("null_arg_expr")), construction_warning);
1277e29da87SCJ Johnson 
1287e29da87SCJ Johnson   // `std::string_view sv(null_arg_expr);`
1296a9487dfSCJ Johnson   auto HandleStackDirectInitialization =
1307e29da87SCJ Johnson       makeRule(varDecl(HasBasicStringViewType,
1317e29da87SCJ Johnson                        hasInitializer(cxxConstructExpr(
1327e29da87SCJ Johnson                            BasicStringViewConstructingFromNullExpr,
1337e29da87SCJ Johnson                            unless(isListInitialization()))),
1347e29da87SCJ Johnson                        isDirectInitialization())
1356a9487dfSCJ Johnson                    .bind("var_decl"),
1366a9487dfSCJ Johnson                changeTo(node("construct_expr"), cat(name("var_decl"))),
1376a9487dfSCJ Johnson                construction_warning);
1386a9487dfSCJ Johnson 
1397e29da87SCJ Johnson   // `std::string_view sv{null_arg_expr};`
1407e29da87SCJ Johnson   auto HandleStackDirectListInitialization =
1417e29da87SCJ Johnson       makeRule(varDecl(HasBasicStringViewType,
1427e29da87SCJ Johnson                        hasInitializer(cxxConstructExpr(
1437e29da87SCJ Johnson                            BasicStringViewConstructingFromNullExpr,
1447e29da87SCJ Johnson                            isListInitialization())),
1457e29da87SCJ Johnson                        isDirectInitialization()),
1467e29da87SCJ Johnson                remove(node("null_arg_expr")), construction_warning);
1476a9487dfSCJ Johnson 
1487e29da87SCJ Johnson   // `struct S { std::string_view sv = null_arg_expr; };`
1497e29da87SCJ Johnson   auto HandleFieldInClassCopyInitialization = makeRule(
1507e29da87SCJ Johnson       fieldDecl(HasBasicStringViewType,
1517e29da87SCJ Johnson                 hasInClassInitializer(ignoringImpCasts(
1527e29da87SCJ Johnson                     cxxConstructExpr(BasicStringViewConstructingFromNullExpr,
1537e29da87SCJ Johnson                                      unless(isListInitialization()))))),
1547e29da87SCJ Johnson       changeTo(node("null_arg_expr"), cat("{}")), construction_warning);
1556a9487dfSCJ Johnson 
1567e29da87SCJ Johnson   // `struct S { std::string_view sv = {null_arg_expr}; };` and
1577e29da87SCJ Johnson   // `struct S { std::string_view sv{null_arg_expr}; };`
1587e29da87SCJ Johnson   auto HandleFieldInClassCopyListAndDirectListInitialization = makeRule(
1597e29da87SCJ Johnson       fieldDecl(HasBasicStringViewType,
1607e29da87SCJ Johnson                 hasInClassInitializer(ignoringImpCasts(
1617e29da87SCJ Johnson                     cxxConstructExpr(BasicStringViewConstructingFromNullExpr,
1627e29da87SCJ Johnson                                      isListInitialization())))),
1637e29da87SCJ Johnson       remove(node("null_arg_expr")), construction_warning);
1646a9487dfSCJ Johnson 
1657e29da87SCJ Johnson   // `class C { std::string_view sv; C() : sv(null_arg_expr) {} };`
1667e29da87SCJ Johnson   auto HandleConstructorDirectInitialization =
1677e29da87SCJ Johnson       makeRule(cxxCtorInitializer(forField(fieldDecl(HasBasicStringViewType)),
1687e29da87SCJ Johnson                                   withInitializer(cxxConstructExpr(
1697e29da87SCJ Johnson                                       BasicStringViewConstructingFromNullExpr,
1707e29da87SCJ Johnson                                       unless(isListInitialization())))),
1717e29da87SCJ Johnson                remove(node("null_arg_expr")), construction_warning);
1726a9487dfSCJ Johnson 
1737e29da87SCJ Johnson   // `class C { std::string_view sv; C() : sv{null_arg_expr} {} };`
1747e29da87SCJ Johnson   auto HandleConstructorDirectListInitialization =
1757e29da87SCJ Johnson       makeRule(cxxCtorInitializer(forField(fieldDecl(HasBasicStringViewType)),
1767e29da87SCJ Johnson                                   withInitializer(cxxConstructExpr(
1777e29da87SCJ Johnson                                       BasicStringViewConstructingFromNullExpr,
1787e29da87SCJ Johnson                                       isListInitialization()))),
1797e29da87SCJ Johnson                remove(node("null_arg_expr")), construction_warning);
1806a9487dfSCJ Johnson 
1817e29da87SCJ Johnson   // `void f(std::string_view sv = null_arg_expr);`
1827e29da87SCJ Johnson   auto HandleDefaultArgumentCopyInitialization = makeRule(
1837e29da87SCJ Johnson       parmVarDecl(HasBasicStringViewType,
1847e29da87SCJ Johnson                   hasInitializer(ignoringImpCasts(
1857e29da87SCJ Johnson                       cxxConstructExpr(BasicStringViewConstructingFromNullExpr,
1867e29da87SCJ Johnson                                        unless(isListInitialization()))))),
1877e29da87SCJ Johnson       changeTo(node("null_arg_expr"), cat("{}")), construction_warning);
1886a9487dfSCJ Johnson 
1897e29da87SCJ Johnson   // `void f(std::string_view sv = {null_arg_expr});`
1907e29da87SCJ Johnson   auto HandleDefaultArgumentCopyListInitialization =
1917e29da87SCJ Johnson       makeRule(parmVarDecl(HasBasicStringViewType,
1927e29da87SCJ Johnson                            hasInitializer(cxxConstructExpr(
1937e29da87SCJ Johnson                                BasicStringViewConstructingFromNullExpr,
1947e29da87SCJ Johnson                                isListInitialization()))),
1957e29da87SCJ Johnson                remove(node("null_arg_expr")), construction_warning);
1966a9487dfSCJ Johnson 
1977e29da87SCJ Johnson   // `new std::string_view(null_arg_expr)`
1987e29da87SCJ Johnson   auto HandleHeapDirectInitialization = makeRule(
1997e29da87SCJ Johnson       cxxNewExpr(has(cxxConstructExpr(BasicStringViewConstructingFromNullExpr,
2007e29da87SCJ Johnson                                       unless(isListInitialization()))),
2017e29da87SCJ Johnson                  unless(isArray()), unless(hasAnyPlacementArg(anything()))),
2027e29da87SCJ Johnson       remove(node("null_arg_expr")), construction_warning);
2037e29da87SCJ Johnson 
2047e29da87SCJ Johnson   // `new std::string_view{null_arg_expr}`
2057e29da87SCJ Johnson   auto HandleHeapDirectListInitialization = makeRule(
2067e29da87SCJ Johnson       cxxNewExpr(has(cxxConstructExpr(BasicStringViewConstructingFromNullExpr,
2077e29da87SCJ Johnson                                       isListInitialization())),
2087e29da87SCJ Johnson                  unless(isArray()), unless(hasAnyPlacementArg(anything()))),
2097e29da87SCJ Johnson       remove(node("null_arg_expr")), construction_warning);
2107e29da87SCJ Johnson 
2117e29da87SCJ Johnson   // `function(null_arg_expr)`
212a5684114SCJ Johnson   auto HandleFunctionArgumentInitialization =
213a5684114SCJ Johnson       makeRule(callExpr(hasAnyArgument(ignoringImpCasts(
214a5684114SCJ Johnson                             BasicStringViewConstructingFromNullExpr)),
2157e29da87SCJ Johnson                         unless(cxxOperatorCallExpr())),
216a5684114SCJ Johnson                changeTo(node("construct_expr"), cat("\"\"")),
217a5684114SCJ Johnson                argument_construction_warning);
2186a9487dfSCJ Johnson 
2197e29da87SCJ Johnson   // `sv = null_arg_expr`
2206a9487dfSCJ Johnson   auto HandleAssignment = makeRule(
2217e29da87SCJ Johnson       cxxOperatorCallExpr(hasOverloadedOperatorName("="),
2227e29da87SCJ Johnson                           hasRHS(materializeTemporaryExpr(
2237e29da87SCJ Johnson                               has(BasicStringViewConstructingFromNullExpr)))),
2246a9487dfSCJ Johnson       changeTo(node("construct_expr"), cat("{}")), assignment_warning);
2256a9487dfSCJ Johnson 
2267e29da87SCJ Johnson   // `sv < null_arg_expr`
2277e29da87SCJ Johnson   auto HandleRelativeComparison = makeRule(
2287e29da87SCJ Johnson       cxxOperatorCallExpr(hasAnyOverloadedOperatorName("<", "<=", ">", ">="),
2297e29da87SCJ Johnson                           hasEitherOperand(ignoringImpCasts(
2307e29da87SCJ Johnson                               BasicStringViewConstructingFromNullExpr))),
2317e29da87SCJ Johnson       changeTo(node("construct_expr"), cat("\"\"")),
2326a9487dfSCJ Johnson       relative_comparison_warning);
2336a9487dfSCJ Johnson 
2347e29da87SCJ Johnson   // `sv == null_arg_expr`
2356a9487dfSCJ Johnson   auto HandleEmptyEqualityComparison = makeRule(
2366a9487dfSCJ Johnson       cxxOperatorCallExpr(
2376a9487dfSCJ Johnson           hasOverloadedOperatorName("=="),
2387e29da87SCJ Johnson           hasOperands(ignoringImpCasts(BasicStringViewConstructingFromNullExpr),
2397e29da87SCJ Johnson                       traverse(clang::TK_IgnoreUnlessSpelledInSource,
2407e29da87SCJ Johnson                                expr().bind("instance"))))
2416a9487dfSCJ Johnson           .bind("root"),
2427e29da87SCJ Johnson       changeTo(node("root"), cat(access("instance", cat("empty")), "()")),
2436a9487dfSCJ Johnson       equality_comparison_warning);
2446a9487dfSCJ Johnson 
2457e29da87SCJ Johnson   // `sv != null_arg_expr`
2466a9487dfSCJ Johnson   auto HandleNonEmptyEqualityComparison = makeRule(
2476a9487dfSCJ Johnson       cxxOperatorCallExpr(
2486a9487dfSCJ Johnson           hasOverloadedOperatorName("!="),
2497e29da87SCJ Johnson           hasOperands(ignoringImpCasts(BasicStringViewConstructingFromNullExpr),
2507e29da87SCJ Johnson                       traverse(clang::TK_IgnoreUnlessSpelledInSource,
2517e29da87SCJ Johnson                                expr().bind("instance"))))
2526a9487dfSCJ Johnson           .bind("root"),
2537e29da87SCJ Johnson       changeTo(node("root"), cat("!", access("instance", cat("empty")), "()")),
2546a9487dfSCJ Johnson       equality_comparison_warning);
2556a9487dfSCJ Johnson 
2567e29da87SCJ Johnson   // `return null_arg_expr;`
2577e29da87SCJ Johnson   auto HandleReturnStatement = makeRule(
2587e29da87SCJ Johnson       returnStmt(hasReturnValue(
2597e29da87SCJ Johnson           ignoringImpCasts(BasicStringViewConstructingFromNullExpr))),
2607e29da87SCJ Johnson       changeTo(node("construct_expr"), cat("{}")), construction_warning);
2617e29da87SCJ Johnson 
2627e29da87SCJ Johnson   // `T(null_arg_expr)`
2637e29da87SCJ Johnson   auto HandleConstructorInvocation =
2647e29da87SCJ Johnson       makeRule(cxxConstructExpr(
2657e29da87SCJ Johnson                    hasAnyArgument(/* `hasArgument` would skip over parens */
2667e29da87SCJ Johnson                                   ignoringImpCasts(
2677e29da87SCJ Johnson                                       BasicStringViewConstructingFromNullExpr)),
2687e29da87SCJ Johnson                    unless(HasBasicStringViewType)),
2697e29da87SCJ Johnson                changeTo(node("construct_expr"), cat("\"\"")),
270a5684114SCJ Johnson                argument_construction_warning);
2717e29da87SCJ Johnson 
2726a9487dfSCJ Johnson   return applyFirst(
2736a9487dfSCJ Johnson       {HandleTemporaryCXXFunctionalCastExpr,
2746a9487dfSCJ Johnson        HandleTemporaryCXXTemporaryObjectExprAndCompoundLiteralExpr,
2757e29da87SCJ Johnson        HandleTemporaryCStyleCastExpr,
2767e29da87SCJ Johnson        HandleTemporaryCXXStaticCastExpr,
2777e29da87SCJ Johnson        HandleStackCopyInitialization,
2787e29da87SCJ Johnson        HandleStackCopyListInitialization,
2797e29da87SCJ Johnson        HandleStackDirectInitialization,
2807e29da87SCJ Johnson        HandleStackDirectListInitialization,
2817e29da87SCJ Johnson        HandleFieldInClassCopyInitialization,
2827e29da87SCJ Johnson        HandleFieldInClassCopyListAndDirectListInitialization,
2837e29da87SCJ Johnson        HandleConstructorDirectInitialization,
2847e29da87SCJ Johnson        HandleConstructorDirectListInitialization,
2857e29da87SCJ Johnson        HandleDefaultArgumentCopyInitialization,
2867e29da87SCJ Johnson        HandleDefaultArgumentCopyListInitialization,
2877e29da87SCJ Johnson        HandleHeapDirectInitialization,
2887e29da87SCJ Johnson        HandleHeapDirectListInitialization,
2896a9487dfSCJ Johnson        HandleFunctionArgumentInitialization,
2907e29da87SCJ Johnson        HandleAssignment,
2917e29da87SCJ Johnson        HandleRelativeComparison,
2927e29da87SCJ Johnson        HandleEmptyEqualityComparison,
2937e29da87SCJ Johnson        HandleNonEmptyEqualityComparison,
2947e29da87SCJ Johnson        HandleReturnStatement,
2957e29da87SCJ Johnson        HandleConstructorInvocation});
2966a9487dfSCJ Johnson }
2976a9487dfSCJ Johnson 
StringviewNullptrCheck(StringRef Name,ClangTidyContext * Context)2986a9487dfSCJ Johnson StringviewNullptrCheck::StringviewNullptrCheck(StringRef Name,
2996a9487dfSCJ Johnson                                                ClangTidyContext *Context)
3006a9487dfSCJ Johnson     : utils::TransformerClangTidyCheck(StringviewNullptrCheckImpl(), Name,
3016a9487dfSCJ Johnson                                        Context) {}
3026a9487dfSCJ Johnson 
303*7d2ea6c4SCarlos Galvez } // namespace clang::tidy::bugprone
304