Skip to content
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
64 changes: 61 additions & 3 deletions infra/indexer/frontend/ast_visitor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,21 @@ void AddVirtualMethodLinksImpl(
}
}

const clang::CXXRecordDecl* GetRecordForType(const clang::QualType& type) {
clang::QualType derived_type = type;
if (const auto* pointer_type = type->getAs<clang::PointerType>()) {
derived_type = pointer_type->getPointeeType();
}
if (derived_type->isDependentType()) {
return nullptr;
}
const auto* record_type = derived_type->castAs<clang::RecordType>();
CHECK(record_type);
const clang::Decl* decl = record_type->getOriginalDecl();
CHECK(decl);
return llvm::cast<clang::CXXRecordDecl>(decl);
}

} // namespace

bool AstVisitor::VisitCallExpr(const clang::CallExpr* expr) {
Expand Down Expand Up @@ -1659,9 +1674,34 @@ void AstVisitor::AddReferencesForExpr(const clang::Expr* expr) {
decl = llvm::cast<clang::DeclRefExpr>(expr)->getDecl();
} else if (llvm::isa<clang::MemberExpr>(expr)) {
const auto* member_expr = llvm::cast<clang::MemberExpr>(expr);
decl = member_expr->getMemberDecl();
if (member_expr->getBase()) {
AddReferencesForExpr(member_expr->getBase());
const clang::ValueDecl* value_decl = member_expr->getMemberDecl();
decl = value_decl;
if (clang::Expr* base = member_expr->getBase()) {
AddReferencesForExpr(base);

// Check if the access is through an inheriting descendant, in which case
// we add a cross-reference to the corresponding synthetic entity.
//
// Skip the case of an explicit qualification (`instance.Base::method`)
// because it is commonly used for members not accessible through the
// instance directly (for disambiguation).
if (!member_expr->getQualifierLoc()) {
if (const auto* expr_record_decl =
GetRecordForType(base->IgnoreParenBaseCasts()->getType())) {
const clang::DeclContext* decl_context =
value_decl->getNonTransparentDeclContext();
// If the base expression is not of the same record type as the parent
// of the retrieved member...
if (const auto* record_decl =
llvm::dyn_cast<clang::CXXRecordDecl>(decl_context);
record_decl && record_decl->getCanonicalDecl() !=
expr_record_decl->getCanonicalDecl()) {
// ...add synthetic entity cross-references.
AddSyntheticMemberReference(expr_record_decl, value_decl,
expr->getSourceRange());
}
}
}
}
} else if (llvm::isa<clang::LambdaExpr>(expr)) {
decl = llvm::cast<clang::LambdaExpr>(expr)
Expand All @@ -1674,6 +1714,24 @@ void AstVisitor::AddReferencesForExpr(const clang::Expr* expr) {
}
}

void AstVisitor::AddSyntheticMemberReference(
const clang::CXXRecordDecl* child_class,
const clang::ValueDecl* inherited_member, const clang::SourceRange& range) {
const EntityId base_member_entity_id = GetEntityIdForDecl(inherited_member);
if (base_member_entity_id == kInvalidEntityId) {
return;
}
const Entity& base_member_entity =
index_.GetEntityById(base_member_entity_id);
const Entity synthetic_inherited_member = Entity(
base_member_entity, GetNamePrefixForDeclContext(child_class, context_),
/*inherited_entity_id=*/base_member_entity_id);
const EntityId synthetic_inherited_member_id =
index_.GetEntityId(synthetic_inherited_member);
auto location_id = GetLocationId(range.getBegin(), range.getEnd());
(void)index_.GetReferenceId({synthetic_inherited_member_id, location_id});
}

void AstVisitor::AddDeclReferenceForSourceRange(const clang::SourceRange& range,
const clang::Decl* decl) {
auto entity_id = GetEntityIdForDecl(decl, /*for_reference=*/true);
Expand Down
3 changes: 3 additions & 0 deletions infra/indexer/frontend/ast_visitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ class AstVisitor : public clang::RecursiveASTVisitor<AstVisitor> {
const clang::Decl* template_decl, const clang::Decl* original_decl);
void SynthesizeInheritedMemberEntities(
const clang::CXXRecordDecl* class_decl);
void AddSyntheticMemberReference(const clang::CXXRecordDecl* child_class,
const clang::ValueDecl* inherited_member,
const clang::SourceRange& range);
void AddTypeReferencesFromLocation(LocationId location_id,
const clang::Type *type,
bool outermost_type = true);
Expand Down
41 changes: 41 additions & 0 deletions infra/indexer/frontend/frontend_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2256,6 +2256,47 @@ TEST(FrontendTest, MoreCursedInheritance) {

// Note that this link skips `C` because it is devoid of `moo(int)`.
EXPECT_HAS_VIRTUAL_LINK(index, "B::moo(int)", "D::moo(int)");

// Qualified-name member accesses only create references to the base class
// entities...
EXPECT_HAS_REFERENCE(
index, Entity::Kind::kFunction, "A::", "foo", "()", "snippet.cc", 2, 2,
"snippet.cc", 19, 19, /*is_incomplete=*/false,
/*template_prototype_entity_id=*/std::nullopt,
/*implicitly_defined_for_entity_id=*/std::nullopt,
/*enum_value=*/std::nullopt, /*inherited_from_entity_id=*/std::nullopt,
/*virtual_method_kind=*/Entity::VirtualMethodKind::kNonPureVirtual);
EXPECT_HAS_REFERENCE(
index, Entity::Kind::kFunction, "B::", "foo", "()", "snippet.cc", 7, 7,
"snippet.cc", 19, 19, /*is_incomplete=*/false,
/*template_prototype_entity_id=*/std::nullopt,
/*implicitly_defined_for_entity_id=*/std::nullopt,
/*enum_value=*/std::nullopt, /*inherited_from_entity_id=*/std::nullopt,
/*virtual_method_kind=*/Entity::VirtualMethodKind::kNonPureVirtual);
// ...whereas an unqualified-name one also creates a reference to the
// synthesized inherited entity in the child.
EXPECT_HAS_REFERENCE(
index, Entity::Kind::kFunction, "C::", "bar", "()", "snippet.cc", 13, 13,
"snippet.cc", 21, 21, /*is_incomplete=*/false,
/*template_prototype_entity_id=*/std::nullopt,
/*implicitly_defined_for_entity_id=*/std::nullopt,
/*enum_value=*/std::nullopt, /*inherited_from_entity_id=*/std::nullopt,
/*virtual_method_kind=*/Entity::VirtualMethodKind::kNonPureVirtual);
EXPECT_HAS_REFERENCE(
index, Entity::Kind::kFunction, "D::", "bar", "()", "snippet.cc", 13, 13,
"snippet.cc", 21, 21, /*is_incomplete=*/false,
/*template_prototype_entity_id=*/std::nullopt,
/*implicitly_defined_for_entity_id=*/std::nullopt,
/*enum_value=*/std::nullopt, /*inherited_from_entity_id=*/
RequiredEntityId(
index, Entity::Kind::kFunction, "C::", "bar", "()", "snippet.cc", 13,
13, /*is_incomplete=*/false,
/*template_prototype_entity_id=*/std::nullopt,
/*implicitly_defined_for_entity_id=*/std::nullopt,
/*enum_value=*/std::nullopt,
/*inherited_from_entity_id=*/std::nullopt,
/*virtual_method_kind=*/Entity::VirtualMethodKind::kNonPureVirtual),
/*virtual_method_kind=*/Entity::VirtualMethodKind::kNonPureVirtual);
}

TEST(FrontendTest, InheritanceThroughTemplateInstantiation) {
Expand Down
Loading