[clangd] Report declaration references in findExplicitReferences.

Reviewers: ilya-biryukov

Subscribers: MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits

Tags: #clang

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

git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@375226 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/clangd/AST.cpp b/clangd/AST.cpp
index 4242721..002f2ef 100644
--- a/clangd/AST.cpp
+++ b/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<DeclaratorDecl>(&ND))
-    return V->getQualifier();
+    return V->getQualifierLoc();
   if (auto *T = llvm::dyn_cast<TagDecl>(&ND))
-    return T->getQualifier();
-  return nullptr;
+    return T->getQualifierLoc();
+  return NestedNameSpecifierLoc();
 }
 
 std::string printUsingNamespaceName(const ASTContext &Ctx,
@@ -153,7 +149,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/clangd/AST.h b/clangd/AST.h
index eddabda..b05e0a2 100644
--- a/clangd/AST.h
+++ b/clangd/AST.h
@@ -104,6 +104,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/clangd/FindTarget.cpp b/clangd/FindTarget.cpp
index ffe808d..4919105 100644
--- a/clangd/FindTarget.cpp
+++ b/clangd/FindTarget.cpp
@@ -412,63 +412,86 @@
   return Targets;
 }
 
-Optional<ReferenceLoc> refInDecl(const Decl *D) {
+llvm::SmallVector<ReferenceLoc, 2> refInDecl(const Decl *D) {
   struct Visitor : ConstDeclVisitor<Visitor> {
-    llvm::Optional<ReferenceLoc> Ref;
+    llvm::SmallVector<ReferenceLoc, 2> 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<CXXDestructorDecl>(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<ReferenceLoc> refInExpr(const Expr *E) {
+llvm::SmallVector<ReferenceLoc, 2> refInExpr(const Expr *E) {
   struct Visitor : ConstStmtVisitor<Visitor> {
     // FIXME: handle more complicated cases, e.g. ObjC, designated initializers.
-    llvm::Optional<ReferenceLoc> Ref;
+    llvm::SmallVector<ReferenceLoc, 2> 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<const NamedDecl *, 1>(
-                             E->decls().begin(), E->decls().end())};
+      Refs.push_back(ReferenceLoc{E->getQualifierLoc(),
+                                  E->getNameInfo().getLoc(),
+                                  /*IsDecl=*/false,
+                                  llvm::SmallVector<const NamedDecl *, 1>(
+                                      E->decls().begin(), E->decls().end())});
     }
   };
 
   Visitor V;
   V.Visit(E);
-  return V.Ref;
+  return V.Refs;
 }
 
-Optional<ReferenceLoc> refInTypeLoc(TypeLoc L) {
+llvm::SmallVector<ReferenceLoc, 2> refInTypeLoc(TypeLoc L) {
   struct Visitor : TypeLocVisitor<Visitor> {
     llvm::Optional<ReferenceLoc> 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<int>' 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<ReferenceLoc> explicitReference(DynTypedNode N) {
+  llvm::SmallVector<ReferenceLoc, 2> explicitReference(DynTypedNode N) {
     if (auto *D = N.get<Decl>())
       return refInDecl(D);
     if (auto *E = N.get<Expr>())
       return refInExpr(E);
     if (auto *NNSL = N.get<NestedNameSpecifierLoc>())
-      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<TypeLoc>())
       return refInTypeLoc(*TL);
     if (const CXXCtorInitializer *CCI = N.get<CXXCtorInitializer>()) {
       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/clangd/FindTarget.h b/clangd/FindTarget.h
index 80a8e787..9ddd24a 100644
--- a/clangd/FindTarget.h
+++ b/clangd/FindTarget.h
@@ -86,6 +86,8 @@
   NestedNameSpecifierLoc Qualifier;
   /// Start location of the last name part, i.e. 'foo' in 'ns::foo<int>'.
   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/clangd/XRefs.cpp b/clangd/XRefs.cpp
index 43904cd..2e75e8c 100644
--- a/clangd/XRefs.cpp
+++ b/clangd/XRefs.cpp
@@ -1308,7 +1308,8 @@
   llvm::DenseSet<const Decl *> 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/clangd/unittests/FindTargetTests.cpp b/clangd/unittests/FindTargetTests.cpp
index 9e1f1c1..bf74b45 100644
--- a/clangd/unittests/FindTargetTests.cpp
+++ b/clangd/unittests/FindTargetTests.cpp
@@ -622,53 +622,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 <class T> struct vector { using value_type = T; };
           template <> struct vector<bool> { using value_type = bool; };
           void foo() {
-            $0^vector<int> vi;
-            $1^vector<bool> vb;
+            $0^vector<int> $1^vi;
+            $2^vector<bool> $3^vb;
           }
         )cpp",
            "0: targets = {vector<int>}\n"
-           "1: targets = {vector<bool>}\n"},
+           "1: targets = {vi}, decl\n"
+           "2: targets = {vector<bool>}\n"
+           "3: targets = {vb}, decl\n"},
           // Template type aliases.
           {R"cpp(
             template <class T> struct vector { using value_type = T; };
             template <> struct vector<bool> { using value_type = bool; };
             template <class T> using valias = vector<T>;
             void foo() {
-              $0^valias<int> vi;
-              $1^valias<bool> vb;
+              $0^valias<int> $1^vi;
+              $2^valias<bool> $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); }
@@ -710,13 +718,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*); }
@@ -752,39 +761,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 <int I>
             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 <class T> struct vector {};
 
             template <template<class> class TT, template<class> class ...TP>
             void foo() {
-              $0^TT<int> x;
-              $1^foo<$2^TT>();
-              $3^foo<$4^vector>()
-              $5^foo<$6^TP...>();
+              $0^TT<int> $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();
@@ -792,13 +804,37 @@
 
             template <int(*FuncParam)()>
             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/clangd/unittests/XRefsTests.cpp b/clangd/unittests/XRefsTests.cpp
index aafcb47..c8b0055 100644
--- a/clangd/unittests/XRefsTests.cpp
+++ b/clangd/unittests/XRefsTests.cpp
@@ -2284,7 +2284,8 @@
       if (const auto *ND = llvm::dyn_cast<NamedDecl>(D))
         Names.push_back(ND->getQualifiedNameAsString());
     }
-    EXPECT_THAT(Names, UnorderedElementsAreArray(C.ExpectedDecls));
+    EXPECT_THAT(Names, UnorderedElementsAreArray(C.ExpectedDecls))
+        << File.code();
   }
 }