//===--- AST.cpp - Utility AST functions  -----------------------*- 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
//
//===----------------------------------------------------------------------===//

#include "AST.h"

#include "SourceCode.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTTypeTraits.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/DeclarationName.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/NestedNameSpecifier.h"
#include "clang/AST/PrettyPrinter.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/TemplateBase.h"
#include "clang/AST/TypeLoc.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/Specifiers.h"
#include "clang/Index/USRGeneration.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/raw_ostream.h"
#include <iterator>
#include <optional>
#include <string>
#include <vector>

namespace clang {
namespace clangd {

namespace {
std::optional<llvm::ArrayRef<TemplateArgumentLoc>>
getTemplateSpecializationArgLocs(const NamedDecl &ND) {
  if (auto *Func = llvm::dyn_cast<FunctionDecl>(&ND)) {
    if (const ASTTemplateArgumentListInfo *Args =
            Func->getTemplateSpecializationArgsAsWritten())
      return Args->arguments();
  } else if (auto *Cls =
                 llvm::dyn_cast<ClassTemplatePartialSpecializationDecl>(&ND)) {
    if (auto *Args = Cls->getTemplateArgsAsWritten())
      return Args->arguments();
  } else if (auto *Var =
                 llvm::dyn_cast<VarTemplatePartialSpecializationDecl>(&ND)) {
    if (auto *Args = Var->getTemplateArgsAsWritten())
      return Args->arguments();
  } else if (auto *Var = llvm::dyn_cast<VarTemplateSpecializationDecl>(&ND)) {
    if (auto *Args = Var->getTemplateArgsInfo())
      return Args->arguments();
  }
  // We return std::nullopt for ClassTemplateSpecializationDecls because it does
  // not contain TemplateArgumentLoc information.
  return std::nullopt;
}

template <class T>
bool isTemplateSpecializationKind(const NamedDecl *D,
                                  TemplateSpecializationKind Kind) {
  if (const auto *TD = dyn_cast<T>(D))
    return TD->getTemplateSpecializationKind() == Kind;
  return false;
}

bool isTemplateSpecializationKind(const NamedDecl *D,
                                  TemplateSpecializationKind Kind) {
  return isTemplateSpecializationKind<FunctionDecl>(D, Kind) ||
         isTemplateSpecializationKind<CXXRecordDecl>(D, Kind) ||
         isTemplateSpecializationKind<VarDecl>(D, Kind);
}

// Store all UsingDirectiveDecls in parent contexts of DestContext, that were
// introduced before InsertionPoint.
llvm::DenseSet<const NamespaceDecl *>
getUsingNamespaceDirectives(const DeclContext *DestContext,
                            SourceLocation Until) {
  const auto &SM = DestContext->getParentASTContext().getSourceManager();
  llvm::DenseSet<const NamespaceDecl *> VisibleNamespaceDecls;
  for (const auto *DC = DestContext; DC; DC = DC->getLookupParent()) {
    for (const auto *D : DC->decls()) {
      if (!SM.isWrittenInSameFile(D->getLocation(), Until) ||
          !SM.isBeforeInTranslationUnit(D->getLocation(), Until))
        continue;
      if (auto *UDD = llvm::dyn_cast<UsingDirectiveDecl>(D))
        VisibleNamespaceDecls.insert(
            UDD->getNominatedNamespace()->getCanonicalDecl());
    }
  }
  return VisibleNamespaceDecls;
}

// Goes over all parents of SourceContext until we find a common ancestor for
// DestContext and SourceContext. Any qualifier including and above common
// ancestor is redundant, therefore we stop at lowest common ancestor.
// In addition to that stops early whenever IsVisible returns true. This can be
// used to implement support for "using namespace" decls.
std::string
getQualification(ASTContext &Context, const DeclContext *DestContext,
                 const DeclContext *SourceContext,
                 llvm::function_ref<bool(NestedNameSpecifier *)> IsVisible) {
  std::vector<const NestedNameSpecifier *> Parents;
  bool ReachedNS = false;
  for (const DeclContext *CurContext = SourceContext; CurContext;
       CurContext = CurContext->getLookupParent()) {
    // Stop once we reach a common ancestor.
    if (CurContext->Encloses(DestContext))
      break;

    NestedNameSpecifier *NNS = nullptr;
    if (auto *TD = llvm::dyn_cast<TagDecl>(CurContext)) {
      // There can't be any more tag parents after hitting a namespace.
      assert(!ReachedNS);
      (void)ReachedNS;
      NNS = NestedNameSpecifier::Create(Context, nullptr, false,
                                        TD->getTypeForDecl());
    } else if (auto *NSD = llvm::dyn_cast<NamespaceDecl>(CurContext)) {
      ReachedNS = true;
      NNS = NestedNameSpecifier::Create(Context, nullptr, NSD);
      // Anonymous and inline namespace names are not spelled while qualifying
      // a name, so skip those.
      if (NSD->isAnonymousNamespace() || NSD->isInlineNamespace())
        continue;
    } else {
      // Other types of contexts cannot be spelled in code, just skip over
      // them.
      continue;
    }
    // Stop if this namespace is already visible at DestContext.
    if (IsVisible(NNS))
      break;

    Parents.push_back(NNS);
  }

  // Go over name-specifiers in reverse order to create necessary qualification,
  // since we stored inner-most parent first.
  std::string Result;
  llvm::raw_string_ostream OS(Result);
  for (const auto *Parent : llvm::reverse(Parents))
    Parent->print(OS, Context.getPrintingPolicy());
  return OS.str();
}

} // namespace

bool isImplicitTemplateInstantiation(const NamedDecl *D) {
  return isTemplateSpecializationKind(D, TSK_ImplicitInstantiation);
}

bool isExplicitTemplateSpecialization(const NamedDecl *D) {
  return isTemplateSpecializationKind(D, TSK_ExplicitSpecialization);
}

bool isImplementationDetail(const Decl *D) {
  return !isSpelledInSource(D->getLocation(),
                            D->getASTContext().getSourceManager());
}

SourceLocation nameLocation(const clang::Decl &D, const SourceManager &SM) {
  auto L = D.getLocation();
  // For `- (void)foo` we want `foo` not the `-`.
  if (const auto *MD = dyn_cast<ObjCMethodDecl>(&D))
    L = MD->getSelectorStartLoc();
  if (isSpelledInSource(L, SM))
    return SM.getSpellingLoc(L);
  return SM.getExpansionLoc(L);
}

std::string printQualifiedName(const NamedDecl &ND) {
  std::string QName;
  llvm::raw_string_ostream OS(QName);
  PrintingPolicy Policy(ND.getASTContext().getLangOpts());
  // Note that inline namespaces are treated as transparent scopes. This
  // reflects the way they're most commonly used for lookup. Ideally we'd
  // include them, but at query time it's hard to find all the inline
  // namespaces to query: the preamble doesn't have a dedicated list.
  Policy.SuppressUnwrittenScope = true;
  // (unnamed struct), not (unnamed struct at /path/to/foo.cc:42:1).
  // In clangd, context is usually available and paths are mostly noise.
  Policy.AnonymousTagLocations = false;
  ND.printQualifiedName(OS, Policy);
  OS.flush();
  assert(!StringRef(QName).startswith("::"));
  return QName;
}

static bool isAnonymous(const DeclarationName &N) {
  return N.isIdentifier() && !N.getAsIdentifierInfo();
}

NestedNameSpecifierLoc getQualifierLoc(const NamedDecl &ND) {
  if (auto *V = llvm::dyn_cast<DeclaratorDecl>(&ND))
    return V->getQualifierLoc();
  if (auto *T = llvm::dyn_cast<TagDecl>(&ND))
    return T->getQualifierLoc();
  return NestedNameSpecifierLoc();
}

std::string printUsingNamespaceName(const ASTContext &Ctx,
                                    const UsingDirectiveDecl &D) {
  PrintingPolicy PP(Ctx.getLangOpts());
  std::string Name;
  llvm::raw_string_ostream Out(Name);

  if (auto *Qual = D.getQualifier())
    Qual->print(Out, PP);
  D.getNominatedNamespaceAsWritten()->printName(Out);
  return Out.str();
}

std::string printName(const ASTContext &Ctx, const NamedDecl &ND) {
  std::string Name;
  llvm::raw_string_ostream Out(Name);
  PrintingPolicy PP(Ctx.getLangOpts());
  // We don't consider a class template's args part of the constructor name.
  PP.SuppressTemplateArgsInCXXConstructors = true;

  // Handle 'using namespace'. They all have the same name - <using-directive>.
  if (auto *UD = llvm::dyn_cast<UsingDirectiveDecl>(&ND)) {
    Out << "using namespace ";
    if (auto *Qual = UD->getQualifier())
      Qual->print(Out, PP);
    UD->getNominatedNamespaceAsWritten()->printName(Out);
    return Out.str();
  }

  if (isAnonymous(ND.getDeclName())) {
    // Come up with a presentation for an anonymous entity.
    if (isa<NamespaceDecl>(ND))
      return "(anonymous namespace)";
    if (auto *Cls = llvm::dyn_cast<RecordDecl>(&ND)) {
      if (Cls->isLambda())
        return "(lambda)";
      return ("(anonymous " + Cls->getKindName() + ")").str();
    }
    if (isa<EnumDecl>(ND))
      return "(anonymous enum)";
    return "(anonymous)";
  }

  // Print nested name qualifier if it was written in the source code.
  if (auto *Qualifier = getQualifierLoc(ND).getNestedNameSpecifier())
    Qualifier->print(Out, PP);
  // Print the name itself.
  ND.getDeclName().print(Out, PP);
  // Print template arguments.
  Out << printTemplateSpecializationArgs(ND);

  return Out.str();
}

std::string printTemplateSpecializationArgs(const NamedDecl &ND) {
  std::string TemplateArgs;
  llvm::raw_string_ostream OS(TemplateArgs);
  PrintingPolicy Policy(ND.getASTContext().getLangOpts());
  if (std::optional<llvm::ArrayRef<TemplateArgumentLoc>> Args =
          getTemplateSpecializationArgLocs(ND)) {
    printTemplateArgumentList(OS, *Args, Policy);
  } else if (auto *Cls = llvm::dyn_cast<ClassTemplateSpecializationDecl>(&ND)) {
    if (const TypeSourceInfo *TSI = Cls->getTypeAsWritten()) {
      // ClassTemplateSpecializationDecls do not contain
      // TemplateArgumentTypeLocs, they only have TemplateArgumentTypes. So we
      // create a new argument location list from TypeSourceInfo.
      auto STL = TSI->getTypeLoc().getAs<TemplateSpecializationTypeLoc>();
      llvm::SmallVector<TemplateArgumentLoc> ArgLocs;
      ArgLocs.reserve(STL.getNumArgs());
      for (unsigned I = 0; I < STL.getNumArgs(); ++I)
        ArgLocs.push_back(STL.getArgLoc(I));
      printTemplateArgumentList(OS, ArgLocs, Policy);
    } else {
      // FIXME: Fix cases when getTypeAsWritten returns null inside clang AST,
      // e.g. friend decls. Currently we fallback to Template Arguments without
      // location information.
      printTemplateArgumentList(OS, Cls->getTemplateArgs().asArray(), Policy);
    }
  }
  OS.flush();
  return TemplateArgs;
}

std::string printNamespaceScope(const DeclContext &DC) {
  for (const auto *Ctx = &DC; Ctx != nullptr; Ctx = Ctx->getParent())
    if (const auto *NS = dyn_cast<NamespaceDecl>(Ctx))
      if (!NS->isAnonymousNamespace() && !NS->isInlineNamespace())
        return printQualifiedName(*NS) + "::";
  return "";
}

static llvm::StringRef
getNameOrErrForObjCInterface(const ObjCInterfaceDecl *ID) {
  return ID ? ID->getName() : "<<error-type>>";
}

std::string printObjCMethod(const ObjCMethodDecl &Method) {
  std::string Name;
  llvm::raw_string_ostream OS(Name);

  OS << (Method.isInstanceMethod() ? '-' : '+') << '[';

  // Should always be true.
  if (const ObjCContainerDecl *C =
          dyn_cast<ObjCContainerDecl>(Method.getDeclContext()))
    OS << printObjCContainer(*C);

  Method.getSelector().print(OS << ' ');
  if (Method.isVariadic())
    OS << ", ...";

  OS << ']';
  OS.flush();
  return Name;
}

std::string printObjCContainer(const ObjCContainerDecl &C) {
  if (const ObjCCategoryDecl *Category = dyn_cast<ObjCCategoryDecl>(&C)) {
    std::string Name;
    llvm::raw_string_ostream OS(Name);
    const ObjCInterfaceDecl *Class = Category->getClassInterface();
    OS << getNameOrErrForObjCInterface(Class) << '(' << Category->getName()
       << ')';
    OS.flush();
    return Name;
  }
  if (const ObjCCategoryImplDecl *CID = dyn_cast<ObjCCategoryImplDecl>(&C)) {
    std::string Name;
    llvm::raw_string_ostream OS(Name);
    const ObjCInterfaceDecl *Class = CID->getClassInterface();
    OS << getNameOrErrForObjCInterface(Class) << '(' << CID->getName() << ')';
    OS.flush();
    return Name;
  }
  return C.getNameAsString();
}

SymbolID getSymbolID(const Decl *D) {
  llvm::SmallString<128> USR;
  if (index::generateUSRForDecl(D, USR))
    return {};
  return SymbolID(USR);
}

SymbolID getSymbolID(const llvm::StringRef MacroName, const MacroInfo *MI,
                     const SourceManager &SM) {
  if (MI == nullptr)
    return {};
  llvm::SmallString<128> USR;
  if (index::generateUSRForMacro(MacroName, MI->getDefinitionLoc(), SM, USR))
    return {};
  return SymbolID(USR);
}

const ObjCImplDecl *getCorrespondingObjCImpl(const ObjCContainerDecl *D) {
  if (const auto *ID = dyn_cast<ObjCInterfaceDecl>(D))
    return ID->getImplementation();
  if (const auto *CD = dyn_cast<ObjCCategoryDecl>(D)) {
    if (CD->IsClassExtension()) {
      if (const auto *ID = CD->getClassInterface())
        return ID->getImplementation();
      return nullptr;
    }
    return CD->getImplementation();
  }
  return nullptr;
}

Symbol::IncludeDirective
preferredIncludeDirective(llvm::StringRef FileName, const LangOptions &LangOpts,
                          ArrayRef<Inclusion> MainFileIncludes,
                          ArrayRef<const Decl *> TopLevelDecls) {
  // Always prefer #include for non-ObjC code.
  if (!LangOpts.ObjC)
    return Symbol::IncludeDirective::Include;
  // If this is not a header file and has ObjC set as the language, prefer
  // #import.
  if (!isHeaderFile(FileName, LangOpts))
    return Symbol::IncludeDirective::Import;

  // Headers lack proper compile flags most of the time, so we might treat a
  // header as ObjC accidentally. Perform some extra checks to make sure this
  // works.

  // Any file with a #import, should keep #import-ing.
  for (auto &Inc : MainFileIncludes)
    if (Inc.Directive == tok::pp_import)
      return Symbol::IncludeDirective::Import;

  // Any file declaring an ObjC decl should also be #import-ing.
  // No need to look over the references, as the file doesn't have any #imports,
  // it must be declaring interesting ObjC-like decls.
  for (const Decl *D : TopLevelDecls)
    if (isa<ObjCContainerDecl, ObjCIvarDecl, ObjCMethodDecl, ObjCPropertyDecl>(
            D))
      return Symbol::IncludeDirective::Import;

  return Symbol::IncludeDirective::Include;
}

std::string printType(const QualType QT, const DeclContext &CurContext,
                      const llvm::StringRef Placeholder) {
  std::string Result;
  llvm::raw_string_ostream OS(Result);
  PrintingPolicy PP(CurContext.getParentASTContext().getPrintingPolicy());
  PP.SuppressTagKeyword = true;
  PP.SuppressUnwrittenScope = true;

  class PrintCB : public PrintingCallbacks {
  public:
    PrintCB(const DeclContext *CurContext) : CurContext(CurContext) {}
    virtual ~PrintCB() {}
    bool isScopeVisible(const DeclContext *DC) const override {
      return DC->Encloses(CurContext);
    }

  private:
    const DeclContext *CurContext;
  };
  PrintCB PCB(&CurContext);
  PP.Callbacks = &PCB;

  QT.print(OS, PP, Placeholder);
  return OS.str();
}

bool hasReservedName(const Decl &D) {
  if (const auto *ND = llvm::dyn_cast<NamedDecl>(&D))
    if (const auto *II = ND->getIdentifier())
      return isReservedName(II->getName());
  return false;
}

bool hasReservedScope(const DeclContext &DC) {
  for (const DeclContext *D = &DC; D; D = D->getParent()) {
    if (D->isTransparentContext() || D->isInlineNamespace())
      continue;
    if (const auto *ND = llvm::dyn_cast<NamedDecl>(D))
      if (hasReservedName(*ND))
        return true;
  }
  return false;
}

QualType declaredType(const TypeDecl *D) {
  if (const auto *CTSD = llvm::dyn_cast<ClassTemplateSpecializationDecl>(D))
    if (const auto *TSI = CTSD->getTypeAsWritten())
      return TSI->getType();
  return D->getASTContext().getTypeDeclType(D);
}

namespace {
/// Computes the deduced type at a given location by visiting the relevant
/// nodes. We use this to display the actual type when hovering over an "auto"
/// keyword or "decltype()" expression.
/// FIXME: This could have been a lot simpler by visiting AutoTypeLocs but it
/// seems that the AutoTypeLocs that can be visited along with their AutoType do
/// not have the deduced type set. Instead, we have to go to the appropriate
/// DeclaratorDecl/FunctionDecl and work our back to the AutoType that does have
/// a deduced type set. The AST should be improved to simplify this scenario.
class DeducedTypeVisitor : public RecursiveASTVisitor<DeducedTypeVisitor> {
  SourceLocation SearchedLocation;

public:
  DeducedTypeVisitor(SourceLocation SearchedLocation)
      : SearchedLocation(SearchedLocation) {}

