[clangd][ObjC] Improve support for class properties
authorDavid Goldman <davg@google.com>
Tue, 6 Apr 2021 17:31:09 +0000 (13:31 -0400)
committerDavid Goldman <davg@google.com>
Wed, 28 Apr 2021 14:06:27 +0000 (10:06 -0400)
Class properties are always implicit short-hands for the getter/setter
class methods.

We need to explicitly visit the interface decl `UIColor` in `UIColor.blueColor`,
otherwise we instead show the method decl even while hovering over
`UIColor` in the expression.

Differential Revision: https://reviews.llvm.org/D99975

clang-tools-extra/clangd/FindTarget.cpp
clang-tools-extra/clangd/unittests/FindTargetTests.cpp

index 6032676..d4cb2fe 100644 (file)
@@ -306,6 +306,9 @@ public:
         Outer.add(OME->getMethodDecl(), Flags);
       }
       void VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr *OPRE) {
+        // FIXME: We miss visiting the class receiver if one exists, which
+        // means we skip the corresponding ObjCInterfaceDecl ref since it
+        // doesn't have a corresponding node.
         if (OPRE->isExplicitProperty())
           Outer.add(OPRE->getExplicitProperty(), Flags);
         else {
@@ -763,6 +766,13 @@ llvm::SmallVector<ReferenceLoc> refInStmt(const Stmt *S,
     }
 
     void VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr *E) {
+      // There's no contained TypeLoc node for a class receiver type.
+      if (E->isClassReceiver()) {
+        Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(),
+                                    E->getReceiverLocation(),
+                                    /*IsDecl=*/false,
+                                    {E->getClassReceiver()}});
+      }
       Refs.push_back(ReferenceLoc{
           NestedNameSpecifierLoc(), E->getLocation(),
           /*IsDecl=*/false,
index 5bcbdbd..f32081a 100644 (file)
@@ -969,6 +969,33 @@ TEST_F(TargetDeclTest, ObjC) {
   )cpp";
   // FIXME: We currently can't disambiguate between multiple protocols.
   EXPECT_DECLS("ObjCObjectTypeLoc", "@protocol Foo", "@protocol Bar");
+
+  Code = R"cpp(
+    @interface Foo
+    + (id)sharedInstance;
+    @end
+    @implementation Foo
+    + (id)sharedInstance { return 0; }
+    @end
+    void test() {
+      id value = [[Foo]].sharedInstance;
+    }
+  )cpp";
+  // FIXME: We currently can't identify the interface here.
+  EXPECT_DECLS("ObjCPropertyRefExpr", "+ (id)sharedInstance");
+
+  Code = R"cpp(
+    @interface Foo
+    + (id)sharedInstance;
+    @end
+    @implementation Foo
+    + (id)sharedInstance { return 0; }
+    @end
+    void test() {
+      id value = Foo.[[sharedInstance]];
+    }
+  )cpp";
+  EXPECT_DECLS("ObjCPropertyRefExpr", "+ (id)sharedInstance");
 }
 
 class FindExplicitReferencesTest : public ::testing::Test {
@@ -1610,6 +1637,41 @@ TEST_F(FindExplicitReferencesTest, All) {
            "0: targets = {f}\n"
            "1: targets = {I::x}\n"
            "2: targets = {I::setY:}\n"},
+       // Objective-C: class properties
+       {
+           R"cpp(
+            @interface I {}
+            @property(class) I *x;
+            @end
+            id local;
+            void foo() {
+              $0^I.$1^x = 0;
+              $2^local = $3^I.$4^x;
+            }
+          )cpp",
+           "0: targets = {I}\n"
+           "1: targets = {I::setX:}\n"
+           "2: targets = {local}\n"
+           "3: targets = {I}\n"
+           "4: targets = {I::x}\n"},
+       // Objective-C: implicit class properties
+       {
+           R"cpp(
+            @interface I {}
+            +(I*)x;
+            +(void)setX:(I*)x;
+            @end
+            id local;
+            void foo() {
+              $0^I.$1^x = 0;
+              $2^local = $3^I.$4^x;
+            }
+          )cpp",
+           "0: targets = {I}\n"
+           "1: targets = {I::setX:}\n"
+           "2: targets = {local}\n"
+           "3: targets = {I}\n"
+           "4: targets = {I::x}\n"},
        {// Objective-C: methods
         R"cpp(
             @interface I