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