//===--- iwyu_ast_util.h - clang-AST utilities for include-what-you-use ---===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

// Utilities that make it easier to work with Clang's AST.

#ifndef INCLUDE_WHAT_YOU_USE_IWYU_AST_UTIL_H_
#define INCLUDE_WHAT_YOU_USE_IWYU_AST_UTIL_H_

#include <map>                          // for map
#include <set>                          // for set
#include <string>                       // for string

#include "iwyu_port.h"  // for CHECK_
#include "iwyu_use_flags.h"
#include "llvm/Support/Casting.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/NestedNameSpecifier.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/TemplateBase.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeLoc.h"
#include "clang/Basic/SourceLocation.h"

namespace clang {
class CXXConstructExpr;
class CXXConstructorDecl;
class CXXDeleteExpr;
class CXXDestructorDecl;
class CXXMethodDecl;
class CXXRecordDecl;
class CallExpr;
class CastExpr;
class ClassTemplateDecl;
class Expr;
class FunctionDecl;
class NamedDecl;
class TagDecl;
class TemplateDecl;
class TemplateName;
class TranslationUnitDecl;
class TypeDecl;
class ValueDecl;
struct ASTTemplateArgumentListInfo;
}  // namespace clang

namespace include_what_you_use {

using std::map;
using std::set;
using std::string;

//------------------------------------------------------------
// ASTNode and friends.

// ASTNode represents a single node of the AST tree.  An AST node may be
// a statement, declaration, type, template-name, etc.  ASTNode keeps
// track of its parent node, as we do AST traversal, allowing queries
// on the "context" of a node.
//
// We also store some state that's useful for iwyu.  For instance,
// we store whether a node is in a 'forward-declarable' context
// (such as a function parameter), meaning all types seen below
// that node are legal to fowrard-declare according to c++.
class ASTNode {
 public:
  // In each case, the caller owns the object, and must guarantee it
  // lives for at least as long as the ASTNode object does.
  ASTNode(const clang::Decl* decl)
      : kind_(kDeclKind), as_decl_(decl),
        parent_(nullptr), in_fwd_decl_context_(false) { }
  ASTNode(const clang::Stmt* stmt)
      : kind_(kStmtKind), as_stmt_(stmt),
        parent_(nullptr), in_fwd_decl_context_(false) { }
  ASTNode(const clang::Type* type)
      : kind_(kTypeKind), as_type_(type),
        parent_(nullptr), in_fwd_decl_context_(false) { }
  ASTNode(const clang::TypeLoc* typeloc)
      : kind_(kTypelocKind), as_typeloc_(typeloc),
        parent_(nullptr), in_fwd_decl_context_(false) { }
  ASTNode(const clang::NestedNameSpecifier* nns)
      : kind_(kNNSKind), as_nns_(nns),
        parent_(nullptr), in_fwd_decl_context_(false) { }
  ASTNode(const clang::NestedNameSpecifierLoc* nnsloc)
      : kind_(kNNSLocKind), as_nnsloc_(nnsloc),
        parent_(nullptr), in_fwd_decl_context_(false) { }
  ASTNode(const clang::TemplateName* template_name)
      : kind_(kTemplateNameKind), as_template_name_(template_name),
        parent_(nullptr), in_fwd_decl_context_(false) { }
  ASTNode(const clang::TemplateArgument* template_arg)
      : kind_(kTemplateArgumentKind), as_template_arg_(template_arg),
        parent_(nullptr), in_fwd_decl_context_(false) { }
  ASTNode(const clang::TemplateArgumentLoc* template_argloc)
      : kind_(kTemplateArgumentLocKind), as_template_argloc_(template_argloc),
        parent_(nullptr), in_fwd_decl_context_(false) { }

  // A 'forward-declare' context means some parent of us can be
  // forward-declared, which means we can be too.  e.g. in
  // MyClass<Foo>* x, Foo is fwd-declarable because MyClass<Foo> is.
  bool in_forward_declare_context() const { return in_fwd_decl_context_; }
  void set_in_forward_declare_context(bool b) { in_fwd_decl_context_ = b; }

  const ASTNode* parent() const { return parent_; }
  void SetParent(const ASTNode* parent) {
    parent_ = parent;
    if (parent)  // We inherit this from parent.
      set_in_forward_declare_context(parent->in_forward_declare_context());
  }

  // The number of nodes above this node in the AST tree.
  int depth() const {
    int depth = 0;
    for (const ASTNode* node = this; node != nullptr; node = node->parent_)
      depth++;
    return depth - 1;   // don't count "this"
  }

