IndexingContext.cpp   [plain text]


//===- IndexingContext.cpp - Indexing context data ------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "IndexingContext.h"
#include "clang/Index/IndexDataConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/DeclObjC.h"
#include "clang/Basic/SourceManager.h"

using namespace clang;
using namespace index;

bool IndexingContext::shouldIndexFunctionLocalSymbols() const {
  return IndexOpts.IndexFunctionLocals;
}

bool IndexingContext::handleDecl(const Decl *D,
                                 SymbolRoleSet Roles,
                                 ArrayRef<SymbolRelation> Relations) {
  return handleDeclOccurrence(D, D->getLocation(), /*IsRef=*/false,
                              cast<Decl>(D->getDeclContext()), Roles, Relations,
                              nullptr, nullptr, D->getDeclContext());
}

bool IndexingContext::handleDecl(const Decl *D, SourceLocation Loc,
                                 SymbolRoleSet Roles,
                                 ArrayRef<SymbolRelation> Relations,
                                 const DeclContext *DC) {
  if (!DC)
    DC = D->getDeclContext();
  return handleDeclOccurrence(D, Loc, /*IsRef=*/false, cast<Decl>(DC),
                              Roles, Relations,
                              nullptr, nullptr, DC);
}

bool IndexingContext::handleReference(const NamedDecl *D, SourceLocation Loc,
                                      const NamedDecl *Parent,
                                      const DeclContext *DC,
                                      SymbolRoleSet Roles,
                                      ArrayRef<SymbolRelation> Relations,
                                      const Expr *RefE,
                                      const Decl *RefD) {
  if (!shouldIndexFunctionLocalSymbols() && isFunctionLocalDecl(D))
    return true;

  if (isa<NonTypeTemplateParmDecl>(D) || isa<TemplateTypeParmDecl>(D))
    return true;
    
  return handleDeclOccurrence(D, Loc, /*IsRef=*/true, Parent, Roles, Relations,
                              RefE, RefD, DC);
}

bool IndexingContext::importedModule(const ImportDecl *ImportD) {
  SourceLocation Loc;
  auto IdLocs = ImportD->getIdentifierLocs();
  if (!IdLocs.empty())
    Loc = IdLocs.front();
  else
    Loc = ImportD->getLocation();
  SourceManager &SM = Ctx->getSourceManager();
  Loc = SM.getFileLoc(Loc);
  if (Loc.isInvalid())
    return true;

  FileID FID;
  unsigned Offset;
  std::tie(FID, Offset) = SM.getDecomposedLoc(Loc);
  if (FID.isInvalid())
    return true;

  if (isSystemFile(FID)) {
    switch (IndexOpts.SystemSymbolFilter) {
    case IndexingOptions::SystemSymbolFilterKind::None:
      return true;
    case IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly:
    case IndexingOptions::SystemSymbolFilterKind::All:
      break;
    }
  }

  SymbolRoleSet Roles = (unsigned)SymbolRole::Declaration;
  if (ImportD->isImplicit())
    Roles |= (unsigned)SymbolRole::Implicit;

  return DataConsumer.handleModuleOccurence(ImportD, Roles, FID, Offset);
}

bool IndexingContext::isFunctionLocalDecl(const Decl *D) {
  assert(D);

  if (isa<TemplateTemplateParmDecl>(D))
    return true;

  if (isa<ObjCTypeParamDecl>(D))
    return true;

  if (!D->getParentFunctionOrMethod())
    return false;

  if (const NamedDecl *ND = dyn_cast<NamedDecl>(D)) {
    switch (ND->getFormalLinkage()) {
    case NoLinkage:
    case VisibleNoLinkage:
    case InternalLinkage:
      return true;
    case UniqueExternalLinkage:
      llvm_unreachable("Not a sema linkage");
    case ExternalLinkage:
      return false;
    }
  }

  return true;
}

bool IndexingContext::isTemplateImplicitInstantiation(const Decl *D) {
  TemplateSpecializationKind TKind = TSK_Undeclared;
  if (const ClassTemplateSpecializationDecl *
      SD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
    TKind = SD->getSpecializationKind();
  }
  if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
    TKind = FD->getTemplateSpecializationKind();
  }
  switch (TKind) {
    case TSK_Undeclared:
    case TSK_ExplicitSpecialization:
      return false;
    case TSK_ImplicitInstantiation:
    case TSK_ExplicitInstantiationDeclaration:
    case TSK_ExplicitInstantiationDefinition:
      return true;
  }
  llvm_unreachable("invalid TemplateSpecializationKind");
}

