Skip to content

[clang][Index] Use HeuristicResolver in libIndex #125153

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/lib/Index/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ add_clang_library(clangIndex
clangFormat
clangFrontend
clangLex
clangSema
clangSerialization
clangToolingCore

Expand Down
47 changes: 14 additions & 33 deletions clang/lib/Index/IndexBody.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "clang/AST/ExprConcepts.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/Type.h"
#include "clang/Sema/HeuristicResolver.h"

using namespace clang;
using namespace clang::index;
Expand Down Expand Up @@ -168,51 +169,31 @@ class BodyIndexer : public RecursiveASTVisitor<BodyIndexer> {
Parent, ParentDC, Roles, Relations, E);
}

bool indexDependentReference(
const Expr *E, const Type *T, const DeclarationNameInfo &NameInfo,
llvm::function_ref<bool(const NamedDecl *ND)> Filter) {
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();
std::vector<const NamedDecl *> Symbols =
RD->lookupDependentName(NameInfo.getName(), Filter);
bool indexDependentReference(const Expr *E, SourceLocation Loc,
std::vector<const NamedDecl *> TargetSymbols) {
// FIXME: Improve overload handling.
if (Symbols.size() != 1)
if (TargetSymbols.size() != 1)
return true;
SourceLocation Loc = NameInfo.getLoc();
if (Loc.isInvalid())
Loc = E->getBeginLoc();
SmallVector<SymbolRelation, 4> Relations;
SymbolRoleSet Roles = getRolesForRef(E, Relations);
return IndexCtx.handleReference(Symbols[0], Loc, Parent, ParentDC, Roles,
Relations, E);
return IndexCtx.handleReference(TargetSymbols[0], Loc, Parent, ParentDC,
Roles, Relations, E);
}

bool VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *E) {
const DeclarationNameInfo &Info = E->getMemberNameInfo();
return indexDependentReference(
E, E->getBaseType().getTypePtrOrNull(), Info,
[](const NamedDecl *D) { return D->isCXXInstanceMember(); });
auto *Resolver = IndexCtx.getResolver();
assert(Resolver);
return indexDependentReference(E, E->getMemberNameInfo().getLoc(),
Resolver->resolveMemberExpr(E));
}

bool VisitDependentScopeDeclRefExpr(DependentScopeDeclRefExpr *E) {
const DeclarationNameInfo &Info = E->getNameInfo();
const NestedNameSpecifier *NNS = E->getQualifier();
return indexDependentReference(
E, NNS->getAsType(), Info,
[](const NamedDecl *D) { return !D->isCXXInstanceMember(); });
auto *Resolver = IndexCtx.getResolver();
assert(Resolver);
return indexDependentReference(E, E->getNameInfo().getLoc(),
Resolver->resolveDeclRefExpr(E));
}