  // If this node knows its location, returns it.  If not, and it's
  // likely its location is very close (say, within a few lines) of
  // its parent, ask its parent.  Unfortunately, there's nothing which
  // tells us whether a parent's location is very close to its child.
  // We assume that they always are (empirically this is true)
  // *except* for the case the parent is in a macro: then it often
  // happens that the parent belongs at the spelling location, while
  // the child is a macro arg and hence belongs in the instantiation
  // location.  Those could be far away, even in different files.  For
  // example: '#define NEW_FUNC(cls) void Func(cls* x) {}'.  Func is
  // at the spelling loc, but its child Type 'cls' is at the
  // instantiation loc.  In that case, or if *no* ancestor of the
  // current node knows its location, returns an invalid SourceLocation.
  clang::SourceLocation GetLocation() const;

  // Returns true if this node points to the exact same
  // decl/typeloc/etc as the one you pass in.  For Decl/Stmt/Type, the
  // pointer is canonical (every instance of type X has the same
  // clang::Type*).  But for most, the value is canonical (each type
  // has the same QualType but not QualType*).  The IsA<> checks are
  // needed to avoid false matches when target_node is nullptr.
  bool ContentIs(const clang::Decl* target_node) const {
    return IsA<clang::Decl>() && GetAs<clang::Decl>() == target_node;
  }
  bool ContentIs(const clang::Stmt* target_node) const {
    return IsA<clang::Stmt>() && GetAs<clang::Stmt>() == target_node;
  }
  bool ContentIs(const clang::Type* target_node) const {
    return IsA<clang::Type>() && GetAs<clang::Type>() == target_node;
  }
  bool ContentIs(const clang::TypeLoc* target_node) const {
    if (!IsA<clang::TypeLoc>())
      return false;
    const clang::TypeLoc* type_loc = GetAs<clang::TypeLoc>();
    if (type_loc == nullptr || target_node == nullptr)
      return type_loc == target_node;
    return *type_loc == *target_node;
  }
  // We don't define ContentIs() for other kinds of AST nodes
  // (e.g. TemplateName) as it's non-trivial (Clang doesn't define
  // equality comparison functions for them) and we don't need that
  // yet.

  // Returns true if the current node or any ancestor of it contains
  // the exact same thing as ptr.  One use of this is to check for
  // recursion.
  template<typename T> bool StackContainsContent(const T* ptr) const {
    for (const ASTNode* node = this; node != nullptr; node = node->parent_) {
      if (node->ContentIs(ptr))
        return true;
    }
    return false;
  }

  // Generation 0 == you, 1 == parent, etc.
  template<typename To> const To* GetAncestorAs(int generation) const {
    const ASTNode* target_node = this;
    for (; generation > 0; --generation) {
      if (target_node->parent_ == nullptr)
        return nullptr;
      target_node = target_node->parent_;
    }
    // DynCast needs a dummy argument of type To* to help its resolution.
    const To* dummy = nullptr;
    return target_node->DynCast<To>(dummy);
  }

  // Convenience methods.

  template<typename To> bool AncestorIsA(int generation) const {
    return GetAncestorAs<To>(generation) != nullptr;
  }

  // Returns true if this node or any of its ancestors contains a T*.
  template<typename T> bool HasAncestorOfType() const {
    for (const ASTNode* node = this; node != nullptr; node = node->parent_) {
      if (node->IsA<T>())
        return true;
    }
    return false;
  }

  template<typename To> const To* GetParentAs() const {
    return GetAncestorAs<To>(1);
  }

  template<typename To> bool ParentIsA() const {
    return AncestorIsA<To>(1);
  }

  template<typename To> const To* GetAs() const {
    return GetAncestorAs<To>(0);
  }

  template<typename To> bool IsA() const {
    return AncestorIsA<To>(0);
  }

 private:
  enum NodeKind {
    kDeclKind, kStmtKind, kTypeKind, kTypelocKind, kNNSKind, kNNSLocKind,
    kTemplateNameKind, kTemplateArgumentKind, kTemplateArgumentLocKind
  };

