blob: ec4ca23942cad91ecb3dcd3665803ab0363535ce [file] [log] [blame]
//===- IndexTypeSourceInfo.cpp - Indexing types ---------------------------===//
//
// 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 "IndexingContext.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "llvm/ADT/ScopeExit.h"
using namespace clang;
using namespace index;
namespace {
class TypeIndexer : public RecursiveASTVisitor<TypeIndexer> {
IndexingContext &IndexCtx;
const NamedDecl *Parent;
const DeclContext *ParentDC;
bool IsBase;
SmallVector<SymbolRelation, 3> Relations;
typedef RecursiveASTVisitor<TypeIndexer> base;
public:
TypeIndexer(IndexingContext &indexCtx, const NamedDecl *parent,
const DeclContext *DC, bool isBase, bool isIBType)
: IndexCtx(indexCtx), Parent(parent), ParentDC(DC), IsBase(isBase) {
if (IsBase) {
assert(Parent);
Relations.emplace_back((unsigned)SymbolRole::RelationBaseOf, Parent);
}
if (isIBType) {
assert(Parent);
Relations.emplace_back((unsigned)SymbolRole::RelationIBTypeOf, Parent);
}
}
bool shouldWalkTypesOfTypeLocs() const { return false; }
#define TRY_TO(CALL_EXPR) \
do { \
if (!CALL_EXPR) \
return false; \
} while (0)
bool VisitTemplateTypeParmTypeLoc(TemplateTypeParmTypeLoc TTPL) {
SourceLocation Loc = TTPL.getNameLoc();
TemplateTypeParmDecl *TTPD = TTPL.getDecl();
return IndexCtx.handleReference(TTPD, Loc, Parent, ParentDC,
SymbolRoleSet());
}
bool VisitTypedefTypeLoc(TypedefTypeLoc TL) {
SourceLocation Loc = TL.getNameLoc();
TypedefNameDecl *ND = TL.getTypedefNameDecl();
if (ND->isTransparentTag()) {
TagDecl *Underlying = ND->getUnderlyingType()->getAsTagDecl();
return IndexCtx.handleReference(Underlying, Loc, Parent,
ParentDC, SymbolRoleSet(), Relations);
}
if (IsBase) {
TRY_TO(IndexCtx.handleReference(ND, Loc,
Parent, ParentDC, SymbolRoleSet()));
if (auto *CD = TL.getType()->getAsCXXRecordDecl()) {
TRY_TO(IndexCtx.handleReference(CD, Loc, Parent, ParentDC,
(unsigned)SymbolRole::Implicit,
Relations));
}
} else {
TRY_TO(IndexCtx.handleReference(ND, Loc,
Parent, ParentDC, SymbolRoleSet(),
Relations));
}
return true;
}
bool traverseParamVarHelper(ParmVarDecl *D) {
TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc()));
if (D->getTypeSourceInfo())
TRY_TO(TraverseTypeLoc(D->getTypeSourceInfo()->getTypeLoc()));
return true;
}
bool TraverseParmVarDecl(ParmVarDecl *D) {
// Avoid visiting default arguments from the definition that were already
// visited in the declaration.
// FIXME: A free function definition can have default arguments.
// Avoiding double visitaiton of default arguments should be handled by the
// visitor probably with a bit in the AST to indicate if the attached
// default argument was 'inherited' or written in source.
if (auto FD = dyn_cast<FunctionDecl>(D->getDeclContext())) {
if (FD->isThisDeclarationADefinition()) {
return traverseParamVarHelper(D);
}
}
return base::TraverseParmVarDecl(D);
}
bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) {
IndexCtx.indexNestedNameSpecifierLoc(NNS, Parent, ParentDC);
return true;
}
bool VisitTagTypeLoc(TagTypeLoc TL) {
TagDecl *D = TL.getDecl();
if (!IndexCtx.shouldIndexFunctionLocalSymbols() &&
D->getParentFunctionOrMethod())
return true;
if (TL.isDefinition()) {
IndexCtx.indexTagDecl(D);
return true;
}
return IndexCtx.handleReference(D, TL.getNameLoc(),
Parent, ParentDC, SymbolRoleSet(),
Relations);
}
bool VisitObjCInterfaceTypeLoc(ObjCInterfaceTypeLoc TL) {
return IndexCtx.handleReference(TL.getIFaceDecl(), TL.getNameLoc(),
Parent, ParentDC, SymbolRoleSet(), Relations);
}
bool VisitObjCObjectTypeLoc(ObjCObjectTypeLoc TL) {
for (unsigned i = 0, e = TL.getNumProtocols(); i != e; ++i) {
IndexCtx.handleReference(TL.getProtocol(i), TL.getProtocolLoc(i),
Parent, ParentDC, SymbolRoleSet(), Relations);
}
return true;
}
void HandleTemplateSpecializationTypeLoc(TemplateName TemplName,
SourceLocation TemplNameLoc,
CXXRecordDecl *ResolvedClass,
bool IsTypeAlias) {
// In presence of type aliases, the resolved class was never written in
// the code so don't report it.
if (!IsTypeAlias && ResolvedClass &&
(!ResolvedClass->isImplicit() ||
IndexCtx.shouldIndexImplicitInstantiation())) {
IndexCtx.handleReference(ResolvedClass, TemplNameLoc, Parent, ParentDC,
SymbolRoleSet(), Relations);
} else if (const TemplateDecl *D = TemplName.getAsTemplateDecl()) {
IndexCtx.handleReference(D, TemplNameLoc, Parent, ParentDC,
SymbolRoleSet(), Relations);
}
}
bool VisitTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc TL) {
auto *T = TL.getTypePtr();
if (!T)
return true;
HandleTemplateSpecializationTypeLoc(
T->getTemplateName(), TL.getTemplateNameLoc(), T->getAsCXXRecordDecl(),
T->isTypeAlias());
return true;
}
bool TraverseTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc TL) {
if (!WalkUpFromTemplateSpecializationTypeLoc(TL))
return false;
if (!TraverseTemplateName(TL.getTypePtr()->getTemplateName()))
return false;
// The relations we have to `Parent` do not apply to our template arguments,
// so clear them while visiting the args.
SmallVector<SymbolRelation, 3> SavedRelations = Relations;
Relations.clear();
auto ResetSavedRelations =
llvm::make_scope_exit([&] { this->Relations = SavedRelations; });
for (unsigned I = 0, E = TL.getNumArgs(); I != E; ++I) {
if (!TraverseTemplateArgumentLoc(TL.getArgLoc(I)))
return false;
}
return true;
}
bool VisitDeducedTemplateSpecializationTypeLoc(DeducedTemplateSpecializationTypeLoc TL) {
auto *T = TL.getTypePtr();
if (!T)
return true;
HandleTemplateSpecializationTypeLoc(
T->getTemplateName(), TL.getTemplateNameLoc(), T->getAsCXXRecordDecl(),
/*IsTypeAlias=*/false);
return true;
}
bool VisitInjectedClassNameTypeLoc(InjectedClassNameTypeLoc TL) {
return IndexCtx.handleReference(TL.getDecl(), TL.getNameLoc(), Parent,
ParentDC, SymbolRoleSet(), Relations);
}
bool VisitDependentNameTypeLoc(DependentNameTypeLoc TL) {
const DependentNameType *DNT = TL.getTypePtr();
const NestedNameSpecifier *NNS = DNT->getQualifier();
const Type *T = NNS->getAsType();
if (!T)
return true;
const TemplateSpecializationType *TST =
T->getAs<TemplateSpecializationType>();
if (!TST)
return true;
TemplateName TN = TST->getTemplateName();
const ClassTemplateDecl *TD =
dyn_cast_or_null<ClassTemplateDecl>(TN.getAsTemplateDecl());
if (!TD)
return true;
CXXRecordDecl *RD = TD->getTemplatedDecl();
if (!RD->hasDefinition())
return true;
RD = RD->getDefinition();
DeclarationName Name(DNT->getIdentifier());
std::vector<const NamedDecl *> Symbols = RD->lookupDependentName(
Name, [](const NamedDecl *ND) { return isa<TypeDecl>(ND); });
if (Symbols.size() != 1)
return true;
return IndexCtx.handleReference(Symbols[0], TL.getNameLoc(), Parent,
ParentDC, SymbolRoleSet(), Relations);
}
bool TraverseStmt(Stmt *S) {
IndexCtx.indexBody(S, Parent, ParentDC);
return true;
}
};
} // anonymous namespace
void IndexingContext::indexTypeSourceInfo(TypeSourceInfo *TInfo,
const NamedDecl *Parent,
const DeclContext *DC,
bool isBase,
bool isIBType) {
if (!TInfo || TInfo->getTypeLoc().isNull())
return;
indexTypeLoc(TInfo->getTypeLoc(), Parent, DC, isBase, isIBType);
}
void IndexingContext::indexTypeLoc(TypeLoc TL,
const NamedDecl *Parent,
const DeclContext *DC,
bool isBase,
bool isIBType) {
if (TL.isNull())
return;
if (!DC)
DC = Parent->getLexicalDeclContext();
TypeIndexer(*this, Parent, DC, isBase, isIBType).TraverseTypeLoc(TL);
}
void IndexingContext::indexNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS,
const NamedDecl *Parent,
const DeclContext *DC) {
if (!NNS)
return;
if (NestedNameSpecifierLoc Prefix = NNS.getPrefix())
indexNestedNameSpecifierLoc(Prefix, Parent, DC);
if (!DC)
DC = Parent->getLexicalDeclContext();
SourceLocation Loc = NNS.getLocalBeginLoc();
switch (NNS.getNestedNameSpecifier()->getKind()) {
case NestedNameSpecifier::Identifier:
case NestedNameSpecifier::Global:
case NestedNameSpecifier::Super:
break;
case NestedNameSpecifier::Namespace:
handleReference(NNS.getNestedNameSpecifier()->getAsNamespace(),
Loc, Parent, DC, SymbolRoleSet());
break;
case NestedNameSpecifier::NamespaceAlias:
handleReference(NNS.getNestedNameSpecifier()->getAsNamespaceAlias(),
Loc, Parent, DC, SymbolRoleSet());
break;
case NestedNameSpecifier::TypeSpec:
case NestedNameSpecifier::TypeSpecWithTemplate:
indexTypeLoc(NNS.getTypeLoc(), Parent, DC);
break;
}
}
void IndexingContext::indexTagDecl(const TagDecl *D,
ArrayRef<SymbolRelation> Relations) {
if (!shouldIndex(D))
return;
if (!shouldIndexFunctionLocalSymbols() && isFunctionLocalSymbol(D))
return;
if (handleDecl(D, /*Roles=*/SymbolRoleSet(), Relations)) {
if (D->isThisDeclarationADefinition()) {
indexNestedNameSpecifierLoc(D->getQualifierLoc(), D);
if (auto CXXRD = dyn_cast<CXXRecordDecl>(D)) {
for (const auto &I : CXXRD->bases()) {
indexTypeSourceInfo(I.getTypeSourceInfo(), CXXRD, CXXRD, /*isBase=*/true);
}
}
indexDeclContext(D);
}
}
}