<commit_before>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// author:  <NAME> <<EMAIL>>
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

#include "cling/Utils/AST.h"

#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclarationName.h"
#include "clang/AST/GlobalDecl.h"
#include "clang/Sema/Sema.h"
#include "clang/Sema/Lookup.h"
#include "clang/AST/DeclTemplate.h"

#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "clang/AST/Mangle.h"

#include <memory>
#include <stdio.h>

using namespace clang;

namespace cling {
namespace utils {

  static
  QualType GetPartiallyDesugaredTypeImpl(const ASTContext& Ctx,
                                         QualType QT,
                               const Transform::Config& TypeConfig,
                                         bool fullyQualifyType,
                                         bool fullyQualifyTmpltArg);

  static
  NestedNameSpecifier* GetPartiallyDesugaredNNS(const ASTContext& Ctx,
                                                NestedNameSpecifier* scope,
                                           const Transform::Config& TypeConfig);

  static NestedNameSpecifier*
  CreateNestedNameSpecifierForScopeOf(const ASTContext& Ctx,
                                      const Decl *decl,
                                      bool FullyQualified);

  static
  NestedNameSpecifier* GetFullyQualifiedNameSpecifier(const ASTContext& Ctx,
                                                      NestedNameSpecifier* scope);

  bool Analyze::IsWrapper(const FunctionDecl* ND) {
    if (!ND)
      return false;

    return StringRef(ND->getNameAsString())
      .startswith(Synthesize::UniquePrefix);
  }

  void Analyze::maybeMangleDeclName(const GlobalDecl& GD,
                                    std::string& mangledName) {
    // copied and adapted from CodeGen::CodeGenModule::getMangledName

    NamedDecl* D
      = cast<NamedDecl>(const_cast<Decl*>(GD.getDecl()));
    std::unique_ptr<MangleContext> mangleCtx;
    mangleCtx.reset(D->getASTContext().createMangleContext());
    if (!mangleCtx->shouldMangleDeclName(D)) {
      IdentifierInfo *II = D->getIdentifier();
      assert(II && "Attempt to mangle unnamed decl.");
      mangledName = II->getName();
      return;
    }

    llvm::raw_string_ostream RawStr(mangledName);
    switch(D->getKind()) {
    case Decl::CXXConstructor:
      //Ctor_Complete,          // Complete object ctor
      //Ctor_Base,              // Base object ctor
      //Ctor_CompleteAllocating // Complete object allocating ctor (unused)
      mangleCtx->mangleCXXCtor(cast<CXXConstructorDecl>(D),
                               GD.getCtorType(), RawStr);
      break;

    case Decl::CXXDestructor:
      //Dtor_Deleting, // Deleting dtor
      //Dtor_Complete, // Complete object dtor
      //Dtor_Base      // Base object dtor
#if defined(LLVM_ON_WIN32)
      // MicrosoftMangle.cpp:954 calls llvm_unreachable when mangling Dtor_Comdat
      if (GD.getDtorType() == Dtor_Comdat) {
        if (const IdentifierInfo* II = D->getIdentifier())
          RawStr << II->getName();
      } else
#endif
      {
        mangleCtx->mangleCXXDtor(cast<CXXDestructorDecl>(D),
                                 GD.getDtorType(), RawStr);
      }
      break;

    default :
      mangleCtx->mangleName(D, RawStr);
      break;
    }
    RawStr.flush();
  }

  Expr* Analyze::GetOrCreateLastExpr(FunctionDecl* FD,
                                     int* FoundAt /*=0*/,
                                     bool omitDeclStmts /*=true*/,
                                     Sema* S /*=0*/) {
    assert(FD && "We need a function declaration!");
    assert((omitDeclStmts || S)
           && "Sema needs to be set when omitDeclStmts is false");
    if (FoundAt)
      *FoundAt = -1;

    Expr* result = 0;
    if (CompoundStmt* CS = dyn_cast<CompoundStmt>(FD->getBody())) {
      ArrayRef<Stmt*> Stmts
        = llvm::makeArrayRef(CS->body_begin(), CS->size());
      int indexOfLastExpr = Stmts.size();
      while(indexOfLastExpr--) {
        if (!isa<NullStmt>(Stmts[indexOfLastExpr]))
          break;
      }

      if (FoundAt)
        *FoundAt = indexOfLastExpr;

      if (indexOfLastExpr < 0)
        return 0;

      if ( (result = dyn_cast<Expr>(Stmts[indexOfLastExpr])) )
        return result;
      if (!omitDeclStmts)
        if (DeclStmt* DS = dyn_cast<DeclStmt>(Stmts[indexOfLastExpr])) {
          std::vector<Stmt*> newBody = Stmts.vec();
          for (DeclStmt::reverse_decl_iterator I = DS->decl_rbegin(),
                 E = DS->decl_rend(); I != E; ++I) {
            if (VarDecl* VD = dyn_cast<VarDecl>(*I)) {
              // Change the void function's return type
              // We can't PushDeclContext, because we don't have scope.
              Sema::ContextRAII pushedDC(*S, FD);

              QualType VDTy = VD->getType().getNonReferenceType();
              // Get the location of the place we will insert.
              SourceLocation Loc
                = newBody[indexOfLastExpr]->getLocEnd().getLocWithOffset(1);
              Expr* DRE = S->BuildDeclRefExpr(VD, VDTy,VK_LValue, Loc).get();
              assert(DRE && "Cannot be null");
              indexOfLastExpr++;
              newBody.insert(newBody.begin() + indexOfLastExpr, DRE);

              // Attach the new body (note: it does dealloc/alloc of all nodes)
              CS->setStmts(S->getASTContext(), newBody);
              if (FoundAt)
                *FoundAt = indexOfLastExpr;
              return DRE;
            }
          }
        }

      return result;
    }

    return result;
  }

  const char* const Synthesize::UniquePrefix = "__cling_Un1Qu3";

  IntegerLiteral* Synthesize::IntegerLiteralExpr(ASTContext& C, uintptr_t Ptr) {
    const llvm::APInt Addr(8 * sizeof(void*), Ptr);
    return IntegerLiteral::Create(C, Addr, C.getUIntPtrType(),
                                  SourceLocation());
  }