bool IndexingContext::shouldIgnoreIfImplicit(const Decl *D) {
  if (isa<ObjCInterfaceDecl>(D))
    return false;
  if (isa<ObjCCategoryDecl>(D))
    return false;
  if (isa<ObjCIvarDecl>(D))
    return false;
  if (isa<ObjCMethodDecl>(D))
    return false;
  if (isa<ImportDecl>(D))
    return false;
  return true;
}

void IndexingContext::setSysrootPath(StringRef path) {
  // Ignore sysroot path if it points to root, otherwise every header will be
  // treated as system one.
  if (path == "/")
    path = StringRef();
  SysrootPath = path;
}

bool IndexingContext::isSystemFile(FileID FID) {
  if (LastFileCheck.first == FID)
    return LastFileCheck.second;

  auto result = [&](bool res) -> bool {
    LastFileCheck = { FID, res };
    return res;
  };

  bool Invalid = false;
  const SrcMgr::SLocEntry &SEntry =
    Ctx->getSourceManager().getSLocEntry(FID, &Invalid);
  if (Invalid || !SEntry.isFile())
    return result(false);

  const SrcMgr::FileInfo &FI = SEntry.getFile();
  if (FI.getFileCharacteristic() != SrcMgr::C_User)
    return result(true);

  auto *CC = FI.getContentCache();
  if (!CC)
    return result(false);
  auto *FE = CC->OrigEntry;
  if (!FE)
    return result(false);

  if (SysrootPath.empty())
    return result(false);

  // Check if directory is in sysroot so that we can consider system headers
  // even the headers found via a user framework search path, pointing inside
  // sysroot.
  auto dirEntry = FE->getDir();
  auto pair = DirEntries.insert(std::make_pair(dirEntry, false));
  bool &isSystemDir = pair.first->second;
  bool wasInserted = pair.second;
  if (wasInserted) {
    isSystemDir = StringRef(dirEntry->getName()).startswith(SysrootPath);
  }
  return result(isSystemDir);
}

static const Decl *adjustTemplateImplicitInstantiation(const Decl *D) {
  if (const ClassTemplateSpecializationDecl *
      SD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
    return SD->getTemplateInstantiationPattern();
  }
  if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
    return FD->getTemplateInstantiationPattern();
  }
  return nullptr;
}

static bool isDeclADefinition(const Decl *D, const DeclContext *ContainerDC, ASTContext &Ctx) {
  if (auto VD = dyn_cast<VarDecl>(D))
    return VD->isThisDeclarationADefinition(Ctx);

  if (auto FD = dyn_cast<FunctionDecl>(D))
    return FD->isThisDeclarationADefinition();

  if (auto TD = dyn_cast<TagDecl>(D))
    return TD->isThisDeclarationADefinition();

  if (auto MD = dyn_cast<ObjCMethodDecl>(D))
    return MD->isThisDeclarationADefinition() || isa<ObjCImplDecl>(ContainerDC);

  if (isa<TypedefNameDecl>(D) ||
      isa<EnumConstantDecl>(D) ||
      isa<FieldDecl>(D) ||
      isa<MSPropertyDecl>(D) ||
      isa<ObjCImplDecl>(D) ||
      isa<ObjCPropertyImplDecl>(D))
    return true;

  return false;
}

static const Decl *adjustParent(const Decl *Parent) {
  if (!Parent)
    return nullptr;
  for (;; Parent = cast<Decl>(Parent->getDeclContext())) {
    if (isa<TranslationUnitDecl>(Parent))
      return nullptr;
    if (isa<LinkageSpecDecl>(Parent) || isa<BlockDecl>(Parent))
      continue;
    if (auto NS = dyn_cast<NamespaceDecl>(Parent)) {
      if (NS->isAnonymousNamespace())
        continue;
    } else if (auto RD = dyn_cast<RecordDecl>(Parent)) {
      if (RD->isAnonymousStructOrUnion())
        continue;
    } else if (auto FD = dyn_cast<FieldDecl>(Parent)) {
      if (FD->getDeclName().isEmpty())
        continue;
    }
    return Parent;
  }
}

