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