  Expr* Synthesize::CStyleCastPtrExpr(Sema* S, QualType Ty, uintptr_t Ptr) {
    ASTContext& Ctx = S->getASTContext();
    return CStyleCastPtrExpr(S, Ty, Synthesize::IntegerLiteralExpr(Ctx, Ptr));
  }

  Expr* Synthesize::CStyleCastPtrExpr(Sema* S, QualType Ty, Expr* E) {
    ASTContext& Ctx = S->getASTContext();
    if (!Ty->isPointerType())
      Ty = Ctx.getPointerType(Ty);

    TypeSourceInfo* TSI = Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation());
    Expr* Result
      = S->BuildCStyleCastExpr(SourceLocation(), TSI,SourceLocation(),E).get();
    assert(Result && "Cannot create CStyleCastPtrExpr");
    return Result;
  }

  static bool
  GetFullyQualifiedTemplateName(const ASTContext& Ctx, TemplateName &tname) {

    bool changed = false;
    NestedNameSpecifier *NNS = 0;

    TemplateDecl *argtdecl = tname.getAsTemplateDecl();
    QualifiedTemplateName *qtname = tname.getAsQualifiedTemplateName();

    if (qtname && !qtname->hasTemplateKeyword()) {
      NNS = qtname->getQualifier();
      NestedNameSpecifier *qNNS = GetFullyQualifiedNameSpecifier(Ctx,NNS);
      if (qNNS != NNS) {
        changed = true;
        NNS = qNNS;
      } else {
        NNS = 0;
      }
    } else {
      NNS = CreateNestedNameSpecifierForScopeOf(Ctx, argtdecl, true);
    }
    if (NNS) {
      tname = Ctx.getQualifiedTemplateName(NNS,
                                           /*TemplateKeyword=*/ false,
                                           argtdecl);
      changed = true;
    }
    return changed;
  }

  static bool
  GetFullyQualifiedTemplateArgument(const ASTContext& Ctx,
                                    TemplateArgument &arg) {
    bool changed = false;

    // Note: we do not handle TemplateArgument::Expression, to replace it
    // we need the information for the template instance decl.
    // See GetPartiallyDesugaredTypeImpl

    if (arg.getKind() == TemplateArgument::Template) {
      TemplateName tname = arg.getAsTemplate();
      changed = GetFullyQualifiedTemplateName(Ctx, tname);
      if (changed) {
        arg = TemplateArgument(tname);
      }
    } else if (arg.getKind() == TemplateArgument::Type) {
      QualType SubTy = arg.getAsType();
      // Check if the type needs more desugaring and recurse.
      QualType QTFQ = TypeName::GetFullyQualifiedType(SubTy, Ctx);
      if (QTFQ != SubTy) {
        arg = TemplateArgument(QTFQ);
        changed = true;
      }
    } else if (arg.getKind() == TemplateArgument::Pack) {
      SmallVector<TemplateArgument, 2> desArgs;
      for (auto I = arg.pack_begin(), E = arg.pack_end(); I != E; ++I) {
        TemplateArgument pack_arg(*I);
        changed = GetFullyQualifiedTemplateArgument(Ctx,pack_arg);
        desArgs.push_back(pack_arg);
      }
      if (changed) {
        // The allocator in ASTContext is mutable ...
        // Keep the argument const to be inline will all the other interfaces
        // like:  NestedNameSpecifier::Create
        ASTContext &mutableCtx( const_cast<ASTContext&>(Ctx) );
        arg = TemplateArgument::CreatePackCopy(mutableCtx, desArgs);
      }
    }
    return changed;
  }

  static const Type*
  GetFullyQualifiedLocalType(const ASTContext& Ctx,
                             const Type *typeptr) {
    // We really just want to handle the template parameter if any ....
    // In case of template specializations iterate over the arguments and
    // fully qualify them as well.
    if (const TemplateSpecializationType* TST
        = llvm::dyn_cast<const TemplateSpecializationType>(typeptr)) {

      bool mightHaveChanged = false;
      llvm::SmallVector<TemplateArgument, 4> desArgs;
      for (TemplateSpecializationType::iterator
             I = TST->begin(), E = TST->end();
          I != E; ++I) {

        // cheap to copy and potentially modified by
        // GetFullyQualifedTemplateArgument
        TemplateArgument arg(*I);
        mightHaveChanged |= GetFullyQualifiedTemplateArgument(Ctx,arg);
        desArgs.push_back(arg);
      }

      // If desugaring happened allocate new type in the AST.
      if (mightHaveChanged) {
        QualType QT
          = Ctx.getTemplateSpecializationType(TST->getTemplateName(),
                                              desArgs,
                                              TST->getCanonicalTypeInternal());
        return QT.getTypePtr();
      }
    } else if (const RecordType *TSTRecord
               = llvm::dyn_cast<const RecordType>(typeptr)) {
      // We are asked to fully qualify and we have a Record Type,
      // which can point to a template instantiation with no sugar in any of
      // its template argument, however we still need to fully qualify them.

      if (const ClassTemplateSpecializationDecl* TSTdecl =
          llvm::dyn_cast<ClassTemplateSpecializationDecl>(TSTRecord->getDecl()))
        {
          const TemplateArgumentList& templateArgs
            = TSTdecl->getTemplateArgs();

          bool mightHaveChanged = false;
          llvm::SmallVector<TemplateArgument, 4> desArgs;
          for(unsigned int I = 0, E = templateArgs.size();
              I != E; ++I) {

            // cheap to copy and potentially modified by
            // GetFullyQualifedTemplateArgument
            TemplateArgument arg(templateArgs[I]);
            mightHaveChanged |= GetFullyQualifiedTemplateArgument(Ctx,arg);
            desArgs.push_back(arg);

          }

          // If desugaring happened allocate new type in the AST.
          if (mightHaveChanged) {
            TemplateName TN(TSTdecl->getSpecializedTemplate());
            QualType QT
              = Ctx.getTemplateSpecializationType(TN, desArgs,
                                         TSTRecord->getCanonicalTypeInternal());
            return QT.getTypePtr();
          }
        }
    }
    return typeptr;
  }

