[clang-tools-extra] [clangd] Update XRefs to support overriden ObjC methods (PR #127109)
David Goldman via cfe-commits
cfe-commits at lists.llvm.org
Thu Feb 13 10:54:33 PST 2025
https://github.com/DavidGoldman created https://github.com/llvm/llvm-project/pull/127109
- Support finding implementors of a protocol and discovering subclasses for ObjC interfaces via the implementations call
- Add tests
>From d335931b8923005d2ffc580c5195b7fa997f3649 Mon Sep 17 00:00:00 2001
From: David Goldman <davg at google.com>
Date: Thu, 13 Feb 2025 13:51:05 -0500
Subject: [PATCH] [clangd] Update XRefs to support overriden ObjC methods
- Also support finding implementators of a protocol
- Also support discovering subclasses for ObjC interfaces
via the implementations call
- Add tests
---
clang-tools-extra/clangd/XRefs.cpp | 32 ++++++++
.../clangd/unittests/XRefsTests.cpp | 75 +++++++++++++++++++
2 files changed, 107 insertions(+)
diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp
index 1a23f6cca7756..095664a2391c6 100644
--- a/clang-tools-extra/clangd/XRefs.cpp
+++ b/clang-tools-extra/clangd/XRefs.cpp
@@ -430,6 +430,18 @@ locateASTReferent(SourceLocation CurLoc, const syntax::Token *TouchedIdentifier,
continue;
}
}
+ // Special case: an Objective-C method can override a parent class or
+ // protocol.
+ if (const auto *OMD = llvm::dyn_cast<ObjCMethodDecl>(D)) {
+ llvm::SmallVector<const ObjCMethodDecl *, 4> Overrides;
+ OMD->getOverriddenMethods(Overrides);
+ for (const auto *Override : Overrides)
+ AddResultDecl(Override);
+ if (!Overrides.empty())
+ LocateASTReferentMetric.record(1, "objc-overriden-method");
+ AddResultDecl(OMD);
+ continue;
+ }
// Special case: the cursor is on an alias, prefer other results.
// This targets "using ns::^Foo", where the target is more interesting.
@@ -1283,6 +1295,17 @@ std::vector<LocatedSymbol> findImplementations(ParsedAST &AST, Position Pos,
} else if (const auto *RD = dyn_cast<CXXRecordDecl>(ND)) {
IDs.insert(getSymbolID(RD));
QueryKind = RelationKind::BaseOf;
+ } else if (const auto *OMD = dyn_cast<ObjCMethodDecl>(ND)) {
+ IDs.insert(getSymbolID(OMD));
+ QueryKind = RelationKind::OverriddenBy;
+
+ llvm::SmallVector<const ObjCMethodDecl *, 4> Overrides;
+ OMD->getOverriddenMethods(Overrides);
+ for (const auto *Override : Overrides)
+ IDs.insert(getSymbolID(Override));
+ } else if (const auto *ID = dyn_cast<ObjCInterfaceDecl>(ND)) {
+ IDs.insert(getSymbolID(ID));
+ QueryKind = RelationKind::BaseOf;
}
}
return findImplementors(std::move(IDs), QueryKind, Index, AST.tuPath());
@@ -1438,6 +1461,15 @@ ReferencesResult findReferences(ParsedAST &AST, Position Pos, uint32_t Limit,
getOverriddenMethods(CMD, OverriddenMethods);
}
}
+ // Special case: Objective-C methods can override a parent class or
+ // protocol, we should be sure to report references to those.
+ if (const auto *OMD = llvm::dyn_cast<ObjCMethodDecl>(ND)) {
+ OverriddenBy.Subjects.insert(getSymbolID(OMD));
+ llvm::SmallVector<const ObjCMethodDecl *, 4> Overrides;
+ OMD->getOverriddenMethods(Overrides);
+ for (const auto *Override : Overrides)
+ OverriddenMethods.insert(getSymbolID(Override));
+ }
}
}
diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
index 7d824d659ad2c..a310a51cf4cfb 100644
--- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp
+++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
@@ -411,6 +411,26 @@ TEST(LocateSymbol, FindOverrides) {
sym("foo", Code.range("2"), std::nullopt)));
}
+TEST(LocateSymbol, FindOverridesObjC) {
+ auto Code = Annotations(R"objc(
+ @interface Foo
+ - (void)$1[[foo]];
+ @end
+
+ @interface Bar : Foo
+ @end
+ @implementation Bar
+ - (void)$2[[fo^o]] {}
+ @end
+ )objc");
+ TestTU TU = TestTU::withCode(Code.code());
+ TU.ExtraArgs.push_back("-xobjective-c++");
+ auto AST = TU.build();
+ EXPECT_THAT(locateSymbolAt(AST, Code.point(), TU.index().get()),
+ UnorderedElementsAre(sym("foo", Code.range("1"), std::nullopt),
+ sym("foo", Code.range("2"), Code.range("2"))));
+}
+
TEST(LocateSymbol, WithIndexPreferredLocation) {
Annotations SymbolHeader(R"cpp(
class $p[[Proto]] {};
@@ -1834,6 +1854,41 @@ TEST(FindImplementations, Inheritance) {
}
}
+TEST(FindImplementations, InheritanceObjC) {
+ llvm::StringRef Test = R"objc(
+ @interface $base^Base
+ - (void)fo$foo^o;
+ @end
+ @protocol Protocol
+ - (void)$protocol^protocol;
+ @end
+ @interface $ChildDecl[[Child]] : Base <Protocol>
+ - (void)concrete;
+ - (void)$fooDecl[[foo]];
+ @end
+ @implementation $ChildDef[[Child]]
+ - (void)concrete {}
+ - (void)$fooDef[[foo]] {}
+ - (void)$protocolDef[[protocol]] {}
+ @end
+ )objc";
+
+ Annotations Code(Test);
+ auto TU = TestTU::withCode(Code.code());
+ TU.ExtraArgs.push_back("-xobjective-c++");
+ auto AST = TU.build();
+ auto Index = TU.index();
+ EXPECT_THAT(
+ findImplementations(AST, Code.point("base"), Index.get()),
+ UnorderedElementsAre(sym("Child", Code.range("ChildDecl"), Code.range("ChildDef"))));
+ EXPECT_THAT(
+ findImplementations(AST, Code.point("foo"), Index.get()),
+ UnorderedElementsAre(sym("foo", Code.range("fooDecl"), Code.range("fooDef"))));
+ EXPECT_THAT(
+ findImplementations(AST, Code.point("protocol"), Index.get()),
+ UnorderedElementsAre(sym("protocol", Code.range("protocolDef"), Code.range("protocolDef"))));
+}
+
TEST(FindImplementations, CaptureDefinition) {
llvm::StringRef Test = R"cpp(
struct Base {
@@ -1963,6 +2018,7 @@ void checkFindRefs(llvm::StringRef Test, bool UseIndex = false) {
Annotations T(Test);
auto TU = TestTU::withCode(T.code());
TU.ExtraArgs.push_back("-std=c++20");
+ TU.ExtraArgs.push_back("-xobjective-c++");
auto AST = TU.build();
std::vector<Matcher<ReferencesResult::Reference>> ExpectedLocations;
@@ -2260,6 +2316,25 @@ TEST(FindReferences, IncludeOverrides) {
checkFindRefs(Test, /*UseIndex=*/true);
}
+TEST(FindReferences, IncludeOverridesObjC) {
+ llvm::StringRef Test =
+ R"objc(
+ @interface Base
+ - (void)$decl(Base)[[f^unc]];
+ @end
+ @interface Derived : Base
+ - (void)$overridedecl(Derived::func)[[func]];
+ @end
+ @implementation Derived
+ - (void)$overridedef[[func]] {}
+ @end
+ void test(Derived *derived, Base *base) {
+ [derived func]; // No references to the overrides.
+ [base $(test)[[func]]];
+ })objc";
+ checkFindRefs(Test, /*UseIndex=*/true);
+}
+
TEST(FindReferences, RefsToBaseMethod) {
llvm::StringRef Test =
R"cpp(
More information about the cfe-commits
mailing list