xref: /freebsd-src/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CallDescription.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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>
21bdd1243dSDimitry Andric #include <optional>
22349cc55cSDimitry Andric 
23349cc55cSDimitry Andric using namespace llvm;
24349cc55cSDimitry Andric using namespace clang;
25349cc55cSDimitry Andric 
26bdd1243dSDimitry Andric using MaybeCount = std::optional<unsigned>;
27349cc55cSDimitry Andric 
28349cc55cSDimitry Andric // A constructor helper.
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;
35bdd1243dSDimitry Andric   return std::nullopt;
36349cc55cSDimitry Andric }
37349cc55cSDimitry Andric 
38*0fca6ea1SDimitry Andric ento::CallDescription::CallDescription(Mode MatchAs,
39bdd1243dSDimitry Andric                                        ArrayRef<StringRef> QualifiedName,
40349cc55cSDimitry Andric                                        MaybeCount RequiredArgs /*= None*/,
41349cc55cSDimitry Andric                                        MaybeCount RequiredParams /*= None*/)
42349cc55cSDimitry Andric     : RequiredArgs(RequiredArgs),
43349cc55cSDimitry Andric       RequiredParams(readRequiredParams(RequiredArgs, RequiredParams)),
44*0fca6ea1SDimitry Andric       MatchAs(MatchAs) {
45349cc55cSDimitry Andric   assert(!QualifiedName.empty());
46349cc55cSDimitry Andric   this->QualifiedName.reserve(QualifiedName.size());
47bdd1243dSDimitry Andric   llvm::transform(QualifiedName, std::back_inserter(this->QualifiedName),
48bdd1243dSDimitry Andric                   [](StringRef From) { return From.str(); });
49349cc55cSDimitry Andric }
50349cc55cSDimitry Andric 
51349cc55cSDimitry Andric bool ento::CallDescription::matches(const CallEvent &Call) const {
52349cc55cSDimitry Andric   // FIXME: Add ObjC Message support.
53349cc55cSDimitry Andric   if (Call.getKind() == CE_ObjCMessage)
54349cc55cSDimitry Andric     return false;
55349cc55cSDimitry Andric 
56349cc55cSDimitry Andric   const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
57349cc55cSDimitry Andric   if (!FD)
58349cc55cSDimitry Andric     return false;
59349cc55cSDimitry Andric 
6081ad6265SDimitry Andric   return matchesImpl(FD, Call.getNumArgs(), Call.parameters().size());
61349cc55cSDimitry Andric }
62349cc55cSDimitry Andric 
6381ad6265SDimitry Andric bool ento::CallDescription::matchesAsWritten(const CallExpr &CE) const {
6481ad6265SDimitry Andric   const auto *FD = dyn_cast_or_null<FunctionDecl>(CE.getCalleeDecl());
6581ad6265SDimitry Andric   if (!FD)
6681ad6265SDimitry Andric     return false;
6781ad6265SDimitry Andric 
6881ad6265SDimitry Andric   return matchesImpl(FD, CE.getNumArgs(), FD->param_size());
6981ad6265SDimitry Andric }
7081ad6265SDimitry Andric 
71*0fca6ea1SDimitry Andric bool ento::CallDescription::matchNameOnly(const NamedDecl *ND) const {
72349cc55cSDimitry Andric   DeclarationName Name = ND->getDeclName();
73*0fca6ea1SDimitry Andric   if (const auto *NameII = Name.getAsIdentifierInfo()) {
74*0fca6ea1SDimitry Andric     if (!II)
75*0fca6ea1SDimitry Andric       II = &ND->getASTContext().Idents.get(getFunctionName());
76*0fca6ea1SDimitry Andric 
77*0fca6ea1SDimitry Andric     return NameII == *II; // Fast case.
78*0fca6ea1SDimitry Andric   }
79349cc55cSDimitry Andric 
80349cc55cSDimitry Andric   // Fallback to the slow stringification and comparison for:
81349cc55cSDimitry Andric   // C++ overloaded operators, constructors, destructors, etc.
82349cc55cSDimitry Andric   // FIXME This comparison is way SLOWER than comparing pointers.
83349cc55cSDimitry Andric   // At some point in the future, we should compare FunctionDecl pointers.
84*0fca6ea1SDimitry Andric   return Name.getAsString() == getFunctionName();
85*0fca6ea1SDimitry Andric }
86349cc55cSDimitry Andric 
87*0fca6ea1SDimitry Andric bool ento::CallDescription::matchQualifiedNameParts(const Decl *D) const {
88349cc55cSDimitry Andric   const auto FindNextNamespaceOrRecord =
89349cc55cSDimitry Andric       [](const DeclContext *Ctx) -> const DeclContext * {
90349cc55cSDimitry Andric     while (Ctx && !isa<NamespaceDecl, RecordDecl>(Ctx))
91349cc55cSDimitry Andric       Ctx = Ctx->getParent();
92349cc55cSDimitry Andric     return Ctx;
93349cc55cSDimitry Andric   };
94349cc55cSDimitry Andric 
95*0fca6ea1SDimitry Andric   auto QualifierPartsIt = begin_qualified_name_parts();
96*0fca6ea1SDimitry Andric   const auto QualifierPartsEndIt = end_qualified_name_parts();
97349cc55cSDimitry Andric 
98349cc55cSDimitry Andric   // Match namespace and record names. Skip unrelated names if they don't
99349cc55cSDimitry Andric   // match.
100349cc55cSDimitry Andric   const DeclContext *Ctx = FindNextNamespaceOrRecord(D->getDeclContext());
101349cc55cSDimitry Andric   for (; Ctx && QualifierPartsIt != QualifierPartsEndIt;
102349cc55cSDimitry Andric        Ctx = FindNextNamespaceOrRecord(Ctx->getParent())) {
103349cc55cSDimitry Andric     // If not matched just continue and try matching for the next one.
104349cc55cSDimitry Andric     if (cast<NamedDecl>(Ctx)->getName() != *QualifierPartsIt)
105349cc55cSDimitry Andric       continue;
106349cc55cSDimitry Andric     ++QualifierPartsIt;
107349cc55cSDimitry Andric   }
108349cc55cSDimitry Andric 
109349cc55cSDimitry Andric   // We matched if we consumed all expected qualifier segments.
110349cc55cSDimitry Andric   return QualifierPartsIt == QualifierPartsEndIt;
111*0fca6ea1SDimitry Andric }
112349cc55cSDimitry Andric 
113*0fca6ea1SDimitry Andric bool ento::CallDescription::matchesImpl(const FunctionDecl *FD, size_t ArgCount,
114*0fca6ea1SDimitry Andric                                         size_t ParamCount) const {
115*0fca6ea1SDimitry Andric   if (!FD)
116349cc55cSDimitry Andric     return false;
117349cc55cSDimitry Andric 
118*0fca6ea1SDimitry Andric   const bool isMethod = isa<CXXMethodDecl>(FD);
119*0fca6ea1SDimitry Andric 
120*0fca6ea1SDimitry Andric   if (MatchAs == Mode::SimpleFunc && isMethod)
121*0fca6ea1SDimitry Andric     return false;
122*0fca6ea1SDimitry Andric 
123*0fca6ea1SDimitry Andric   if (MatchAs == Mode::CXXMethod && !isMethod)
124*0fca6ea1SDimitry Andric     return false;
125*0fca6ea1SDimitry Andric 
126*0fca6ea1SDimitry Andric   if (MatchAs == Mode::CLibraryMaybeHardened) {
127*0fca6ea1SDimitry Andric     // In addition to accepting FOO() with CLibrary rules, we also want to
128*0fca6ea1SDimitry Andric     // accept calls to __FOO_chk() and __builtin___FOO_chk().
129*0fca6ea1SDimitry Andric     if (CheckerContext::isCLibraryFunction(FD) &&
130*0fca6ea1SDimitry Andric         CheckerContext::isHardenedVariantOf(FD, getFunctionName())) {
131*0fca6ea1SDimitry Andric       // Check that the actual argument/parameter counts are greater or equal
132*0fca6ea1SDimitry Andric       // to the required counts. (Setting a requirement to std::nullopt matches
133*0fca6ea1SDimitry Andric       // anything, so in that case value_or ensures that the value is compared
134*0fca6ea1SDimitry Andric       // with itself.)
135*0fca6ea1SDimitry Andric       return (RequiredArgs.value_or(ArgCount) <= ArgCount &&
136*0fca6ea1SDimitry Andric               RequiredParams.value_or(ParamCount) <= ParamCount);
137*0fca6ea1SDimitry Andric     }
138*0fca6ea1SDimitry Andric   }
139*0fca6ea1SDimitry Andric 
140*0fca6ea1SDimitry Andric   if (RequiredArgs.value_or(ArgCount) != ArgCount ||
141*0fca6ea1SDimitry Andric       RequiredParams.value_or(ParamCount) != ParamCount)
142*0fca6ea1SDimitry Andric     return false;
143*0fca6ea1SDimitry Andric 
144*0fca6ea1SDimitry Andric   if (MatchAs == Mode::CLibrary || MatchAs == Mode::CLibraryMaybeHardened)
145*0fca6ea1SDimitry Andric     return CheckerContext::isCLibraryFunction(FD, getFunctionName());
146*0fca6ea1SDimitry Andric 
147*0fca6ea1SDimitry Andric   if (!matchNameOnly(FD))
148349cc55cSDimitry Andric     return false;
149349cc55cSDimitry Andric 
150349cc55cSDimitry Andric   if (!hasQualifiedNameParts())
151349cc55cSDimitry Andric     return true;
152349cc55cSDimitry Andric 
153*0fca6ea1SDimitry Andric   return matchQualifiedNameParts(FD);
154349cc55cSDimitry Andric }
155349cc55cSDimitry Andric 
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 
163349cc55cSDimitry Andric bool ento::CallDescriptionSet::contains(const CallEvent &Call) const {
164349cc55cSDimitry Andric   return static_cast<bool>(Impl.lookup(Call));
165349cc55cSDimitry Andric }
16681ad6265SDimitry Andric 
16781ad6265SDimitry Andric bool ento::CallDescriptionSet::containsAsWritten(const CallExpr &CE) const {
16881ad6265SDimitry Andric   return static_cast<bool>(Impl.lookupAsWritten(CE));
16981ad6265SDimitry Andric }
170