[clangd] Improve handling of Objective-C protocols in types

Improve support for Objective-C protocols for types/type locs

Differential Revision: https://reviews.llvm.org/D98984
This commit is contained in:
David Goldman 2021-03-19 16:23:15 -04:00
parent 4ebb01cbcb
commit c20e4fbfa6
3 changed files with 85 additions and 53 deletions

View File

@ -432,10 +432,13 @@ public:
Outer.add(OIT->getDecl(), Flags); Outer.add(OIT->getDecl(), Flags);
} }
void VisitObjCObjectType(const ObjCObjectType *OOT) { void VisitObjCObjectType(const ObjCObjectType *OOT) {
// FIXME: ObjCObjectTypeLoc has no children for the protocol list, so // Make all of the protocols targets since there's no child nodes for
// there is no node in id<Foo> that refers to ObjCProtocolDecl Foo. // protocols. This isn't needed for the base type, which *does* have a
if (OOT->isObjCQualifiedId() && OOT->getNumProtocols() == 1) // child `ObjCInterfaceTypeLoc`. This structure is a hack, but it works
Outer.add(OOT->getProtocol(0), Flags); // well for go-to-definition.
unsigned NumProtocols = OOT->getNumProtocols();
for (unsigned I = 0; I < NumProtocols; I++)
Outer.add(OOT->getProtocol(I), Flags);
} }
}; };
Visitor(*this, Flags).Visit(T.getTypePtr()); Visitor(*this, Flags).Visit(T.getTypePtr());
@ -813,30 +816,34 @@ refInTypeLoc(TypeLoc L, const HeuristicResolver *Resolver) {
Visitor(const HeuristicResolver *Resolver) : Resolver(Resolver) {} Visitor(const HeuristicResolver *Resolver) : Resolver(Resolver) {}
const HeuristicResolver *Resolver; const HeuristicResolver *Resolver;
llvm::Optional<ReferenceLoc> Ref; llvm::SmallVector<ReferenceLoc> Refs;
void VisitElaboratedTypeLoc(ElaboratedTypeLoc L) { void VisitElaboratedTypeLoc(ElaboratedTypeLoc L) {
// We only know about qualifier, rest if filled by inner locations. // We only know about qualifier, rest if filled by inner locations.
size_t InitialSize = Refs.size();
Visit(L.getNamedTypeLoc().getUnqualifiedLoc()); Visit(L.getNamedTypeLoc().getUnqualifiedLoc());
// Fill in the qualifier. size_t NewSize = Refs.size();
if (!Ref) // Add qualifier for the newly-added refs.
return; for (unsigned I = InitialSize; I < NewSize; ++I) {
assert(!Ref->Qualifier.hasQualifier() && "qualifier already set"); ReferenceLoc *Ref = &Refs[I];
Ref->Qualifier = L.getQualifierLoc(); // Fill in the qualifier.
assert(!Ref->Qualifier.hasQualifier() && "qualifier already set");
Ref->Qualifier = L.getQualifierLoc();
}
} }
void VisitTagTypeLoc(TagTypeLoc L) { void VisitTagTypeLoc(TagTypeLoc L) {
Ref = ReferenceLoc{NestedNameSpecifierLoc(), Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(),
L.getNameLoc(), L.getNameLoc(),
/*IsDecl=*/false, /*IsDecl=*/false,
{L.getDecl()}}; {L.getDecl()}});
} }
void VisitTemplateTypeParmTypeLoc(TemplateTypeParmTypeLoc L) { void VisitTemplateTypeParmTypeLoc(TemplateTypeParmTypeLoc L) {
Ref = ReferenceLoc{NestedNameSpecifierLoc(), Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(),
L.getNameLoc(), L.getNameLoc(),
/*IsDecl=*/false, /*IsDecl=*/false,
{L.getDecl()}}; {L.getDecl()}});
} }
void VisitTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc L) { void VisitTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc L) {
@ -848,63 +855,71 @@ refInTypeLoc(TypeLoc L, const HeuristicResolver *Resolver) {
// 1. valias with mask 'Alias'. // 1. valias with mask 'Alias'.
// 2. 'vector<int>' with mask 'Underlying'. // 2. 'vector<int>' with mask 'Underlying'.
// we want to return only #1 in this case. // we want to return only #1 in this case.
Ref = ReferenceLoc{ Refs.push_back(ReferenceLoc{
NestedNameSpecifierLoc(), L.getTemplateNameLoc(), /*IsDecl=*/false, NestedNameSpecifierLoc(), L.getTemplateNameLoc(), /*IsDecl=*/false,
explicitReferenceTargets(DynTypedNode::create(L.getType()), explicitReferenceTargets(DynTypedNode::create(L.getType()),
DeclRelation::Alias, Resolver)}; DeclRelation::Alias, Resolver)});
} }
void VisitDeducedTemplateSpecializationTypeLoc( void VisitDeducedTemplateSpecializationTypeLoc(
DeducedTemplateSpecializationTypeLoc L) { DeducedTemplateSpecializationTypeLoc L) {
Ref = ReferenceLoc{ Refs.push_back(ReferenceLoc{
NestedNameSpecifierLoc(), L.getNameLoc(), /*IsDecl=*/false, NestedNameSpecifierLoc(), L.getNameLoc(), /*IsDecl=*/false,
explicitReferenceTargets(DynTypedNode::create(L.getType()), explicitReferenceTargets(DynTypedNode::create(L.getType()),
DeclRelation::Alias, Resolver)}; DeclRelation::Alias, Resolver)});
} }
void VisitInjectedClassNameTypeLoc(InjectedClassNameTypeLoc TL) { void VisitInjectedClassNameTypeLoc(InjectedClassNameTypeLoc TL) {
Ref = ReferenceLoc{NestedNameSpecifierLoc(), Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(),
TL.getNameLoc(), TL.getNameLoc(),
/*IsDecl=*/false, /*IsDecl=*/false,
{TL.getDecl()}}; {TL.getDecl()}});
} }
void VisitDependentTemplateSpecializationTypeLoc( void VisitDependentTemplateSpecializationTypeLoc(
DependentTemplateSpecializationTypeLoc L) { DependentTemplateSpecializationTypeLoc L) {
Ref = ReferenceLoc{L.getQualifierLoc(), L.getTemplateNameLoc(), Refs.push_back(
/*IsDecl=*/false, ReferenceLoc{L.getQualifierLoc(), L.getTemplateNameLoc(),
explicitReferenceTargets( /*IsDecl=*/false,
DynTypedNode::create(L.getType()), {}, Resolver)}; explicitReferenceTargets(
DynTypedNode::create(L.getType()), {}, Resolver)});
} }
void VisitDependentNameTypeLoc(DependentNameTypeLoc L) { void VisitDependentNameTypeLoc(DependentNameTypeLoc L) {
Ref = ReferenceLoc{L.getQualifierLoc(), L.getNameLoc(), /*IsDecl=*/false, Refs.push_back(
explicitReferenceTargets( ReferenceLoc{L.getQualifierLoc(), L.getNameLoc(),
DynTypedNode::create(L.getType()), {}, Resolver)}; /*IsDecl=*/false,
explicitReferenceTargets(
DynTypedNode::create(L.getType()), {}, Resolver)});
} }
void VisitTypedefTypeLoc(TypedefTypeLoc L) { void VisitTypedefTypeLoc(TypedefTypeLoc L) {
Ref = ReferenceLoc{NestedNameSpecifierLoc(), Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(),
L.getNameLoc(), L.getNameLoc(),
/*IsDecl=*/false, /*IsDecl=*/false,
{L.getTypedefNameDecl()}}; {L.getTypedefNameDecl()}});
} }
void VisitObjCInterfaceTypeLoc(ObjCInterfaceTypeLoc L) { void VisitObjCInterfaceTypeLoc(ObjCInterfaceTypeLoc L) {
Ref = ReferenceLoc{NestedNameSpecifierLoc(), Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(),
L.getNameLoc(), L.getNameLoc(),
/*IsDecl=*/false, /*IsDecl=*/false,
{L.getIFaceDecl()}}; {L.getIFaceDecl()}});
} }
// FIXME: add references to protocols in ObjCObjectTypeLoc and maybe void VisitObjCObjectTypeLoc(ObjCObjectTypeLoc L) {
// ObjCObjectPointerTypeLoc. unsigned NumProtocols = L.getNumProtocols();
for (unsigned I = 0; I < NumProtocols; I++) {
Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(),
L.getProtocolLoc(I),
/*IsDecl=*/false,
{L.getProtocol(I)}});
}
}
}; };
Visitor V{Resolver}; Visitor V{Resolver};
V.Visit(L.getUnqualifiedLoc()); V.Visit(L.getUnqualifiedLoc());
if (!V.Ref) return V.Refs;
return {};
return {*V.Ref};
} }
class ExplicitReferenceCollector class ExplicitReferenceCollector