  // Handle auto initializers:
  //- auto i = 1;
  //- decltype(auto) i = 1;
  //- auto& i = 1;
  //- auto* i = &a;
  bool VisitDeclaratorDecl(DeclaratorDecl *D) {
    if (!D->getTypeSourceInfo() ||
        !D->getTypeSourceInfo()->getTypeLoc().getContainedAutoTypeLoc() ||
        D->getTypeSourceInfo()
                ->getTypeLoc()
                .getContainedAutoTypeLoc()
                .getNameLoc() != SearchedLocation)
      return true;

    if (auto *AT = D->getType()->getContainedAutoType()) {
      DeducedType = AT->desugar();
    }
    return true;
  }

  // Handle auto return types:
  //- auto foo() {}
  //- auto& foo() {}
  //- auto foo() -> int {}
  //- auto foo() -> decltype(1+1) {}
  //- operator auto() const { return 10; }
  bool VisitFunctionDecl(FunctionDecl *D) {
    if (!D->getTypeSourceInfo())
      return true;
    // Loc of auto in return type (c++14).
    auto CurLoc = D->getReturnTypeSourceRange().getBegin();
    // Loc of "auto" in operator auto()
    if (CurLoc.isInvalid() && isa<CXXConversionDecl>(D))
      CurLoc = D->getTypeSourceInfo()->getTypeLoc().getBeginLoc();
    // Loc of "auto" in function with trailing return type (c++11).
    if (CurLoc.isInvalid())
      CurLoc = D->getSourceRange().getBegin();
    if (CurLoc != SearchedLocation)
      return true;

    const AutoType *AT = D->getReturnType()->getContainedAutoType();
    if (AT && !AT->getDeducedType().isNull()) {
      DeducedType = AT->getDeducedType();
    } else if (auto *DT = dyn_cast<DecltypeType>(D->getReturnType())) {
      // auto in a trailing return type just points to a DecltypeType and
      // getContainedAutoType does not unwrap it.
      if (!DT->getUnderlyingType().isNull())
        DeducedType = DT->getUnderlyingType();
    } else if (!D->getReturnType().isNull()) {
      DeducedType = D->getReturnType();
    }
    return true;
  }

