xref: /openbsd-src/gnu/llvm/clang/lib/StaticAnalyzer/Core/CallDescription.cpp (revision 12c855180aad702bbcca06e0398d774beeafb155)
1*12c85518Srobert //===- CallDescription.cpp - function/method call matching     --*- C++ -*-===//
2*12c85518Srobert //
3*12c85518Srobert // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*12c85518Srobert // See https://llvm.org/LICENSE.txt for license information.
5*12c85518Srobert // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*12c85518Srobert //
7*12c85518Srobert //===----------------------------------------------------------------------===//
8*12c85518Srobert //
9*12c85518Srobert /// \file This file defines a generic mechanism for matching for function and
10*12c85518Srobert /// method calls of C, C++, and Objective-C languages. Instances of these
11*12c85518Srobert /// classes are frequently used together with the CallEvent classes.
12*12c85518Srobert //
13*12c85518Srobert //===----------------------------------------------------------------------===//
14*12c85518Srobert 
15*12c85518Srobert #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
16*12c85518Srobert #include "clang/AST/Decl.h"
17*12c85518Srobert #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
18*12c85518Srobert #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
19*12c85518Srobert #include "llvm/ADT/ArrayRef.h"
20*12c85518Srobert #include <iterator>
21*12c85518Srobert #include <optional>
22*12c85518Srobert 
23*12c85518Srobert using namespace llvm;
24*12c85518Srobert using namespace clang;
25*12c85518Srobert 
26*12c85518Srobert using MaybeCount = std::optional<unsigned>;
27*12c85518Srobert 
28*12c85518Srobert // A constructor helper.
readRequiredParams(MaybeCount RequiredArgs,MaybeCount RequiredParams)29*12c85518Srobert static MaybeCount readRequiredParams(MaybeCount RequiredArgs,
30*12c85518Srobert                                      MaybeCount RequiredParams) {
31*12c85518Srobert   if (RequiredParams)
32*12c85518Srobert     return RequiredParams;
33*12c85518Srobert   if (RequiredArgs)
34*12c85518Srobert     return RequiredArgs;
35*12c85518Srobert   return std::nullopt;
36*12c85518Srobert }
37*12c85518Srobert 
CallDescription(CallDescriptionFlags Flags,ArrayRef<StringRef> QualifiedName,MaybeCount RequiredArgs,MaybeCount RequiredParams)38*12c85518Srobert ento::CallDescription::CallDescription(CallDescriptionFlags Flags,
39*12c85518Srobert                                        ArrayRef<StringRef> QualifiedName,
40*12c85518Srobert                                        MaybeCount RequiredArgs /*= None*/,
41*12c85518Srobert                                        MaybeCount RequiredParams /*= None*/)
42*12c85518Srobert     : RequiredArgs(RequiredArgs),
43*12c85518Srobert       RequiredParams(readRequiredParams(RequiredArgs, RequiredParams)),
44*12c85518Srobert       Flags(Flags) {
45*12c85518Srobert   assert(!QualifiedName.empty());
46*12c85518Srobert   this->QualifiedName.reserve(QualifiedName.size());
47*12c85518Srobert   llvm::transform(QualifiedName, std::back_inserter(this->QualifiedName),
48*12c85518Srobert                   [](StringRef From) { return From.str(); });
49*12c85518Srobert }
50*12c85518Srobert 
51*12c85518Srobert /// Construct a CallDescription with default flags.
CallDescription(ArrayRef<StringRef> QualifiedName,MaybeCount RequiredArgs,MaybeCount RequiredParams)52*12c85518Srobert ento::CallDescription::CallDescription(ArrayRef<StringRef> QualifiedName,
53*12c85518Srobert                                        MaybeCount RequiredArgs /*= None*/,
54*12c85518Srobert                                        MaybeCount RequiredParams /*= None*/)
55*12c85518Srobert     : CallDescription(CDF_None, QualifiedName, RequiredArgs, RequiredParams) {}
56*12c85518Srobert 
matches(const CallEvent & Call) const57*12c85518Srobert bool ento::CallDescription::matches(const CallEvent &Call) const {
58*12c85518Srobert   // FIXME: Add ObjC Message support.
59*12c85518Srobert   if (Call.getKind() == CE_ObjCMessage)
60*12c85518Srobert     return false;
61*12c85518Srobert 
62*12c85518Srobert   const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
63*12c85518Srobert   if (!FD)
64*12c85518Srobert     return false;
65*12c85518Srobert 
66*12c85518Srobert   return matchesImpl(FD, Call.getNumArgs(), Call.parameters().size());
67*12c85518Srobert }
68*12c85518Srobert 
matchesAsWritten(const CallExpr & CE) const69*12c85518Srobert bool ento::CallDescription::matchesAsWritten(const CallExpr &CE) const {
70*12c85518Srobert   const auto *FD = dyn_cast_or_null<FunctionDecl>(CE.getCalleeDecl());
71*12c85518Srobert   if (!FD)
72*12c85518Srobert     return false;
73*12c85518Srobert 
74*12c85518Srobert   return matchesImpl(FD, CE.getNumArgs(), FD->param_size());
75*12c85518Srobert }
76*12c85518Srobert 
matchesImpl(const FunctionDecl * Callee,size_t ArgCount,size_t ParamCount) const77*12c85518Srobert bool ento::CallDescription::matchesImpl(const FunctionDecl *Callee,
78*12c85518Srobert                                         size_t ArgCount,
79*12c85518Srobert                                         size_t ParamCount) const {
80*12c85518Srobert   const auto *FD = Callee;
81*12c85518Srobert   if (!FD)
82*12c85518Srobert     return false;
83*12c85518Srobert 
84*12c85518Srobert   if (Flags & CDF_MaybeBuiltin) {
85*12c85518Srobert     return CheckerContext::isCLibraryFunction(FD, getFunctionName()) &&
86*12c85518Srobert            (!RequiredArgs || *RequiredArgs <= ArgCount) &&
87*12c85518Srobert            (!RequiredParams || *RequiredParams <= ParamCount);
88*12c85518Srobert   }
89*12c85518Srobert 
90*12c85518Srobert   if (!II) {
91*12c85518Srobert     II = &FD->getASTContext().Idents.get(getFunctionName());
92*12c85518Srobert   }
93*12c85518Srobert 
94*12c85518Srobert   const auto MatchNameOnly = [](const CallDescription &CD,
95*12c85518Srobert                                 const NamedDecl *ND) -> bool {
96*12c85518Srobert     DeclarationName Name = ND->getDeclName();
97*12c85518Srobert     if (const auto *II = Name.getAsIdentifierInfo())
98*12c85518Srobert       return II == *CD.II; // Fast case.
99*12c85518Srobert 
100*12c85518Srobert     // Fallback to the slow stringification and comparison for:
101*12c85518Srobert     // C++ overloaded operators, constructors, destructors, etc.
102*12c85518Srobert     // FIXME This comparison is way SLOWER than comparing pointers.
103*12c85518Srobert     // At some point in the future, we should compare FunctionDecl pointers.
104*12c85518Srobert     return Name.getAsString() == CD.getFunctionName();
105*12c85518Srobert   };
106*12c85518Srobert 
107*12c85518Srobert   const auto ExactMatchArgAndParamCounts =
108*12c85518Srobert       [](size_t ArgCount, size_t ParamCount,
109*12c85518Srobert          const CallDescription &CD) -> bool {
110*12c85518Srobert     const bool ArgsMatch = !CD.RequiredArgs || *CD.RequiredArgs == ArgCount;
111*12c85518Srobert     const bool ParamsMatch =
112*12c85518Srobert         !CD.RequiredParams || *CD.RequiredParams == ParamCount;
113*12c85518Srobert     return ArgsMatch && ParamsMatch;
114*12c85518Srobert   };
115*12c85518Srobert 
116*12c85518Srobert   const auto MatchQualifiedNameParts = [](const CallDescription &CD,
117*12c85518Srobert                                           const Decl *D) -> bool {
118*12c85518Srobert     const auto FindNextNamespaceOrRecord =
119*12c85518Srobert         [](const DeclContext *Ctx) -> const DeclContext * {
120*12c85518Srobert       while (Ctx && !isa<NamespaceDecl, RecordDecl>(Ctx))
121*12c85518Srobert         Ctx = Ctx->getParent();
122*12c85518Srobert       return Ctx;
123*12c85518Srobert     };
124*12c85518Srobert 
125*12c85518Srobert     auto QualifierPartsIt = CD.begin_qualified_name_parts();
126*12c85518Srobert     const auto QualifierPartsEndIt = CD.end_qualified_name_parts();
127*12c85518Srobert 
128*12c85518Srobert     // Match namespace and record names. Skip unrelated names if they don't
129*12c85518Srobert     // match.
130*12c85518Srobert     const DeclContext *Ctx = FindNextNamespaceOrRecord(D->getDeclContext());
131*12c85518Srobert     for (; Ctx && QualifierPartsIt != QualifierPartsEndIt;
132*12c85518Srobert          Ctx = FindNextNamespaceOrRecord(Ctx->getParent())) {
133*12c85518Srobert       // If not matched just continue and try matching for the next one.
134*12c85518Srobert       if (cast<NamedDecl>(Ctx)->getName() != *QualifierPartsIt)
135*12c85518Srobert         continue;
136*12c85518Srobert       ++QualifierPartsIt;
137*12c85518Srobert     }
138*12c85518Srobert 
139*12c85518Srobert     // We matched if we consumed all expected qualifier segments.
140*12c85518Srobert     return QualifierPartsIt == QualifierPartsEndIt;
141*12c85518Srobert   };
142*12c85518Srobert 
143*12c85518Srobert   // Let's start matching...
144*12c85518Srobert   if (!ExactMatchArgAndParamCounts(ArgCount, ParamCount, *this))
145*12c85518Srobert     return false;
146*12c85518Srobert 
147*12c85518Srobert   if (!MatchNameOnly(*this, FD))
148*12c85518Srobert     return false;
149*12c85518Srobert 
150*12c85518Srobert   if (!hasQualifiedNameParts())
151*12c85518Srobert     return true;
152*12c85518Srobert 
153*12c85518Srobert   return MatchQualifiedNameParts(*this, FD);
154*12c85518Srobert }
155*12c85518Srobert 
CallDescriptionSet(std::initializer_list<CallDescription> && List)156*12c85518Srobert ento::CallDescriptionSet::CallDescriptionSet(
157*12c85518Srobert     std::initializer_list<CallDescription> &&List) {
158*12c85518Srobert   Impl.LinearMap.reserve(List.size());
159*12c85518Srobert   for (const CallDescription &CD : List)
160*12c85518Srobert     Impl.LinearMap.push_back({CD, /*unused*/ true});
161*12c85518Srobert }
162*12c85518Srobert 
contains(const CallEvent & Call) const163*12c85518Srobert bool ento::CallDescriptionSet::contains(const CallEvent &Call) const {
164*12c85518Srobert   return static_cast<bool>(Impl.lookup(Call));
165*12c85518Srobert }
166*12c85518Srobert 
containsAsWritten(const CallExpr & CE) const167*12c85518Srobert bool ento::CallDescriptionSet::containsAsWritten(const CallExpr &CE) const {
168*12c85518Srobert   return static_cast<bool>(Impl.lookupAsWritten(CE));
169*12c85518Srobert }
170