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 | |