  // Handle non-auto decltype, e.g.:
  // - auto foo() -> decltype(expr) {}
  // - decltype(expr);
  bool VisitDecltypeTypeLoc(DecltypeTypeLoc TL) {
    if (TL.getBeginLoc() != SearchedLocation)
      return true;

    // A DecltypeType's underlying type can be another DecltypeType! E.g.
    //  int I = 0;
    //  decltype(I) J = I;
    //  decltype(J) K = J;
    const DecltypeType *DT = dyn_cast<DecltypeType>(TL.getTypePtr());
    while (DT && !DT->getUnderlyingType().isNull()) {
      DeducedType = DT->getUnderlyingType();
      DT = dyn_cast<DecltypeType>(DeducedType.getTypePtr());
    }
    return true;
  }

  // Handle functions/lambdas with `auto` typed parameters.
  // We deduce the type if there's exactly one instantiation visible.
  bool VisitParmVarDecl(ParmVarDecl *PVD) {
    if (!PVD->getType()->isDependentType())
      return true;
    // 'auto' here does not name an AutoType, but an implicit template param.
    TemplateTypeParmTypeLoc Auto =
        getContainedAutoParamType(PVD->getTypeSourceInfo()->getTypeLoc());
    if (Auto.isNull() || Auto.getNameLoc() != SearchedLocation)
      return true;

    // We expect the TTP to be attached to this function template.
    // Find the template and the param index.
    auto *Templated = llvm::dyn_cast<FunctionDecl>(PVD->getDeclContext());
    if (!Templated)
      return true;
    auto *FTD = Templated->getDescribedFunctionTemplate();
    if (!FTD)
      return true;
    int ParamIndex = paramIndex(*FTD, *Auto.getDecl());
    if (ParamIndex < 0) {
      assert(false && "auto TTP is not from enclosing function?");
      return true;
    }

    // Now find the instantiation and the deduced template type arg.
    auto *Instantiation =
        llvm::dyn_cast_or_null<FunctionDecl>(getOnlyInstantiation(Templated));
    if (!Instantiation)
      return true;
    const auto *Args = Instantiation->getTemplateSpecializationArgs();
    if (Args->size() != FTD->getTemplateParameters()->size())
      return true; // no weird variadic stuff
    DeducedType = Args->get(ParamIndex).getAsType();
    return true;
  }

