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