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.
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 
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.
51 ento::CallDescription::CallDescription(ArrayRef<const char *> QualifiedName,
52                                        MaybeCount RequiredArgs /*= None*/,
53                                        MaybeCount RequiredParams /*= None*/)
54     : CallDescription(CDF_None, QualifiedName, RequiredArgs, RequiredParams) {}
55 
56 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 
68 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 
76 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 
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 
162 bool ento::CallDescriptionSet::contains(const CallEvent &Call) const {
163   return static_cast<bool>(Impl.lookup(Call));
164 }
165 
166 bool ento::CallDescriptionSet::containsAsWritten(const CallExpr &CE) const {
167   return static_cast<bool>(Impl.lookupAsWritten(CE));
168 }
169