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 @@ -605,6 +605,10 @@ bool canSafelySkipNode(const DynTypedNode &N) { SourceRange S = N.getSourceRange(); if (auto *TL = N.get()) { + // FIXME: TypeLoc::getBeginLoc()/getEndLoc() are pretty fragile + // heuristics. We should consider only pruning critical TypeLoc nodes, to + // be more robust. + // DeclTypeTypeLoc::getSourceRange() is incomplete, which would lead to // failing // to descend into the child expression. @@ -616,6 +620,10 @@ // rid of this patch. if (auto DT = TL->getAs()) S.setEnd(DT.getUnderlyingExpr()->getEndLoc()); + // AttributedTypeLoc may point to the attribute's range, NOT the modified + // type's range. + if (auto AT = TL->getAs()) + S = AT.getModifiedLoc().getSourceRange(); } 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,24 @@ 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,34 @@ 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"; + }}, + { + 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"; + }}, }; // 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.