  static int paramIndex(const TemplateDecl &TD, NamedDecl &Param) {
    unsigned I = 0;
    for (auto *ND : *TD.getTemplateParameters()) {
      if (&Param == ND)
        return I;
      ++I;
    }
    return -1;
  }

  QualType DeducedType;
};
} // namespace

std::optional<QualType> getDeducedType(ASTContext &ASTCtx, SourceLocation Loc) {
  if (!Loc.isValid())
    return {};
  DeducedTypeVisitor V(Loc);
  V.TraverseAST(ASTCtx);
  if (V.DeducedType.isNull())
    return std::nullopt;
  return V.DeducedType;
}

TemplateTypeParmTypeLoc getContainedAutoParamType(TypeLoc TL) {
  if (auto QTL = TL.getAs<QualifiedTypeLoc>())
    return getContainedAutoParamType(QTL.getUnqualifiedLoc());
  if (llvm::isa<PointerType, ReferenceType, ParenType>(TL.getTypePtr()))
    return getContainedAutoParamType(TL.getNextTypeLoc());
  if (auto FTL = TL.getAs<FunctionTypeLoc>())
    return getContainedAutoParamType(FTL.getReturnLoc());
  if (auto TTPTL = TL.getAs<TemplateTypeParmTypeLoc>()) {
    if (TTPTL.getTypePtr()->getDecl()->isImplicit())
      return TTPTL;
  }
  return {};
}

template <typename TemplateDeclTy>
static NamedDecl *getOnlyInstantiationImpl(TemplateDeclTy *TD) {
  NamedDecl *Only = nullptr;
  for (auto *Spec : TD->specializations()) {
    if (Spec->getTemplateSpecializationKind() == TSK_ExplicitSpecialization)
      continue;
    if (Only != nullptr)
      return nullptr;
    Only = Spec;
  }
  return Only;
}

NamedDecl *getOnlyInstantiation(NamedDecl *TemplatedDecl) {
  if (TemplateDecl *TD = TemplatedDecl->getDescribedTemplate()) {
    if (auto *CTD = llvm::dyn_cast<ClassTemplateDecl>(TD))
      return getOnlyInstantiationImpl(CTD);
    if (auto *FTD = llvm::dyn_cast<FunctionTemplateDecl>(TD))
      return getOnlyInstantiationImpl(FTD);
    if (auto *VTD = llvm::dyn_cast<VarTemplateDecl>(TD))
      return getOnlyInstantiationImpl(VTD);
  }
  return nullptr;
}

std::vector<const Attr *> getAttributes(const DynTypedNode &N) {
  std::vector<const Attr *> Result;
  if (const auto *TL = N.get<TypeLoc>()) {
    for (AttributedTypeLoc ATL = TL->getAs<AttributedTypeLoc>(); !ATL.isNull();
         ATL = ATL.getModifiedLoc().getAs<AttributedTypeLoc>()) {
      if (const Attr *A = ATL.getAttr())
        Result.push_back(A);
      assert(!ATL.getModifiedLoc().isNull());
    }
  }
  if (const auto *S = N.get<AttributedStmt>()) {
    for (; S != nullptr; S = dyn_cast<AttributedStmt>(S->getSubStmt()))
      for (const Attr *A : S->getAttrs())
        if (A)
          Result.push_back(A);
  }
  if (const auto *D = N.get<Decl>()) {
    for (const Attr *A : D->attrs())
      if (A)
        Result.push_back(A);
  }
  return Result;
}

std::string getQualification(ASTContext &Context,
                             const DeclContext *DestContext,
                             SourceLocation InsertionPoint,
                             const NamedDecl *ND) {
  auto VisibleNamespaceDecls =
      getUsingNamespaceDirectives(DestContext, InsertionPoint);
  return getQualification(
      Context, DestContext, ND->getDeclContext(),
      [&](NestedNameSpecifier *NNS) {
        if (NNS->getKind() != NestedNameSpecifier::Namespace)
          return false;
        const auto *CanonNSD = NNS->getAsNamespace()->getCanonicalDecl();
        return llvm::any_of(VisibleNamespaceDecls,
                            [CanonNSD](const NamespaceDecl *NSD) {
                              return NSD->getCanonicalDecl() == CanonNSD;
                            });
      });
}

std::string getQualification(ASTContext &Context,
                             const DeclContext *DestContext,
                             const NamedDecl *ND,
                             llvm::ArrayRef<std::string> VisibleNamespaces) {
  for (llvm::StringRef NS : VisibleNamespaces) {
    assert(NS.endswith("::"));
    (void)NS;
  }
  return getQualification(
      Context, DestContext, ND->getDeclContext(),
      [&](NestedNameSpecifier *NNS) {
        return llvm::any_of(VisibleNamespaces, [&](llvm::StringRef Namespace) {
          std::string NS;
          llvm::raw_string_ostream OS(NS);
          NNS->print(OS, Context.getPrintingPolicy());
          return OS.str() == Namespace;
        });
      });
}

bool hasUnstableLinkage(const Decl *D) {
  // Linkage of a ValueDecl depends on the type.
  // If that's not deduced yet, deducing it may change the linkage.
  auto *VD = llvm::dyn_cast_or_null<ValueDecl>(D);
  return VD && !VD->getType().isNull() && VD->getType()->isUndeducedType();
}

bool isDeeplyNested(const Decl *D, unsigned MaxDepth) {
  size_t ContextDepth = 0;
  for (auto *Ctx = D->getDeclContext(); Ctx && !Ctx->isTranslationUnit();
       Ctx = Ctx->getParent()) {
    if (++ContextDepth == MaxDepth)
      return true;
  }
  return false;
}

namespace {

// returns true for `X` in `template <typename... X> void foo()`
bool isTemplateTypeParameterPack(NamedDecl *D) {
  if (const auto *TTPD = dyn_cast<TemplateTypeParmDecl>(D)) {
    return TTPD->isParameterPack();
  }
  return false;
}

// Returns the template parameter pack type from an instantiated function
// template, if it exists, nullptr otherwise.
const TemplateTypeParmType *getFunctionPackType(const FunctionDecl *Callee) {
  if (const auto *TemplateDecl = Callee->getPrimaryTemplate()) {
    auto TemplateParams = TemplateDecl->getTemplateParameters()->asArray();
    // find the template parameter pack from the back
    const auto It = std::find_if(TemplateParams.rbegin(), TemplateParams.rend(),
                                 isTemplateTypeParameterPack);
    if (It != TemplateParams.rend()) {
      const auto *TTPD = dyn_cast<TemplateTypeParmDecl>(*It);
      return TTPD->getTypeForDecl()->castAs<TemplateTypeParmType>();
    }
  }
  return nullptr;
}

// Returns the template parameter pack type that this parameter was expanded
// from (if in the Args... or Args&... or Args&&... form), if this is the case,
// nullptr otherwise.
const TemplateTypeParmType *getUnderylingPackType(const ParmVarDecl *Param) {
  const auto *PlainType = Param->getType().getTypePtr();
  if (auto *RT = dyn_cast<ReferenceType>(PlainType))
    PlainType = RT->getPointeeTypeAsWritten().getTypePtr();
  if (const auto *SubstType = dyn_cast<SubstTemplateTypeParmType>(PlainType)) {
    const auto *ReplacedParameter = SubstType->getReplacedParameter();
    if (ReplacedParameter->isParameterPack()) {
      return ReplacedParameter->getTypeForDecl()
          ->castAs<TemplateTypeParmType>();
    }
  }
  return nullptr;
}

// This visitor walks over the body of an instantiated function template.
// The template accepts a parameter pack and the visitor records whether
// the pack parameters were forwarded to another call. For example, given:
//
// template <typename T, typename... Args>
// auto make_unique(Args... args) {
//   return unique_ptr<T>(new T(args...));
// }
//
// When called as `make_unique<std::string>(2, 'x')` this yields a function
// `make_unique<std::string, int, char>` with two parameters.
// The visitor records that those two parameters are forwarded to the
// `constructor std::string(int, char);`.
//
// This information is recorded in the `ForwardingInfo` split into fully
// resolved parameters (passed as argument to a parameter that is not an
// expanded template type parameter pack) and forwarding parameters (passed to a
// parameter that is an expanded template type parameter pack).
class ForwardingCallVisitor
    : public RecursiveASTVisitor<ForwardingCallVisitor> {
public:
  ForwardingCallVisitor(ArrayRef<const ParmVarDecl *> Parameters)
      : Parameters{Parameters}, PackType{getUnderylingPackType(
                                    Parameters.front())} {}

  bool VisitCallExpr(CallExpr *E) {
    auto *Callee = getCalleeDeclOrUniqueOverload(E);
    if (Callee) {
      handleCall(Callee, E->arguments());
    }
    return !Info.has_value();
  }

  bool VisitCXXConstructExpr(CXXConstructExpr *E) {
    auto *Callee = E->getConstructor();
    if (Callee) {
      handleCall(Callee, E->arguments());
    }
    return !Info.has_value();
  }

  // The expanded parameter pack to be resolved
  ArrayRef<const ParmVarDecl *> Parameters;
  // The type of the parameter pack
  const TemplateTypeParmType *PackType;

  struct ForwardingInfo {
    // If the parameters were resolved to another FunctionDecl, these are its
    // first non-variadic parameters (i.e. the first entries of the parameter
    // pack that are passed as arguments bound to a non-pack parameter.)
    ArrayRef<const ParmVarDecl *> Head;
    // If the parameters were resolved to another FunctionDecl, these are its
    // variadic parameters (i.e. the entries of the parameter pack that are
    // passed as arguments bound to a pack parameter.)
    ArrayRef<const ParmVarDecl *> Pack;
    // If the parameters were resolved to another FunctionDecl, these are its
    // last non-variadic parameters (i.e. the last entries of the parameter pack
    // that are passed as arguments bound to a non-pack parameter.)
    ArrayRef<const ParmVarDecl *> Tail;
    // If the parameters were resolved to another forwarding FunctionDecl, this
    // is it.
    std::optional<FunctionDecl *> PackTarget;
  };

  // The output of this visitor
  std::optional<ForwardingInfo> Info;

private:
  // inspects the given callee with the given args to check whether it
  // contains Parameters, and sets Info accordingly.
  void handleCall(FunctionDecl *Callee, typename CallExpr::arg_range Args) {
    // Skip functions with less parameters, they can't be the target.
    if (Callee->parameters().size() < Parameters.size())
      return;
    if (llvm::any_of(Args,
                     [](const Expr *E) { return isa<PackExpansionExpr>(E); })) {
      return;
    }
    auto PackLocation = findPack(Args);
    if (!PackLocation)
      return;
    ArrayRef<ParmVarDecl *> MatchingParams =
        Callee->parameters().slice(*PackLocation, Parameters.size());
    // Check whether the function has a parameter pack as the last template
    // parameter
    if (const auto *TTPT = getFunctionPackType(Callee)) {
      // In this case: Separate the parameters into head, pack and tail
      auto IsExpandedPack = [&](const ParmVarDecl *P) {
        return getUnderylingPackType(P) == TTPT;
      };
      ForwardingInfo FI;
      FI.Head = MatchingParams.take_until(IsExpandedPack);
      FI.Pack =
          MatchingParams.drop_front(FI.Head.size()).take_while(IsExpandedPack);
      FI.Tail = MatchingParams.drop_front(FI.Head.size() + FI.Pack.size());
      FI.PackTarget = Callee;
      Info = FI;
      return;
    }
    // Default case: assume all parameters were fully resolved
    ForwardingInfo FI;
    FI.Head = MatchingParams;
    Info = FI;
  }

  // Returns the beginning of the expanded pack represented by Parameters
  // in the given arguments, if it is there.
  std::optional<size_t> findPack(typename CallExpr::arg_range Args) {
    // find the argument directly referring to the first parameter
    assert(Parameters.size() <= static_cast<size_t>(llvm::size(Args)));
    for (auto Begin = Args.begin(), End = Args.end() - Parameters.size() + 1;
         Begin != End; ++Begin) {
      if (const auto *RefArg = unwrapForward(*Begin)) {
        if (Parameters.front() != RefArg->getDecl())
          continue;
        // Check that this expands all the way until the last parameter.
        // It's enough to look at the last parameter, because it isn't possible
        // to expand without expanding all of them.
        auto ParamEnd = Begin + Parameters.size() - 1;
        RefArg = unwrapForward(*ParamEnd);
        if (!RefArg || Parameters.back() != RefArg->getDecl())
          continue;
        return std::distance(Args.begin(), Begin);
      }
    }
    return std::nullopt;
  }

  static FunctionDecl *getCalleeDeclOrUniqueOverload(CallExpr *E) {
    Decl *CalleeDecl = E->getCalleeDecl();
    auto *Callee = dyn_cast_or_null<FunctionDecl>(CalleeDecl);
    if (!Callee) {
      if (auto *Lookup = dyn_cast<UnresolvedLookupExpr>(E->getCallee())) {
        Callee = resolveOverload(Lookup, E);
      }
    }
    // Ignore the callee if the number of arguments is wrong (deal with va_args)
    if (Callee && Callee->getNumParams() == E->getNumArgs())
      return Callee;
    return nullptr;
  }

  static FunctionDecl *resolveOverload(UnresolvedLookupExpr *Lookup,
                                       CallExpr *E) {
    FunctionDecl *MatchingDecl = nullptr;
    if (!Lookup->requiresADL()) {
      // Check whether there is a single overload with this number of
      // parameters
      for (auto *Candidate : Lookup->decls()) {
        if (auto *FuncCandidate = dyn_cast_or_null<FunctionDecl>(Candidate)) {
          if (FuncCandidate->getNumParams() == E->getNumArgs()) {
            if (MatchingDecl) {
              // there are multiple candidates - abort
              return nullptr;
            }
            MatchingDecl = FuncCandidate;
          }
        }
      }
    }
    return MatchingDecl;
  }

  // Tries to get to the underlying argument by unwrapping implicit nodes and
  // std::forward.
  static const DeclRefExpr *unwrapForward(const Expr *E) {
    E = E->IgnoreImplicitAsWritten();
    // There might be an implicit copy/move constructor call on top of the
    // forwarded arg.
    // FIXME: Maybe mark implicit calls in the AST to properly filter here.
    if (const auto *Const = dyn_cast<CXXConstructExpr>(E))
      if (Const->getConstructor()->isCopyOrMoveConstructor())
        E = Const->getArg(0)->IgnoreImplicitAsWritten();
    if (const auto *Call = dyn_cast<CallExpr>(E)) {
      const auto Callee = Call->getBuiltinCallee();
      if (Callee == Builtin::BIforward) {
        return dyn_cast<DeclRefExpr>(
            Call->getArg(0)->IgnoreImplicitAsWritten());
      }
    }
    return dyn_cast<DeclRefExpr>(E);
  }
};

} // namespace

SmallVector<const ParmVarDecl *>
resolveForwardingParameters(const FunctionDecl *D, unsigned MaxDepth) {
  auto Parameters = D->parameters();
  // If the function has a template parameter pack
  if (const auto *TTPT = getFunctionPackType(D)) {
    // Split the parameters into head, pack and tail
    auto IsExpandedPack = [TTPT](const ParmVarDecl *P) {
      return getUnderylingPackType(P) == TTPT;
    };
    ArrayRef<const ParmVarDecl *> Head = Parameters.take_until(IsExpandedPack);
    ArrayRef<const ParmVarDecl *> Pack =
        Parameters.drop_front(Head.size()).take_while(IsExpandedPack);
    ArrayRef<const ParmVarDecl *> Tail =
        Parameters.drop_front(Head.size() + Pack.size());
    SmallVector<const ParmVarDecl *> Result(Parameters.size());
    // Fill in non-pack parameters
    auto HeadIt = std::copy(Head.begin(), Head.end(), Result.begin());
    auto TailIt = std::copy(Tail.rbegin(), Tail.rend(), Result.rbegin());
    // Recurse on pack parameters
    size_t Depth = 0;
    const FunctionDecl *CurrentFunction = D;
    llvm::SmallSet<const FunctionTemplateDecl *, 4> SeenTemplates;
    if (const auto *Template = D->getPrimaryTemplate()) {
      SeenTemplates.insert(Template);
    }
    while (!Pack.empty() && CurrentFunction && Depth < MaxDepth) {
      // Find call expressions involving the pack
      ForwardingCallVisitor V{Pack};
      V.TraverseStmt(CurrentFunction->getBody());
      if (!V.Info) {
        break;
      }
      // If we found something: Fill in non-pack parameters
      auto Info = *V.Info;
      HeadIt = std::copy(Info.Head.begin(), Info.Head.end(), HeadIt);
      TailIt = std::copy(Info.Tail.rbegin(), Info.Tail.rend(), TailIt);
      // Prepare next recursion level
      Pack = Info.Pack;
      CurrentFunction = Info.PackTarget.value_or(nullptr);
      Depth++;
      // If we are recursing into a previously encountered function: Abort
      if (CurrentFunction) {
        if (const auto *Template = CurrentFunction->getPrimaryTemplate()) {
          bool NewFunction = SeenTemplates.insert(Template).second;
          if (!NewFunction) {
            return {Parameters.begin(), Parameters.end()};
          }
        }
      }
    }
    // Fill in the remaining unresolved pack parameters
    HeadIt = std::copy(Pack.begin(), Pack.end(), HeadIt);
    assert(TailIt.base() == HeadIt);
    return Result;
  }
  return {Parameters.begin(), Parameters.end()};
}

bool isExpandedFromParameterPack(const ParmVarDecl *D) {
  return getUnderylingPackType(D) != nullptr;
}

} // namespace clangd
} // namespace clang