  static NestedNameSpecifier* CreateOuterNNS(const ASTContext& Ctx,
                                             const Decl* D,
                                             bool FullyQualify) {
    const DeclContext* DC = D->getDeclContext();
    if (const NamespaceDecl* NS = dyn_cast<NamespaceDecl>(DC)) {
      while (NS && NS->isInline()) {
        // Ignore inline namespace;
        NS = dyn_cast_or_null<NamespaceDecl>(NS->getDeclContext());
      }
      if (NS->getDeclName())
        return TypeName::CreateNestedNameSpecifier(Ctx, NS);
      return 0; // no starting '::', no anonymous
    } else if (const TagDecl* TD = dyn_cast<TagDecl>(DC)) {
      return TypeName::CreateNestedNameSpecifier(Ctx, TD, FullyQualify);
    } else if (const TypedefNameDecl* TDD = dyn_cast<TypedefNameDecl>(DC)) {
      return TypeName::CreateNestedNameSpecifier(Ctx, TDD, FullyQualify);
    }
    return 0; // no starting '::'
  }

  static
  NestedNameSpecifier* GetFullyQualifiedNameSpecifier(const ASTContext& Ctx,
                                                  NestedNameSpecifier* scope) {
    // Return a fully qualified version of this name specifier
    if (scope->getKind() == NestedNameSpecifier::Global) {
      // Already fully qualified.
      return scope;
    }

    if (const Type *type = scope->getAsType()) {
      // Find decl context.
      const TagDecl* TD = 0;
      if (const TagType* tagdecltype = dyn_cast<TagType>(type)) {
        TD = tagdecltype->getDecl();
      } else {
        TD = type->getAsCXXRecordDecl();
      }
      if (TD) {
        return TypeName::CreateNestedNameSpecifier(Ctx, TD,
                                                   true /*FullyQualified*/);
      } else if (const TypedefType* TDD = dyn_cast<TypedefType>(type)) {
        return TypeName::CreateNestedNameSpecifier(Ctx, TDD->getDecl(),
                                                   true /*FullyQualified*/);
      }
    } else if (const NamespaceDecl* NS = scope->getAsNamespace()) {
      return TypeName::CreateNestedNameSpecifier(Ctx, NS);
    } else if (const NamespaceAliasDecl* alias = scope->getAsNamespaceAlias()) {
      const NamespaceDecl* NS = alias->getNamespace()->getCanonicalDecl();
      return TypeName::CreateNestedNameSpecifier(Ctx, NS);
    }

    return scope;
  }

  static
  NestedNameSpecifier* SelectPrefix(const ASTContext& Ctx,
                                    const DeclContext *declContext,
                                    NestedNameSpecifier *original_prefix,
                             const Transform::Config& TypeConfig) {
    // We have to also desugar the prefix.
    NestedNameSpecifier* prefix = 0;
    if (declContext) {
      // We had a scope prefix as input, let see if it is still
      // the same as the scope of the result and if it is, then
      // we use it.
      if (declContext->isNamespace()) {
        // Deal with namespace.  This is mostly about dealing with
        // namespace aliases (i.e. keeping the one the user used).
        const NamespaceDecl *new_ns =dyn_cast<NamespaceDecl>(declContext);
        if (new_ns) {
          new_ns = new_ns->getCanonicalDecl();
          NamespaceDecl *old_ns = 0;
          if (original_prefix) {
            original_prefix->getAsNamespace();
            if (NamespaceAliasDecl *alias =
                     original_prefix->getAsNamespaceAlias())
            {
              old_ns = alias->getNamespace()->getCanonicalDecl();
            }
          }
          if (old_ns == new_ns) {
            // This is the same namespace, use the original prefix
            // as a starting point.
            prefix = GetFullyQualifiedNameSpecifier(Ctx,original_prefix);
          } else {
            prefix = TypeName::CreateNestedNameSpecifier(Ctx,
                                               dyn_cast<NamespaceDecl>(new_ns));
          }
        }
      } else {
        const CXXRecordDecl* newtype=dyn_cast<CXXRecordDecl>(declContext);
        if (newtype && original_prefix) {
          // Deal with a class
          const Type *oldtype = original_prefix->getAsType();
          if (oldtype &&
              // NOTE: Should we compare the RecordDecl instead?
              oldtype->getAsCXXRecordDecl() == newtype)
          {
            // This is the same type, use the original prefix as a starting
            // point.
            prefix = GetPartiallyDesugaredNNS(Ctx,original_prefix,TypeConfig);
          } else {
            const TagDecl *tdecl = dyn_cast<TagDecl>(declContext);
            if (tdecl) {
              prefix = TypeName::CreateNestedNameSpecifier(Ctx, tdecl,
                                                      false /*FullyQualified*/);
            }
          }
        } else {
          // We should only create the nested name specifier
          // if the outer scope is really a TagDecl.
          // It could also be a CXXMethod for example.
          const TagDecl *tdecl = dyn_cast<TagDecl>(declContext);
          if (tdecl) {
            prefix = TypeName::CreateNestedNameSpecifier(Ctx,tdecl,
                                                      false /*FullyQualified*/);
          }
        }
      }
    } else {
      prefix = GetFullyQualifiedNameSpecifier(Ctx,original_prefix);
    }
    return prefix;
  }

