1349cc55cSDimitry Andric //===- CallDescription.cpp - function/method call matching     --*- C++ -*-===//
2349cc55cSDimitry Andric //
3349cc55cSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4349cc55cSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5349cc55cSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6349cc55cSDimitry Andric //
7349cc55cSDimitry Andric //===----------------------------------------------------------------------===//
8349cc55cSDimitry Andric //
9349cc55cSDimitry Andric /// \file This file defines a generic mechanism for matching for function and
10349cc55cSDimitry Andric /// method calls of C, C++, and Objective-C languages. Instances of these
11349cc55cSDimitry Andric /// classes are frequently used together with the CallEvent classes.
12349cc55cSDimitry Andric //
13349cc55cSDimitry Andric //===----------------------------------------------------------------------===//
14349cc55cSDimitry Andric 
15349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
1681ad6265SDimitry Andric #include "clang/AST/Decl.h"
17349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
18349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
19349cc55cSDimitry Andric #include "llvm/ADT/ArrayRef.h"
20349cc55cSDimitry Andric #include <iterator>
21*bdd1243dSDimitry Andric #include <optional>
22349cc55cSDimitry Andric 
23349cc55cSDimitry Andric using namespace llvm;
24349cc55cSDimitry Andric using namespace clang;
25349cc55cSDimitry Andric 
26*bdd1243dSDimitry Andric using MaybeCount = std::optional<unsigned>;
27349cc55cSDimitry Andric 
28349cc55cSDimitry Andric // A constructor helper.
readRequiredParams(MaybeCount RequiredArgs,MaybeCount RequiredParams)29349cc55cSDimitry Andric static MaybeCount readRequiredParams(MaybeCount RequiredArgs,
30349cc55cSDimitry Andric                                      MaybeCount RequiredParams) {
31349cc55cSDimitry Andric   if (RequiredParams)
32349cc55cSDimitry Andric     return RequiredParams;
33349cc55cSDimitry Andric   if (RequiredArgs)
34349cc55cSDimitry Andric     return RequiredArgs;
35*bdd1243dSDimitry Andric   return std::nullopt;
36349cc55cSDimitry Andric }
37349cc55cSDimitry Andric 
CallDescription(CallDescriptionFlags Flags,ArrayRef<StringRef> QualifiedName,MaybeCount RequiredArgs,MaybeCount RequiredParams)38349cc55cSDimitry Andric ento::CallDescription::CallDescription(CallDescriptionFlags Flags,
39*bdd1243dSDimitry Andric                                        ArrayRef<StringRef> QualifiedName,
40349cc55cSDimitry Andric                                        MaybeCount RequiredArgs /*= None*/,
41349cc55cSDimitry Andric                                        MaybeCount RequiredParams /*= None*/)
42349cc55cSDimitry Andric     : RequiredArgs(RequiredArgs),
43349cc55cSDimitry Andric       RequiredParams(readRequiredParams(RequiredArgs, RequiredParams)),
44349cc55cSDimitry Andric       Flags(Flags) {
45349cc55cSDimitry Andric   assert(!QualifiedName.empty());
46349cc55cSDimitry Andric   this->QualifiedName.reserve(QualifiedName.size());
47*bdd1243dSDimitry Andric   llvm::transform(QualifiedName, std::back_inserter(this->QualifiedName),
48*bdd1243dSDimitry Andric                   [](StringRef From) { return From.str(); });
49349cc55cSDimitry Andric }
50349cc55cSDimitry Andric 
51349cc55cSDimitry Andric /// Construct a CallDescription with default flags.
CallDescription(ArrayRef<StringRef> QualifiedName,MaybeCount RequiredArgs,MaybeCount RequiredParams)52*bdd1243dSDimitry Andric ento::CallDescription::CallDescription(ArrayRef<StringRef> QualifiedName,
53349cc55cSDimitry Andric                                        MaybeCount RequiredArgs /*= None*/,
54349cc55cSDimitry Andric                                        MaybeCount RequiredParams /*= None*/)
55349cc55cSDimitry Andric     : CallDescription(CDF_None, QualifiedName, RequiredArgs, RequiredParams) {}
56349cc55cSDimitry Andric 
matches(const CallEvent & Call) const57349cc55cSDimitry Andric bool ento::CallDescription::matches(const CallEvent &Call) const {
58349cc55cSDimitry Andric   // FIXME: Add ObjC Message support.
59349cc55cSDimitry Andric   if (Call.getKind() == CE_ObjCMessage)
60349cc55cSDimitry Andric     return false;
61349cc55cSDimitry Andric 
62349cc55cSDimitry Andric   const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
63349cc55cSDimitry Andric   if (!FD)
64349cc55cSDimitry Andric     return false;
65349cc55cSDimitry Andric 
6681ad6265SDimitry Andric   return matchesImpl(FD, Call.getNumArgs(), Call.parameters().size());
67349cc55cSDimitry Andric }
68349cc55cSDimitry Andric 
matchesAsWritten(const CallExpr & CE) const6981ad6265SDimitry Andric bool ento::CallDescription::matchesAsWritten(const CallExpr &CE) const {
7081ad6265SDimitry Andric   const auto *FD = dyn_cast_or_null<FunctionDecl>(CE.getCalleeDecl());
7181ad6265SDimitry Andric   if (!FD)
7281ad6265SDimitry Andric     return false;
7381ad6265SDimitry Andric 
7481ad6265SDimitry Andric   return matchesImpl(FD, CE.getNumArgs(), FD->param_size());
7581ad6265SDimitry Andric }
7681ad6265SDimitry Andric 
matchesImpl(const FunctionDecl * Callee,size_t ArgCount,size_t ParamCount) const7781ad6265SDimitry Andric bool ento::CallDescription::matchesImpl(const FunctionDecl *Callee,
7881ad6265SDimitry Andric                                         size_t ArgCount,
7981ad6265SDimitry Andric                                         size_t ParamCount) const {
8081ad6265SDimitry Andric   const auto *FD = Callee;
8181ad6265SDimitry Andric   if (!FD)
8281ad6265SDimitry Andric     return false;
8381ad6265SDimitry Andric 
8481ad6265SDimitry Andric   if (Flags & CDF_MaybeBuiltin) {
8581ad6265SDimitry Andric     return CheckerContext::isCLibraryFunction(FD, getFunctionName()) &&
8681ad6265SDimitry Andric            (!RequiredArgs || *RequiredArgs <= ArgCount) &&
8781ad6265SDimitry Andric            (!RequiredParams || *RequiredParams <= ParamCount);
8881ad6265SDimitry Andric   }
8981ad6265SDimitry Andric 
9081ad6265SDimitry Andric   if (!II) {
9181ad6265SDimitry Andric     II = &FD->getASTContext().Idents.get(getFunctionName());
92349cc55cSDimitry Andric   }
93349cc55cSDimitry Andric 
94349cc55cSDimitry Andric   const auto MatchNameOnly = [](const CallDescription &CD,
95349cc55cSDimitry Andric                                 const NamedDecl *ND) -> bool {
96349cc55cSDimitry Andric     DeclarationName Name = ND->getDeclName();
97349cc55cSDimitry Andric     if (const auto *II = Name.getAsIdentifierInfo())
9881ad6265SDimitry Andric       return II == *CD.II; // Fast case.
99349cc55cSDimitry Andric 
100349cc55cSDimitry Andric     // Fallback to the slow stringification and comparison for:
101349cc55cSDimitry Andric     // C++ overloaded operators, constructors, destructors, etc.
102349cc55cSDimitry Andric     // FIXME This comparison is way SLOWER than comparing pointers.
103349cc55cSDimitry Andric     // At some point in the future, we should compare FunctionDecl pointers.
104349cc55cSDimitry Andric     return Name.getAsString() == CD.getFunctionName();
105349cc55cSDimitry Andric   };
106349cc55cSDimitry Andric 
107349cc55cSDimitry Andric   const auto ExactMatchArgAndParamCounts =
10881ad6265SDimitry Andric       [](size_t ArgCount, size_t ParamCount,
10981ad6265SDimitry Andric          const CallDescription &CD) -> bool {
11081ad6265SDimitry Andric     const bool ArgsMatch = !CD.RequiredArgs || *CD.RequiredArgs == ArgCount;
111349cc55cSDimitry Andric     const bool ParamsMatch =
11281ad6265SDimitry Andric         !CD.RequiredParams || *CD.RequiredParams == ParamCount;
113349cc55cSDimitry Andric     return ArgsMatch && ParamsMatch;
114349cc55cSDimitry Andric   };
115349cc55cSDimitry Andric 
116349cc55cSDimitry Andric   const auto MatchQualifiedNameParts = [](const CallDescription &CD,
117349cc55cSDimitry Andric                                           const Decl *D) -> bool {
118349cc55cSDimitry Andric     const auto FindNextNamespaceOrRecord =
119349cc55cSDimitry Andric         [](const DeclContext *Ctx) -> const DeclContext * {
120349cc55cSDimitry Andric       while (Ctx && !isa<NamespaceDecl, RecordDecl>(Ctx))
121349cc55cSDimitry Andric         Ctx = Ctx->getParent();
122349cc55cSDimitry Andric       return Ctx;
123349cc55cSDimitry Andric     };
124349cc55cSDimitry Andric 
125349cc55cSDimitry Andric     auto QualifierPartsIt = CD.begin_qualified_name_parts();
126349cc55cSDimitry Andric     const auto QualifierPartsEndIt = CD.end_qualified_name_parts();
127349cc55cSDimitry Andric 
128349cc55cSDimitry Andric     // Match namespace and record names. Skip unrelated names if they don't
129349cc55cSDimitry Andric     // match.
130349cc55cSDimitry Andric     const DeclContext *Ctx = FindNextNamespaceOrRecord(D->getDeclContext());
131349cc55cSDimitry Andric     for (; Ctx && QualifierPartsIt != QualifierPartsEndIt;
132349cc55cSDimitry Andric          Ctx = FindNextNamespaceOrRecord(Ctx->getParent())) {
133349cc55cSDimitry Andric       // If not matched just continue and try matching for the next one.
134349cc55cSDimitry Andric       if (cast<NamedDecl>(Ctx)->getName() != *QualifierPartsIt)
135349cc55cSDimitry Andric         continue;
136349cc55cSDimitry Andric       ++QualifierPartsIt;
137349cc55cSDimitry Andric     }
138349cc55cSDimitry Andric 
139349cc55cSDimitry Andric     // We matched if we consumed all expected qualifier segments.
140349cc55cSDimitry Andric     return QualifierPartsIt == QualifierPartsEndIt;
141349cc55cSDimitry Andric   };
142349cc55cSDimitry Andric 
143349cc55cSDimitry Andric   // Let's start matching...
14481ad6265SDimitry Andric   if (!ExactMatchArgAndParamCounts(ArgCount, ParamCount, *this))
145349cc55cSDimitry Andric     return false;
146349cc55cSDimitry Andric 
147349cc55cSDimitry Andric   if (!MatchNameOnly(*this, FD))
148349cc55cSDimitry Andric     return false;
149349cc55cSDimitry Andric 
150349cc55cSDimitry Andric   if (!hasQualifiedNameParts())
151349cc55cSDimitry Andric     return true;
152349cc55cSDimitry Andric 
153349cc55cSDimitry Andric   return MatchQualifiedNameParts(*this, FD);
154349cc55cSDimitry Andric }
155349cc55cSDimitry Andric 
CallDescriptionSet(std::initializer_list<CallDescription> && List)156349cc55cSDimitry Andric ento::CallDescriptionSet::CallDescriptionSet(
157349cc55cSDimitry Andric     std::initializer_list<CallDescription> &&List) {
158349cc55cSDimitry Andric   Impl.LinearMap.reserve(List.size());
159349cc55cSDimitry Andric   for (const CallDescription &CD : List)
160349cc55cSDimitry Andric     Impl.LinearMap.push_back({CD, /*unused*/ true});
161349cc55cSDimitry Andric }
162349cc55cSDimitry Andric 
contains(const CallEvent & Call) const163349cc55cSDimitry Andric bool ento::CallDescriptionSet::contains(const CallEvent &Call) const {
164349cc55cSDimitry Andric   return static_cast<bool>(Impl.lookup(Call));
165349cc55cSDimitry Andric }
16681ad6265SDimitry Andric 
containsAsWritten(const CallExpr & CE) const16781ad6265SDimitry Andric bool ento::CallDescriptionSet::containsAsWritten(const CallExpr &CE) const {
16881ad6265SDimitry Andric   return static_cast<bool>(Impl.lookupAsWritten(CE));
16981ad6265SDimitry Andric }
170