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
15 #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
16 #include "clang/AST/Decl.h"
17 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
18 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
19 #include "llvm/ADT/ArrayRef.h"
20 #include "llvm/ADT/Optional.h"
21 #include <iterator>
22
23 using namespace llvm;
24 using namespace clang;
25
26 using MaybeCount = Optional<unsigned>;
27
28 // A constructor helper.
readRequiredParams(MaybeCount RequiredArgs,MaybeCount RequiredParams)29 static MaybeCount readRequiredParams(MaybeCount RequiredArgs,
30 MaybeCount RequiredParams) {
31 if (RequiredParams)
32 return RequiredParams;
33 if (RequiredArgs)
34 return RequiredArgs;
35 return None;
36 }
37
CallDescription(CallDescriptionFlags Flags,ArrayRef<const char * > QualifiedName,MaybeCount RequiredArgs,MaybeCount RequiredParams)38 ento::CallDescription::CallDescription(CallDescriptionFlags Flags,
39 ArrayRef<const char *> QualifiedName,
40 MaybeCount RequiredArgs /*= None*/,
41 MaybeCount RequiredParams /*= None*/)
42 : RequiredArgs(RequiredArgs),
43 RequiredParams(readRequiredParams(RequiredArgs, RequiredParams)),
44 Flags(Flags) {
45 assert(!QualifiedName.empty());
46 this->QualifiedName.reserve(QualifiedName.size());
47 llvm::copy(QualifiedName, std::back_inserter(this->QualifiedName));
48 }
49
50 /// Construct a CallDescription with default flags.
CallDescription(ArrayRef<const char * > QualifiedName,MaybeCount RequiredArgs,MaybeCount RequiredParams)51 ento::CallDescription::CallDescription(ArrayRef<const char *> QualifiedName,
52 MaybeCount RequiredArgs /*= None*/,
53 MaybeCount RequiredParams /*= None*/)
54 : CallDescription(CDF_None, QualifiedName, RequiredArgs, RequiredParams) {}
55
matches(const CallEvent & Call) const56 bool ento::CallDescription::matches(const CallEvent &Call) const {
57 // FIXME: Add ObjC Message support.
58 if (Call.getKind() == CE_ObjCMessage)
59 return false;
60
61 const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
62 if (!FD)
63 return false;
64
65 return matchesImpl(FD, Call.getNumArgs(), Call.parameters().size());
66 }
67
matchesAsWritten(const CallExpr & CE) const68 bool ento::CallDescription::matchesAsWritten(const CallExpr &CE) const {
69 const auto *FD = dyn_cast_or_null<FunctionDecl>(CE.getCalleeDecl());
70 if (!FD)
71 return false;
72
73 return matchesImpl(FD, CE.getNumArgs(), FD->param_size());
74 }
75
matchesImpl(const FunctionDecl * Callee,size_t ArgCount,size_t ParamCount) const76 bool ento::CallDescription::matchesImpl(const FunctionDecl *Callee,
77 size_t ArgCount,
78 size_t ParamCount) const {
79 const auto *FD = Callee;
80 if (!FD)
81 return false;
82
83 if (Flags & CDF_MaybeBuiltin) {
84 return CheckerContext::isCLibraryFunction(FD, getFunctionName()) &&
85 (!RequiredArgs || *RequiredArgs <= ArgCount) &&
86 (!RequiredParams || *RequiredParams <= ParamCount);
87 }
88
89 if (!II) {
90 II = &FD->getASTContext().Idents.get(getFunctionName());
91 }
92
93 const auto MatchNameOnly = [](const CallDescription &CD,
94 const NamedDecl *ND) -> bool {
95 DeclarationName Name = ND->getDeclName();
96 if (const auto *II = Name.getAsIdentifierInfo())
97 return II == *CD.II; // Fast case.
98
99 // Fallback to the slow stringification and comparison for:
100 // C++ overloaded operators, constructors, destructors, etc.
101 // FIXME This comparison is way SLOWER than comparing pointers.
102 // At some point in the future, we should compare FunctionDecl pointers.
103 return Name.getAsString() == CD.getFunctionName();
104 };
105
106 const auto ExactMatchArgAndParamCounts =
107 [](size_t ArgCount, size_t ParamCount,
108 const CallDescription &CD) -> bool {
109 const bool ArgsMatch = !CD.RequiredArgs || *CD.RequiredArgs == ArgCount;
110 const bool ParamsMatch =
111 !CD.RequiredParams || *CD.RequiredParams == ParamCount;
112 return ArgsMatch && ParamsMatch;
113 };
114
115 const auto MatchQualifiedNameParts = [](const CallDescription &CD,
116 const Decl *D) -> bool {
117 const auto FindNextNamespaceOrRecord =
118 [](const DeclContext *Ctx) -> const DeclContext * {
119 while (Ctx && !isa<NamespaceDecl, RecordDecl>(Ctx))
120 Ctx = Ctx->getParent();
121 return Ctx;
122 };
123
124 auto QualifierPartsIt = CD.begin_qualified_name_parts();
125 const auto QualifierPartsEndIt = CD.end_qualified_name_parts();
126
127 // Match namespace and record names. Skip unrelated names if they don't
128 // match.
129 const DeclContext *Ctx = FindNextNamespaceOrRecord(D->getDeclContext());
130 for (; Ctx && QualifierPartsIt != QualifierPartsEndIt;
131 Ctx = FindNextNamespaceOrRecord(Ctx->getParent())) {
132 // If not matched just continue and try matching for the next one.
133 if (cast<NamedDecl>(Ctx)->getName() != *QualifierPartsIt)
134 continue;
135 ++QualifierPartsIt;
136 }
137
138 // We matched if we consumed all expected qualifier segments.
139 return QualifierPartsIt == QualifierPartsEndIt;
140 };
141
142 // Let's start matching...
143 if (!ExactMatchArgAndParamCounts(ArgCount, ParamCount, *this))
144 return false;
145
146 if (!MatchNameOnly(*this, FD))
147 return false;
148
149 if (!hasQualifiedNameParts())
150 return true;
151
152 return MatchQualifiedNameParts(*this, FD);
153 }
154
CallDescriptionSet(std::initializer_list<CallDescription> && List)155 ento::CallDescriptionSet::CallDescriptionSet(
156 std::initializer_list<CallDescription> &&List) {
157 Impl.LinearMap.reserve(List.size());
158 for (const CallDescription &CD : List)
159 Impl.LinearMap.push_back({CD, /*unused*/ true});
160 }
161
contains(const CallEvent & Call) const162 bool ento::CallDescriptionSet::contains(const CallEvent &Call) const {
163 return static_cast<bool>(Impl.lookup(Call));
164 }
165
containsAsWritten(const CallExpr & CE) const166 bool ento::CallDescriptionSet::containsAsWritten(const CallExpr &CE) const {
167 return static_cast<bool>(Impl.lookupAsWritten(CE));
168 }
169