  static
  NestedNameSpecifier* SelectPrefix(const ASTContext& Ctx,
                                    const ElaboratedType *etype,
                                    NestedNameSpecifier *original_prefix,
                             const Transform::Config& TypeConfig) {
    // We have to also desugar the prefix.

    NestedNameSpecifier* prefix = etype->getQualifier();
    if (original_prefix && prefix) {
      // We had a scope prefix as input, let see if it is still
      // the same as the scope of the result and if it is, then
      // we use it.
      const Type *newtype = prefix->getAsType();
      if (newtype) {
        // Deal with a class
        const Type *oldtype = original_prefix->getAsType();
        if (oldtype &&
            // NOTE: Should we compare the RecordDecl instead?
            oldtype->getAsCXXRecordDecl() == newtype->getAsCXXRecordDecl())
        {
          // This is the same type, use the original prefix as a starting
          // point.
          prefix = GetPartiallyDesugaredNNS(Ctx,original_prefix,TypeConfig);
        } else {
          prefix = GetPartiallyDesugaredNNS(Ctx,prefix,TypeConfig);
        }
      } else {
        // Deal with namespace.  This is mostly about dealing with
        // namespace aliases (i.e. keeping the one the user used).
        const NamespaceDecl *new_ns = prefix->getAsNamespace();
        if (new_ns) {
          new_ns = new_ns->getCanonicalDecl();
        }
        else if (NamespaceAliasDecl *alias = prefix->getAsNamespaceAlias() )
        {
          new_ns = alias->getNamespace()->getCanonicalDecl();
        }
        if (new_ns) {
          const NamespaceDecl *old_ns = original_prefix->getAsNamespace();
          if (old_ns) {
            old_ns = old_ns->getCanonicalDecl();
          }
          else if (NamespaceAliasDecl *alias =
                   original_prefix->getAsNamespaceAlias())
          {
            old_ns = alias->getNamespace()->getCanonicalDecl();
          }
          if (old_ns == new_ns) {
            // This is the same namespace, use the original prefix
            // as a starting point.
            prefix = GetFullyQualifiedNameSpecifier(Ctx,original_prefix);
          } else {
            prefix = GetFullyQualifiedNameSpecifier(Ctx,prefix);
          }
        } else {
          prefix = GetFullyQualifiedNameSpecifier(Ctx,prefix);
        }
      }
    }
    return prefix;
  }


  static
  NestedNameSpecifier* GetPartiallyDesugaredNNS(const ASTContext& Ctx,
                                                NestedNameSpecifier* scope,
                                          const Transform::Config& TypeConfig) {
    // Desugar the scope qualifier if needed.

    if (const Type* scope_type = scope->getAsType()) {

      // this is not a namespace, so we might need to desugar
      QualType desugared = GetPartiallyDesugaredTypeImpl(Ctx,
                                                         QualType(scope_type,0),
                                                         TypeConfig,
                                                         /*qualifyType=*/false,
                                                      /*qualifyTmpltArg=*/true);

      NestedNameSpecifier* outer_scope = scope->getPrefix();
      const ElaboratedType* etype
         = dyn_cast<ElaboratedType>(desugared.getTypePtr());
      if (etype) {
        // The desugarding returned an elaborated type even-though we
        // did not request it (/*fullyQualify=*/false), so we must have been
        // looking a typedef pointing at a (or another) scope.

        if (outer_scope) {
          outer_scope = SelectPrefix(Ctx,etype,outer_scope,TypeConfig);
        } else {
          outer_scope = GetPartiallyDesugaredNNS(Ctx,etype->getQualifier(),
                                                 TypeConfig);
        }
        desugared = etype->getNamedType();
      } else {

        Decl* decl = 0;
        const TypedefType* typedeftype =
          dyn_cast_or_null<TypedefType>(&(*desugared));
        if (typedeftype) {
          decl = typedeftype->getDecl();
        } else {
          // There are probably other cases ...
          const TagType* tagdecltype = dyn_cast_or_null<TagType>(&(*desugared));
          if (tagdecltype) {
            decl = tagdecltype->getDecl();
          } else {
            decl = desugared->getAsCXXRecordDecl();
          }
        }
        if (decl) {
          NamedDecl* outer
            = dyn_cast_or_null<NamedDecl>(decl->getDeclContext());
          NamespaceDecl* outer_ns
            = dyn_cast_or_null<NamespaceDecl>(decl->getDeclContext());
          if (outer
              && !(outer_ns && outer_ns->isAnonymousNamespace())
              && outer->getName().size() ) {
            outer_scope = SelectPrefix(Ctx,decl->getDeclContext(),
                                       outer_scope,TypeConfig);
          } else {
            outer_scope = 0;
          }
        } else if (outer_scope) {
          outer_scope = GetPartiallyDesugaredNNS(Ctx, outer_scope, TypeConfig);
        }
      }
      return NestedNameSpecifier::Create(Ctx,outer_scope,
                                         false /* template keyword wanted */,
                                         desugared.getTypePtr());
    } else {
      return  GetFullyQualifiedNameSpecifier(Ctx,scope);
    }
  }

  bool Analyze::IsStdOrCompilerDetails(const NamedDecl &decl)
  {
    // Return true if the TagType is a 'details' of the std implementation
    // or declared within std.
    // Details means (For now) declared in __gnu_cxx or starting with
    // underscore.

    IdentifierInfo *info = decl.getDeclName().getAsIdentifierInfo();
    if (info && info->getNameStart()[0] == '_') {
      // We have a name starting by _, this is reserve for compiler
      // implementation, so let's not desugar to it.
      return true;
    }
    // And let's check if it is in one of the know compiler implementation
    // namespace.
    const NamedDecl *outer =dyn_cast_or_null<NamedDecl>(decl.getDeclContext());
    while (outer && outer->getName().size() ) {
      if (outer->getName().compare("std") == 0 ||
          outer->getName().compare("__gnu_cxx") == 0) {
        return true;
      }
      outer = dyn_cast_or_null<NamedDecl>(outer->getDeclContext());
    }
    return false;
  }

  bool Analyze::IsStdClass(const clang::NamedDecl &cl)
  {
    // Return true if the class or template is declared directly in the
    // std namespace (modulo inline namespace).

    const clang::DeclContext *ctx = cl.getDeclContext();

    while (ctx && ctx->isInlineNamespace()) {
      ctx = ctx->getParent();
    }

    if (ctx && ctx->isNamespace())
    {
      const clang::NamedDecl *parent = llvm::dyn_cast<clang::NamedDecl> (ctx);
      if (parent) {
        if (parent->getDeclContext()->isTranslationUnit()
            && parent->getQualifiedNameAsString()=="std") {
          return true;
        }
      }
    }
    return false;
  }