  // Returns a casted pointer if this object actually is of the given
  // type (or a subclass of the given type), and nullptr otherwise.  We
  // have to use overloading on To's kind_, in these helper
  // methods, in order to get llvm's dyn_cast to compile -- it gets
  // upset (at compile time, sadly) if from-type and to-type aren't in
  // the same type hierarchy.  So To must be specified both in the
  // template arg and in the method parameter.
  template<typename To> const To* DynCast(const clang::Decl*) const {
    if (kind_ != kDeclKind) return nullptr;
    return ::llvm::dyn_cast<To>(as_decl_);
  }
  template<typename To> const To* DynCast(const clang::Stmt*) const {
    if (kind_ != kStmtKind) return nullptr;
    return ::llvm::dyn_cast<To>(as_stmt_);
  }
  template<typename To> const To* DynCast(const clang::Type*) const {
    // We also will cast ourselves to a type if we're a typeloc.
    // This simplifies a lot of code lower down that doesn't care
    // to distinguish.  For code that *does* care to distinguish,
    // it should check for typelocs first:
    //   if (node.IsA<FooTypeLoc>()) ... else if (node.IsA<FooType>()) ...
    if (kind_ == kTypelocKind)
      return ::llvm::dyn_cast<To>(as_typeloc_->getTypePtr());
    if (kind_ != kTypeKind) return nullptr;
    return ::llvm::dyn_cast<To>(as_type_);
  }
  template<typename To> const To* DynCast(const clang::TypeLoc*) const {
    if (kind_ != kTypelocKind) return nullptr;
    return ::llvm::dyn_cast<To>(as_typeloc_);
  }
  template<typename To> const To* DynCast(
      const clang::NestedNameSpecifier*) const {
    // Like Type, this will cast to NNS if we're an NNSLoc.  For code
    // that cares to distinguish, it should check for nnslocs first.
    if (kind_ == kNNSLocKind)
      return as_nnsloc_->getNestedNameSpecifier();
    if (kind_ != kNNSKind) return nullptr;
    return as_nns_;
  }
  template<typename To> const To* DynCast(
      const clang::NestedNameSpecifierLoc*) const {
    if (kind_ != kNNSLocKind) return nullptr;
    return as_nnsloc_;
  }
  template<typename To> const To* DynCast(const clang::TemplateName*) const {
    if (kind_ != kTemplateNameKind) return nullptr;
    return as_template_name_;
  }
  template<typename To> const To* DynCast(
      const clang::TemplateArgument*) const {
    // We also will cast ourselves to a templateargument if we're a
    // templateargumentloc.  This simplifies a lot of code lower down
    // that doesn't care to distinguish.  For code that *does* care to
    // distinguish, it should check for typelocs first.
    if (kind_ == kTemplateArgumentLocKind)
      return &as_template_argloc_->getArgument();
    if (kind_ != kTemplateArgumentKind) return nullptr;
    return as_template_arg_;
  }
  template<typename To> const To* DynCast(
      const clang::TemplateArgumentLoc*) const {
    if (kind_ != kTemplateArgumentLocKind) return nullptr;
    return as_template_argloc_;
  }
  // We also allow casting to void*
  template<typename Ignored> const void* DynCast(const void*) const {
    switch (kind_) {   // this is just to avoid aliasing violations.
      case kDeclKind:  return as_decl_;
      case kStmtKind:  return as_stmt_;
      case kTypeKind:  return as_type_;
      case kTypelocKind:  return as_typeloc_;
      case kNNSKind:  return as_nns_;
      case kNNSLocKind:  return as_nnsloc_;
      case kTemplateNameKind:  return as_template_name_;
      case kTemplateArgumentKind:  return as_template_arg_;
      case kTemplateArgumentLocKind:  return as_template_argloc_;
    }
    CHECK_UNREACHABLE_("Unknown kind");
  }

  // If this node is of a type that knows its location, sets loc and
  // returns true, otherwise returns false and leaves loc unchanged.
  bool FillLocationIfKnown(clang::SourceLocation* loc) const;

  const NodeKind kind_;
  union {
    const clang::Decl* as_decl_;
    const clang::Stmt* as_stmt_;
    const clang::Type* as_type_;
    const clang::TypeLoc* as_typeloc_;
    const clang::NestedNameSpecifier* as_nns_;
    const clang::NestedNameSpecifierLoc* as_nnsloc_;
    const clang::TemplateName* as_template_name_;
    const clang::TemplateArgument* as_template_arg_;
    const clang::TemplateArgumentLoc* as_template_argloc_;
  };
  const ASTNode* parent_;
  bool in_fwd_decl_context_;
};

// --- Helper classes for ASTNode.

// An object of this type modifies a given variable in the constructor
// and restores its original value in the destructor.
template<typename T> class ValueSaver {
 public:
  ValueSaver(T* p, const T& newval) : ptr_(p), oldval_(*ptr_) {
    *ptr_ = newval;
  }
  // The one-arg version just uses the current value as newval.
  explicit ValueSaver(T* p) : ptr_(p), oldval_(*ptr_) { }

  ~ValueSaver() { *ptr_ = oldval_; }

 private:
  T* const ptr_;
  const T oldval_;
};

// An object of this type updates current_ast_node_ to be the given
// node, and sets the given node's parent to be the old
// current_ast_node_.  It then undoes this work in its destructor.
// The caller owns both old_current_node and new_current_node, and
// must make sure each of them lives at least as long as this object.
class CurrentASTNodeUpdater {
 public:
  CurrentASTNodeUpdater(ASTNode** old_current_node,
                        ASTNode* new_current_node)
      : old_current_node_value_(*old_current_node),
        node_saver_(old_current_node, new_current_node) {
    new_current_node->SetParent(old_current_node_value_);
  }

