Skip to content

Commit ce0345a

Browse files
OSS-Fuzz Teamcopybara-github
authored andcommitted
Add cross-references to synthesized inherited members
Indexer-PiperOrigin-RevId: 794883704
1 parent 946475d commit ce0345a

File tree

3 files changed

+105
-3
lines changed

3 files changed

+105
-3
lines changed

infra/indexer/frontend/ast_visitor.cc

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,21 @@ void AddVirtualMethodLinksImpl(
852852
}
853853
}
854854

855+
const clang::CXXRecordDecl* GetRecordForType(const clang::QualType& type) {
856+
clang::QualType derived_type = type;
857+
if (const auto* pointer_type = type->getAs<clang::PointerType>()) {
858+
derived_type = pointer_type->getPointeeType();
859+
}
860+
if (derived_type->isDependentType()) {
861+
return nullptr;
862+
}
863+
const auto* record_type = derived_type->castAs<clang::RecordType>();
864+
CHECK(record_type);
865+
const clang::Decl* decl = record_type->getOriginalDecl();
866+
CHECK(decl);
867+
return llvm::cast<clang::CXXRecordDecl>(decl);
868+
}
869+
855870
} // namespace
856871

857872
bool AstVisitor::VisitCallExpr(const clang::CallExpr* expr) {
@@ -1659,9 +1674,34 @@ void AstVisitor::AddReferencesForExpr(const clang::Expr* expr) {
16591674
decl = llvm::cast<clang::DeclRefExpr>(expr)->getDecl();
16601675
} else if (llvm::isa<clang::MemberExpr>(expr)) {
16611676
const auto* member_expr = llvm::cast<clang::MemberExpr>(expr);
1662-
decl = member_expr->getMemberDecl();
1663-
if (member_expr->getBase()) {
1664-
AddReferencesForExpr(member_expr->getBase());
1677+
const clang::ValueDecl* value_decl = member_expr->getMemberDecl();
1678+
decl = value_decl;
1679+
if (clang::Expr* base = member_expr->getBase()) {
1680+
AddReferencesForExpr(base);
1681+
1682+
// Check if the access is through an inheriting descendant, in which case
1683+
// we add a cross-reference to the corresponding synthetic entity.
1684+
//
1685+
// Skip the case of an explicit qualification (`instance.Base::method`)
1686+
// because it is commonly used for members not accessible through the
1687+
// instance directly (for disambiguation).
1688+
if (!member_expr->getQualifierLoc()) {
1689+
if (const auto* expr_record_decl =
1690+
GetRecordForType(base->IgnoreParenBaseCasts()->getType())) {
1691+
const clang::DeclContext* decl_context =
1692+
value_decl->getNonTransparentDeclContext();
1693+
// If the base expression is not of the same record type as the parent
1694+
// of the retrieved member...
1695+
if (const auto* record_decl =
1696+
llvm::dyn_cast<clang::CXXRecordDecl>(decl_context);
1697+
record_decl && record_decl->getCanonicalDecl() !=
1698+
expr_record_decl->getCanonicalDecl()) {
1699+
// ...add synthetic entity cross-references.
1700+
AddSyntheticMemberReference(expr_record_decl, value_decl,
1701+
expr->getSourceRange());
1702+
}
1703+
}
1704+
}
16651705
}
16661706
} else if (llvm::isa<clang::LambdaExpr>(expr)) {
16671707
decl = llvm::cast<clang::LambdaExpr>(expr)
@@ -1674,6 +1714,24 @@ void AstVisitor::AddReferencesForExpr(const clang::Expr* expr) {
16741714
}
16751715
}
16761716

1717+
void AstVisitor::AddSyntheticMemberReference(
1718+
const clang::CXXRecordDecl* child_class,
1719+
const clang::ValueDecl* inherited_member, const clang::SourceRange& range) {
1720+
const EntityId base_member_entity_id = GetEntityIdForDecl(inherited_member);
1721+
if (base_member_entity_id == kInvalidEntityId) {
1722+
return;
1723+
}
1724+
const Entity& base_member_entity =
1725+
index_.GetEntityById(base_member_entity_id);
1726+
const Entity synthetic_inherited_member = Entity(
1727+
base_member_entity, GetNamePrefixForDeclContext(child_class, context_),
1728+
/*inherited_entity_id=*/base_member_entity_id);
1729+
const EntityId synthetic_inherited_member_id =
1730+
index_.GetEntityId(synthetic_inherited_member);
1731+
auto location_id = GetLocationId(range.getBegin(), range.getEnd());
1732+
(void)index_.GetReferenceId({synthetic_inherited_member_id, location_id});
1733+
}
1734+
16771735
void AstVisitor::AddDeclReferenceForSourceRange(const clang::SourceRange& range,
16781736
const clang::Decl* decl) {
16791737
auto entity_id = GetEntityIdForDecl(decl, /*for_reference=*/true);

infra/indexer/frontend/ast_visitor.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ class AstVisitor : public clang::RecursiveASTVisitor<AstVisitor> {
8282
const clang::Decl* template_decl, const clang::Decl* original_decl);
8383
void SynthesizeInheritedMemberEntities(
8484
const clang::CXXRecordDecl* class_decl);
85+
void AddSyntheticMemberReference(const clang::CXXRecordDecl* child_class,
86+
const clang::ValueDecl* inherited_member,
87+
const clang::SourceRange& range);
8588
void AddTypeReferencesFromLocation(LocationId location_id,
8689
const clang::Type *type,
8790
bool outermost_type = true);

infra/indexer/frontend/frontend_test.cc

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2256,6 +2256,47 @@ TEST(FrontendTest, MoreCursedInheritance) {
22562256

22572257
// Note that this link skips `C` because it is devoid of `moo(int)`.
22582258
EXPECT_HAS_VIRTUAL_LINK(index, "B::moo(int)", "D::moo(int)");
2259+
2260+
// Qualified-name member accesses only create references to the base class
2261+
// entities...
2262+
EXPECT_HAS_REFERENCE(
2263+
index, Entity::Kind::kFunction, "A::", "foo", "()", "snippet.cc", 2, 2,
2264+
"snippet.cc", 19, 19, /*is_incomplete=*/false,
2265+
/*template_prototype_entity_id=*/std::nullopt,
2266+
/*implicitly_defined_for_entity_id=*/std::nullopt,
2267+
/*enum_value=*/std::nullopt, /*inherited_from_entity_id=*/std::nullopt,
2268+
/*virtual_method_kind=*/Entity::VirtualMethodKind::kNonPureVirtual);
2269+
EXPECT_HAS_REFERENCE(
2270+
index, Entity::Kind::kFunction, "B::", "foo", "()", "snippet.cc", 7, 7,
2271+
"snippet.cc", 19, 19, /*is_incomplete=*/false,
2272+
/*template_prototype_entity_id=*/std::nullopt,
2273+
/*implicitly_defined_for_entity_id=*/std::nullopt,
2274+
/*enum_value=*/std::nullopt, /*inherited_from_entity_id=*/std::nullopt,
2275+
/*virtual_method_kind=*/Entity::VirtualMethodKind::kNonPureVirtual);
2276+
// ...whereas an unqualified-name one also creates a reference to the
2277+
// synthesized inherited entity in the child.
2278+
EXPECT_HAS_REFERENCE(
2279+
index, Entity::Kind::kFunction, "C::", "bar", "()", "snippet.cc", 13, 13,
2280+
"snippet.cc", 21, 21, /*is_incomplete=*/false,
2281+
/*template_prototype_entity_id=*/std::nullopt,
2282+
/*implicitly_defined_for_entity_id=*/std::nullopt,
2283+
/*enum_value=*/std::nullopt, /*inherited_from_entity_id=*/std::nullopt,
2284+
/*virtual_method_kind=*/Entity::VirtualMethodKind::kNonPureVirtual);
2285+
EXPECT_HAS_REFERENCE(
2286+
index, Entity::Kind::kFunction, "D::", "bar", "()", "snippet.cc", 13, 13,
2287+
"snippet.cc", 21, 21, /*is_incomplete=*/false,
2288+
/*template_prototype_entity_id=*/std::nullopt,
2289+
/*implicitly_defined_for_entity_id=*/std::nullopt,
2290+
/*enum_value=*/std::nullopt, /*inherited_from_entity_id=*/
2291+
RequiredEntityId(
2292+
index, Entity::Kind::kFunction, "C::", "bar", "()", "snippet.cc", 13,
2293+
13, /*is_incomplete=*/false,
2294+
/*template_prototype_entity_id=*/std::nullopt,
2295+
/*implicitly_defined_for_entity_id=*/std::nullopt,
2296+
/*enum_value=*/std::nullopt,
2297+
/*inherited_from_entity_id=*/std::nullopt,
2298+
/*virtual_method_kind=*/Entity::VirtualMethodKind::kNonPureVirtual),
2299+
/*virtual_method_kind=*/Entity::VirtualMethodKind::kNonPureVirtual);
22592300
}
22602301

22612302
TEST(FrontendTest, InheritanceThroughTemplateInstantiation) {

0 commit comments

Comments
 (0)