aboutsummaryrefslogtreecommitdiff
path: root/clang/include/clang/Analysis/AnyCall.h
blob: 16371eb1da1835dba388780072eb4dd4dc1782e3 (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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
//=== AnyCall.h - Abstraction over different callables --------*- 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
//
//===----------------------------------------------------------------------===//
//
// A utility class for performing generic operations over different callables.
//
//===----------------------------------------------------------------------===//
//
#ifndef LLVM_CLANG_ANALYSIS_ANY_CALL_H
#define LLVM_CLANG_ANALYSIS_ANY_CALL_H

#include "clang/AST/Decl.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ExprObjC.h"

namespace clang {

/// An instance of this class corresponds to a call.
/// It might be a syntactically-concrete call, done as a part of evaluating an
/// expression, or it may be an abstract callee with no associated expression.
class AnyCall {
public:
  enum Kind {
    /// A function, function pointer, or a C++ method call
    Function,

    /// A call to an Objective-C method
    ObjCMethod,

    /// A call to an Objective-C block
    Block,

    /// An implicit C++ destructor call (called implicitly
    /// or by operator 'delete')
    Destructor,

    /// An implicit or explicit C++ constructor call
    Constructor,

    /// A C++ inherited constructor produced by a "using T::T" directive
    InheritedConstructor,

    /// A C++ allocation function call (operator `new`), via C++ new-expression
    Allocator,

    /// A C++ deallocation function call (operator `delete`), via C++
    /// delete-expression
    Deallocator
  };

private:
  /// Either expression or declaration (but not both at the same time)
  /// can be null.

  /// Call expression, is null when is not known (then declaration is non-null),
  /// or for implicit destructor calls (when no expression exists.)
  const Expr *E = nullptr;

  /// Corresponds to a statically known declaration of the called function,
  /// or null if it is not known (e.g. for a function pointer).
  const Decl *D = nullptr;
  Kind K;

public:
  AnyCall(const CallExpr *CE) : E(CE) {
    D = CE->getCalleeDecl();
    K = (CE->getCallee()->getType()->getAs<BlockPointerType>()) ? Block
                                                                : Function;
    if (D && ((K == Function && !isa<FunctionDecl>(D)) ||
              (K == Block && !isa<BlockDecl>(D))))
      D = nullptr;
  }

  AnyCall(const ObjCMessageExpr *ME)
      : E(ME), D(ME->getMethodDecl()), K(ObjCMethod) {}

  AnyCall(const CXXNewExpr *NE)
      : E(NE), D(NE->getOperatorNew()), K(Allocator) {}

  AnyCall(const CXXDeleteExpr *NE)
      : E(NE), D(NE->getOperatorDelete()), K(Deallocator) {}

  AnyCall(const CXXConstructExpr *NE)
      : E(NE), D(NE->getConstructor()), K(Constructor) {}

  AnyCall(const CXXInheritedCtorInitExpr *CIE)
      : E(CIE), D(CIE->getConstructor()), K(InheritedConstructor) {}

  AnyCall(const CXXDestructorDecl *D) : E(nullptr), D(D), K(Destructor) {}

  AnyCall(const CXXConstructorDecl *D) : E(nullptr), D(D), K(Constructor) {}

  AnyCall(const ObjCMethodDecl *D) : E(nullptr), D(D), K(ObjCMethod) {}

  AnyCall(const FunctionDecl *D) : E(nullptr), D(D) {
    if (isa<CXXConstructorDecl>(D)) {
      K = Constructor;
    } else if (isa <CXXDestructorDecl>(D)) {
      K = Destructor;
    } else {
      K = Function;
    }

  }

  /// If {@code E} is a generic call (to ObjC method /function/block/etc),
  /// return a constructed {@code AnyCall} object. Return None otherwise.
  static Optional<AnyCall> forExpr(const Expr *E) {
    if (const auto *ME = dyn_cast<ObjCMessageExpr>(E)) {
      return AnyCall(ME);
    } else if (const auto *CE = dyn_cast<CallExpr>(E)) {
      return AnyCall(CE);
    } else if (const auto *CXNE = dyn_cast<CXXNewExpr>(E)) {
      return AnyCall(CXNE);
    } else if (const auto *CXDE = dyn_cast<CXXDeleteExpr>(E)) {
      return AnyCall(CXDE);
    } else if (const auto *CXCE = dyn_cast<CXXConstructExpr>(E)) {
      return AnyCall(CXCE);
    } else if (const auto *CXCIE = dyn_cast<CXXInheritedCtorInitExpr>(E)) {
      return AnyCall(CXCIE);
    } else {
      return None;
    }
  }

  /// If {@code D} is a callable (Objective-C method or a function), return
  /// a constructed {@code AnyCall} object. Return None otherwise.
  // FIXME: block support.
  static Optional<AnyCall> forDecl(const Decl *D) {
    if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
      return AnyCall(FD);
    } else if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
      return AnyCall(MD);
    }
    return None;
  }

  /// \returns formal parameters for direct calls (including virtual calls)
  ArrayRef<ParmVarDecl *> parameters() const {
    if (!D)
      return None;

    if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
      return FD->parameters();
    } else if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
      return MD->parameters();
    } else if (const auto *BD = dyn_cast<BlockDecl>(D)) {
      return BD->parameters();
    } else {
      return None;
    }
  }

  using param_const_iterator = ArrayRef<ParmVarDecl *>::const_iterator;
  param_const_iterator param_begin() const { return parameters().begin(); }
  param_const_iterator param_end() const { return parameters().end(); }
  size_t param_size() const { return parameters().size(); }
  bool param_empty() const { return parameters().empty(); }

  QualType getReturnType(ASTContext &Ctx) const {
    switch (K) {
    case Function:
      if (E)
        return cast<CallExpr>(E)->getCallReturnType(Ctx);
      return cast<FunctionDecl>(D)->getReturnType();
    case ObjCMethod:
      if (E)
        return cast<ObjCMessageExpr>(E)->getCallReturnType(Ctx);
      return cast<ObjCMethodDecl>(D)->getReturnType();
    case Block:
      // FIXME: BlockDecl does not know its return type,
      // hence the asymmetry with the function and method cases above.
      return cast<CallExpr>(E)->getCallReturnType(Ctx);
    case Destructor:
    case Constructor:
    case InheritedConstructor:
    case Allocator:
    case Deallocator:
      return cast<FunctionDecl>(D)->getReturnType();
    }
    llvm_unreachable("Unknown AnyCall::Kind");
  }

  /// \returns Function identifier if it is a named declaration,
  /// {@code nullptr} otherwise.
  const IdentifierInfo *getIdentifier() const {
    if (const auto *ND = dyn_cast_or_null<NamedDecl>(D))
      return ND->getIdentifier();
    return nullptr;
  }

  const Decl *getDecl() const {
    return D;
  }

  const Expr *getExpr() const {
    return E;
  }

  Kind getKind() const {
    return K;
  }

  void dump() const {
    if (E)
      E->dump();
    if (D)
      D->dump();
  }
};

}

#endif // LLVM_CLANG_ANALYSIS_ANY_CALL_H