Coverage Report

Created: 2023-09-21 18:56

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