diff --git a/clang-tools-extra/clangd/index/Relation.h b/clang-tools-extra/clangd/index/Relation.h --- a/clang-tools-extra/clangd/index/Relation.h +++ b/clang-tools-extra/clangd/index/Relation.h @@ -21,11 +21,16 @@ enum class RelationKind : uint8_t { BaseOf, + OverriddenBy, }; /// Represents a relation between two symbols. -/// For an example "A is a base class of B" may be represented -/// as { Subject = A, Predicate = BaseOf, Object = B }. +/// For an example: +/// - "A is a base class of B" is represented as +/// { Subject = A, Predicate = BaseOf, Object = B }. +/// - "Derived::Foo overrides Base::Foo" is represented as +/// { Subject = Base::Foo, Predicate = OverriddenBy, Object = Derived::Foo +/// }. struct Relation { SymbolID Subject; RelationKind Predicate; @@ -41,6 +46,8 @@ std::tie(Other.Subject, Other.Predicate, Other.Object); } }; +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const RelationKind R); +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Relation &R); class RelationSlab { public: diff --git a/clang-tools-extra/clangd/index/Relation.cpp b/clang-tools-extra/clangd/index/Relation.cpp --- a/clang-tools-extra/clangd/index/Relation.cpp +++ b/clang-tools-extra/clangd/index/Relation.cpp @@ -13,6 +13,20 @@ namespace clang { namespace clangd { +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const RelationKind R) { + switch (R) { + case RelationKind::BaseOf: + return OS << "BaseOf"; + case RelationKind::OverriddenBy: + return OS << "OverriddenBy"; + } + llvm_unreachable("Unhandled RelationKind enum."); +} + +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Relation &R) { + return OS << R.Subject << " " << R.Predicate << " " << R.Object; +} + llvm::iterator_range RelationSlab::lookup(const SymbolID &Subject, RelationKind Predicate) const { auto IterPair = std::equal_range(Relations.begin(), Relations.end(), diff --git a/clang-tools-extra/clangd/index/Serialization.cpp b/clang-tools-extra/clangd/index/Serialization.cpp --- a/clang-tools-extra/clangd/index/Serialization.cpp +++ b/clang-tools-extra/clangd/index/Serialization.cpp @@ -443,7 +443,7 @@ // The current versioning scheme is simple - non-current versions are rejected. // If you make a breaking change, bump this version number to invalidate stored // data. Later we may want to support some backward compatibility. -constexpr static uint32_t Version = 14; +constexpr static uint32_t Version = 15; llvm::Expected readRIFF(llvm::StringRef Data) { auto RIFF = riff::readFile(Data); diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp b/clang-tools-extra/clangd/index/SymbolCollector.cpp --- a/clang-tools-extra/clangd/index/SymbolCollector.cpp +++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp @@ -15,6 +15,7 @@ #include "SourceCode.h" #include "SymbolLocation.h" #include "URI.h" +#include "index/Relation.h" #include "index/SymbolID.h" #include "support/Logger.h" #include "clang/AST/Decl.h" @@ -187,9 +188,12 @@ return Result; } -bool shouldIndexRelation(const index::SymbolRelation &R) { - // We currently only index BaseOf relations, for type hierarchy subtypes. - return R.Roles & static_cast(index::SymbolRole::RelationBaseOf); +llvm::Optional indexableRelation(const index::SymbolRelation &R) { + if (R.Roles & static_cast(index::SymbolRole::RelationBaseOf)) + return RelationKind::BaseOf; + if (R.Roles & static_cast(index::SymbolRole::RelationOverrideOf)) + return RelationKind::OverriddenBy; + return None; } } // namespace @@ -486,14 +490,10 @@ void SymbolCollector::processRelations( const NamedDecl &ND, const SymbolID &ID, ArrayRef Relations) { - // Store subtype relations. - if (!dyn_cast(&ND)) - return; - for (const auto &R : Relations) { - if (!shouldIndexRelation(R)) + auto RKind = indexableRelation(R); + if (!RKind) continue; - const Decl *Object = R.RelatedSymbol; auto ObjectID = getSymbolID(Object); @@ -509,7 +509,10 @@ // in the index and find nothing, but that's a situation they // probably need to handle for other reasons anyways. // We currently do (B) because it's simpler. - this->Relations.insert(Relation{ID, RelationKind::BaseOf, ObjectID}); + if (*RKind == RelationKind::BaseOf) + this->Relations.insert({ID, *RKind, ObjectID}); + else if (*RKind == RelationKind::OverriddenBy) + this->Relations.insert({ObjectID, *RKind, ID}); } } diff --git a/clang-tools-extra/clangd/test/index-serialization/Inputs/sample.h b/clang-tools-extra/clangd/test/index-serialization/Inputs/sample.h --- a/clang-tools-extra/clangd/test/index-serialization/Inputs/sample.h +++ b/clang-tools-extra/clangd/test/index-serialization/Inputs/sample.h @@ -1,4 +1,6 @@ #pragma once // Introduce a symbol. -struct Foo {}; +struct Foo { + virtual void Func() {} +}; diff --git a/clang-tools-extra/clangd/test/index-serialization/Inputs/sample.cpp b/clang-tools-extra/clangd/test/index-serialization/Inputs/sample.cpp --- a/clang-tools-extra/clangd/test/index-serialization/Inputs/sample.cpp +++ b/clang-tools-extra/clangd/test/index-serialization/Inputs/sample.cpp @@ -2,4 +2,7 @@ #include "sample.h" // This introduces a symbol, a reference and a relation. -struct Bar : public Foo {}; +struct Bar : public Foo { + // This introduces an OverriddenBy relation by implementing Foo::Func. + void Func() override {} +}; diff --git a/clang-tools-extra/clangd/test/index-serialization/Inputs/sample.idx b/clang-tools-extra/clangd/test/index-serialization/Inputs/sample.idx index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@(arg); return rangesMatch(Pos.Location, Range); } +MATCHER_P2(OverriddenBy, Subject, Object, "") { + return arg == Relation{Subject.ID, RelationKind::OverriddenBy, Object.ID}; +} ::testing::Matcher &> HaveRanges(const std::vector Ranges) { return ::testing::UnorderedPointwise(RefRange(), Ranges); @@ -1031,7 +1034,7 @@ HaveRanges(Header.ranges("macro"))))); } -TEST_F(SymbolCollectorTest, Relations) { +TEST_F(SymbolCollectorTest, BaseOfRelations) { std::string Header = R"( class Base {}; class Derived : public Base {}; @@ -1043,6 +1046,77 @@ Contains(Relation{Base.ID, RelationKind::BaseOf, Derived.ID})); } +TEST_F(SymbolCollectorTest, OverrideRelationsSimpleInheritance) { + std::string Header = R"cpp( + class A { + virtual void foo(); + }; + class B : public A { + void foo() override; // A::foo + virtual void bar(); + }; + class C : public B { + void bar() override; // B::bar + }; + class D: public C { + void foo() override; // B::foo + void bar() override; // C::bar + }; + )cpp"; + runSymbolCollector(Header, /*Main=*/""); + const Symbol &AFoo = findSymbol(Symbols, "A::foo"); + const Symbol &BFoo = findSymbol(Symbols, "B::foo"); + const Symbol &DFoo = findSymbol(Symbols, "D::foo"); + + const Symbol &BBar = findSymbol(Symbols, "B::bar"); + const Symbol &CBar = findSymbol(Symbols, "C::bar"); + const Symbol &DBar = findSymbol(Symbols, "D::bar"); + + std::vector Result; + for (const Relation &R : Relations) + if (R.Predicate == RelationKind::OverriddenBy) + Result.push_back(R); + EXPECT_THAT(Result, UnorderedElementsAre( + OverriddenBy(AFoo, BFoo), OverriddenBy(BBar, CBar), + OverriddenBy(BFoo, DFoo), OverriddenBy(CBar, DBar))); +} + +TEST_F(SymbolCollectorTest, OverrideRelationsMultipleInheritance) { + std::string Header = R"cpp( + class A { + virtual void foo(); + }; + class B { + virtual void bar(); + }; + class C : public B { + void bar() override; // B::bar + virtual void baz(); + } + class D : public A, C { + void foo() override; // A::foo + void bar() override; // C::bar + void baz() override; // C::baz + }; + )cpp"; + runSymbolCollector(Header, /*Main=*/""); + const Symbol &AFoo = findSymbol(Symbols, "A::foo"); + const Symbol &BBar = findSymbol(Symbols, "B::bar"); + const Symbol &CBar = findSymbol(Symbols, "C::bar"); + const Symbol &CBaz = findSymbol(Symbols, "C::baz"); + const Symbol &DFoo = findSymbol(Symbols, "D::foo"); + const Symbol &DBar = findSymbol(Symbols, "D::bar"); + const Symbol &DBaz = findSymbol(Symbols, "D::baz"); + + std::vector Result; + for (const Relation &R : Relations) + if (R.Predicate == RelationKind::OverriddenBy) + Result.push_back(R); + EXPECT_THAT(Result, UnorderedElementsAre( + OverriddenBy(BBar, CBar), OverriddenBy(AFoo, DFoo), + OverriddenBy(CBar, DBar), OverriddenBy(CBaz, DBaz))); +} + TEST_F(SymbolCollectorTest, CountReferences) { const std::string Header = R"( class W;