diff --git a/clang-tools-extra/clangd/AST.h b/clang-tools-extra/clangd/AST.h --- a/clang-tools-extra/clangd/AST.h +++ b/clang-tools-extra/clangd/AST.h @@ -98,6 +98,12 @@ /// explicit specialization. bool isExplicitTemplateSpecialization(const NamedDecl *D); +/// Returns a nested name specifier loc of \p ND if it was present in the +/// source, e.g. +/// void ns::something::foo() -> returns 'ns::something' +/// void foo() -> returns null +NestedNameSpecifierLoc getQualifierLoc(const NamedDecl &ND); + } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp --- a/clang-tools-extra/clangd/AST.cpp +++ b/clang-tools-extra/clangd/AST.cpp @@ -103,16 +103,12 @@ return N.isIdentifier() && !N.getAsIdentifierInfo(); } -/// Returns a nested name specifier of \p ND if it was present in the source, -/// e.g. -/// void ns::something::foo() -> returns 'ns::something' -/// void foo() -> returns null -static NestedNameSpecifier *getQualifier(const NamedDecl &ND) { +NestedNameSpecifierLoc getQualifierLoc(const NamedDecl &ND) { if (auto *V = llvm::dyn_cast(&ND)) - return V->getQualifier(); + return V->getQualifierLoc(); if (auto *T = llvm::dyn_cast(&ND)) - return T->getQualifier(); - return nullptr; + return T->getQualifierLoc(); + return NestedNameSpecifierLoc(); } std::string printName(const ASTContext &Ctx, const NamedDecl &ND) { @@ -141,7 +137,7 @@ } // Print nested name qualifier if it was written in the source code. - if (auto *Qualifier = getQualifier(ND)) + if (auto *Qualifier = getQualifierLoc(ND).getNestedNameSpecifier()) Qualifier->print(Out, PP); // Print the name itself. ND.getDeclName().print(Out, PP); diff --git a/clang-tools-extra/clangd/FindTarget.h b/clang-tools-extra/clangd/FindTarget.h --- a/clang-tools-extra/clangd/FindTarget.h +++ b/clang-tools-extra/clangd/FindTarget.h @@ -86,6 +86,8 @@ NestedNameSpecifierLoc Qualifier; /// Start location of the last name part, i.e. 'foo' in 'ns::foo'. SourceLocation NameLoc; + /// True if the reference is a declaration or definition; + bool IsDecl = false; // FIXME: add info about template arguments. /// A list of targets referenced by this name. Normally this has a single /// element, but multiple is also possible, e.g. in case of using declarations diff --git a/clang-tools-extra/clangd/FindTarget.cpp b/clang-tools-extra/clangd/FindTarget.cpp --- a/clang-tools-extra/clangd/FindTarget.cpp +++ b/clang-tools-extra/clangd/FindTarget.cpp @@ -412,63 +412,86 @@ return Targets; } -Optional refInDecl(const Decl *D) { +llvm::SmallVector refInDecl(const Decl *D) { struct Visitor : ConstDeclVisitor { - llvm::Optional Ref; + llvm::SmallVector Refs; void VisitUsingDirectiveDecl(const UsingDirectiveDecl *D) { - Ref = ReferenceLoc{D->getQualifierLoc(), - D->getIdentLocation(), - {D->getNominatedNamespaceAsWritten()}}; + // We want to keep it as non-declaration references, as the + // "using namespace" declaration doesn't have a name. + Refs.push_back(ReferenceLoc{D->getQualifierLoc(), + D->getIdentLocation(), + /*IsDecl=*/false, + {D->getNominatedNamespaceAsWritten()}}); } void VisitUsingDecl(const UsingDecl *D) { - Ref = ReferenceLoc{D->getQualifierLoc(), D->getLocation(), - explicitReferenceTargets(DynTypedNode::create(*D), - DeclRelation::Underlying)}; + // "using ns::identifer;" is a non-declaration reference. + Refs.push_back( + ReferenceLoc{D->getQualifierLoc(), D->getLocation(), /*IsDecl=*/false, + explicitReferenceTargets(DynTypedNode::create(*D), + DeclRelation::Underlying)}); } void VisitNamespaceAliasDecl(const NamespaceAliasDecl *D) { - Ref = ReferenceLoc{D->getQualifierLoc(), - D->getTargetNameLoc(), - {D->getAliasedNamespace()}}; + // For namespace alias, "namespace Foo = Target;", we add two references. + // Add a declaration reference for Foo. + VisitNamedDecl(D); + // Add a non-declaration reference for Target. + Refs.push_back(ReferenceLoc{D->getQualifierLoc(), + D->getTargetNameLoc(), + /*IsDecl=*/false, + {D->getAliasedNamespace()}}); + } + + void VisitNamedDecl(const NamedDecl *ND) { + // FIXME: decide on how to surface destructors when we need them. + if (llvm::isa(ND)) + return; + Refs.push_back(ReferenceLoc{ + getQualifierLoc(*ND), ND->getLocation(), /*IsDecl=*/true, {ND}}); } }; Visitor V; V.Visit(D); - return V.Ref; + return V.Refs; } -Optional refInExpr(const Expr *E) { +llvm::SmallVector refInExpr(const Expr *E) { struct Visitor : ConstStmtVisitor { // FIXME: handle more complicated cases, e.g. ObjC, designated initializers. - llvm::Optional Ref; + llvm::SmallVector Refs; void VisitDeclRefExpr(const DeclRefExpr *E) { - Ref = ReferenceLoc{ - E->getQualifierLoc(), E->getNameInfo().getLoc(), {E->getFoundDecl()}}; + Refs.push_back(ReferenceLoc{E->getQualifierLoc(), + E->getNameInfo().getLoc(), + /*IsDecl=*/false, + {E->getFoundDecl()}}); } void VisitMemberExpr(const MemberExpr *E) { - Ref = ReferenceLoc{E->getQualifierLoc(), - E->getMemberNameInfo().getLoc(), - {E->getFoundDecl()}}; + Refs.push_back(ReferenceLoc{E->getQualifierLoc(), + E->getMemberNameInfo().getLoc(), + /*IsDecl=*/false, + {E->getFoundDecl()}}); } void VisitOverloadExpr(const OverloadExpr *E) { - Ref = ReferenceLoc{E->getQualifierLoc(), E->getNameInfo().getLoc(), - llvm::SmallVector( - E->decls().begin(), E->decls().end())}; + Refs.push_back(ReferenceLoc{E->getQualifierLoc(), + E->getNameInfo().getLoc(), + /*IsDecl=*/false, + llvm::SmallVector( + E->decls().begin(), E->decls().end())}); } }; Visitor V; V.Visit(E); - return V.Ref; + return V.Refs; } -Optional refInTypeLoc(TypeLoc L) { +llvm::SmallVector refInTypeLoc(TypeLoc L) { struct Visitor : TypeLocVisitor { llvm::Optional Ref; @@ -483,13 +506,17 @@ } void VisitTagTypeLoc(TagTypeLoc L) { - Ref = - ReferenceLoc{NestedNameSpecifierLoc(), L.getNameLoc(), {L.getDecl()}}; + Ref = ReferenceLoc{NestedNameSpecifierLoc(), + L.getNameLoc(), + /*IsDecl=*/false, + {L.getDecl()}}; } void VisitTemplateTypeParmTypeLoc(TemplateTypeParmTypeLoc L) { - Ref = - ReferenceLoc{NestedNameSpecifierLoc(), L.getNameLoc(), {L.getDecl()}}; + Ref = ReferenceLoc{NestedNameSpecifierLoc(), + L.getNameLoc(), + /*IsDecl=*/false, + {L.getDecl()}}; } void VisitTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc L) { @@ -502,14 +529,14 @@ // 2. 'vector' with mask 'Underlying'. // we want to return only #1 in this case. Ref = ReferenceLoc{ - NestedNameSpecifierLoc(), L.getTemplateNameLoc(), + NestedNameSpecifierLoc(), L.getTemplateNameLoc(), /*IsDecl=*/false, explicitReferenceTargets(DynTypedNode::create(L.getType()), DeclRelation::Alias)}; } void VisitDeducedTemplateSpecializationTypeLoc( DeducedTemplateSpecializationTypeLoc L) { Ref = ReferenceLoc{ - NestedNameSpecifierLoc(), L.getNameLoc(), + NestedNameSpecifierLoc(), L.getNameLoc(), /*IsDecl=*/false, explicitReferenceTargets(DynTypedNode::create(L.getType()), DeclRelation::Alias)}; } @@ -517,25 +544,29 @@ void VisitDependentTemplateSpecializationTypeLoc( DependentTemplateSpecializationTypeLoc L) { Ref = ReferenceLoc{ - L.getQualifierLoc(), L.getTemplateNameLoc(), + L.getQualifierLoc(), L.getTemplateNameLoc(), /*IsDecl=*/false, explicitReferenceTargets(DynTypedNode::create(L.getType()))}; } void VisitDependentNameTypeLoc(DependentNameTypeLoc L) { Ref = ReferenceLoc{ - L.getQualifierLoc(), L.getNameLoc(), + L.getQualifierLoc(), L.getNameLoc(), /*IsDecl=*/false, explicitReferenceTargets(DynTypedNode::create(L.getType()))}; } void VisitTypedefTypeLoc(TypedefTypeLoc L) { - Ref = ReferenceLoc{ - NestedNameSpecifierLoc(), L.getNameLoc(), {L.getTypedefNameDecl()}}; + Ref = ReferenceLoc{NestedNameSpecifierLoc(), + L.getNameLoc(), + /*IsDecl=*/false, + {L.getTypedefNameDecl()}}; } }; Visitor V; V.Visit(L.getUnqualifiedLoc()); - return V.Ref; + if (!V.Ref) + return {}; + return {*V.Ref}; } class ExplicitReferenceColletor @@ -575,6 +606,7 @@ case TemplateArgument::TemplateExpansion: reportReference(ReferenceLoc{A.getTemplateQualifierLoc(), A.getTemplateNameLoc(), + /*IsDecl=*/false, {A.getArgument() .getAsTemplateOrTemplatePattern() .getAsTemplateDecl()}}, @@ -625,34 +657,33 @@ /// be references. However, declarations can have references inside them, /// e.g. 'namespace foo = std' references namespace 'std' and this /// function will return the corresponding reference. - llvm::Optional explicitReference(DynTypedNode N) { + llvm::SmallVector explicitReference(DynTypedNode N) { if (auto *D = N.get()) return refInDecl(D); if (auto *E = N.get()) return refInExpr(E); if (auto *NNSL = N.get()) - return ReferenceLoc{NNSL->getPrefix(), NNSL->getLocalBeginLoc(), - explicitReferenceTargets(DynTypedNode::create( - *NNSL->getNestedNameSpecifier()))}; + return {ReferenceLoc{NNSL->getPrefix(), NNSL->getLocalBeginLoc(), false, + explicitReferenceTargets(DynTypedNode::create( + *NNSL->getNestedNameSpecifier()))}}; if (const TypeLoc *TL = N.get()) return refInTypeLoc(*TL); if (const CXXCtorInitializer *CCI = N.get()) { if (CCI->isBaseInitializer()) return refInTypeLoc(CCI->getBaseClassLoc()); assert(CCI->isAnyMemberInitializer()); - return ReferenceLoc{NestedNameSpecifierLoc(), - CCI->getMemberLocation(), - {CCI->getAnyMember()}}; + return {ReferenceLoc{NestedNameSpecifierLoc(), + CCI->getMemberLocation(), + /*IsDecl=*/false, + {CCI->getAnyMember()}}}; } // We do not have location information for other nodes (QualType, etc) - return llvm::None; + return {}; } void visitNode(DynTypedNode N) { - auto Ref = explicitReference(N); - if (!Ref) - return; - reportReference(*Ref, N); + for (const auto &R : explicitReference(N)) + reportReference(R, N); } void reportReference(const ReferenceLoc &Ref, DynTypedNode N) { @@ -727,6 +758,8 @@ PrintingPolicy(LangOptions())); OS << "'"; } + if (R.IsDecl) + OS << ", decl"; return OS; } diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -1307,7 +1307,8 @@ llvm::DenseSet DeclRefs; findExplicitReferences(FD, [&](ReferenceLoc Ref) { for (const Decl *D : Ref.Targets) { - if (!index::isFunctionLocalSymbol(D) && !D->isTemplateParameter()) + if (!index::isFunctionLocalSymbol(D) && !D->isTemplateParameter() && + !Ref.IsDecl) DeclRefs.insert(D); } }); 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 @@ -606,53 +606,61 @@ struct Struct { int a; }; using Typedef = int; void foo() { - $0^Struct x; - $1^Typedef y; - static_cast<$2^Struct*>(0); + $0^Struct $1^x; + $2^Typedef $3^y; + static_cast<$4^Struct*>(0); } )cpp", "0: targets = {Struct}\n" - "1: targets = {Typedef}\n" - "2: targets = {Struct}\n"}, + "1: targets = {x}, decl\n" + "2: targets = {Typedef}\n" + "3: targets = {y}, decl\n" + "4: targets = {Struct}\n"}, // Name qualifiers. {R"cpp( namespace a { namespace b { struct S { typedef int type; }; } } void foo() { - $0^a::$1^b::$2^S x; - using namespace $3^a::$4^b; - $5^S::$6^type y; + $0^a::$1^b::$2^S $3^x; + using namespace $4^a::$5^b; + $6^S::$7^type $8^y; } )cpp", "0: targets = {a}\n" "1: targets = {a::b}, qualifier = 'a::'\n" "2: targets = {a::b::S}, qualifier = 'a::b::'\n" - "3: targets = {a}\n" - "4: targets = {a::b}, qualifier = 'a::'\n" - "5: targets = {a::b::S}\n" - "6: targets = {a::b::S::type}, qualifier = 'struct S::'\n"}, + "3: targets = {x}, decl\n" + "4: targets = {a}\n" + "5: targets = {a::b}, qualifier = 'a::'\n" + "6: targets = {a::b::S}\n" + "7: targets = {a::b::S::type}, qualifier = 'struct S::'\n" + "8: targets = {y}, decl\n"}, // Simple templates. {R"cpp( template struct vector { using value_type = T; }; template <> struct vector { using value_type = bool; }; void foo() { - $0^vector vi; - $1^vector vb; + $0^vector $1^vi; + $2^vector $3^vb; } )cpp", "0: targets = {vector}\n" - "1: targets = {vector}\n"}, + "1: targets = {vi}, decl\n" + "2: targets = {vector}\n" + "3: targets = {vb}, decl\n"}, // Template type aliases. {R"cpp( template struct vector { using value_type = T; }; template <> struct vector { using value_type = bool; }; template using valias = vector; void foo() { - $0^valias vi; - $1^valias vb; + $0^valias $1^vi; + $2^valias $3^vb; } )cpp", "0: targets = {valias}\n" - "1: targets = {valias}\n"}, + "1: targets = {vi}, decl\n" + "2: targets = {valias}\n" + "3: targets = {vb}, decl\n"}, // MemberExpr should know their using declaration. {R"cpp( struct X { void func(int); } @@ -694,13 +702,14 @@ }; void foo() { - for (int x : $0^vector()) { - $1^x = 10; + for (int $0^x : $1^vector()) { + $2^x = 10; } } )cpp", - "0: targets = {vector}\n" - "1: targets = {x}\n"}, + "0: targets = {x}, decl\n" + "1: targets = {vector}\n" + "2: targets = {x}\n"}, // Handle UnresolvedLookupExpr. {R"cpp( namespace ns1 { void func(char*); } @@ -736,39 +745,42 @@ void foo() { static_cast<$0^T>(0); $1^T(); - $2^T t; + $2^T $3^t; } )cpp", "0: targets = {T}\n" "1: targets = {T}\n" - "2: targets = {T}\n"}, + "2: targets = {T}\n" + "3: targets = {t}, decl\n"}, // Non-type template parameters. {R"cpp( template void foo() { - int x = $0^I; + int $0^x = $1^I; } )cpp", - "0: targets = {I}\n"}, + "0: targets = {x}, decl\n" + "1: targets = {I}\n"}, // Template template parameters. {R"cpp( template struct vector {}; template class TT, template class ...TP> void foo() { - $0^TT x; - $1^foo<$2^TT>(); - $3^foo<$4^vector>() - $5^foo<$6^TP...>(); + $0^TT $1^x; + $2^foo<$3^TT>(); + $4^foo<$5^vector>() + $6^foo<$7^TP...>(); } )cpp", "0: targets = {TT}\n" - "1: targets = {foo}\n" - "2: targets = {TT}\n" - "3: targets = {foo}\n" - "4: targets = {vector}\n" - "5: targets = {foo}\n" - "6: targets = {TP}\n"}, + "1: targets = {x}, decl\n" + "2: targets = {foo}\n" + "3: targets = {TT}\n" + "4: targets = {foo}\n" + "5: targets = {vector}\n" + "6: targets = {foo}\n" + "7: targets = {TP}\n"}, // Non-type template parameters with declarations. {R"cpp( int func(); @@ -776,13 +788,37 @@ template void foo() { - $0^wrapper<$1^func> w; - $2^FuncParam(); + $0^wrapper<$1^func> $2^w; + $3^FuncParam(); } )cpp", "0: targets = {wrapper<&func>}\n" "1: targets = {func}\n" - "2: targets = {FuncParam}\n"}, + "2: targets = {w}, decl\n" + "3: targets = {FuncParam}\n"}, + {R"cpp( + namespace ns {} + class S {}; + void foo() { + class $0^Foo { $1^Foo(); ~$2^Foo(); int $3^field; }; + int $4^Var; + enum $5^E { $6^ABC }; + typedef int $7^INT; + using $8^INT2 = int; + namespace $9^NS = $10^ns; + } + )cpp", + "0: targets = {Foo}, decl\n" + "1: targets = {foo()::Foo::Foo}, decl\n" + "2: targets = {Foo}\n" + "3: targets = {foo()::Foo::field}, decl\n" + "4: targets = {Var}, decl\n" + "5: targets = {E}, decl\n" + "6: targets = {foo()::ABC}, decl\n" + "7: targets = {INT}, decl\n" + "8: targets = {INT2}, decl\n" + "9: targets = {NS}, decl\n" + "10: targets = {ns}\n"}, }; for (const auto &C : Cases) { diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp --- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp +++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp @@ -2277,7 +2277,8 @@ if (const auto *ND = llvm::dyn_cast(D)) Names.push_back(ND->getQualifiedNameAsString()); } - EXPECT_THAT(Names, UnorderedElementsAreArray(C.ExpectedDecls)); + EXPECT_THAT(Names, UnorderedElementsAreArray(C.ExpectedDecls)) + << File.code(); } }