301 lines · cpp
1//===----------------------------------------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "StringviewNullptrCheck.h"10#include "../utils/TransformerClangTidyCheck.h"11#include "clang/AST/Decl.h"12#include "clang/AST/OperationKinds.h"13#include "clang/ASTMatchers/ASTMatchers.h"14#include "clang/Tooling/Transformer/RangeSelector.h"15#include "clang/Tooling/Transformer/RewriteRule.h"16#include "clang/Tooling/Transformer/Stencil.h"17#include "llvm/ADT/StringRef.h"18 19namespace clang::tidy::bugprone {20 21using namespace ::clang::ast_matchers;22using namespace ::clang::transformer;23 24namespace {25 26AST_MATCHER_P(InitListExpr, initCountIs, unsigned, N) {27 return Node.getNumInits() == N;28}29 30AST_MATCHER(clang::VarDecl, isDirectInitialization) {31 return Node.getInitStyle() != clang::VarDecl::InitializationStyle::CInit;32}33 34} // namespace35 36static RewriteRuleWith<std::string> stringviewNullptrCheckImpl() {37 auto ConstructionWarning =38 cat("constructing basic_string_view from null is undefined; replace with "39 "the default constructor");40 auto StaticCastWarning =41 cat("casting to basic_string_view from null is undefined; replace with "42 "the empty string");43 auto ArgumentConstructionWarning =44 cat("passing null as basic_string_view is undefined; replace with the "45 "empty string");46 auto AssignmentWarning =47 cat("assignment to basic_string_view from null is undefined; replace "48 "with the default constructor");49 auto RelativeComparisonWarning =50 cat("comparing basic_string_view to null is undefined; replace with the "51 "empty string");52 auto EqualityComparisonWarning =53 cat("comparing basic_string_view to null is undefined; replace with the "54 "emptiness query");55 56 // Matches declarations and expressions of type `basic_string_view`57 auto HasBasicStringViewType = hasType(hasUnqualifiedDesugaredType(recordType(58 hasDeclaration(cxxRecordDecl(hasName("::std::basic_string_view"))))));59 60 // Matches `nullptr` and `(nullptr)` binding to a pointer61 auto NullLiteral = implicitCastExpr(62 hasCastKind(clang::CK_NullToPointer),63 hasSourceExpression(ignoringParens(cxxNullPtrLiteralExpr())));64 65 // Matches `{nullptr}` and `{(nullptr)}` binding to a pointer66 auto NullInitList = initListExpr(initCountIs(1), hasInit(0, NullLiteral));67 68 // Matches `{}`69 auto EmptyInitList = initListExpr(initCountIs(0));70 71 // Matches null construction without `basic_string_view` type spelling72 auto BasicStringViewConstructingFromNullExpr =73 cxxConstructExpr(74 HasBasicStringViewType, argumentCountIs(1),75 hasAnyArgument(/* `hasArgument` would skip over parens */ anyOf(76 NullLiteral, NullInitList, EmptyInitList)),77 unless(cxxTemporaryObjectExpr(/* filters out type spellings */)),78 has(expr().bind("null_arg_expr")))79 .bind("construct_expr");80 81 // `std::string_view(null_arg_expr)`82 auto HandleTemporaryCXXFunctionalCastExpr =83 makeRule(cxxFunctionalCastExpr(hasSourceExpression(84 BasicStringViewConstructingFromNullExpr)),85 remove(node("null_arg_expr")), ConstructionWarning);86 87 // `std::string_view{null_arg_expr}` and `(std::string_view){null_arg_expr}`88 auto HandleTemporaryCXXTemporaryObjectExprAndCompoundLiteralExpr = makeRule(89 cxxTemporaryObjectExpr(cxxConstructExpr(90 HasBasicStringViewType, argumentCountIs(1),91 hasAnyArgument(/* `hasArgument` would skip over parens */ anyOf(92 NullLiteral, NullInitList, EmptyInitList)),93 has(expr().bind("null_arg_expr")))),94 remove(node("null_arg_expr")), ConstructionWarning);95 96 // `(std::string_view) null_arg_expr`97 auto HandleTemporaryCStyleCastExpr =98 makeRule(cStyleCastExpr(hasSourceExpression(99 BasicStringViewConstructingFromNullExpr)),100 changeTo(node("null_arg_expr"), cat("{}")), ConstructionWarning);101 102 // `static_cast<std::string_view>(null_arg_expr)`103 auto HandleTemporaryCXXStaticCastExpr =104 makeRule(cxxStaticCastExpr(hasSourceExpression(105 BasicStringViewConstructingFromNullExpr)),106 changeTo(node("null_arg_expr"), cat("\"\"")), StaticCastWarning);107 108 // `std::string_view sv = null_arg_expr;`109 auto HandleStackCopyInitialization =110 makeRule(varDecl(HasBasicStringViewType,111 hasInitializer(ignoringImpCasts(cxxConstructExpr(112 BasicStringViewConstructingFromNullExpr,113 unless(isListInitialization())))),114 unless(isDirectInitialization())),115 changeTo(node("null_arg_expr"), cat("{}")), ConstructionWarning);116 117 // `std::string_view sv = {null_arg_expr};`118 auto HandleStackCopyListInitialization =119 makeRule(varDecl(HasBasicStringViewType,120 hasInitializer(cxxConstructExpr(121 BasicStringViewConstructingFromNullExpr,122 isListInitialization())),123 unless(isDirectInitialization())),124 remove(node("null_arg_expr")), ConstructionWarning);125 126 // `std::string_view sv(null_arg_expr);`127 auto HandleStackDirectInitialization =128 makeRule(varDecl(HasBasicStringViewType,129 hasInitializer(cxxConstructExpr(130 BasicStringViewConstructingFromNullExpr,131 unless(isListInitialization()))),132 isDirectInitialization())133 .bind("var_decl"),134 changeTo(node("construct_expr"), cat(name("var_decl"))),135 ConstructionWarning);136 137 // `std::string_view sv{null_arg_expr};`138 auto HandleStackDirectListInitialization =139 makeRule(varDecl(HasBasicStringViewType,140 hasInitializer(cxxConstructExpr(141 BasicStringViewConstructingFromNullExpr,142 isListInitialization())),143 isDirectInitialization()),144 remove(node("null_arg_expr")), ConstructionWarning);145 146 // `struct S { std::string_view sv = null_arg_expr; };`147 auto HandleFieldInClassCopyInitialization = makeRule(148 fieldDecl(HasBasicStringViewType,149 hasInClassInitializer(ignoringImpCasts(150 cxxConstructExpr(BasicStringViewConstructingFromNullExpr,151 unless(isListInitialization()))))),152 changeTo(node("null_arg_expr"), cat("{}")), ConstructionWarning);153 154 // `struct S { std::string_view sv = {null_arg_expr}; };` and155 // `struct S { std::string_view sv{null_arg_expr}; };`156 auto HandleFieldInClassCopyListAndDirectListInitialization = makeRule(157 fieldDecl(HasBasicStringViewType,158 hasInClassInitializer(ignoringImpCasts(159 cxxConstructExpr(BasicStringViewConstructingFromNullExpr,160 isListInitialization())))),161 remove(node("null_arg_expr")), ConstructionWarning);162 163 // `class C { std::string_view sv; C() : sv(null_arg_expr) {} };`164 auto HandleConstructorDirectInitialization =165 makeRule(cxxCtorInitializer(forField(fieldDecl(HasBasicStringViewType)),166 withInitializer(cxxConstructExpr(167 BasicStringViewConstructingFromNullExpr,168 unless(isListInitialization())))),169 remove(node("null_arg_expr")), ConstructionWarning);170 171 // `class C { std::string_view sv; C() : sv{null_arg_expr} {} };`172 auto HandleConstructorDirectListInitialization =173 makeRule(cxxCtorInitializer(forField(fieldDecl(HasBasicStringViewType)),174 withInitializer(cxxConstructExpr(175 BasicStringViewConstructingFromNullExpr,176 isListInitialization()))),177 remove(node("null_arg_expr")), ConstructionWarning);178 179 // `void f(std::string_view sv = null_arg_expr);`180 auto HandleDefaultArgumentCopyInitialization =181 makeRule(parmVarDecl(HasBasicStringViewType,182 hasInitializer(ignoringImpCasts(cxxConstructExpr(183 BasicStringViewConstructingFromNullExpr,184 unless(isListInitialization()))))),185 changeTo(node("null_arg_expr"), cat("{}")), ConstructionWarning);186 187 // `void f(std::string_view sv = {null_arg_expr});`188 auto HandleDefaultArgumentCopyListInitialization =189 makeRule(parmVarDecl(HasBasicStringViewType,190 hasInitializer(cxxConstructExpr(191 BasicStringViewConstructingFromNullExpr,192 isListInitialization()))),193 remove(node("null_arg_expr")), ConstructionWarning);194 195 // `new std::string_view(null_arg_expr)`196 auto HandleHeapDirectInitialization = makeRule(197 cxxNewExpr(has(cxxConstructExpr(BasicStringViewConstructingFromNullExpr,198 unless(isListInitialization()))),199 unless(isArray()), unless(hasAnyPlacementArg(anything()))),200 remove(node("null_arg_expr")), ConstructionWarning);201 202 // `new std::string_view{null_arg_expr}`203 auto HandleHeapDirectListInitialization = makeRule(204 cxxNewExpr(has(cxxConstructExpr(BasicStringViewConstructingFromNullExpr,205 isListInitialization())),206 unless(isArray()), unless(hasAnyPlacementArg(anything()))),207 remove(node("null_arg_expr")), ConstructionWarning);208 209 // `function(null_arg_expr)`210 auto HandleFunctionArgumentInitialization =211 makeRule(callExpr(hasAnyArgument(ignoringImpCasts(212 BasicStringViewConstructingFromNullExpr)),213 unless(cxxOperatorCallExpr())),214 changeTo(node("construct_expr"), cat("\"\"")),215 ArgumentConstructionWarning);216 217 // `sv = null_arg_expr`218 auto HandleAssignment = makeRule(219 cxxOperatorCallExpr(hasOverloadedOperatorName("="),220 hasRHS(materializeTemporaryExpr(221 has(BasicStringViewConstructingFromNullExpr)))),222 changeTo(node("construct_expr"), cat("{}")), AssignmentWarning);223 224 // `sv < null_arg_expr`225 auto HandleRelativeComparison = makeRule(226 cxxOperatorCallExpr(hasAnyOverloadedOperatorName("<", "<=", ">", ">="),227 hasEitherOperand(ignoringImpCasts(228 BasicStringViewConstructingFromNullExpr))),229 changeTo(node("construct_expr"), cat("\"\"")), RelativeComparisonWarning);230 231 // `sv == null_arg_expr`232 auto HandleEmptyEqualityComparison = makeRule(233 cxxOperatorCallExpr(234 hasOverloadedOperatorName("=="),235 hasOperands(ignoringImpCasts(BasicStringViewConstructingFromNullExpr),236 traverse(clang::TK_IgnoreUnlessSpelledInSource,237 expr().bind("instance"))))238 .bind("root"),239 changeTo(node("root"), cat(access("instance", cat("empty")), "()")),240 EqualityComparisonWarning);241 242 // `sv != null_arg_expr`243 auto HandleNonEmptyEqualityComparison = makeRule(244 cxxOperatorCallExpr(245 hasOverloadedOperatorName("!="),246 hasOperands(ignoringImpCasts(BasicStringViewConstructingFromNullExpr),247 traverse(clang::TK_IgnoreUnlessSpelledInSource,248 expr().bind("instance"))))249 .bind("root"),250 changeTo(node("root"), cat("!", access("instance", cat("empty")), "()")),251 EqualityComparisonWarning);252 253 // `return null_arg_expr;`254 auto HandleReturnStatement = makeRule(255 returnStmt(hasReturnValue(256 ignoringImpCasts(BasicStringViewConstructingFromNullExpr))),257 changeTo(node("construct_expr"), cat("{}")), ConstructionWarning);258 259 // `T(null_arg_expr)`260 auto HandleConstructorInvocation =261 makeRule(cxxConstructExpr(262 hasAnyArgument(/* `hasArgument` would skip over parens */263 ignoringImpCasts(264 BasicStringViewConstructingFromNullExpr)),265 unless(HasBasicStringViewType)),266 changeTo(node("construct_expr"), cat("\"\"")),267 ArgumentConstructionWarning);268 269 return applyFirst(270 {HandleTemporaryCXXFunctionalCastExpr,271 HandleTemporaryCXXTemporaryObjectExprAndCompoundLiteralExpr,272 HandleTemporaryCStyleCastExpr,273 HandleTemporaryCXXStaticCastExpr,274 HandleStackCopyInitialization,275 HandleStackCopyListInitialization,276 HandleStackDirectInitialization,277 HandleStackDirectListInitialization,278 HandleFieldInClassCopyInitialization,279 HandleFieldInClassCopyListAndDirectListInitialization,280 HandleConstructorDirectInitialization,281 HandleConstructorDirectListInitialization,282 HandleDefaultArgumentCopyInitialization,283 HandleDefaultArgumentCopyListInitialization,284 HandleHeapDirectInitialization,285 HandleHeapDirectListInitialization,286 HandleFunctionArgumentInitialization,287 HandleAssignment,288 HandleRelativeComparison,289 HandleEmptyEqualityComparison,290 HandleNonEmptyEqualityComparison,291 HandleReturnStatement,292 HandleConstructorInvocation});293}294 295StringviewNullptrCheck::StringviewNullptrCheck(StringRef Name,296 ClangTidyContext *Context)297 : utils::TransformerClangTidyCheck(stringviewNullptrCheckImpl(), Name,298 Context) {}299 300} // namespace clang::tidy::bugprone301