//===--- SuspiciousStringviewDataUsageCheck.cpp - clang-tidy --------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "SuspiciousStringviewDataUsageCheck.h" #include "../utils/Matchers.h" #include "../utils/OptionsUtils.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" using namespace clang::ast_matchers; namespace clang::tidy::bugprone { SuspiciousStringviewDataUsageCheck::SuspiciousStringviewDataUsageCheck( StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), StringViewTypes(utils::options::parseStringList(Options.get( "StringViewTypes", "::std::basic_string_view;::llvm::StringRef"))), AllowedCallees( utils::options::parseStringList(Options.get("AllowedCallees", ""))) {} void SuspiciousStringviewDataUsageCheck::storeOptions( ClangTidyOptions::OptionMap &Opts) { Options.store(Opts, "StringViewTypes", utils::options::serializeStringList(StringViewTypes)); Options.store(Opts, "AllowedCallees", utils::options::serializeStringList(AllowedCallees)); } bool SuspiciousStringviewDataUsageCheck::isLanguageVersionSupported( const LangOptions &LangOpts) const { return LangOpts.CPlusPlus; } std::optional SuspiciousStringviewDataUsageCheck::getCheckTraversalKind() const { return TK_AsIs; } void SuspiciousStringviewDataUsageCheck::registerMatchers(MatchFinder *Finder) { auto AncestorCall = anyOf( cxxConstructExpr(), callExpr(unless(cxxOperatorCallExpr())), lambdaExpr(), initListExpr( hasType(qualType(hasCanonicalType(hasDeclaration(recordDecl())))))); auto DataMethod = cxxMethodDecl(hasName("data"), ofClass(matchers::matchesAnyListedName(StringViewTypes))); auto SizeCall = cxxMemberCallExpr( callee(cxxMethodDecl(hasAnyName("size", "length"))), on(ignoringParenImpCasts( matchers::isStatementIdenticalToBoundNode("self")))); auto DescendantSizeCall = expr(hasDescendant( expr(SizeCall, hasAncestor(expr(AncestorCall).bind("ancestor-size")), hasAncestor(expr(equalsBoundNode("parent"), equalsBoundNode("ancestor-size")))))); Finder->addMatcher( cxxMemberCallExpr( on(ignoringParenImpCasts(expr().bind("self"))), callee(DataMethod), expr().bind("data-call"), hasParent(expr(anyOf( invocation( expr().bind("parent"), unless(cxxOperatorCallExpr()), hasAnyArgument( ignoringParenImpCasts(equalsBoundNode("data-call"))), unless(hasAnyArgument(ignoringParenImpCasts(SizeCall))), unless(hasAnyArgument(DescendantSizeCall)), hasDeclaration(namedDecl( unless(matchers::matchesAnyListedName(AllowedCallees))))), initListExpr(expr().bind("parent"), hasType(qualType(hasCanonicalType(hasDeclaration( recordDecl(unless(matchers::matchesAnyListedName( AllowedCallees))))))), unless(DescendantSizeCall)))))), this); } void SuspiciousStringviewDataUsageCheck::check( const MatchFinder::MatchResult &Result) { const auto *DataCallExpr = Result.Nodes.getNodeAs("data-call"); diag(DataCallExpr->getExprLoc(), "result of a `data()` call may not be null terminated, provide size " "information to the callee to prevent potential issues") << DataCallExpr->getCallee()->getSourceRange(); } } // namespace clang::tidy::bugprone