xref: /llvm-project/clang/lib/StaticAnalyzer/Core/CallDescription.cpp (revision 58bad2862cf136f9483eb005bbfa6915d459b46d)
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"
20de9d7e42SBalazs Benics #include <iterator>
21a1580d7bSKazu Hirata #include <optional>
220b9d3a6eSBalazs Benics 
230b9d3a6eSBalazs Benics using namespace llvm;
240b9d3a6eSBalazs Benics using namespace clang;
250b9d3a6eSBalazs Benics 
266ad0788cSKazu Hirata using MaybeCount = std::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;
3518060066SKazu Hirata   return std::nullopt;
360b9d3a6eSBalazs Benics }
370b9d3a6eSBalazs Benics 
CallDescription(Mode MatchAs,ArrayRef<StringRef> QualifiedName,MaybeCount RequiredArgs,MaybeCount RequiredParams)3852a460f9SNagyDonat ento::CallDescription::CallDescription(Mode MatchAs,
39d9ab3e82Sserge-sans-paille                                        ArrayRef<StringRef> QualifiedName,
40d5de568cSBalazs Benics                                        MaybeCount RequiredArgs /*= None*/,
41d5de568cSBalazs Benics                                        MaybeCount RequiredParams /*= None*/)
42de9d7e42SBalazs Benics     : RequiredArgs(RequiredArgs),
430b9d3a6eSBalazs Benics       RequiredParams(readRequiredParams(RequiredArgs, RequiredParams)),
4452a460f9SNagyDonat       MatchAs(MatchAs) {
450b9d3a6eSBalazs Benics   assert(!QualifiedName.empty());
46de9d7e42SBalazs Benics   this->QualifiedName.reserve(QualifiedName.size());
47d9ab3e82Sserge-sans-paille   llvm::transform(QualifiedName, std::back_inserter(this->QualifiedName),
48d9ab3e82Sserge-sans-paille                   [](StringRef From) { return From.str(); });
490b9d3a6eSBalazs Benics }
500b9d3a6eSBalazs Benics 
matches(const CallEvent & Call) const516c512703SBalazs Benics bool ento::CallDescription::matches(const CallEvent &Call) const {
526c512703SBalazs Benics   // FIXME: Add ObjC Message support.
536c512703SBalazs Benics   if (Call.getKind() == CE_ObjCMessage)
546c512703SBalazs Benics     return false;
556c512703SBalazs Benics 
566c512703SBalazs Benics   const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
576c512703SBalazs Benics   if (!FD)
586c512703SBalazs Benics     return false;
596c512703SBalazs Benics 
6032ac21d0SKristóf Umann   return matchesImpl(FD, Call.getNumArgs(), Call.parameters().size());
6132ac21d0SKristóf Umann }
6232ac21d0SKristóf Umann 
matchesAsWritten(const CallExpr & CE) const6332ac21d0SKristóf Umann bool ento::CallDescription::matchesAsWritten(const CallExpr &CE) const {
6432ac21d0SKristóf Umann   const auto *FD = dyn_cast_or_null<FunctionDecl>(CE.getCalleeDecl());
6532ac21d0SKristóf Umann   if (!FD)
6632ac21d0SKristóf Umann     return false;
6732ac21d0SKristóf Umann 
6832ac21d0SKristóf Umann   return matchesImpl(FD, CE.getNumArgs(), FD->param_size());
6932ac21d0SKristóf Umann }
7032ac21d0SKristóf Umann 
matchNameOnly(const NamedDecl * ND) const71fb299caeSNagyDonat bool ento::CallDescription::matchNameOnly(const NamedDecl *ND) const {
726c512703SBalazs Benics   DeclarationName Name = ND->getDeclName();
73fb299caeSNagyDonat   if (const auto *NameII = Name.getAsIdentifierInfo()) {
74fb299caeSNagyDonat     if (!II)
75fb299caeSNagyDonat       II = &ND->getASTContext().Idents.get(getFunctionName());
76fb299caeSNagyDonat 
77fb299caeSNagyDonat     return NameII == *II; // Fast case.
78fb299caeSNagyDonat   }
796c512703SBalazs Benics 
806c512703SBalazs Benics   // Fallback to the slow stringification and comparison for:
816c512703SBalazs Benics   // C++ overloaded operators, constructors, destructors, etc.
826c512703SBalazs Benics   // FIXME This comparison is way SLOWER than comparing pointers.
836c512703SBalazs Benics   // At some point in the future, we should compare FunctionDecl pointers.
84fb299caeSNagyDonat   return Name.getAsString() == getFunctionName();
85*9a16c12aSNagyDonat }
866c512703SBalazs Benics 
matchQualifiedNameParts(const Decl * D) const87fb299caeSNagyDonat bool ento::CallDescription::matchQualifiedNameParts(const Decl *D) const {
886c512703SBalazs Benics   const auto FindNextNamespaceOrRecord =
896c512703SBalazs Benics       [](const DeclContext *Ctx) -> const DeclContext * {
906c512703SBalazs Benics     while (Ctx && !isa<NamespaceDecl, RecordDecl>(Ctx))
916c512703SBalazs Benics       Ctx = Ctx->getParent();
926c512703SBalazs Benics     return Ctx;
936c512703SBalazs Benics   };
946c512703SBalazs Benics 
95fb299caeSNagyDonat   auto QualifierPartsIt = begin_qualified_name_parts();
96fb299caeSNagyDonat   const auto QualifierPartsEndIt = end_qualified_name_parts();
976c512703SBalazs Benics 
986c512703SBalazs Benics   // Match namespace and record names. Skip unrelated names if they don't
996c512703SBalazs Benics   // match.
1006c512703SBalazs Benics   const DeclContext *Ctx = FindNextNamespaceOrRecord(D->getDeclContext());
1016c512703SBalazs Benics   for (; Ctx && QualifierPartsIt != QualifierPartsEndIt;
1026c512703SBalazs Benics        Ctx = FindNextNamespaceOrRecord(Ctx->getParent())) {
1036c512703SBalazs Benics     // If not matched just continue and try matching for the next one.
1046c512703SBalazs Benics     if (cast<NamedDecl>(Ctx)->getName() != *QualifierPartsIt)
1056c512703SBalazs Benics       continue;
1066c512703SBalazs Benics     ++QualifierPartsIt;
1076c512703SBalazs Benics   }
1086c512703SBalazs Benics 
1096c512703SBalazs Benics   // We matched if we consumed all expected qualifier segments.
1106c512703SBalazs Benics   return QualifierPartsIt == QualifierPartsEndIt;
111*9a16c12aSNagyDonat }
1126c512703SBalazs Benics 
matchesImpl(const FunctionDecl * FD,size_t ArgCount,size_t ParamCount) const113fb299caeSNagyDonat bool ento::CallDescription::matchesImpl(const FunctionDecl *FD, size_t ArgCount,
114fb299caeSNagyDonat                                         size_t ParamCount) const {
115fb299caeSNagyDonat   if (!FD)
1166c512703SBalazs Benics     return false;
1176c512703SBalazs Benics 
118fb299caeSNagyDonat   const bool isMethod = isa<CXXMethodDecl>(FD);
119fb299caeSNagyDonat 
120fb299caeSNagyDonat   if (MatchAs == Mode::SimpleFunc && isMethod)
121fb299caeSNagyDonat     return false;
122fb299caeSNagyDonat 
123fb299caeSNagyDonat   if (MatchAs == Mode::CXXMethod && !isMethod)
124fb299caeSNagyDonat     return false;
125fb299caeSNagyDonat 
126fb299caeSNagyDonat   if (MatchAs == Mode::CLibraryMaybeHardened) {
127fb299caeSNagyDonat     // In addition to accepting FOO() with CLibrary rules, we also want to
128fb299caeSNagyDonat     // accept calls to __FOO_chk() and __builtin___FOO_chk().
129fb299caeSNagyDonat     if (CheckerContext::isCLibraryFunction(FD) &&
130fb299caeSNagyDonat         CheckerContext::isHardenedVariantOf(FD, getFunctionName())) {
131fb299caeSNagyDonat       // Check that the actual argument/parameter counts are greater or equal
132fb299caeSNagyDonat       // to the required counts. (Setting a requirement to std::nullopt matches
133fb299caeSNagyDonat       // anything, so in that case value_or ensures that the value is compared
134fb299caeSNagyDonat       // with itself.)
135fb299caeSNagyDonat       return (RequiredArgs.value_or(ArgCount) <= ArgCount &&
136fb299caeSNagyDonat               RequiredParams.value_or(ParamCount) <= ParamCount);
137fb299caeSNagyDonat     }
138fb299caeSNagyDonat   }
139fb299caeSNagyDonat 
140fb299caeSNagyDonat   if (RequiredArgs.value_or(ArgCount) != ArgCount ||
141fb299caeSNagyDonat       RequiredParams.value_or(ParamCount) != ParamCount)
142fb299caeSNagyDonat     return false;
143fb299caeSNagyDonat 
144fb299caeSNagyDonat   if (MatchAs == Mode::CLibrary || MatchAs == Mode::CLibraryMaybeHardened)
145fb299caeSNagyDonat     return CheckerContext::isCLibraryFunction(FD, getFunctionName());
146fb299caeSNagyDonat 
147fb299caeSNagyDonat   if (!matchNameOnly(FD))
1486c512703SBalazs Benics     return false;
1496c512703SBalazs Benics 
1506c512703SBalazs Benics   if (!hasQualifiedNameParts())
1516c512703SBalazs Benics     return true;
1526c512703SBalazs Benics 
153fb299caeSNagyDonat   return matchQualifiedNameParts(FD);
1546c512703SBalazs Benics }
1556c512703SBalazs Benics 
CallDescriptionSet(std::initializer_list<CallDescription> && List)156d448fcd9SBalazs Benics ento::CallDescriptionSet::CallDescriptionSet(
157d448fcd9SBalazs Benics     std::initializer_list<CallDescription> &&List) {
158d448fcd9SBalazs Benics   Impl.LinearMap.reserve(List.size());
159d448fcd9SBalazs Benics   for (const CallDescription &CD : List)
160d448fcd9SBalazs Benics     Impl.LinearMap.push_back({CD, /*unused*/ true});
161d448fcd9SBalazs Benics }
162d448fcd9SBalazs Benics 
contains(const CallEvent & Call) const163d448fcd9SBalazs Benics bool ento::CallDescriptionSet::contains(const CallEvent &Call) const {
164d448fcd9SBalazs Benics   return static_cast<bool>(Impl.lookup(Call));
165d448fcd9SBalazs Benics }
16632ac21d0SKristóf Umann 
containsAsWritten(const CallExpr & CE) const16732ac21d0SKristóf Umann bool ento::CallDescriptionSet::containsAsWritten(const CallExpr &CE) const {
16832ac21d0SKristóf Umann   return static_cast<bool>(Impl.lookupAsWritten(CE));
16932ac21d0SKristóf Umann }
170