10b9d3a6eSBalazs Benics //===- CallDescription.cpp - function/method call matching --*- C++ -*-===//
20b9d3a6eSBalazs Benics //
30b9d3a6eSBalazs Benics // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b9d3a6eSBalazs Benics // See https://llvm.org/LICENSE.txt for license information.
50b9d3a6eSBalazs Benics // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b9d3a6eSBalazs Benics //
70b9d3a6eSBalazs Benics //===----------------------------------------------------------------------===//
80b9d3a6eSBalazs Benics //
90b9d3a6eSBalazs Benics /// \file This file defines a generic mechanism for matching for function and
100b9d3a6eSBalazs Benics /// method calls of C, C++, and Objective-C languages. Instances of these
110b9d3a6eSBalazs Benics /// classes are frequently used together with the CallEvent classes.
120b9d3a6eSBalazs Benics //
130b9d3a6eSBalazs Benics //===----------------------------------------------------------------------===//
140b9d3a6eSBalazs Benics
150b9d3a6eSBalazs Benics #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
1632ac21d0SKristóf Umann #include "clang/AST/Decl.h"
170b9d3a6eSBalazs Benics #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
186c512703SBalazs Benics #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
190b9d3a6eSBalazs Benics #include "llvm/ADT/ArrayRef.h"
200b9d3a6eSBalazs Benics #include "llvm/ADT/Optional.h"
21de9d7e42SBalazs Benics #include <iterator>
220b9d3a6eSBalazs Benics
230b9d3a6eSBalazs Benics using namespace llvm;
240b9d3a6eSBalazs Benics using namespace clang;
250b9d3a6eSBalazs Benics
26d5de568cSBalazs Benics using MaybeCount = Optional<unsigned>;
2797f1bf15SBalazs Benics
280b9d3a6eSBalazs Benics // A constructor helper.
readRequiredParams(MaybeCount RequiredArgs,MaybeCount RequiredParams)29d5de568cSBalazs Benics static MaybeCount readRequiredParams(MaybeCount RequiredArgs,
30d5de568cSBalazs Benics MaybeCount RequiredParams) {
310b9d3a6eSBalazs Benics if (RequiredParams)
320b9d3a6eSBalazs Benics return RequiredParams;
330b9d3a6eSBalazs Benics if (RequiredArgs)
3497f1bf15SBalazs Benics return RequiredArgs;
350b9d3a6eSBalazs Benics return None;
360b9d3a6eSBalazs Benics }
370b9d3a6eSBalazs Benics
CallDescription(CallDescriptionFlags Flags,ArrayRef<const char * > QualifiedName,MaybeCount RequiredArgs,MaybeCount RequiredParams)38e6ef134fSBalazs Benics ento::CallDescription::CallDescription(CallDescriptionFlags Flags,
3997f1bf15SBalazs Benics ArrayRef<const char *> QualifiedName,
40d5de568cSBalazs Benics MaybeCount RequiredArgs /*= None*/,
41d5de568cSBalazs Benics MaybeCount RequiredParams /*= None*/)
42de9d7e42SBalazs Benics : RequiredArgs(RequiredArgs),
430b9d3a6eSBalazs Benics RequiredParams(readRequiredParams(RequiredArgs, RequiredParams)),
440b9d3a6eSBalazs Benics Flags(Flags) {
450b9d3a6eSBalazs Benics assert(!QualifiedName.empty());
46de9d7e42SBalazs Benics this->QualifiedName.reserve(QualifiedName.size());
47de9d7e42SBalazs Benics llvm::copy(QualifiedName, std::back_inserter(this->QualifiedName));
480b9d3a6eSBalazs Benics }
490b9d3a6eSBalazs Benics
500b9d3a6eSBalazs Benics /// Construct a CallDescription with default flags.
CallDescription(ArrayRef<const char * > QualifiedName,MaybeCount RequiredArgs,MaybeCount RequiredParams)5197f1bf15SBalazs Benics ento::CallDescription::CallDescription(ArrayRef<const char *> QualifiedName,
52d5de568cSBalazs Benics MaybeCount RequiredArgs /*= None*/,
53d5de568cSBalazs Benics MaybeCount RequiredParams /*= None*/)
54e6ef134fSBalazs Benics : CallDescription(CDF_None, QualifiedName, RequiredArgs, RequiredParams) {}
55d448fcd9SBalazs Benics
matches(const CallEvent & Call) const566c512703SBalazs Benics bool ento::CallDescription::matches(const CallEvent &Call) const {
576c512703SBalazs Benics // FIXME: Add ObjC Message support.
586c512703SBalazs Benics if (Call.getKind() == CE_ObjCMessage)
596c512703SBalazs Benics return false;
606c512703SBalazs Benics
616c512703SBalazs Benics const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
626c512703SBalazs Benics if (!FD)
636c512703SBalazs Benics return false;
646c512703SBalazs Benics
6532ac21d0SKristóf Umann return matchesImpl(FD, Call.getNumArgs(), Call.parameters().size());
6632ac21d0SKristóf Umann }
6732ac21d0SKristóf Umann
matchesAsWritten(const CallExpr & CE) const6832ac21d0SKristóf Umann bool ento::CallDescription::matchesAsWritten(const CallExpr &CE) const {
6932ac21d0SKristóf Umann const auto *FD = dyn_cast_or_null<FunctionDecl>(CE.getCalleeDecl());
7032ac21d0SKristóf Umann if (!FD)
7132ac21d0SKristóf Umann return false;
7232ac21d0SKristóf Umann
7332ac21d0SKristóf Umann return matchesImpl(FD, CE.getNumArgs(), FD->param_size());
7432ac21d0SKristóf Umann }
7532ac21d0SKristóf Umann
matchesImpl(const FunctionDecl * Callee,size_t ArgCount,size_t ParamCount) const7632ac21d0SKristóf Umann bool ento::CallDescription::matchesImpl(const FunctionDecl *Callee,
7732ac21d0SKristóf Umann size_t ArgCount,
7832ac21d0SKristóf Umann size_t ParamCount) const {
7932ac21d0SKristóf Umann const auto *FD = Callee;
8032ac21d0SKristóf Umann if (!FD)
8132ac21d0SKristóf Umann return false;
8232ac21d0SKristóf Umann
836c512703SBalazs Benics if (Flags & CDF_MaybeBuiltin) {
846c512703SBalazs Benics return CheckerContext::isCLibraryFunction(FD, getFunctionName()) &&
8532ac21d0SKristóf Umann (!RequiredArgs || *RequiredArgs <= ArgCount) &&
8632ac21d0SKristóf Umann (!RequiredParams || *RequiredParams <= ParamCount);
876c512703SBalazs Benics }
886c512703SBalazs Benics
89452db157SKazu Hirata if (!II) {
9032ac21d0SKristóf Umann II = &FD->getASTContext().Idents.get(getFunctionName());
916c512703SBalazs Benics }
926c512703SBalazs Benics
936c512703SBalazs Benics const auto MatchNameOnly = [](const CallDescription &CD,
946c512703SBalazs Benics const NamedDecl *ND) -> bool {
956c512703SBalazs Benics DeclarationName Name = ND->getDeclName();
966c512703SBalazs Benics if (const auto *II = Name.getAsIdentifierInfo())
97*ca4af13eSKazu Hirata return II == *CD.II; // Fast case.
986c512703SBalazs Benics
996c512703SBalazs Benics // Fallback to the slow stringification and comparison for:
1006c512703SBalazs Benics // C++ overloaded operators, constructors, destructors, etc.
1016c512703SBalazs Benics // FIXME This comparison is way SLOWER than comparing pointers.
1026c512703SBalazs Benics // At some point in the future, we should compare FunctionDecl pointers.
1036c512703SBalazs Benics return Name.getAsString() == CD.getFunctionName();
1046c512703SBalazs Benics };
1056c512703SBalazs Benics
1066c512703SBalazs Benics const auto ExactMatchArgAndParamCounts =
10732ac21d0SKristóf Umann [](size_t ArgCount, size_t ParamCount,
10832ac21d0SKristóf Umann const CallDescription &CD) -> bool {
10932ac21d0SKristóf Umann const bool ArgsMatch = !CD.RequiredArgs || *CD.RequiredArgs == ArgCount;
1106c512703SBalazs Benics const bool ParamsMatch =
11132ac21d0SKristóf Umann !CD.RequiredParams || *CD.RequiredParams == ParamCount;
1126c512703SBalazs Benics return ArgsMatch && ParamsMatch;
1136c512703SBalazs Benics };
1146c512703SBalazs Benics
1156c512703SBalazs Benics const auto MatchQualifiedNameParts = [](const CallDescription &CD,
1166c512703SBalazs Benics const Decl *D) -> bool {
1176c512703SBalazs Benics const auto FindNextNamespaceOrRecord =
1186c512703SBalazs Benics [](const DeclContext *Ctx) -> const DeclContext * {
1196c512703SBalazs Benics while (Ctx && !isa<NamespaceDecl, RecordDecl>(Ctx))
1206c512703SBalazs Benics Ctx = Ctx->getParent();
1216c512703SBalazs Benics return Ctx;
1226c512703SBalazs Benics };
1236c512703SBalazs Benics
1246c512703SBalazs Benics auto QualifierPartsIt = CD.begin_qualified_name_parts();
1256c512703SBalazs Benics const auto QualifierPartsEndIt = CD.end_qualified_name_parts();
1266c512703SBalazs Benics
1276c512703SBalazs Benics // Match namespace and record names. Skip unrelated names if they don't
1286c512703SBalazs Benics // match.
1296c512703SBalazs Benics const DeclContext *Ctx = FindNextNamespaceOrRecord(D->getDeclContext());
1306c512703SBalazs Benics for (; Ctx && QualifierPartsIt != QualifierPartsEndIt;
1316c512703SBalazs Benics Ctx = FindNextNamespaceOrRecord(Ctx->getParent())) {
1326c512703SBalazs Benics // If not matched just continue and try matching for the next one.
1336c512703SBalazs Benics if (cast<NamedDecl>(Ctx)->getName() != *QualifierPartsIt)
1346c512703SBalazs Benics continue;
1356c512703SBalazs Benics ++QualifierPartsIt;
1366c512703SBalazs Benics }
1376c512703SBalazs Benics
1386c512703SBalazs Benics // We matched if we consumed all expected qualifier segments.
1396c512703SBalazs Benics return QualifierPartsIt == QualifierPartsEndIt;
1406c512703SBalazs Benics };
1416c512703SBalazs Benics
1426c512703SBalazs Benics // Let's start matching...
14332ac21d0SKristóf Umann if (!ExactMatchArgAndParamCounts(ArgCount, ParamCount, *this))
1446c512703SBalazs Benics return false;
1456c512703SBalazs Benics
1466c512703SBalazs Benics if (!MatchNameOnly(*this, FD))
1476c512703SBalazs Benics return false;
1486c512703SBalazs Benics
1496c512703SBalazs Benics if (!hasQualifiedNameParts())
1506c512703SBalazs Benics return true;
1516c512703SBalazs Benics
1526c512703SBalazs Benics return MatchQualifiedNameParts(*this, FD);
1536c512703SBalazs Benics }
1546c512703SBalazs Benics
CallDescriptionSet(std::initializer_list<CallDescription> && List)155d448fcd9SBalazs Benics ento::CallDescriptionSet::CallDescriptionSet(
156d448fcd9SBalazs Benics std::initializer_list<CallDescription> &&List) {
157d448fcd9SBalazs Benics Impl.LinearMap.reserve(List.size());
158d448fcd9SBalazs Benics for (const CallDescription &CD : List)
159d448fcd9SBalazs Benics Impl.LinearMap.push_back({CD, /*unused*/ true});
160d448fcd9SBalazs Benics }
161d448fcd9SBalazs Benics
contains(const CallEvent & Call) const162d448fcd9SBalazs Benics bool ento::CallDescriptionSet::contains(const CallEvent &Call) const {
163d448fcd9SBalazs Benics return static_cast<bool>(Impl.lookup(Call));
164d448fcd9SBalazs Benics }
16532ac21d0SKristóf Umann
containsAsWritten(const CallExpr & CE) const16632ac21d0SKristóf Umann bool ento::CallDescriptionSet::containsAsWritten(const CallExpr &CE) const {
16732ac21d0SKristóf Umann return static_cast<bool>(Impl.lookupAsWritten(CE));
16832ac21d0SKristóf Umann }
169