diff --git a/clang-tools-extra/clangd/Selection.cpp b/clang-tools-extra/clangd/Selection.cpp --- a/clang-tools-extra/clangd/Selection.cpp +++ b/clang-tools-extra/clangd/Selection.cpp @@ -616,6 +616,11 @@ // rid of this patch. if (auto DT = TL->getAs()) S.setEnd(DT.getUnderlyingExpr()->getEndLoc()); + // AttributeTypeLoc points to the attribute's range, NOT the modified + // type's range. + if (auto AT = TL->getAs()) + S = AT.getModifiedLoc() + .getSourceRange(); // should we just return false? } if (!SelChecker.mayHit(S)) { dlog("{1}skip: {0}", printNodeToString(N, PrintPolicy), indent()); diff --git a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp --- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp +++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp @@ -820,6 +820,26 @@ EXPECT_DECLS("ObjCPropertyRefExpr", "@property(atomic, retain, readwrite) I *x"); + Code = R"cpp( + @interface MYObject + @end + @interface Interface + @property(retain) [[MYObject]] *x; + @end + )cpp"; + EXPECT_DECLS("ObjCInterfaceTypeLoc", + "@interface MYObject"); + + Code = R"cpp( + @interface MYObject2 + @end + @interface Interface + @property(retain, nonnull) [[MYObject2]] *x; + @end + )cpp"; + EXPECT_DECLS("ObjCInterfaceTypeLoc", + "@interface MYObject2"); + Code = R"cpp( @protocol Foo @end diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp --- a/clang-tools-extra/clangd/unittests/HoverTests.cpp +++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -1991,6 +1991,91 @@ HI.NamespaceScope = "ObjC::"; // FIXME: fix it HI.Definition = "char data"; }}, + { + R"cpp( + @interface MYObject + @end + @interface Interface + @property(retain) [[MYOb^ject]] *x; + @end + )cpp", + [](HoverInfo &HI) { + HI.Name = "MYObject"; + HI.Kind = index::SymbolKind::Class; + HI.NamespaceScope = ""; + HI.Definition = "@interface MYObject\n@end"; + }}, + { + // Should work even with nullability attribute. + R"cpp( + @interface MYObject + @end + @interface Interface + @property(nonnull) [[MYOb^ject]] *x; + @end + )cpp", + [](HoverInfo &HI) { + HI.Name = "MYObject"; + HI.Kind = index::SymbolKind::Class; + HI.NamespaceScope = ""; + HI.Definition = "@interface MYObject\n@end"; + }}, + { + R"cpp( + @interface MYObject + @end + @interface Interface + - (void)doWith:([[MYOb^ject]] *)object; + @end + )cpp", + [](HoverInfo &HI) { + HI.Name = "MYObject"; + HI.Kind = index::SymbolKind::Class; + HI.NamespaceScope = ""; + HI.Definition = "@interface MYObject\n@end"; + }}, + { + R"cpp( + @interface MYObject + @end + @interface Interface + - (void)doWith:(nullable [[MYOb^ject]] *)object; + @end + )cpp", + [](HoverInfo &HI) { + HI.Name = "MYObject"; + HI.Kind = index::SymbolKind::Class; + HI.NamespaceScope = ""; + HI.Definition = "@interface MYObject\n@end"; + }}, + { + R"cpp( + @class ForwardDeclared; + @interface Interface + @property(retain) [[Forward^Declared]] *x; + @end + )cpp", + [](HoverInfo &HI) { + HI.Name = "ForwardDeclared"; + HI.Kind = index::SymbolKind::Class; + HI.NamespaceScope = ""; + HI.Definition = "@class ForwardDeclared;"; + }}, + { + R"cpp( + @protocol Fooey + - (void)foo; + @end + @interface Interface + @property(retain) id<[[Fo^oey]]> fooey; + @end + )cpp", + [](HoverInfo &HI) { + HI.Name = "Fooey"; + HI.Kind = index::SymbolKind::Protocol; + HI.NamespaceScope = ""; + HI.Definition = "@protocol Fooey\n@end"; + }}, }; // Create a tiny index, so tests above can verify documentation is fetched. diff --git a/clang-tools-extra/clangd/unittests/SelectionTests.cpp b/clang-tools-extra/clangd/unittests/SelectionTests.cpp --- a/clang-tools-extra/clangd/unittests/SelectionTests.cpp +++ b/clang-tools-extra/clangd/unittests/SelectionTests.cpp @@ -356,6 +356,22 @@ )cpp", "DeclRefExpr"}, + // Objective-C nullability attributes. + { + R"cpp( + @interface I{} + @property(nullable) [[^I]] *x; + @end + )cpp", + "ObjCInterfaceTypeLoc"}, + { + R"cpp( + @interface I{} + - (void)doSomething:(nonnull [[i^d]])argument; + @end + )cpp", + "TypedefTypeLoc"}, + // Objective-C OpaqueValueExpr/PseudoObjectExpr has weird ASTs. // Need to traverse the contents of the OpaqueValueExpr to the POE, // and ensure we traverse only the syntactic form of the PseudoObjectExpr.