  // See Sema::PushOnScopeChains
  bool Analyze::isOnScopeChains(const NamedDecl* ND, Sema& SemaR) {

    // Named decls without name shouldn't be in. Eg: struct {int a};
    if (!ND->getDeclName())
      return false;

    // Out-of-line definitions shouldn't be pushed into scope in C++.
    // Out-of-line variable and function definitions shouldn't even in C.
    if ((isa<VarDecl>(ND) || isa<FunctionDecl>(ND)) && ND->isOutOfLine() &&<commit_msg>Use clang's method for checking if we are in std::*<commit_after>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// author:  <NAME> <<EMAIL>>
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

#include "cling/Utils/AST.h"

#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclarationName.h"
#include "clang/AST/GlobalDecl.h"
#include "clang/Sema/Sema.h"
#include "clang/Sema/Lookup.h"
#include "clang/AST/DeclTemplate.h"

#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "clang/AST/Mangle.h"

#include <memory>
#include <stdio.h>

using namespace clang;

namespace cling {
namespace utils {

  static
  QualType GetPartiallyDesugaredTypeImpl(const ASTContext& Ctx,
                                         QualType QT,
                               const Transform::Config& TypeConfig,
                                         bool fullyQualifyType,
                                         bool fullyQualifyTmpltArg);

  static
  NestedNameSpecifier* GetPartiallyDesugaredNNS(const ASTContext& Ctx,
                                                NestedNameSpecifier* scope,
                                           const Transform::Config& TypeConfig);

  static NestedNameSpecifier*
  CreateNestedNameSpecifierForScopeOf(const ASTContext& Ctx,
                                      const Decl *decl,
                                      bool FullyQualified);

  static
  NestedNameSpecifier* GetFullyQualifiedNameSpecifier(const ASTContext& Ctx,
                                                      NestedNameSpecifier* scope);

  bool Analyze::IsWrapper(const FunctionDecl* ND) {
    if (!ND)
      return false;

    return StringRef(ND->getNameAsString())
      .startswith(Synthesize::UniquePrefix);
  }

  void Analyze::maybeMangleDeclName(const GlobalDecl& GD,
                                    std::string& mangledName) {
    // copied and adapted from CodeGen::CodeGenModule::getMangledName

    NamedDecl* D
      = cast<NamedDecl>(const_cast<Decl*>(GD.getDecl()));
    std::unique_ptr<MangleContext> mangleCtx;
    mangleCtx.reset(D->getASTContext().createMangleContext());
    if (!mangleCtx->shouldMangleDeclName(D)) {
      IdentifierInfo *II = D->getIdentifier();
      assert(II && "Attempt to mangle unnamed decl.");
      mangledName = II->getName();
      return;
    }

    llvm::raw_string_ostream RawStr(mangledName);
    switch(D->getKind()) {
    case Decl::CXXConstructor:
      //Ctor_Complete,          // Complete object ctor
      //Ctor_Base,              // Base object ctor
      //Ctor_CompleteAllocating // Complete object allocating ctor (unused)
      mangleCtx->mangleCXXCtor(cast<CXXConstructorDecl>(D),
                               GD.getCtorType(), RawStr);
      break;

    case Decl::CXXDestructor:
      //Dtor_Deleting, // Deleting dtor
      //Dtor_Complete, // Complete object dtor
      //Dtor_Base      // Base object dtor
#if defined(LLVM_ON_WIN32)
      // MicrosoftMangle.cpp:954 calls llvm_unreachable when mangling Dtor_Comdat
      if (GD.getDtorType() == Dtor_Comdat) {
        if (const IdentifierInfo* II = D->getIdentifier())
          RawStr << II->getName();
      } else
#endif
      {
        mangleCtx->mangleCXXDtor(cast<CXXDestructorDecl>(D),
                                 GD.getDtorType(), RawStr);
      }
      break;

    default :
      mangleCtx->mangleName(D, RawStr);
      break;
    }
    RawStr.flush();
  }

  Expr* Analyze::GetOrCreateLastExpr(FunctionDecl* FD,
                                     int* FoundAt /*=0*/,
                                     bool omitDeclStmts /*=true*/,
                                     Sema* S /*=0*/) {
    assert(FD && "We need a function declaration!");
    assert((omitDeclStmts || S)
           && "Sema needs to be set when omitDeclStmts is false");
    if (FoundAt)
      *FoundAt = -1;

    Expr* result = 0;
    if (CompoundStmt* CS = dyn_cast<CompoundStmt>(FD->getBody())) {
      ArrayRef<Stmt*> Stmts
        = llvm::makeArrayRef(CS->body_begin(), CS->size());
      int indexOfLastExpr = Stmts.size();
      while(indexOfLastExpr--) {
        if (!isa<NullStmt>(Stmts[indexOfLastExpr]))
          break;
      }

      if (FoundAt)
        *FoundAt = indexOfLastExpr;

      if (indexOfLastExpr < 0)
        return 0;

      if ( (result = dyn_cast<Expr>(Stmts[indexOfLastExpr])) )
        return result;
      if (!omitDeclStmts)
        if (DeclStmt* DS = dyn_cast<DeclStmt>(Stmts[indexOfLastExpr])) {
          std::vector<Stmt*> newBody = Stmts.vec();
          for (DeclStmt::reverse_decl_iterator I = DS->decl_rbegin(),
                 E = DS->decl_rend(); I != E; ++I) {
            if (VarDecl* VD = dyn_cast<VarDecl>(*I)) {
              // Change the void function's return type
              // We can't PushDeclContext, because we don't have scope.
              Sema::ContextRAII pushedDC(*S, FD);

              QualType VDTy = VD->getType().getNonReferenceType();
              // Get the location of the place we will insert.
              SourceLocation Loc
                = newBody[indexOfLastExpr]->getLocEnd().getLocWithOffset(1);
              Expr* DRE = S->BuildDeclRefExpr(VD, VDTy,VK_LValue, Loc).get();
              assert(DRE && "Cannot be null");
              indexOfLastExpr++;
              newBody.insert(newBody.begin() + indexOfLastExpr, DRE);

              // Attach the new body (note: it does dealloc/alloc of all nodes)
              CS->setStmts(S->getASTContext(), newBody);
              if (FoundAt)
                *FoundAt = indexOfLastExpr;
              return DRE;
            }
          }
        }

      return result;
    }

    return result;
  }

  const char* const Synthesize::UniquePrefix = "__cling_Un1Qu3";

  IntegerLiteral* Synthesize::IntegerLiteralExpr(ASTContext& C, uintptr_t Ptr) {
    const llvm::APInt Addr(8 * sizeof(void*), Ptr);
    return IntegerLiteral::Create(C, Addr, C.getUIntPtrType(),
                                  SourceLocation());
  }

  Expr* Synthesize::CStyleCastPtrExpr(Sema* S, QualType Ty, uintptr_t Ptr) {
    ASTContext& Ctx = S->getASTContext();
    return CStyleCastPtrExpr(S, Ty, Synthesize::IntegerLiteralExpr(Ctx, Ptr));
  }

  Expr* Synthesize::CStyleCastPtrExpr(Sema* S, QualType Ty, Expr* E) {
    ASTContext& Ctx = S->getASTContext();
    if (!Ty->isPointerType())
      Ty = Ctx.getPointerType(Ty);

    TypeSourceInfo* TSI = Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation());
    Expr* Result
      = S->BuildCStyleCastExpr(SourceLocation(), TSI,SourceLocation(),E).get();
    assert(Result && "Cannot create CStyleCastPtrExpr");
    return Result;
  }

  static bool
  GetFullyQualifiedTemplateName(const ASTContext& Ctx, TemplateName &tname) {

    bool changed = false;
    NestedNameSpecifier *NNS = 0;

    TemplateDecl *argtdecl = tname.getAsTemplateDecl();
    QualifiedTemplateName *qtname = tname.getAsQualifiedTemplateName();

    if (qtname && !qtname->hasTemplateKeyword()) {
      NNS = qtname->getQualifier();
      NestedNameSpecifier *qNNS = GetFullyQualifiedNameSpecifier(Ctx,NNS);
      if (qNNS != NNS) {
        changed = true;
        NNS = qNNS;
      } else {
        NNS = 0;
      }
    } else {
      NNS = CreateNestedNameSpecifierForScopeOf(Ctx, argtdecl, true);
    }
    if (NNS) {
      tname = Ctx.getQualifiedTemplateName(NNS,
                                           /*TemplateKeyword=*/ false,
                                           argtdecl);
      changed = true;
    }
    return changed;
  }

  static bool
  GetFullyQualifiedTemplateArgument(const ASTContext& Ctx,
                                    TemplateArgument &arg) {
    bool changed = false;

    // Note: we do not handle TemplateArgument::Expression, to replace it
    // we need the information for the template instance decl.
    // See GetPartiallyDesugaredTypeImpl

    if (arg.getKind() == TemplateArgument::Template) {
      TemplateName tname = arg.getAsTemplate();
      changed = GetFullyQualifiedTemplateName(Ctx, tname);
      if (changed) {
        arg = TemplateArgument(tname);
      }
    } else if (arg.getKind() == TemplateArgument::Type) {
      QualType SubTy = arg.getAsType();
      // Check if the type needs more desugaring and recurse.
      QualType QTFQ = TypeName::GetFullyQualifiedType(SubTy, Ctx);
      if (QTFQ != SubTy) {
        arg = TemplateArgument(QTFQ);
        changed = true;
      }
    } else if (arg.getKind() == TemplateArgument::Pack) {
      SmallVector<TemplateArgument, 2> desArgs;
      for (auto I = arg.pack_begin(), E = arg.pack_end(); I != E; ++I) {
        TemplateArgument pack_arg(*I);
        changed = GetFullyQualifiedTemplateArgument(Ctx,pack_arg);
        desArgs.push_back(pack_arg);
      }
      if (changed) {
        // The allocator in ASTContext is mutable ...
        // Keep the argument const to be inline will all the other interfaces
        // like:  NestedNameSpecifier::Create
        ASTContext &mutableCtx( const_cast<ASTContext&>(Ctx) );
        arg = TemplateArgument::CreatePackCopy(mutableCtx, desArgs);
      }
    }
    return changed;
  }

  static const Type*
  GetFullyQualifiedLocalType(const ASTContext& Ctx,
                             const Type *typeptr) {
    // We really just want to handle the template parameter if any ....
    // In case of template specializations iterate over the arguments and
    // fully qualify them as well.
    if (const TemplateSpecializationType* TST
        = llvm::dyn_cast<const TemplateSpecializationType>(typeptr)) {

      bool mightHaveChanged = false;
      llvm::SmallVector<TemplateArgument, 4> desArgs;
      for (TemplateSpecializationType::iterator
             I = TST->begin(), E = TST->end();
          I != E; ++I) {

        // cheap to copy and potentially modified by
        // GetFullyQualifedTemplateArgument
        TemplateArgument arg(*I);
        mightHaveChanged |= GetFullyQualifiedTemplateArgument(Ctx,arg);
        desArgs.push_back(arg);
      }

      // If desugaring happened allocate new type in the AST.
      if (mightHaveChanged) {
        QualType QT
          = Ctx.getTemplateSpecializationType(TST->getTemplateName(),
                                              desArgs,
                                              TST->getCanonicalTypeInternal());
        return QT.getTypePtr();
      }
    } else if (const RecordType *TSTRecord
               = llvm::dyn_cast<const RecordType>(typeptr)) {
      // We are asked to fully qualify and we have a Record Type,
      // which can point to a template instantiation with no sugar in any of
      // its template argument, however we still need to fully qualify them.

      if (const ClassTemplateSpecializationDecl* TSTdecl =
          llvm::dyn_cast<ClassTemplateSpecializationDecl>(TSTRecord->getDecl()))
        {
          const TemplateArgumentList& templateArgs
            = TSTdecl->getTemplateArgs();

          bool mightHaveChanged = false;
          llvm::SmallVector<TemplateArgument, 4> desArgs;
          for(unsigned int I = 0, E = templateArgs.size();
              I != E; ++I) {

            // cheap to copy and potentially modified by
            // GetFullyQualifedTemplateArgument
            TemplateArgument arg(templateArgs[I]);
            mightHaveChanged |= GetFullyQualifiedTemplateArgument(Ctx,arg);
            desArgs.push_back(arg);

          }

          // If desugaring happened allocate new type in the AST.
          if (mightHaveChanged) {
            TemplateName TN(TSTdecl->getSpecializedTemplate());
            QualType QT
              = Ctx.getTemplateSpecializationType(TN, desArgs,
                                         TSTRecord->getCanonicalTypeInternal());
            return QT.getTypePtr();
          }
        }
    }
    return typeptr;
  }

  static NestedNameSpecifier* CreateOuterNNS(const ASTContext& Ctx,
                                             const Decl* D,
                                             bool FullyQualify) {
    const DeclContext* DC = D->getDeclContext();
    if (const NamespaceDecl* NS = dyn_cast<NamespaceDecl>(DC)) {
      while (NS && NS->isInline()) {
        // Ignore inline namespace;
        NS = dyn_cast_or_null<NamespaceDecl>(NS->getDeclContext());
      }
      if (NS->getDeclName())
        return TypeName::CreateNestedNameSpecifier(Ctx, NS);
      return 0; // no starting '::', no anonymous
    } else if (const TagDecl* TD = dyn_cast<TagDecl>(DC)) {
      return TypeName::CreateNestedNameSpecifier(Ctx, TD, FullyQualify);
    } else if (const TypedefNameDecl* TDD = dyn_cast<TypedefNameDecl>(DC)) {
      return TypeName::CreateNestedNameSpecifier(Ctx, TDD, FullyQualify);
    }
    return 0; // no starting '::'
  }

  static
  NestedNameSpecifier* GetFullyQualifiedNameSpecifier(const ASTContext& Ctx,
                                                  NestedNameSpecifier* scope) {
    // Return a fully qualified version of this name specifier
    if (scope->getKind() == NestedNameSpecifier::Global) {
      // Already fully qualified.
      return scope;
    }

    if (const Type *type = scope->getAsType()) {
      // Find decl context.
      const TagDecl* TD = 0;
      if (const TagType* tagdecltype = dyn_cast<TagType>(type)) {
        TD = tagdecltype->getDecl();
      } else {
        TD = type->getAsCXXRecordDecl();
      }
      if (TD) {
        return TypeName::CreateNestedNameSpecifier(Ctx, TD,
                                                   true /*FullyQualified*/);
      } else if (const TypedefType* TDD = dyn_cast<TypedefType>(type)) {
        return TypeName::CreateNestedNameSpecifier(Ctx, TDD->getDecl(),
                                                   true /*FullyQualified*/);
      }
    } else if (const NamespaceDecl* NS = scope->getAsNamespace()) {
      return TypeName::CreateNestedNameSpecifier(Ctx, NS);
    } else if (const NamespaceAliasDecl* alias = scope->getAsNamespaceAlias()) {
      const NamespaceDecl* NS = alias->getNamespace()->getCanonicalDecl();
      return TypeName::CreateNestedNameSpecifier(Ctx, NS);
    }

    return scope;
  }

  static
  NestedNameSpecifier* SelectPrefix(const ASTContext& Ctx,
                                    const DeclContext *declContext,
                                    NestedNameSpecifier *original_prefix,
                             const Transform::Config& TypeConfig) {
    // We have to also desugar the prefix.
    NestedNameSpecifier* prefix = 0;
    if (declContext) {
      // We had a scope prefix as input, let see if it is still
      // the same as the scope of the result and if it is, then
      // we use it.
      if (declContext->isNamespace()) {
        // Deal with namespace.  This is mostly about dealing with
        // namespace aliases (i.e. keeping the one the user used).
        const NamespaceDecl *new_ns =dyn_cast<NamespaceDecl>(declContext);
        if (new_ns) {
          new_ns = new_ns->getCanonicalDecl();
          NamespaceDecl *old_ns = 0;
          if (original_prefix) {
            original_prefix->getAsNamespace();
            if (NamespaceAliasDecl *alias =
                     original_prefix->getAsNamespaceAlias())
            {
              old_ns = alias->getNamespace()->getCanonicalDecl();
            }
          }
          if (old_ns == new_ns) {
            // This is the same namespace, use the original prefix
            // as a starting point.
            prefix = GetFullyQualifiedNameSpecifier(Ctx,original_prefix);
          } else {
            prefix = TypeName::CreateNestedNameSpecifier(Ctx,
                                               dyn_cast<NamespaceDecl>(new_ns));
          }
        }
      } else {
        const CXXRecordDecl* newtype=dyn_cast<CXXRecordDecl>(declContext);
        if (newtype && original_prefix) {
          // Deal with a class
          const Type *oldtype = original_prefix->getAsType();
          if (oldtype &&
              // NOTE: Should we compare the RecordDecl instead?
              oldtype->getAsCXXRecordDecl() == newtype)
          {
            // This is the same type, use the original prefix as a starting
            // point.
            prefix = GetPartiallyDesugaredNNS(Ctx,original_prefix,TypeConfig);
          } else {
            const TagDecl *tdecl = dyn_cast<TagDecl>(declContext);
            if (tdecl) {
              prefix = TypeName::CreateNestedNameSpecifier(Ctx, tdecl,
                                                      false /*FullyQualified*/);
            }
          }
        } else {
          // We should only create the nested name specifier
          // if the outer scope is really a TagDecl.
          // It could also be a CXXMethod for example.
          const TagDecl *tdecl = dyn_cast<TagDecl>(declContext);
          if (tdecl) {
            prefix = TypeName::CreateNestedNameSpecifier(Ctx,tdecl,
                                                      false /*FullyQualified*/);
          }
        }
      }
    } else {
      prefix = GetFullyQualifiedNameSpecifier(Ctx,original_prefix);
    }
    return prefix;
  }

  static
  NestedNameSpecifier* SelectPrefix(const ASTContext& Ctx,
                                    const ElaboratedType *etype,
                                    NestedNameSpecifier *original_prefix,
                             const Transform::Config& TypeConfig) {
    // We have to also desugar the prefix.

    NestedNameSpecifier* prefix = etype->getQualifier();
    if (original_prefix && prefix) {
      // We had a scope prefix as input, let see if it is still
      // the same as the scope of the result and if it is, then
      // we use it.
      const Type *newtype = prefix->getAsType();
      if (newtype) {
        // Deal with a class
        const Type *oldtype = original_prefix->getAsType();
        if (oldtype &&
            // NOTE: Should we compare the RecordDecl instead?
            oldtype->getAsCXXRecordDecl() == newtype->getAsCXXRecordDecl())
        {
          // This is the same type, use the original prefix as a starting
          // point.
          prefix = GetPartiallyDesugaredNNS(Ctx,original_prefix,TypeConfig);
        } else {
          prefix = GetPartiallyDesugaredNNS(Ctx,prefix,TypeConfig);
        }
      } else {
        // Deal with namespace.  This is mostly about dealing with
        // namespace aliases (i.e. keeping the one the user used).
        const NamespaceDecl *new_ns = prefix->getAsNamespace();
        if (new_ns) {
          new_ns = new_ns->getCanonicalDecl();
        }
        else if (NamespaceAliasDecl *alias = prefix->getAsNamespaceAlias() )
        {
          new_ns = alias->getNamespace()->getCanonicalDecl();
        }
        if (new_ns) {
          const NamespaceDecl *old_ns = original_prefix->getAsNamespace();
          if (old_ns) {
            old_ns = old_ns->getCanonicalDecl();
          }
          else if (NamespaceAliasDecl *alias =
                   original_prefix->getAsNamespaceAlias())
          {
            old_ns = alias->getNamespace()->getCanonicalDecl();
          }
          if (old_ns == new_ns) {
            // This is the same namespace, use the original prefix
            // as a starting point.
            prefix = GetFullyQualifiedNameSpecifier(Ctx,original_prefix);
          } else {
            prefix = GetFullyQualifiedNameSpecifier(Ctx,prefix);
          }
        } else {
          prefix = GetFullyQualifiedNameSpecifier(Ctx,prefix);
        }
      }
    }
    return prefix;
  }


  static
  NestedNameSpecifier* GetPartiallyDesugaredNNS(const ASTContext& Ctx,
                                                NestedNameSpecifier* scope,
                                          const Transform::Config& TypeConfig) {
    // Desugar the scope qualifier if needed.

    if (const Type* scope_type = scope->getAsType()) {

      // this is not a namespace, so we might need to desugar
      QualType desugared = GetPartiallyDesugaredTypeImpl(Ctx,
                                                         QualType(scope_type,0),
                                                         TypeConfig,
                                                         /*qualifyType=*/false,
                                                      /*qualifyTmpltArg=*/true);

      NestedNameSpecifier* outer_scope = scope->getPrefix();
      const ElaboratedType* etype
         = dyn_cast<ElaboratedType>(desugared.getTypePtr());
      if (etype) {
        // The desugarding returned an elaborated type even-though we
        // did not request it (/*fullyQualify=*/false), so we must have been
        // looking a typedef pointing at a (or another) scope.

        if (outer_scope) {
          outer_scope = SelectPrefix(Ctx,etype,outer_scope,TypeConfig);
        } else {
          outer_scope = GetPartiallyDesugaredNNS(Ctx,etype->getQualifier(),
                                                 TypeConfig);
        }
        desugared = etype->getNamedType();
      } else {

        Decl* decl = 0;
        const TypedefType* typedeftype =
          dyn_cast_or_null<TypedefType>(&(*desugared));
        if (typedeftype) {
          decl = typedeftype->getDecl();
        } else {
          // There are probably other cases ...
          const TagType* tagdecltype = dyn_cast_or_null<TagType>(&(*desugared));
          if (tagdecltype) {
            decl = tagdecltype->getDecl();
          } else {
            decl = desugared->getAsCXXRecordDecl();
          }
        }
        if (decl) {
          NamedDecl* outer
            = dyn_cast_or_null<NamedDecl>(decl->getDeclContext());
          NamespaceDecl* outer_ns
            = dyn_cast_or_null<NamespaceDecl>(decl->getDeclContext());
          if (outer
              && !(outer_ns && outer_ns->isAnonymousNamespace())
              && outer->getName().size() ) {
            outer_scope = SelectPrefix(Ctx,decl->getDeclContext(),
                                       outer_scope,TypeConfig);
          } else {
            outer_scope = 0;
          }
        } else if (outer_scope) {
          outer_scope = GetPartiallyDesugaredNNS(Ctx, outer_scope, TypeConfig);
        }
      }
      return NestedNameSpecifier::Create(Ctx,outer_scope,
                                         false /* template keyword wanted */,
                                         desugared.getTypePtr());
    } else {
      return  GetFullyQualifiedNameSpecifier(Ctx,scope);
    }
  }

  bool Analyze::IsStdOrCompilerDetails(const NamedDecl &decl)
  {
    // Return true if the TagType is a 'details' of the std implementation
    // or declared within std.
    // Details means (For now) declared in __gnu_cxx or starting with
    // underscore.

    IdentifierInfo *info = decl.getDeclName().getAsIdentifierInfo();
    if (info && info->getNameStart()[0] == '_') {
      // We have a name starting by _, this is reserve for compiler
      // implementation, so let's not desugar to it.
      return true;
    }
    // And let's check if it is in one of the know compiler implementation
    // namespace.
    const NamedDecl *outer =dyn_cast_or_null<NamedDecl>(decl.getDeclContext());
    while (outer && outer->getName().size() ) {
      if (outer->getName().compare("std") == 0 ||
          outer->getName().compare("__gnu_cxx") == 0) {
        return true;
      }
      outer = dyn_cast_or_null<NamedDecl>(outer->getDeclContext());
    }
    return false;
  }

  bool Analyze::IsStdClass(const clang::NamedDecl &cl)
  {
    // Return true if the class or template is declared directly in the
    // std namespace (modulo inline namespace).

    return cl.getDeclContext()->isStdNamespace();
  }

  // See Sema::PushOnScopeChains
  bool Analyze::isOnScopeChains(const NamedDecl* ND, Sema& SemaR) {

    // Named decls without name shouldn't be in. Eg: struct {int a};
    if (!ND->getDeclName())
      return false;

    // Out-of-line definitions shouldn't be pushed into scope in C++.
    // Out-of-line variable and function definitions shouldn't even in C.
    if ((isa<VarDecl>(ND) || isa<FunctionDecl>(ND)) && ND->isOutOfLine() &&
{'fraction_non_alphanumeric': 0.10849861811087629, 'fraction_numerical': 0.0013006015282067955, 'mean_word_length': 1.935922677644532, 'pattern_counts': {'":': 0, '<': 98, '<?xml version=': 0, '>': 279, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 146, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6627936', 'n_tokens_mistral': 13635, 'n_tokens_neox': 13349, 'n_words': 3315}