[clang-tools-extra] r331189 - [clangd] Using index for GoToDefinition.

Haojian Wu via cfe-commits cfe-commits at lists.llvm.org
Mon Apr 30 08:24:17 PDT 2018


Author: hokein
Date: Mon Apr 30 08:24:17 2018
New Revision: 331189

URL: http://llvm.org/viewvc/llvm-project?rev=331189&view=rev
Log:
[clangd] Using index for GoToDefinition.

Summary:
This patch adds index support for GoToDefinition -- when we don't get the
definition from local AST, we query our index (Static&Dynamic) index to
get it.

Since we currently collect top-level symbol in the index, it doesn't support all
cases (e.g. class members), we will extend the index to include more symbols in
the future.

Reviewers: sammccall

Subscribers: klimek, ilya-biryukov, jkorous-apple, ioeric, MaskRay, cfe-commits

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

Modified:
    clang-tools-extra/trunk/clangd/ClangdServer.cpp
    clang-tools-extra/trunk/clangd/XRefs.cpp
    clang-tools-extra/trunk/clangd/XRefs.h
    clang-tools-extra/trunk/clangd/index/FileIndex.cpp
    clang-tools-extra/trunk/clangd/index/FileIndex.h
    clang-tools-extra/trunk/unittests/clangd/XRefsTests.cpp

