//=== 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_ANYCALL_H
 
#define LLVM_CLANG_ANALYSIS_ANYCALL_H
 
 
 
#include "clang/AST/Decl.h"
 
#include "clang/AST/ExprCXX.h"
 
#include "clang/AST/ExprObjC.h"
 
#include <optional>
 
 
 
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 @c E is a generic call (to ObjC method /function/block/etc),
 
  /// return a constructed @c AnyCall object. Return std::nullopt otherwise.
 
  static std::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 std::nullopt;
 
    }
 
  }
 
 
 
  /// If @c D is a callable (Objective-C method or a function), return
 
  /// a constructed @c AnyCall object. Return std::nullopt otherwise.
 
  // FIXME: block support.
 
  static std::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 std::nullopt;
 
  }
 
 
 
  /// \returns formal parameters for direct calls (including virtual calls)
 
  ArrayRef<ParmVarDecl *> parameters() const {
 
    if (!D)
 
      return std::nullopt;
 
 
 
    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 std::nullopt;
 
    }
 
  }
 
 
 
  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,
 
  /// @c 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_ANYCALL_H