clang 19.0.0git
CallDescription.cpp
Go to the documentation of this file.
1//===- CallDescription.cpp - function/method call matching --*- C++ -*-===//
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/// \file This file defines a generic mechanism for matching for function and
10/// method calls of C, C++, and Objective-C languages. Instances of these
11/// classes are frequently used together with the CallEvent classes.
12//
13//===----------------------------------------------------------------------===//
14
16#include "clang/AST/Decl.h"
19#include "llvm/ADT/ArrayRef.h"
20#include <iterator>
21#include <optional>
22
23using namespace llvm;
24using namespace clang;
25
26using MaybeCount = std::optional<unsigned>;
27
28// A constructor helper.
30 MaybeCount RequiredParams) {
31 if (RequiredParams)
32 return RequiredParams;
33 if (RequiredArgs)
34 return RequiredArgs;
35 return std::nullopt;
36}
37
39 ArrayRef<StringRef> QualifiedName,
40 MaybeCount RequiredArgs /*= None*/,
41 MaybeCount RequiredParams /*= None*/)
42 : RequiredArgs(RequiredArgs),
43 RequiredParams(readRequiredParams(RequiredArgs, RequiredParams)),
44 MatchAs(MatchAs) {
45 assert(!QualifiedName.empty());
46 this->QualifiedName.reserve(QualifiedName.size());
47 llvm::transform(QualifiedName, std::back_inserter(this->QualifiedName),
48 [](StringRef From) { return From.str(); });
49}
50
51/// Construct a CallDescription with default flags.
53 MaybeCount RequiredArgs /*= None*/,
54 MaybeCount RequiredParams /*= None*/)
55 : CallDescription(Mode::Unspecified, QualifiedName, RequiredArgs,
56 RequiredParams) {}
57
59 // FIXME: Add ObjC Message support.
60 if (Call.getKind() == CE_ObjCMessage)
61 return false;
62
63 const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
64 if (!FD)
65 return false;
66
67 return matchesImpl(FD, Call.getNumArgs(), Call.parameters().size());
68}
69
71 const auto *FD = dyn_cast_or_null<FunctionDecl>(CE.getCalleeDecl());
72 if (!FD)
73 return false;
74
75 return matchesImpl(FD, CE.getNumArgs(), FD->param_size());
76}
77
78bool ento::CallDescription::matchNameOnly(const NamedDecl *ND) const {
79 DeclarationName Name = ND->getDeclName();
80 if (const auto *NameII = Name.getAsIdentifierInfo()) {
81 if (!II)
82 II = &ND->getASTContext().Idents.get(getFunctionName());
83
84 return NameII == *II; // Fast case.
85 }
86
87 // Fallback to the slow stringification and comparison for:
88 // C++ overloaded operators, constructors, destructors, etc.
89 // FIXME This comparison is way SLOWER than comparing pointers.
90 // At some point in the future, we should compare FunctionDecl pointers.
91 return Name.getAsString() == getFunctionName();
92}
93
94bool ento::CallDescription::matchQualifiedNameParts(const Decl *D) const {
95 const auto FindNextNamespaceOrRecord =
96 [](const DeclContext *Ctx) -> const DeclContext * {
97 while (Ctx && !isa<NamespaceDecl, RecordDecl>(Ctx))
98 Ctx = Ctx->getParent();
99 return Ctx;
100 };
101
102 auto QualifierPartsIt = begin_qualified_name_parts();
103 const auto QualifierPartsEndIt = end_qualified_name_parts();
104
105 // Match namespace and record names. Skip unrelated names if they don't
106 // match.
107 const DeclContext *Ctx = FindNextNamespaceOrRecord(D->getDeclContext());
108 for (; Ctx && QualifierPartsIt != QualifierPartsEndIt;
109 Ctx = FindNextNamespaceOrRecord(Ctx->getParent())) {
110 // If not matched just continue and try matching for the next one.
111 if (cast<NamedDecl>(Ctx)->getName() != *QualifierPartsIt)
112 continue;
113 ++QualifierPartsIt;
114 }
115
116 // We matched if we consumed all expected qualifier segments.
117 return QualifierPartsIt == QualifierPartsEndIt;
118}
119
120bool ento::CallDescription::matchesImpl(const FunctionDecl *FD, size_t ArgCount,
121 size_t ParamCount) const {
122 if (!FD)
123 return false;
124
125 const bool isMethod = isa<CXXMethodDecl>(FD);
126
127 if (MatchAs == Mode::SimpleFunc && isMethod)
128 return false;
129
130 if (MatchAs == Mode::CXXMethod && !isMethod)
131 return false;
132
133 if (MatchAs == Mode::CLibraryMaybeHardened) {
134 // In addition to accepting FOO() with CLibrary rules, we also want to
135 // accept calls to __FOO_chk() and __builtin___FOO_chk().
137 CheckerContext::isHardenedVariantOf(FD, getFunctionName())) {
138 // Check that the actual argument/parameter counts are greater or equal
139 // to the required counts. (Setting a requirement to std::nullopt matches
140 // anything, so in that case value_or ensures that the value is compared
141 // with itself.)
142 return (RequiredArgs.value_or(ArgCount) <= ArgCount &&
143 RequiredParams.value_or(ParamCount) <= ParamCount);
144 }
145 }
146
147 if (RequiredArgs.value_or(ArgCount) != ArgCount ||
148 RequiredParams.value_or(ParamCount) != ParamCount)
149 return false;
150
151 if (MatchAs == Mode::CLibrary || MatchAs == Mode::CLibraryMaybeHardened)
152 return CheckerContext::isCLibraryFunction(FD, getFunctionName());
153
154 if (!matchNameOnly(FD))
155 return false;
156
157 if (!hasQualifiedNameParts())
158 return true;
159
160 return matchQualifiedNameParts(FD);
161}
162
164 std::initializer_list<CallDescription> &&List) {
165 Impl.LinearMap.reserve(List.size());
166 for (const CallDescription &CD : List)
167 Impl.LinearMap.push_back({CD, /*unused*/ true});
168}
169
171 return static_cast<bool>(Impl.lookup(Call));
172}
173
175 return static_cast<bool>(Impl.lookupAsWritten(CE));
176}
static MaybeCount readRequiredParams(MaybeCount RequiredArgs, MaybeCount RequiredParams)
std::optional< unsigned > MaybeCount
IdentifierTable & Idents
Definition: ASTContext.h:644
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2820
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
Definition: Expr.h:2998
Decl * getCalleeDecl()
Definition: Expr.h:2984
DeclContext - This is used only as base class of specific decl types that can act as declaration cont...
Definition: DeclBase.h:1438
DeclContext * getParent()
getParent - Returns the containing DeclContext.
Definition: DeclBase.h:2068
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:85
ASTContext & getASTContext() const LLVM_READONLY
Definition: DeclBase.cpp:501
DeclContext * getDeclContext()
Definition: DeclBase.h:456
The name of a declaration.
Represents a function declaration or definition.
Definition: Decl.h:1971
IdentifierInfo & get(StringRef Name)
Return the identifier token info for the specified named identifier.
This represents a decl that may have a name.
Definition: Decl.h:249
DeclarationName getDeclName() const
Get the actual, stored name of the declaration, which may be a special name.
Definition: Decl.h:315
CallDescriptionSet(std::initializer_list< CallDescription > &&List)
bool containsAsWritten(const CallExpr &CE) const
When available, always prefer lookup with a CallEvent! This function exists only when that is not ava...
bool contains(const CallEvent &Call) const
A CallDescription is a pattern that can be used to match calls based on the qualified name and the ar...
CallDescription(Mode MatchAs, ArrayRef< StringRef > QualifiedName, MaybeCount RequiredArgs=std::nullopt, MaybeCount RequiredParams=std::nullopt)
Constructs a CallDescription object.
bool matches(const CallEvent &Call) const
Returns true if the CallEvent is a call to a function that matches the CallDescription.
bool matchesAsWritten(const CallExpr &CE) const
Returns true if the CallExpr is a call to a function that matches the CallDescription.
Represents an abstract call to a function or method along a particular path.
Definition: CallEvent.h:153
static bool isCLibraryFunction(const FunctionDecl *FD, StringRef Name=StringRef())
Returns true if the given function is an externally-visible function in the top-level namespace,...
static bool isHardenedVariantOf(const FunctionDecl *FD, StringRef Name)
In builds that use source hardening (-D_FORTIFY_SOURCE), many standard functions are implemented as m...
@ CE_ObjCMessage
Definition: CallEvent.h:77
The JSON file list parser is used to communicate input to InstallAPI.
Diagnostic wrappers for TextAPI types for error reporting.
Definition: Dominators.h:30