 private:
  ASTNode* const old_current_node_value_;
  const ValueSaver<ASTNode*> node_saver_;
};

// --- Utilities for ASTNode.

// See if a given ast_node is a 'real' ElaboratedType(Loc).  (An
// elaboration is 'class Foo myvar' instead of just 'Foo myvar'.)
// We avoid 'fake' elaborations that are caused because clang also
// uses ElaboratedType for namespaces ('ns::Foo myvar').
bool IsElaborationNode(const ASTNode* ast_node);

// Walk up to parents of the given node so long as each parent is an
// elaboration node (in the sense of IsElaborationNode).
// Can expand from a node representing 'X' to e.g. 'struct X' or 'mylib::X'.
const ASTNode* MostElaboratedAncestor(const ASTNode* ast_node);

// See if a given ast_node is a qualified name part of an ElaboratedType
// node (e.g. 'class ns::Foo x', 'class ::Global x' or 'class Outer::Inner x'.)
bool IsQualifiedNameNode(const ASTNode* ast_node);

// Return true if the given ast_node is inside a C++ method body.  Do
// this by walking up the AST tree until you find a CXXMethodDecl,
// then see if the node just before you reached it is the body.  We
// also check if the node is in an initializer (either explicitly or
// implicitly), or the implicit (non-body) code of a destructor.
bool IsNodeInsideCXXMethodBody(const ASTNode* ast_node);

// Return UseFlags for the current node.
// These flags provide context around the use to help later IWYU analysis,
UseFlags ComputeUseFlags(const ASTNode* ast_node);

// Return true if we're a nested class as written, that is, we're a
// class decl inside another class decl.  The parent class may be
// templated, but we should not be.  (We could extend the function to
// handle that case, but there's been no need yet.)
bool IsNestedClassAsWritten(const ASTNode* ast_node);

// Is ast_node the 'D' in the following:
//    template<template <typename A> class T = D> class C { ... }
// ('D' might be something like 'vector').
bool IsDefaultTemplateTemplateArg(const ASTNode* ast_node);

// Returns true if this node is a ConstructExpr that is being used to
// construct a field in a class (that is, it's part of a constructor
// initializer list).
bool IsCXXConstructExprInInitializer(const ASTNode* ast_node);

// Returns true if this node is a CXXConstructExpr in CXXNewExpr.
bool IsCXXConstructExprInNewExpr(const ASTNode* ast_node);

// If ASTNode is of a kind that has a qualifier (something that
// comes before the ::), return that, else return nullptr.
const clang::NestedNameSpecifier* GetQualifier(const ASTNode* ast_node);

// Returns true if any parent is a typedef: my_typedef.a, or
// MyTypedef::a, or MyTypedef::subclass::a, etc.  Note it does
// *not* return true if the ast_node itself is a typedef.
bool IsMemberOfATypedef(const ASTNode* ast_node);

// Returns the decl-context of the deepest decl in the ast-chain.
const clang::DeclContext* GetDeclContext(const ASTNode* ast_node);

//------------------------------------------------------------
// Helper functions for working with raw Clang AST nodes.

// --- Printers.

string PrintableLoc(clang::SourceLocation loc);
string PrintableDecl(const clang::Decl* decl, bool terse=true);
string PrintableStmt(const clang::Stmt* stmt);
string PrintableType(const clang::Type* type);
string PrintableTypeLoc(const clang::TypeLoc& typeloc);
string PrintableNestedNameSpecifier(const clang::NestedNameSpecifier* nns);
string PrintableTemplateName(const clang::TemplateName& tpl_name);
string PrintableTemplateArgument(const clang::TemplateArgument& arg);
string PrintableTemplateArgumentLoc(const clang::TemplateArgumentLoc& arg);
string PrintableASTNode(const ASTNode* node);
// This prints to errs().  It's useful for debugging (e.g. inside gdb).
void PrintASTNode(const ASTNode* node);
void PrintStmt(const clang::Stmt* stmt);

// Written name means name without unwritten scopes.  Unwritten scopes are
// anonymous and inline namespaces.  Always consider using
// GetWrittenQualifiedNameAsString instead of
// NamedDecl::getQualifiedNameAsString.
string GetWrittenQualifiedNameAsString(const clang::NamedDecl* named_decl);

// --- Type conversion utilities.

namespace internal {

// For implementing DynCastFrom() -- don't use directly.
template <typename T>
class DynCastPtr {
 public:
  explicit DynCastPtr(T* ptr) : ptr_(ptr) { }

