| 1 | //===--- SuspiciousStringviewDataUsageCheck.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 "SuspiciousStringviewDataUsageCheck.h" |
| 10 | #include "../utils/Matchers.h" |
| 11 | #include "../utils/OptionsUtils.h" |
| 12 | #include "clang/AST/ASTContext.h" |
| 13 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
| 14 | |
| 15 | using namespace clang::ast_matchers; |
| 16 | |
| 17 | namespace clang::tidy::bugprone { |
| 18 | |
| 19 | SuspiciousStringviewDataUsageCheck::SuspiciousStringviewDataUsageCheck( |
| 20 | StringRef Name, ClangTidyContext *Context) |
| 21 | : ClangTidyCheck(Name, Context), |
| 22 | StringViewTypes(utils::options::parseStringList(Option: Options.get( |
| 23 | LocalName: "StringViewTypes" , Default: "::std::basic_string_view;::llvm::StringRef" ))), |
| 24 | AllowedCallees( |
| 25 | utils::options::parseStringList(Option: Options.get(LocalName: "AllowedCallees" , Default: "" ))) {} |
| 26 | |
| 27 | void SuspiciousStringviewDataUsageCheck::storeOptions( |
| 28 | ClangTidyOptions::OptionMap &Opts) { |
| 29 | Options.store(Options&: Opts, LocalName: "StringViewTypes" , |
| 30 | Value: utils::options::serializeStringList(Strings: StringViewTypes)); |
| 31 | Options.store(Options&: Opts, LocalName: "AllowedCallees" , |
| 32 | Value: utils::options::serializeStringList(Strings: AllowedCallees)); |
| 33 | } |
| 34 | |
| 35 | bool SuspiciousStringviewDataUsageCheck::isLanguageVersionSupported( |
| 36 | const LangOptions &LangOpts) const { |
| 37 | return LangOpts.CPlusPlus; |
| 38 | } |
| 39 | |
| 40 | std::optional<TraversalKind> |
| 41 | SuspiciousStringviewDataUsageCheck::getCheckTraversalKind() const { |
| 42 | return TK_AsIs; |
| 43 | } |
| 44 | |
| 45 | void SuspiciousStringviewDataUsageCheck::registerMatchers(MatchFinder *Finder) { |
| 46 | |
| 47 | auto AncestorCall = anyOf( |
| 48 | cxxConstructExpr(), callExpr(unless(cxxOperatorCallExpr())), lambdaExpr(), |
| 49 | initListExpr( |
| 50 | hasType(InnerMatcher: qualType(hasCanonicalType(InnerMatcher: hasDeclaration(InnerMatcher: recordDecl())))))); |
| 51 | |
| 52 | auto DataMethod = |
| 53 | cxxMethodDecl(hasName(Name: "data" ), |
| 54 | ofClass(InnerMatcher: matchers::matchesAnyListedName(NameList: StringViewTypes))); |
| 55 | |
| 56 | auto SizeCall = cxxMemberCallExpr( |
| 57 | callee(InnerMatcher: cxxMethodDecl(hasAnyName("size" , "length" ))), |
| 58 | on(InnerMatcher: ignoringParenImpCasts( |
| 59 | InnerMatcher: matchers::isStatementIdenticalToBoundNode(ID: "self" )))); |
| 60 | |
| 61 | auto DescendantSizeCall = expr(hasDescendant( |
| 62 | expr(SizeCall, hasAncestor(expr(AncestorCall).bind(ID: "ancestor-size" )), |
| 63 | hasAncestor(expr(equalsBoundNode(ID: "parent" ), |
| 64 | equalsBoundNode(ID: "ancestor-size" )))))); |
| 65 | |
| 66 | Finder->addMatcher( |
| 67 | NodeMatch: cxxMemberCallExpr( |
| 68 | on(InnerMatcher: ignoringParenImpCasts(InnerMatcher: expr().bind(ID: "self" ))), callee(InnerMatcher: DataMethod), |
| 69 | expr().bind(ID: "data-call" ), |
| 70 | hasParent(expr(anyOf( |
| 71 | invocation( |
| 72 | expr().bind(ID: "parent" ), unless(cxxOperatorCallExpr()), |
| 73 | hasAnyArgument( |
| 74 | InnerMatcher: ignoringParenImpCasts(InnerMatcher: equalsBoundNode(ID: "data-call" ))), |
| 75 | unless(hasAnyArgument(InnerMatcher: ignoringParenImpCasts(InnerMatcher: SizeCall))), |
| 76 | unless(hasAnyArgument(InnerMatcher: DescendantSizeCall)), |
| 77 | hasDeclaration(InnerMatcher: namedDecl( |
| 78 | unless(matchers::matchesAnyListedName(NameList: AllowedCallees))))), |
| 79 | initListExpr(expr().bind(ID: "parent" ), |
| 80 | hasType(InnerMatcher: qualType(hasCanonicalType(InnerMatcher: hasDeclaration( |
| 81 | InnerMatcher: recordDecl(unless(matchers::matchesAnyListedName( |
| 82 | NameList: AllowedCallees))))))), |
| 83 | unless(DescendantSizeCall)))))), |
| 84 | Action: this); |
| 85 | } |
| 86 | |
| 87 | void SuspiciousStringviewDataUsageCheck::check( |
| 88 | const MatchFinder::MatchResult &Result) { |
| 89 | const auto *DataCallExpr = |
| 90 | Result.Nodes.getNodeAs<CXXMemberCallExpr>(ID: "data-call" ); |
| 91 | diag(Loc: DataCallExpr->getExprLoc(), |
| 92 | Description: "result of a `data()` call may not be null terminated, provide size " |
| 93 | "information to the callee to prevent potential issues" ) |
| 94 | << DataCallExpr->getCallee()->getSourceRange(); |
| 95 | } |
| 96 | |
| 97 | } // namespace clang::tidy::bugprone |
| 98 | |