xref: /llvm-project/clang-tools-extra/clang-tidy/bugprone/SuspiciousStringviewDataUsageCheck.cpp (revision 28c1279db3541bc7b246fd9da0da3da4e09f0eed)
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 
SuspiciousStringviewDataUsageCheck(StringRef Name,ClangTidyContext * Context)19 SuspiciousStringviewDataUsageCheck::SuspiciousStringviewDataUsageCheck(
20     StringRef Name, ClangTidyContext *Context)
21     : ClangTidyCheck(Name, Context),
22       StringViewTypes(utils::options::parseStringList(Options.get(
23           "StringViewTypes", "::std::basic_string_view;::llvm::StringRef"))),
24       AllowedCallees(
25           utils::options::parseStringList(Options.get("AllowedCallees", ""))) {}
26 
storeOptions(ClangTidyOptions::OptionMap & Opts)27 void SuspiciousStringviewDataUsageCheck::storeOptions(
28     ClangTidyOptions::OptionMap &Opts) {
29   Options.store(Opts, "StringViewTypes",
30                 utils::options::serializeStringList(StringViewTypes));
31   Options.store(Opts, "AllowedCallees",
32                 utils::options::serializeStringList(AllowedCallees));
33 }
34 
isLanguageVersionSupported(const LangOptions & LangOpts) const35 bool SuspiciousStringviewDataUsageCheck::isLanguageVersionSupported(
36     const LangOptions &LangOpts) const {
37   return LangOpts.CPlusPlus;
38 }
39 
40 std::optional<TraversalKind>
getCheckTraversalKind() const41 SuspiciousStringviewDataUsageCheck::getCheckTraversalKind() const {
42   return TK_AsIs;
43 }
44 
registerMatchers(MatchFinder * Finder)45 void SuspiciousStringviewDataUsageCheck::registerMatchers(MatchFinder *Finder) {
46 
47   auto AncestorCall = anyOf(
48       cxxConstructExpr(), callExpr(unless(cxxOperatorCallExpr())), lambdaExpr(),
49       initListExpr(
50           hasType(qualType(hasCanonicalType(hasDeclaration(recordDecl()))))));
51 
52   auto DataMethod =
53       cxxMethodDecl(hasName("data"),
54                     ofClass(matchers::matchesAnyListedName(StringViewTypes)));
55 
56   auto SizeCall = cxxMemberCallExpr(
57       callee(cxxMethodDecl(hasAnyName("size", "length"))),
58       on(ignoringParenImpCasts(
59           matchers::isStatementIdenticalToBoundNode("self"))));
60 
61   auto DescendantSizeCall = expr(hasDescendant(
62       expr(SizeCall, hasAncestor(expr(AncestorCall).bind("ancestor-size")),
63            hasAncestor(expr(equalsBoundNode("parent"),
64                             equalsBoundNode("ancestor-size"))))));
65 
66   Finder->addMatcher(
67       cxxMemberCallExpr(
68           on(ignoringParenImpCasts(expr().bind("self"))), callee(DataMethod),
69           expr().bind("data-call"),
70           hasParent(expr(anyOf(
71               invocation(
72                   expr().bind("parent"), unless(cxxOperatorCallExpr()),
73                   hasAnyArgument(
74                       ignoringParenImpCasts(equalsBoundNode("data-call"))),
75                   unless(hasAnyArgument(ignoringParenImpCasts(SizeCall))),
76                   unless(hasAnyArgument(DescendantSizeCall)),
77                   hasDeclaration(namedDecl(
78                       unless(matchers::matchesAnyListedName(AllowedCallees))))),
79               initListExpr(expr().bind("parent"),
80                            hasType(qualType(hasCanonicalType(hasDeclaration(
81                                recordDecl(unless(matchers::matchesAnyListedName(
82                                    AllowedCallees))))))),
83                            unless(DescendantSizeCall)))))),
84       this);
85 }
86 
check(const MatchFinder::MatchResult & Result)87 void SuspiciousStringviewDataUsageCheck::check(
88     const MatchFinder::MatchResult &Result) {
89   const auto *DataCallExpr =
90       Result.Nodes.getNodeAs<CXXMemberCallExpr>("data-call");
91   diag(DataCallExpr->getExprLoc(),
92        "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