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