bool VisitDesignatedInitExpr(DesignatedInitExpr *E) {
Expand Down
12 changes: 12 additions & 0 deletions clang/lib/Index/IndexingContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Index/IndexDataConsumer.h"
#include "clang/Sema/HeuristicResolver.h"

using namespace clang;
using namespace index;
Expand All @@ -25,6 +26,17 @@ static bool isGeneratedDecl(const Decl *D) {
return false;
}

IndexingContext::IndexingContext(IndexingOptions IndexOpts,
IndexDataConsumer &DataConsumer)
: IndexOpts(IndexOpts), DataConsumer(DataConsumer) {}

IndexingContext::~IndexingContext() = default;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@HighCommander4 you defaulted the destructor in the source file b/c of the std::unique_ptr<HeuristicResolver> Resolver which would require the HeuristicResolver to be complete when the destructor is defined, correct?

This was flagged by static analysis as a rule of three violation but it looks like it makes sense in this case but it should have been explicitly documented with a comment b/c it is not obvious w/o some digging.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's right; the motivation is to avoid unnecessary header dependencies (e.g. IndexingContext.h including HeuristicResolver.h), to limit incremental rebuild times when a header is modified.

I'm happy to add a comment.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sent out #138640


void IndexingContext::setASTContext(ASTContext &ctx) {
Ctx = &ctx;
Resolver = Ctx ? std::make_unique<HeuristicResolver>(*Ctx) : nullptr;
}

bool IndexingContext::shouldIndex(const Decl *D) {
return !isGeneratedDecl(D);
}
Expand Down
10 changes: 7 additions & 3 deletions clang/lib/Index/IndexingContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ namespace clang {
class Decl;
class DeclGroupRef;
class ImportDecl;
class HeuristicResolver;
class TagDecl;
class TypeSourceInfo;
class NamedDecl;
Expand All @@ -39,15 +40,18 @@ class IndexingContext {
IndexingOptions IndexOpts;
IndexDataConsumer &DataConsumer;
ASTContext *Ctx = nullptr;
std::unique_ptr<HeuristicResolver> Resolver;

public:
IndexingContext(IndexingOptions IndexOpts, IndexDataConsumer &DataConsumer)
: IndexOpts(IndexOpts), DataConsumer(DataConsumer) {}
IndexingContext(IndexingOptions IndexOpts, IndexDataConsumer &DataConsumer);
~IndexingContext();

const IndexingOptions &getIndexOpts() const { return IndexOpts; }
IndexDataConsumer &getDataConsumer() { return DataConsumer; }

void setASTContext(ASTContext &ctx) { Ctx = &ctx; }
void setASTContext(ASTContext &ctx);

HeuristicResolver *getResolver() const { return Resolver.get(); }

bool shouldIndex(const Decl *D);

Expand Down
22 changes: 11 additions & 11 deletions clang/test/Index/Core/index-dependent-source.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
int invalid;

class Base {
void baseFunction();
void baseFunction() const;

int baseField;

Expand All @@ -13,7 +13,7 @@ class Base {
template<typename T>
class BaseTemplate {
public:
T baseTemplateFunction();
T baseTemplateFunction() const;

T baseTemplateField;

Expand All @@ -25,7 +25,7 @@ class TemplateClass: public Base , public BaseTemplate<T> {
public:
~TemplateClass();

T function() { }
T function() const { }

static void staticFunction() { }

Expand All @@ -48,27 +48,27 @@ template<typename T, typename S>
void indexSimpleDependentDeclarations(const TemplateClass<T, S> &object) {
// Valid instance members:
object.function();
// CHECK: [[@LINE-1]]:10 | instance-method/C++ | function | c:@ST>2#T#T@TemplateClass@F@function# | <no-cgname> | Ref,Call,RelCall,RelCont | rel: 1
// CHECK: [[@LINE-1]]:10 | instance-method/C++ | function | c:@ST>2#T#T@TemplateClass@F@function#1 | <no-cgname> | Ref,Call,RelCall,RelCont | rel: 1
object.field;
// CHECK: [[@LINE-1]]:10 | field/C++ | field | c:@ST>2#T#T@TemplateClass@FI@field | <no-cgname> | Ref,RelCont | rel: 1
object.baseFunction();
// CHECK: [[@LINE-1]]:10 | instance-method/C++ | baseFunction | c:@S@Base@F@baseFunction# | __ZN4Base12baseFunctionEv | Ref,Call,RelCall,RelCont | rel: 1
// CHECK: [[@LINE-1]]:10 | instance-method/C++ | baseFunction | c:@S@Base@F@baseFunction#1 | __ZNK4Base12baseFunctionEv | Ref,Call,RelCall,RelCont | rel: 1
object.baseField;
// CHECK: [[@LINE-1]]:10 | field/C++ | baseField | c:@S@Base@FI@baseField | <no-cgname> | Ref,RelCont | rel: 1
object.baseTemplateFunction();
// CHECK: [[@LINE-1]]:10 | instance-method/C++ | baseTemplateFunction | c:@ST>1#T@BaseTemplate@F@baseTemplateFunction# | <no-cgname> | Ref,Call,RelCall,RelCont | rel: 1
// CHECK: [[@LINE-1]]:10 | instance-method/C++ | baseTemplateFunction | c:@ST>1#T@BaseTemplate@F@baseTemplateFunction#1 | <no-cgname> | Ref,Call,RelCall,RelCont | rel: 1
object.baseTemplateField;
// CHECK: [[@LINE-1]]:10 | field/C++ | baseTemplateField | c:@ST>1#T@BaseTemplate@FI@baseTemplateField | <no-cgname> | Ref,RelCont | rel: 1

// Invalid instance members:
// Static members (these are still valid to access via an instance):
object.variable;
// CHECK-NOT: [[@LINE-1]]:10
// CHECK: [[@LINE-1]]:10 | static-property/C++ | variable | c:@ST>2#T#T@TemplateClass@variable | __ZN13TemplateClass8variableE | Ref,RelCont | rel: 1
object.staticFunction();
// CHECK-NOT: [[@LINE-1]]:10
// CHECK: [[@LINE-1]]:10 | static-method/C++ | staticFunction | c:@ST>2#T#T@TemplateClass@F@staticFunction#S | <no-cgname> | Ref,Call,RelCall,RelCont | rel: 1
object.Struct;
// CHECK-NOT: [[@LINE-1]]:10
// CHECK: [[@LINE-1]]:10 | struct/C | Struct | c:@ST>2#T#T@TemplateClass@S@Struct | <no-cgname> | Ref,RelCont | rel: 1
object.EnumValue;
// CHECK-NOT: [[@LINE-1]]:10
// CHECK: [[@LINE-1]]:10 | enumerator/C | EnumValue | c:@ST>2#T#T@TemplateClass@E@Enum@EnumValue | <no-cgname> | Ref,RelCont | rel: 1

// Valid static members:
TemplateClass<T, S>::staticFunction();
Expand Down