aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h
blob: 396c9a4de440092c238a3dc39a820d5a92f18471 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
//===- CallDescription.h - function/method call matching       --*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
/// \file This file defines a generic mechanism for matching for function and
/// method calls of C, C++, and Objective-C languages. Instances of these
/// classes are frequently used together with the CallEvent classes.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_CALLDESCRIPTION_H
#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_CALLDESCRIPTION_H

#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/Optional.h"
#include "llvm/Support/Compiler.h"
#include <vector>

namespace clang {
class IdentifierInfo;
} // namespace clang

namespace clang {
namespace ento {

enum CallDescriptionFlags : unsigned {
  CDF_None = 0,

  /// Describes a C standard function that is sometimes implemented as a macro
  /// that expands to a compiler builtin with some __builtin prefix.
  /// The builtin may as well have a few extra arguments on top of the requested
  /// number of arguments.
  CDF_MaybeBuiltin = 1 << 0,
};

/// This class represents a description of a function call using the number of
/// arguments and the name of the function.
class CallDescription {
  friend class CallEvent;
  using MaybeCount = Optional<unsigned>;

  mutable Optional<const IdentifierInfo *> II;
  // The list of the qualified names used to identify the specified CallEvent,
  // e.g. "{a, b}" represent the qualified names, like "a::b".
  std::vector<std::string> QualifiedName;
  MaybeCount RequiredArgs;
  MaybeCount RequiredParams;
  int Flags;

public:
  /// Constructs a CallDescription object.
  ///
  /// @param QualifiedName The list of the name qualifiers of the function that
  /// will be matched. The user is allowed to skip any of the qualifiers.
  /// For example, {"std", "basic_string", "c_str"} would match both
  /// std::basic_string<...>::c_str() and std::__1::basic_string<...>::c_str().
  ///
  /// @param RequiredArgs The number of arguments that is expected to match a
  /// call. Omit this parameter to match every occurrence of call with a given
  /// name regardless the number of arguments.
  CallDescription(CallDescriptionFlags Flags,
                  ArrayRef<const char *> QualifiedName,
                  MaybeCount RequiredArgs = None,
                  MaybeCount RequiredParams = None);

  /// Construct a CallDescription with default flags.
  CallDescription(ArrayRef<const char *> QualifiedName,
                  MaybeCount RequiredArgs = None,
                  MaybeCount RequiredParams = None);

  CallDescription(std::nullptr_t) = delete;

  /// Get the name of the function that this object matches.
  StringRef getFunctionName() const { return QualifiedName.back(); }

  /// Get the qualified name parts in reversed order.
  /// E.g. { "std", "vector", "data" } -> "vector", "std"
  auto begin_qualified_name_parts() const {
    return std::next(QualifiedName.rbegin());
  }
  auto end_qualified_name_parts() const { return QualifiedName.rend(); }

  /// It's false, if and only if we expect a single identifier, such as
  /// `getenv`. It's true for `std::swap`, or `my::detail::container::data`.
  bool hasQualifiedNameParts() const { return QualifiedName.size() > 1; }

  /// @name Matching CallDescriptions against a CallEvent
  /// @{

  /// Returns true if the CallEvent is a call to a function that matches
  /// the CallDescription.
  ///
  /// \note This function is not intended to be used to match Obj-C method
  /// calls.
  bool matches(const CallEvent &Call) const;

  /// Returns true whether the CallEvent matches on any of the CallDescriptions
  /// supplied.
  ///
  /// \note This function is not intended to be used to match Obj-C method
  /// calls.
  friend bool matchesAny(const CallEvent &Call, const CallDescription &CD1) {
    return CD1.matches(Call);
  }

  /// \copydoc clang::ento::matchesAny(const CallEvent &, const CallDescription &)
  template <typename... Ts>
  friend bool matchesAny(const CallEvent &Call, const CallDescription &CD1,
                         const Ts &...CDs) {
    return CD1.matches(Call) || matchesAny(Call, CDs...);
  }
  /// @}
};

/// An immutable map from CallDescriptions to arbitrary data. Provides a unified
/// way for checkers to react on function calls.
template <typename T> class CallDescriptionMap {
  friend class CallDescriptionSet;

  // Some call descriptions aren't easily hashable (eg., the ones with qualified
  // names in which some sections are omitted), so let's put them
  // in a simple vector and use linear lookup.
  // TODO: Implement an actual map for fast lookup for "hashable" call
  // descriptions (eg., the ones for C functions that just match the name).
  std::vector<std::pair<CallDescription, T>> LinearMap;

public:
  CallDescriptionMap(
      std::initializer_list<std::pair<CallDescription, T>> &&List)
      : LinearMap(List) {}

  ~CallDescriptionMap() = default;

  // These maps are usually stored once per checker, so let's make sure
  // we don't do redundant copies.
  CallDescriptionMap(const CallDescriptionMap &) = delete;
  CallDescriptionMap &operator=(const CallDescription &) = delete;

  LLVM_NODISCARD const T *lookup(const CallEvent &Call) const {
    // Slow path: linear lookup.
    // TODO: Implement some sort of fast path.
    for (const std::pair<CallDescription, T> &I : LinearMap)
      if (I.first.matches(Call))
        return &I.second;

    return nullptr;
  }
};

/// An immutable set of CallDescriptions.
/// Checkers can efficiently decide if a given CallEvent matches any
/// CallDescription in the set.
class CallDescriptionSet {
  CallDescriptionMap<bool /*unused*/> Impl = {};

public:
  CallDescriptionSet(std::initializer_list<CallDescription> &&List);

  CallDescriptionSet(const CallDescriptionSet &) = delete;
  CallDescriptionSet &operator=(const CallDescription &) = delete;

  LLVM_NODISCARD bool contains(const CallEvent &Call) const;
};

} // namespace ento
} // namespace clang

#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_CALLDESCRIPTION_H