  template <typename U> operator U*() const {
    return ::llvm::dyn_cast_or_null<U>(ptr_);
  }

 private:
  T* ptr_;
};

}  // namespace internal

// dyn_cast<> and dyn_cast_or_null<> require the user to write the
// type of the target pointer, which is redundant when the result is
// immediately assigned to a newly declared pointer variable of the
// target type (the typical case).  DynCastFrom() lets us omit the
// target type, e.g.
//
//   if (const CXXConstructExpr* expr = DynCastFrom(source_expr)) ...
//
// instead of
//
//   if (const CXXConstructExpr* expr =
//       dyn_cast_or_null<CXXConstructExpr>(source_expr)) ...
//
// For readability, DynCastFrom() should only be used as the
// initializer of a variable declaration, where the target type is
// obvious.
template <typename T>
internal::DynCastPtr<T> DynCastFrom(T* ptr) {
  return internal::DynCastPtr<T>(ptr);
}

// --- Utilities for Decl.

// Returns true if the decl is for a templatized function.
bool IsTemplatizedFunctionDecl(const clang::FunctionDecl* decl);

// Returns true if the given class has at least one implicit
// conversion constructor.
bool HasImplicitConversionCtor(const clang::CXXRecordDecl* cxx_class);

// Returns true if the given method is an override with covariant return type
// compared to its base.
bool HasCovariantReturnType(const clang::CXXMethodDecl* method_decl);

// If this decl is a (possibly templatized) class, return the decl
// that defines the class, if present.  Otherwise return nullptr.
const clang::RecordDecl* GetDefinitionForClass(const clang::Decl* decl);

// Given a class, returns a SourceRange that encompasses the beginning
// of the class declaration (including template<> prefix, etc) to the
// class name.  Used to determine where forward-declares are.
clang::SourceRange GetSourceRangeOfClassDecl(const clang::Decl* decl);

// Collect all late-parsed function templates in a translation unit.
set<clang::FunctionDecl*> GetLateParsedFunctionDecls(
    clang::TranslationUnitDecl* decl);

// One can't have partial template specialization or default template
// args for function templates, but they're complicated in their own
// way: they can have deduced template arguments (deduced from the
// function arguments).  When a templated function call does not
// specify the template arguments explicitly, but instead derives them
// from the function arguments, clang canonicalizes (desugars) the
// template args.  For
//    template<class T> void MyFunc(T t) { ... }
//    MyFunc(typedef_type)
// clang will say this is a call to MyFunc<canonical_type>().  Also:
//    MyFunc(my_int_vector)
// clang will say this is MyFunc<vector<int, alloc<int>>(), with
// no indication that alloc<int> is actually a default parameter.
// Equally bad:
//    template<class T> void OtherFunc(MyClass<T> t) { ... }
//    typedef MyClass<Foo> FooClass;
//    OtherFunc(my_foo_class);
// clang will see T as MyClass<Foo> even though my_foo_class hides the
// use of Foo through the typedef.
//    This routine attempts to solve all these problems by looking at
// the type-as-written for the actual arguments (and return value) to
// try to reverse engineer the derived-argument matching that was
// done.  (It's easy in the rare cases the template args are
// explicitly specified.)  It returns a map from the unsugared
// (canonical) types of each template argument to its sugared
// (as-written) type.  For now we ignore non-type template args.
// We also include mappings for component types: if we have an entry
// 'vector<TypedefType>' -> 'vector<Foo>', we also add an entry
// 'TypedefType' -> 'Foo'.
// NOTE: This routine is far from perfect.  To really do this right,
// we'd need to refactor SemaTemplateDeduction to take an argument to
// not canonicalize deduced template arguments.
// calling_expr should be a CallExpr, CXXConstructExpr, or DeclRefExpr.
map<const clang::Type*, const clang::Type*> GetTplTypeResugarMapForFunction(
    const clang::FunctionDecl* decl, const clang::Expr* calling_expr);

// If class_decl is instantiated from a class template,
// returns the decl for that template; otherwise returns class_decl.
// As an example, consider this code:
//    template<class T> class Foo { ... };   // template decl
//    template<> class Foo<int> { ... };     // explicit specialization
//    template class Foo<char>;              // note: no body specified
//    Foo<int> v1;
//    Foo<float> v2;
//    Foo<char> v3;
// The types Foo<int>, Foo<float>, and Foo<char> all have a
// corresponding decl.  In the case of Foo<int>, the decl is the
// explicit specialization.  This is *not* a decl that this routine
// deals with; the routine will just return its argument in this case.
// But in the case of Foo<float>, the decl is an implicit
// instantiation of Foo<T>, and this routine will return the decl for
// Foo<T>.  Foo<char> is a rarer corner case: an explicit
// instantiation definition (it just causes code for that template
// case to be generated).  It is treated the same as Foo<float>: we
// return the template decl, which provides the actual class body.
// We try to return a decl that's also a definition, when possible.
const clang::NamedDecl* GetInstantiatedFromDecl(
    const clang::CXXRecordDecl* class_decl);

// For an implicitly instantiated templated c++ class -- that is, a
// class like vector<int> that isn't explicitly written in the source
// code but instead derived from vector<T> -- returns the
// class-as-written (vector<T>, in this case).  For an implicitly
// instantiated template function -- Fn<int> when all that is written
// in the source code is Fn<T> -- returns the function-as-written.
// (In each case, prefers the definition of the class or function
// as-written, rather than a forward-declaration.)  Otherwise, returns
// the original input.
const clang::NamedDecl* GetDefinitionAsWritten(const clang::NamedDecl* decl);

// Returns true if this decl is part of a friend decl.
bool IsFriendDecl(const clang::Decl* decl);

// Returns true if this decl is an explicit template instantiation declaration
// or definition.
bool IsExplicitInstantiation(const clang::Decl* decl);

// Returns true if this decl is nested inside an inline namespace.
bool IsInInlineNamespace(const clang::Decl* decl);

// Returns true if a named decl looks like a forward-declaration of a
// class (rather than a definition, a friend declaration, or an 'in
// place' declaration like 'struct Foo' in 'void MyFunc(struct Foo*);'
// Always returns false for enums.
bool IsForwardDecl(const clang::NamedDecl* decl);

// Returns true if this decl is defined inside another class/struct.
// Unlike IsNestedClassAsWritten(), which works on an ASTNode, this
// function considers decl to be nested even if it's not syntactically
// written inside its outer class (that is, 'class Foo::Bar {...}' is
// considered nested, even though it's not written inside Foo).
bool IsNestedClass(const clang::TagDecl* decl);

bool HasDefaultTemplateParameters(const clang::TemplateDecl* decl);

// For any decl that inherits from clang::Redeclarable *except* for
// classes and class templates -- enums, typedefs, functions, vars --
// returns all the declarations of decl.  For any other decl, the
// output is just the input decl.  Output decls are guaranteed to be
// of the same type as the input Decl.  Because iwyu fundamentally
// treats classes different from other redeclarable types, it has
// its own separate function.  (If that proves to be annoying, we
// can merge them.)
set<const clang::NamedDecl*> GetNonclassRedecls(const clang::NamedDecl* decl);

// Given a class, returns a set of all declarations of that class
// (forward-declarations and, if present, the definition).  This
// accepts both RecordDecls and ClassTemplateDecls -- the return Decls
// are guaranteed to be of the same type as the input Decl.  Returns
// the empty set if the input is not a RecordDecl or
// ClassTemplateDecl.  Otherwise, always returns at least one element
// (since the input decl is its own redecl).
set<const clang::NamedDecl*> GetClassRedecls(const clang::NamedDecl* decl);

// Returns the redecl of decl that occurs first in the translation
// unit (that is, is the first one you'd see if you did 'cc -E').
// Returns nullptr if the input is not a RecordDecl or ClassTemplateDecl.
const clang::NamedDecl* GetFirstRedecl(const clang::NamedDecl* decl);

// Given a class or class template, returns the declaration of that
// class that specifies the values of the default template arguments.
// If there are no default template arguments, returns nullptr.
const clang::ClassTemplateDecl* GetClassRedeclSpecifyingDefaultTplArgs(
    const clang::ClassTemplateDecl* decl);

// Picks one redecl from GetClassRedecls() arbitrarily.
// This is used to recover from the clang bug that mixes friend decls
// with 'real' redecls (http://llvm.org/bugs/show_bug.cgi?id=8669);
// this function returns a 'real' redecl.  If the input decl is a
// friend decl, returns an arbitrary non-friend redecl of it;
// otherwise returns decl itself.
// TODO(csilvers): remove once PR 8669 is fixed.
const clang::NamedDecl* GetNonfriendClassRedecl(const clang::NamedDecl* decl);

// Returns true if the innermost DeclContext for each decl is the
// same, and it's a class (or struct).
bool DeclsAreInSameClass(const clang::Decl* decl1, const clang::Decl* decl2);

// Returns true if the given decl/name is a builtin function
bool IsBuiltinFunction(const clang::NamedDecl* decl);

// --- Utilities for Type.

const clang::Type* GetTypeOf(const clang::Expr* expr);
// Returns the type of the constructed class.
const clang::Type* GetTypeOf(const clang::CXXConstructExpr* expr);
// Returns the type of the given variable, function, or enum declaration.
const clang::Type* GetTypeOf(const clang::ValueDecl* decl);
// ...or class, struct, union, enum, typedef, or template type.
const clang::Type* GetTypeOf(const clang::TypeDecl* decl);

// Template parameters are always reduced to the canonical type.
const clang::Type* GetCanonicalType(const clang::Type* type);

// A 'component' of a type is a type beneath it in the AST tree.
// So 'Foo*' has component 'Foo', as does 'vector<Foo>', while
// vector<pair<Foo, Bar>> has components pair<Foo,Bar>, Foo, and Bar.
set<const clang::Type*> GetComponentsOfType(const clang::Type* type);

// The ElaborationType -- which says whether a type is preceded by
// 'class' or 'struct' ('class Foo'), or whether the type-name has a
// namespace ('ns::Foo') -- often pops where it's not wanted.  This
// removes the elaboration if it exists, else it's a noop.  Note that
// if the type has both kinds of elaborations ('struct ns::Foo'), they
// will both be removed.
const clang::Type* RemoveElaboration(const clang::Type* type);

// Returns true if the type has any template arguments.
bool IsTemplatizedType(const clang::Type* type);

// Returns true if the type is a RecordType or a TemplateSpecializationType.
bool IsClassType(const clang::Type* type);

// Read past SubstTemplateTypeParmType to the underlying type, if type
// is itself a SubstTemplateTypeParmType.  Thus: T is converted to int
// if we are parsing a template instantiated with T being int.
// However, vector<T> is *not* converted to vector<int>.
const clang::Type* RemoveSubstTemplateTypeParm(const clang::Type* type);

// Returns true if any type involved (recursively examining template
// arguments) satisfies the given predicate.
bool InvolvesTypeForWhich(const clang::Type* type,
                          std::function<bool(const clang::Type*)> pred);

// Returns true if type is a pointer type (pointer or reference,
// looking through elaborations like 'class Foo*' (vs 'Foo*'),
// but *not* following typedefs (which is why we can't just use
// type->isPointerType()).
// TODO(csilvers): what about array-type?
bool IsPointerOrReferenceAsWritten(const clang::Type* type);

const clang::Type* RemoveReferenceAsWritten(const clang::Type* type);

// Gets rid of all the pointers and references to get to the 'base'
// type.  Also removes all elaborations (like 'class' keyword).  We
// can't just use the default desugar() routine, because we *don't*
// want to look through typedefs.
const clang::Type* RemovePointersAndReferencesAsWritten(
    const clang::Type* type);

// Remove one layer of pointers (or references) from type.  We go
// through typedefs and the like, but only if we have to in order to
// figure out the dereferenced type, which is why we don't just use
// desugar().  Returns nullptr if not a pointer.
const clang::Type* RemovePointerFromType(const clang::Type* type);

// This follows typedefs/etc to remove pointers, if necessary.
const clang::Type* RemovePointersAndReferences(const clang::Type* type);

// To the best of our ability, maps a type to a declaration of that
// type *that is written in the source code*.  For most types, of
// course, declarations are always written, but for implicit template
// specializations they typically aren't.  For instance, if the type
// is vector<int>, there is a decl for 'class vector<int>', but it's
// not written anywhere; what is written is generic code, for
// 'class vector<T>'.  'class vector<int>' is an implicit declaration,
// constructed at need.  This routine does not return that implicit
// declaration.
//    If the type is a substituted template parameter, we get a
// decl for the substituted type.  That is, for this code:
//   template<class T> void MyFunc() { T foo; }
//   MyFunc<MyClass>();
// If we're evaluating MyFunc<MyClass>() and see the type that's in
// the function body, this function returns a decl for MyClass.
//    If the type is built-in, or otherwise doesn't have a decl,
// this function returns nullptr.
const clang::NamedDecl* TypeToDeclAsWritten(const clang::Type* type);

// This is similar to TypeToDeclAsWritten, but in this case we are less
// interested in how the type was written; we want the Decl which we can
// explore the contents of, for example to determine which of its template
// arguments are used in a manner that constitutes a full use.
//
// The difference arises particularly for type aliases, where
// TypeToDeclAsWritten returns the Decl for the alias, whereas
// TypeToDeclForContent returns the underlying aliased Decl.
const clang::NamedDecl* TypeToDeclForContent(const clang::Type* type);

// Returns true if it's possible to implicitly convert a value of a
// different type to 'type' via an implicit constructor.
bool HasImplicitConversionConstructor(const clang::Type* type);

// clang desugars template arguments: follows typedefs, etc.  We
// want the unsugared type, so this function provides a map from
// the desugared type back to the original type-as-written, as
// determined from the class's template arguments.  For default
// template arguments that are not specified by the caller, we
// map the type to nullptr, to indicate there's no inherent sugaring.
// We also include mappings for component types: if we have an entry
// 'vector<TypedefType>' -> 'vector<Foo>', we also add an entry
// 'TypedefType' -> 'Foo'.
// For ease of calling, this accept any type, but will return an empty
// map for any input that's not a template specialization type.
map<const clang::Type*, const clang::Type*> GetTplTypeResugarMapForClass(
    const clang::Type* type);

// Like GetTplTypeResugarMapForClass, but if a type has
// components (for instance, 'Foo*' and 'vector<Foo>' both
// have a component Foo), we don't include the components
// in the result-map.
map<const clang::Type*, const clang::Type*>
GetTplTypeResugarMapForClassNoComponentTypes(const clang::Type* type);

// --- Utilities for Stmt.

// Returns true if the given expr is '&<something>'.
bool IsAddressOf(const clang::Expr* expr);

// If this function call comes from a class method -- either a normal
// one or a static one -- returns the type of the class.  Otherwise,
// returns nullptr.  Note that static class methods do *not* have a
// CXXMemberCallExpr type, which is why we take a CallExpr.
const clang::Type* TypeOfParentIfMethod(const clang::CallExpr* expr);

// Given a function call, return the first argument that's a class
// (possibly a template specialization).  Note we ignore pointers to a
// class.  This is used with 'free' overloaded operators ('ostream&
// operator<<(ostream& a, int x)' to figure out what class the
// operator 'logically' belongs to.  This is a heuristic (the operator
// may "belong" to more than one argument, for instance), but covers
// all the common cases.  Returns nullptr if no class-type argument is
// found.
const clang::Expr* GetFirstClassArgument(clang::CallExpr* expr);

// Returns nullptr if we're deleting an argument that has no destructor.
const clang::CXXDestructorDecl* GetDestructorForDeleteExpr(
    const clang::CXXDeleteExpr* expr);

// Returns nullptr if the constructor has no corresponding destructor.
const clang::CXXDestructorDecl* GetSiblingDestructorFor(
    const clang::CXXConstructorDecl* ctor);
const clang::CXXDestructorDecl* GetSiblingDestructorFor(
    const clang::CXXConstructExpr* ctor_expr);

// Figuring out the function type is non-trivial because the callee
// may be a function pointer.  This code is based on clang's Expr.cpp.
// Should never return nullptr.
const clang::FunctionType* GetCalleeFunctionType(clang::CallExpr* expr);

// Returns the list of explicit template args for all exprs that support
// such a concept (declrefexpr, memberexpr), and empty list if none is present.
clang::TemplateArgumentListInfo GetExplicitTplArgs(const clang::Expr* expr);

// Workaround for https://github.com/llvm/llvm-project/issues/53044. Remove this
// wrapper in favor of Expr::getConversionFunction  when that is fixed upstream.
const clang::NamedDecl* GetConversionFunction(const clang::CastExpr* expr);

}  // namespace include_what_you_use

#endif  // INCLUDE_WHAT_YOU_USE_IWYU_AST_UTIL_H_