static const Decl *getCanonicalDecl(const Decl *D) {
  D = D->getCanonicalDecl();
  if (auto TD = dyn_cast<TemplateDecl>(D)) {
    D = TD->getTemplatedDecl();
    assert(D->isCanonicalDecl());
  }

  return D;
}

bool IndexingContext::handleDeclOccurrence(const Decl *D, SourceLocation Loc,
                                           bool IsRef, const Decl *Parent,
                                           SymbolRoleSet Roles,
                                           ArrayRef<SymbolRelation> Relations,
                                           const Expr *OrigE,
                                           const Decl *OrigD,
                                           const DeclContext *ContainerDC) {
  if (D->isImplicit() && !isa<ObjCMethodDecl>(D))
    return true;
  if (!isa<NamedDecl>(D) ||
      (cast<NamedDecl>(D)->getDeclName().isEmpty() &&
       !isa<TagDecl>(D) && !isa<ObjCCategoryDecl>(D)))
    return true;

  SourceManager &SM = Ctx->getSourceManager();
  Loc = SM.getFileLoc(Loc);
  if (Loc.isInvalid())
    return true;

  FileID FID;
  unsigned Offset;
  std::tie(FID, Offset) = SM.getDecomposedLoc(Loc);
  if (FID.isInvalid())
    return true;

  if (isSystemFile(FID)) {
    switch (IndexOpts.SystemSymbolFilter) {
    case IndexingOptions::SystemSymbolFilterKind::None:
      return true;
    case IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly:
      if (IsRef)
        return true;
      break;
    case IndexingOptions::SystemSymbolFilterKind::All:
      break;
    }
  }

  if (isTemplateImplicitInstantiation(D)) {
    if (!IsRef)
      return true;
    D = adjustTemplateImplicitInstantiation(D);
    if (!D)
      return true;
    assert(!isTemplateImplicitInstantiation(D));
  }

  if (!OrigD)
    OrigD = D;

  if (IsRef)
    Roles |= (unsigned)SymbolRole::Reference;
  else if (isDeclADefinition(D, ContainerDC, *Ctx))
    Roles |= (unsigned)SymbolRole::Definition;
  else
    Roles |= (unsigned)SymbolRole::Declaration;

  D = getCanonicalDecl(D);
  if (D->isImplicit() && !isa<ObjCMethodDecl>(D) &&
      !(isa<FunctionDecl>(D) && cast<FunctionDecl>(D)->getBuiltinID())) {
    // operator new declarations will link to the implicit one as canonical.
    return true;
  }
  Parent = adjustParent(Parent);
  if (Parent)
    Parent = getCanonicalDecl(Parent);
  assert((!Parent || !Parent->isImplicit() ||
          (isa<FunctionDecl>(Parent) &&
           cast<FunctionDecl>(Parent)->getBuiltinID()) ||
          isa<ObjCInterfaceDecl>(Parent) || isa<ObjCMethodDecl>(Parent)) &&
         "unexpected implicit parent!");

  SmallVector<SymbolRelation, 6> FinalRelations;
  FinalRelations.reserve(Relations.size()+1);

  auto addRelation = [&](SymbolRelation Rel) {
    auto It = std::find_if(FinalRelations.begin(), FinalRelations.end(),
                [&](SymbolRelation Elem)->bool {
                  return Elem.RelatedSymbol == Rel.RelatedSymbol;
                });
    if (It != FinalRelations.end()) {
      It->Roles |= Rel.Roles;
    } else {
      FinalRelations.push_back(Rel);
    }
    Roles |= Rel.Roles;
  };

  if (!IsRef && Parent && !cast<DeclContext>(Parent)->isFunctionOrMethod()) {
    addRelation(SymbolRelation{(unsigned)SymbolRole::RelationChildOf, Parent});
  }
  for (auto &Rel : Relations) {
    addRelation(SymbolRelation(Rel.Roles,
                               Rel.RelatedSymbol->getCanonicalDecl()));
  }

  IndexDataConsumer::ASTNodeInfo Node{ OrigE, OrigD, Parent, ContainerDC };
  return DataConsumer.handleDeclOccurence(D, Roles, FinalRelations, FID, Offset,
                                          Node);
}