Modified: clang-tools-extra/trunk/clangd/ClangdServer.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.cpp?rev=331189&r1=331188&r2=331189&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdServer.cpp (original)
+++ clang-tools-extra/trunk/clangd/ClangdServer.cpp Mon Apr 30 08:24:17 2018
@@ -355,11 +355,11 @@ void ClangdServer::dumpAST(PathRef File,
 void ClangdServer::findDefinitions(PathRef File, Position Pos,
                                    Callback<std::vector<Location>> CB) {
   auto FS = FSProvider.getFileSystem();
-  auto Action = [Pos, FS](Callback<std::vector<Location>> CB,
-                          llvm::Expected<InputsAndAST> InpAST) {
+  auto Action = [Pos, FS, this](Callback<std::vector<Location>> CB,
+                                llvm::Expected<InputsAndAST> InpAST) {
     if (!InpAST)
       return CB(InpAST.takeError());
-    CB(clangd::findDefinitions(InpAST->AST, Pos));
+    CB(clangd::findDefinitions(InpAST->AST, Pos, this->FileIdx.get()));
   };
 
   WorkScheduler.runWithAST("Definitions", File, Bind(Action, std::move(CB)));

Modified: clang-tools-extra/trunk/clangd/XRefs.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/XRefs.cpp?rev=331189&r1=331188&r2=331189&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/XRefs.cpp (original)
+++ clang-tools-extra/trunk/clangd/XRefs.cpp Mon Apr 30 08:24:17 2018
@@ -14,6 +14,7 @@
 #include "clang/AST/DeclTemplate.h"
 #include "clang/Index/IndexDataConsumer.h"
 #include "clang/Index/IndexingAction.h"
+#include "clang/Index/USRGeneration.h"
 #include "llvm/Support/Path.h"
 namespace clang {
 namespace clangd {
@@ -34,6 +35,33 @@ const Decl *GetDefinition(const Decl *D)
   return nullptr;
 }
 
+// Convert a SymbolLocation to LSP's Location.
+// HintPath is used to resolve the path of URI.
+// FIXME: figure out a good home for it, and share the implementation with
+// FindSymbols.
+llvm::Optional<Location> ToLSPLocation(const SymbolLocation &Loc,
+                                       llvm::StringRef HintPath) {
+  if (!Loc)
+    return llvm::None;
+  auto Uri = URI::parse(Loc.FileURI);
+  if (!Uri) {
+    log("Could not parse URI: " + Loc.FileURI);
+    return llvm::None;
+  }
+  auto Path = URI::resolve(*Uri, HintPath);
+  if (!Path) {
+    log("Could not resolve URI: " + Loc.FileURI);
+    return llvm::None;
+  }
+  Location LSPLoc;
+  LSPLoc.uri = URIForFile(*Path);
+  LSPLoc.range.start.line = Loc.Start.Line;
+  LSPLoc.range.start.character = Loc.Start.Column;
+  LSPLoc.range.end.line = Loc.End.Line;
+  LSPLoc.range.end.character = Loc.End.Column;
+  return LSPLoc;
+}
+
 struct MacroDecl {
   StringRef Name;
   const MacroInfo *Info;
@@ -128,6 +156,38 @@ private:
   }
 };
 
+struct IdentifiedSymbol {
+  std::vector<const Decl *> Decls;
+  std::vector<MacroDecl> Macros;
+};
+
+IdentifiedSymbol getSymbolAtPosition(ParsedAST &AST, SourceLocation Pos) {
+  auto DeclMacrosFinder = DeclarationAndMacrosFinder(
+      llvm::errs(), Pos, AST.getASTContext(), AST.getPreprocessor());
+  index::IndexingOptions IndexOpts;
+  IndexOpts.SystemSymbolFilter =
+      index::IndexingOptions::SystemSymbolFilterKind::All;
+  IndexOpts.IndexFunctionLocals = true;
+  indexTopLevelDecls(AST.getASTContext(), AST.getTopLevelDecls(),
+                     DeclMacrosFinder, IndexOpts);
+
+  return {DeclMacrosFinder.takeDecls(), DeclMacrosFinder.takeMacroInfos()};
+}
+
+llvm::Optional<std::string>
+getAbsoluteFilePath(const FileEntry *F, const SourceManager &SourceMgr) {
+  SmallString<64> FilePath = F->tryGetRealPathName();
+  if (FilePath.empty())
+    FilePath = F->getName();
+  if (!llvm::sys::path::is_absolute(FilePath)) {
+    if (!SourceMgr.getFileManager().makeAbsolutePath(FilePath)) {
+      log("Could not turn relative path to absolute: " + FilePath);
+      return llvm::None;
+    }
+  }
+  return FilePath.str().str();
+}
+
 llvm::Optional<Location>
 makeLocation(ParsedAST &AST, const SourceRange &ValSourceRange) {
   const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
@@ -145,24 +205,29 @@ makeLocation(ParsedAST &AST, const Sourc
   Range R = {Begin, End};
   Location L;
 
-  SmallString<64> FilePath = F->tryGetRealPathName();
-  if (FilePath.empty())
-    FilePath = F->getName();
-  if (!llvm::sys::path::is_absolute(FilePath)) {
-    if (!SourceMgr.getFileManager().makeAbsolutePath(FilePath)) {
-      log("Could not turn relative path to absolute: " + FilePath);
-      return llvm::None;
-    }
+  auto FilePath = getAbsoluteFilePath(F, SourceMgr);
+  if (!FilePath) {
+    log("failed to get path!");
+    return llvm::None;
   }
-
-  L.uri = URIForFile(FilePath.str());
+  L.uri = URIForFile(*FilePath);
   L.range = R;
   return L;
 }
 
+// Get the symbol ID for a declaration, if possible.
+llvm::Optional<SymbolID> getSymbolID(const Decl *D) {
+  llvm::SmallString<128> USR;
+  if (index::generateUSRForDecl(D, USR)) {
+    return None;
+  }
+  return SymbolID(USR);
+}
+
 } // namespace
 
-std::vector<Location> findDefinitions(ParsedAST &AST, Position Pos) {
+std::vector<Location> findDefinitions(ParsedAST &AST, Position Pos,
+                                      const SymbolIndex *Index) {
   const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
   SourceLocation SourceLocationBeg =
       getBeginningOfIdentifier(AST, Pos, SourceMgr.getMainFileID());
@@ -179,32 +244,97 @@ std::vector<Location> findDefinitions(Pa
   if (!Result.empty())
     return Result;
 
-  DeclarationAndMacrosFinder DeclMacrosFinder(llvm::errs(), SourceLocationBeg,
-                                              AST.getASTContext(),
-                                              AST.getPreprocessor());
-  index::IndexingOptions IndexOpts;
-  IndexOpts.SystemSymbolFilter =
-      index::IndexingOptions::SystemSymbolFilterKind::All;
-  IndexOpts.IndexFunctionLocals = true;
+  // Identified symbols at a specific position.
+  auto Symbols = getSymbolAtPosition(AST, SourceLocationBeg);
 
-  indexTopLevelDecls(AST.getASTContext(), AST.getTopLevelDecls(),
-                     DeclMacrosFinder, IndexOpts);
-
-  std::vector<const Decl *> Decls = DeclMacrosFinder.takeDecls();
-  std::vector<MacroDecl> MacroInfos = DeclMacrosFinder.takeMacroInfos();
-
-  for (auto D : Decls) {
-    auto Loc = findNameLoc(D);
+  for (auto Item : Symbols.Macros) {
+    auto Loc = Item.Info->getDefinitionLoc();
     auto L = makeLocation(AST, SourceRange(Loc, Loc));
     if (L)
       Result.push_back(*L);
   }
 
-  for (auto Item : MacroInfos) {
-    auto Loc = Item.Info->getDefinitionLoc();
+  // Declaration and definition are different terms in C-family languages, and
+  // LSP only defines the "GoToDefinition" specification, so we try to perform
+  // the "most sensible" GoTo operation:
+  //
+  //  - We use the location from AST and index (if available) to provide the
+  //    final results. When there are duplicate results, we prefer AST over
+  //    index because AST is more up-to-date.
+  //
+  //  - For each symbol, we will return a location of the canonical declaration
+  //    (e.g. function declaration in header), and a location of definition if
+  //    they are available.
+  //
+  // So the work flow:
+  //
+  //   1. Identify the symbols being search for by traversing the AST.
+  //   2. Populate one of the locations with the AST location.
+  //   3. Use the AST information to query the index, and populate the index
+  //      location (if available).
+  //   4. Return all populated locations for all symbols, definition first (
+  //      which  we think is the users wants most often).
+  struct CandidateLocation {
+    llvm::Optional<Location> Def;
+    llvm::Optional<Location> Decl;
+  };
+  llvm::DenseMap<SymbolID, CandidateLocation> ResultCandidates;
+
+  // Emit all symbol locations (declaration or definition) from AST.
+  for (const auto *D : Symbols.Decls) {
+    // Fake key for symbols don't have USR (no SymbolID).
+    // Ideally, there should be a USR for each identified symbols. Symbols
+    // without USR are rare and unimportant cases, we use the a fake holder to
+    // minimize the invasiveness of these cases.
+    SymbolID Key("");
+    if (auto ID = getSymbolID(D))
+      Key = *ID;
+
+    auto &Candidate = ResultCandidates[Key];
+    auto Loc = findNameLoc(D);
     auto L = makeLocation(AST, SourceRange(Loc, Loc));
-    if (L)
-      Result.push_back(*L);
+    // The declaration in the identified symbols is a definition if possible
+    // otherwise it is declaration.
+    bool IsDef = GetDefinition(D) == D;
+    // Populate one of the slots with location for the AST.
+    if (!IsDef)
+      Candidate.Decl = L;
+    else
+      Candidate.Def = L;
+  }
+
+  if (Index) {
+    LookupRequest QueryRequest;
+    // Build request for index query, using SymbolID.
+    for (auto It : ResultCandidates)
+      QueryRequest.IDs.insert(It.first);
+    std::string HintPath;
+    const FileEntry *FE =
+        SourceMgr.getFileEntryForID(SourceMgr.getMainFileID());
+    if (auto Path = getAbsoluteFilePath(FE, SourceMgr))
+      HintPath = *Path;
+    // Query the index and populate the empty slot.
+    Index->lookup(
+        QueryRequest, [&HintPath, &ResultCandidates](const Symbol &Sym) {
+          auto It = ResultCandidates.find(Sym.ID);
+          assert(It != ResultCandidates.end());
+          auto &Value = It->second;
+
+          if (!Value.Def)
+            Value.Def = ToLSPLocation(Sym.Definition, HintPath);
+          if (!Value.Decl)
+            Value.Decl = ToLSPLocation(Sym.CanonicalDeclaration, HintPath);
+        });
+  }
+
+  // Populate the results, definition first.
+  for (auto It : ResultCandidates) {
+    const auto &Candidate = It.second;
+    if (Candidate.Def)
+      Result.push_back(*Candidate.Def);
+    if (Candidate.Decl &&
+        Candidate.Decl != Candidate.Def) // Decl and Def might be the same
+      Result.push_back(*Candidate.Decl);
   }
 
   return Result;
@@ -280,23 +410,16 @@ std::vector<DocumentHighlight> findDocum
   SourceLocation SourceLocationBeg =
       getBeginningOfIdentifier(AST, Pos, SourceMgr.getMainFileID());
 
-  DeclarationAndMacrosFinder DeclMacrosFinder(llvm::errs(), SourceLocationBeg,
-                                              AST.getASTContext(),
-                                              AST.getPreprocessor());
-  index::IndexingOptions IndexOpts;
-  IndexOpts.SystemSymbolFilter =
-      index::IndexingOptions::SystemSymbolFilterKind::All;
-  IndexOpts.IndexFunctionLocals = true;
-
-  // Macro occurences are not currently handled.
-  indexTopLevelDecls(AST.getASTContext(), AST.getTopLevelDecls(),
-                     DeclMacrosFinder, IndexOpts);
-
-  std::vector<const Decl *> SelectedDecls = DeclMacrosFinder.takeDecls();
+  auto Symbols = getSymbolAtPosition(AST, SourceLocationBeg);
+  std::vector<const Decl *> SelectedDecls = Symbols.Decls;
 
   DocumentHighlightsFinder DocHighlightsFinder(
       llvm::errs(), AST.getASTContext(), AST.getPreprocessor(), SelectedDecls);
 
+  index::IndexingOptions IndexOpts;
+  IndexOpts.SystemSymbolFilter =
+      index::IndexingOptions::SystemSymbolFilterKind::All;
+  IndexOpts.IndexFunctionLocals = true;
   indexTopLevelDecls(AST.getASTContext(), AST.getTopLevelDecls(),
                      DocHighlightsFinder, IndexOpts);
 
@@ -409,25 +532,14 @@ Hover getHover(ParsedAST &AST, Position
   const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
   SourceLocation SourceLocationBeg =
       getBeginningOfIdentifier(AST, Pos, SourceMgr.getMainFileID());
-  DeclarationAndMacrosFinder DeclMacrosFinder(llvm::errs(), SourceLocationBeg,
-                                              AST.getASTContext(),
-                                              AST.getPreprocessor());
+  // Identified symbols at a specific position.
+  auto Symbols = getSymbolAtPosition(AST, SourceLocationBeg);
 
-  index::IndexingOptions IndexOpts;
-  IndexOpts.SystemSymbolFilter =
-      index::IndexingOptions::SystemSymbolFilterKind::All;
-  IndexOpts.IndexFunctionLocals = true;
-
-  indexTopLevelDecls(AST.getASTContext(), AST.getTopLevelDecls(),
-                     DeclMacrosFinder, IndexOpts);
+  if (!Symbols.Macros.empty())
+    return getHoverContents(Symbols.Macros[0].Name);
 
-  std::vector<MacroDecl> Macros = DeclMacrosFinder.takeMacroInfos();
-  if (!Macros.empty())
-    return getHoverContents(Macros[0].Name);
-
-  std::vector<const Decl *> Decls = DeclMacrosFinder.takeDecls();
-  if (!Decls.empty())
-    return getHoverContents(Decls[0]);
+  if (!Symbols.Decls.empty())
+    return getHoverContents(Symbols.Decls[0]);
 
   return Hover();
 }

Modified: clang-tools-extra/trunk/clangd/XRefs.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/XRefs.h?rev=331189&r1=331188&r2=331189&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/XRefs.h (original)
+++ clang-tools-extra/trunk/clangd/XRefs.h Mon Apr 30 08:24:17 2018
@@ -15,13 +15,15 @@
 
 #include "ClangdUnit.h"
 #include "Protocol.h"
+#include "index/Index.h"
 #include <vector>
 
 namespace clang {
 namespace clangd {
 
 /// Get definition of symbol at a specified \p Pos.
-std::vector<Location> findDefinitions(ParsedAST &AST, Position Pos);
+std::vector<Location> findDefinitions(ParsedAST &AST, Position Pos,
+                                      const SymbolIndex *Index = nullptr);
 
 /// Returns highlights for all usages of a symbol at \p Pos.
 std::vector<DocumentHighlight> findDocumentHighlights(ParsedAST &AST,

Modified: clang-tools-extra/trunk/clangd/index/FileIndex.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/index/FileIndex.cpp?rev=331189&r1=331188&r2=331189&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/index/FileIndex.cpp (original)
+++ clang-tools-extra/trunk/clangd/index/FileIndex.cpp Mon Apr 30 08:24:17 2018
@@ -13,12 +13,9 @@
 
 namespace clang {
 namespace clangd {
-namespace {
 
-/// Retrieves namespace and class level symbols in \p Decls.
-std::unique_ptr<SymbolSlab> indexAST(ASTContext &Ctx,
-                                     std::shared_ptr<Preprocessor> PP,
-                                     llvm::ArrayRef<const Decl *> Decls) {
+SymbolSlab indexAST(ParsedAST *AST) {
+  assert(AST && "AST must not be nullptr!");
   SymbolCollector::Options CollectorOpts;
   // FIXME(ioeric): we might also want to collect include headers. We would need
   // to make sure all includes are canonicalized (with CanonicalIncludes), which
@@ -29,21 +26,18 @@ std::unique_ptr<SymbolSlab> indexAST(AST
   CollectorOpts.CountReferences = false;
 
   SymbolCollector Collector(std::move(CollectorOpts));
-  Collector.setPreprocessor(std::move(PP));
+  Collector.setPreprocessor(AST->getPreprocessorPtr());
   index::IndexingOptions IndexOpts;
   // We only need declarations, because we don't count references.
   IndexOpts.SystemSymbolFilter =
       index::IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly;
   IndexOpts.IndexFunctionLocals = false;
 
-  index::indexTopLevelDecls(Ctx, Decls, Collector, IndexOpts);
-  auto Symbols = llvm::make_unique<SymbolSlab>();
-  *Symbols = Collector.takeSymbols();
-  return Symbols;
+  index::indexTopLevelDecls(AST->getASTContext(), AST->getTopLevelDecls(),
+                            Collector, IndexOpts);
+  return Collector.takeSymbols();
 }
 
-} // namespace
-
 void FileSymbols::update(PathRef Path, std::unique_ptr<SymbolSlab> Slab) {
   std::lock_guard<std::mutex> Lock(Mutex);
   if (!Slab)
@@ -79,8 +73,8 @@ void FileIndex::update(PathRef Path, Par
   if (!AST) {
     FSymbols.update(Path, nullptr);
   } else {
-    auto Slab = indexAST(AST->getASTContext(), AST->getPreprocessorPtr(),
-                         AST->getTopLevelDecls());
+    auto Slab = llvm::make_unique<SymbolSlab>();
+    *Slab = indexAST(AST);
     FSymbols.update(Path, std::move(Slab));
   }
   auto Symbols = FSymbols.allSymbols();

Modified: clang-tools-extra/trunk/clangd/index/FileIndex.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/index/FileIndex.h?rev=331189&r1=331188&r2=331189&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/index/FileIndex.h (original)
+++ clang-tools-extra/trunk/clangd/index/FileIndex.h Mon Apr 30 08:24:17 2018
@@ -71,6 +71,10 @@ private:
   MemIndex Index;
 };
 
+/// Retrieves namespace and class level symbols in \p AST.
+/// Exposed to assist in unit tests.
+SymbolSlab indexAST(ParsedAST *AST);
+
 } // namespace clangd
 } // namespace clang
 

Modified: clang-tools-extra/trunk/unittests/clangd/XRefsTests.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/XRefsTests.cpp?rev=331189&r1=331188&r2=331189&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/XRefsTests.cpp (original)
+++ clang-tools-extra/trunk/unittests/clangd/XRefsTests.cpp Mon Apr 30 08:24:17 2018
@@ -13,11 +13,14 @@
 #include "SyncAPI.h"
 #include "TestFS.h"
 #include "XRefs.h"
+#include "gmock/gmock.h"
+#include "index/FileIndex.h"
+#include "index/SymbolCollector.h"
 #include "clang/Frontend/CompilerInvocation.h"
 #include "clang/Frontend/PCHContainerOperations.h"
 #include "clang/Frontend/Utils.h"
+#include "clang/Index/IndexingAction.h"
 #include "llvm/Support/Path.h"
-#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
 namespace clang {
@@ -37,17 +40,33 @@ class IgnoreDiagnostics : public Diagnos
 };
 
 // FIXME: this is duplicated with FileIndexTests. Share it.
-ParsedAST build(StringRef Code) {
-  auto CI = createInvocationFromCommandLine(
-      {"clang", "-xc++", testPath("Foo.cpp").c_str()});
-  auto Buf = MemoryBuffer::getMemBuffer(Code);
+ParsedAST build(StringRef MainCode, StringRef HeaderCode = "") {
+  auto HeaderPath = testPath("foo.h");
+  auto MainPath = testPath("foo.cpp");
+  llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> VFS(
+      new vfs::InMemoryFileSystem());
+  VFS->addFile(MainPath, 0, llvm::MemoryBuffer::getMemBuffer(MainCode));
+  VFS->addFile(HeaderPath, 0, llvm::MemoryBuffer::getMemBuffer(HeaderCode));
+  std::vector<const char *> Cmd = {"clang", "-xc++", MainPath.c_str()};
+  if (!HeaderCode.empty()) {
+    std::vector<const char *> args = {"-include", HeaderPath.c_str()};
+    Cmd.insert(Cmd.begin() + 1, args.begin(), args.end());
+  }
+  auto CI = createInvocationFromCommandLine(Cmd);
+
+  auto Buf = MemoryBuffer::getMemBuffer(MainCode);
   auto AST = ParsedAST::Build(std::move(CI), nullptr, std::move(Buf),
-                              std::make_shared<PCHContainerOperations>(),
-                              vfs::getRealFileSystem());
+                              std::make_shared<PCHContainerOperations>(), VFS);
   assert(AST.hasValue());
   return std::move(*AST);
 }
 
+std::unique_ptr<SymbolIndex> buildIndex(StringRef MainCode,
+                                        StringRef HeaderCode) {
+  auto AST = build(MainCode, HeaderCode);
+  return MemIndex::build(indexAST(&AST));
+}
+
 // Extracts ranges from an annotated example, and constructs a matcher for a
 // highlight set. Ranges should be named $read/$write as appropriate.
 Matcher<const std::vector<DocumentHighlight> &>
@@ -106,6 +125,65 @@ TEST(HighlightsTest, All) {
 
 MATCHER_P(RangeIs, R, "") { return arg.range == R; }
 
+TEST(GoToDefinition, WithIndex) {
+  Annotations SymbolHeader(R"cpp(
+        class $forward[[Forward]];
+        class $foo[[Foo]] {};
+
+        void $f1[[f1]]();
+
+        inline void $f2[[f2]]() {}
+      )cpp");
+  Annotations SymbolCpp(R"cpp(
+      class $forward[[forward]] {};
+      void  $f1[[f1]]() {}
+    )cpp");
+
+  auto Index = buildIndex(SymbolCpp.code(), SymbolHeader.code());
+  auto runFindDefinitionsWithIndex = [&Index](const Annotations &Main) {
+    auto AST = build(/*MainCode=*/Main.code(),
+                     /*HeaderCode=*/"");
+    return clangd::findDefinitions(AST, Main.point(), Index.get());
+  };
+
+  Annotations Test(R"cpp(// only declaration in AST.
+        void [[f1]]();
+        int main() {
+          ^f1();
+        }
+      )cpp");
+  EXPECT_THAT(runFindDefinitionsWithIndex(Test),
+              testing::ElementsAreArray(
+                  {RangeIs(SymbolCpp.range("f1")), RangeIs(Test.range())}));
+
+  Test = Annotations(R"cpp(// definition in AST.
+        void [[f1]]() {}
+        int main() {
+          ^f1();
+        }
+      )cpp");
+  EXPECT_THAT(runFindDefinitionsWithIndex(Test),
+              testing::ElementsAreArray(
+                  {RangeIs(Test.range()), RangeIs(SymbolHeader.range("f1"))}));
+
+  Test = Annotations(R"cpp(// forward declaration in AST.
+        class [[Foo]];
+        F^oo* create();
+      )cpp");
+  EXPECT_THAT(runFindDefinitionsWithIndex(Test),
+              testing::ElementsAreArray(
+                  {RangeIs(SymbolHeader.range("foo")), RangeIs(Test.range())}));
+
+  Test = Annotations(R"cpp(// defintion in AST.
+        class [[Forward]] {};
+        F^orward create();
+      )cpp");
+  EXPECT_THAT(runFindDefinitionsWithIndex(Test),
+              testing::ElementsAreArray({
+                  RangeIs(Test.range()), RangeIs(SymbolHeader.range("forward")),
+              }));
+}
+
 TEST(GoToDefinition, All) {
   const char *Tests[] = {
       R"cpp(// Local variable




More information about the cfe-commits mailing list