View File

@ -943,14 +943,32 @@ TEST_F(TargetDeclTest, ObjC) {
)cpp"; )cpp";
EXPECT_DECLS("ObjCObjectTypeLoc", "@protocol Foo"); EXPECT_DECLS("ObjCObjectTypeLoc", "@protocol Foo");
Code = R"cpp(
@class C;
@protocol Foo
@end
void test([[C]]<Foo> *p);
)cpp";
EXPECT_DECLS("ObjCInterfaceTypeLoc", "@class C;");
Code = R"cpp( Code = R"cpp(
@class C; @class C;
@protocol Foo @protocol Foo
@end @end
void test(C<[[Foo]]> *p); void test(C<[[Foo]]> *p);
)cpp"; )cpp";
// FIXME: there's no AST node corresponding to 'Foo', so we're stuck. EXPECT_DECLS("ObjCObjectTypeLoc", "@protocol Foo");
EXPECT_DECLS("ObjCObjectTypeLoc");
Code = R"cpp(
@class C;
@protocol Foo
@end
@protocol Bar
@end
void test(C<[[Foo]], Bar> *p);
)cpp";
// FIXME: We currently can't disambiguate between multiple protocols.
EXPECT_DECLS("ObjCObjectTypeLoc", "@protocol Foo", "@protocol Bar");
} }
class FindExplicitReferencesTest : public ::testing::Test { class FindExplicitReferencesTest : public ::testing::Test {
@ -1610,12 +1628,12 @@ TEST_F(FindExplicitReferencesTest, All) {
@protocol P @protocol P
@end @end
void foo() { void foo() {
// FIXME: should reference P $0^I<$1^P> *$2^x;
$0^I<P> *$1^x;
} }
)cpp", )cpp",
"0: targets = {I}\n" "0: targets = {I}\n"
"1: targets = {x}, decl\n"}, "1: targets = {P}\n"
"2: targets = {x}, decl\n"},
// Designated initializers. // Designated initializers.
{R"cpp( {R"cpp(

View File

@ -676,8 +676,7 @@ sizeof...($TemplateParameter[[Elements]]);
@end @end
@interface $Class_decl[[Klass]] <$Interface[[Protocol]]> @interface $Class_decl[[Klass]] <$Interface[[Protocol]]>
@end @end
// FIXME: protocol list in ObjCObjectType should be highlighted. id<$Interface[[Protocol]]> $Variable_decl[[x]];
id<Protocol> $Variable_decl[[x]];
)cpp", )cpp",
R"cpp( R"cpp(
// ObjC: Categories // ObjC: Categories