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