| //=== 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 @c E is a generic call (to ObjC method /function/block/etc), |
| /// return a constructed @c 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 @c D is a callable (Objective-C method or a function), return |
| /// a constructed @c 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, |
| /// @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_ANY_CALL_H |