[PATCH] D75479: [clangd] go-to-def on names in comments etc that are used nearby.

2020-04-22 Thread Sam McCall via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG3f1c2bf1712c: [clangd] go-to-def on names in comments etc 
that are used nearby. (authored by sammccall).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D75479/new/

https://reviews.llvm.org/D75479

Files:
  clang-tools-extra/clangd/SourceCode.cpp
  clang-tools-extra/clangd/SourceCode.h
  clang-tools-extra/clangd/XRefs.cpp
  clang-tools-extra/clangd/XRefs.h
  clang-tools-extra/clangd/unittests/SourceCodeTests.cpp
  clang-tools-extra/clangd/unittests/XRefsTests.cpp

Index: clang-tools-extra/clangd/unittests/XRefsTests.cpp
===
--- clang-tools-extra/clangd/unittests/XRefsTests.cpp
+++ clang-tools-extra/clangd/unittests/XRefsTests.cpp
@@ -685,10 +685,15 @@
 
 auto AST = TU.build();
 auto Index = TU.index();
-auto Results = locateSymbolNamedTextuallyAt(
-AST, Index.get(),
+auto Word = SpelledWord::touching(
 cantFail(sourceLocationInMainFile(AST.getSourceManager(), T.point())),
-testPath(TU.Filename));
+AST.getTokens(), AST.getLangOpts());
+if (!Word) {
+  ADD_FAILURE() << "No word touching point!" << Test;
+  continue;
+}
+auto Results =
+locateSymbolTextually(*Word, AST, Index.get(), testPath(TU.Filename));
 
 if (!WantDecl) {
   EXPECT_THAT(Results, IsEmpty()) << Test;
@@ -788,10 +793,12 @@
   auto TU = TestTU::withCode(T.code());
   auto AST = TU.build();
   auto Index = TU.index();
-  auto Results = locateSymbolNamedTextuallyAt(
-  AST, Index.get(),
+  auto Word = SpelledWord::touching(
   cantFail(sourceLocationInMainFile(AST.getSourceManager(), T.point())),
-  testPath(TU.Filename));
+  AST.getTokens(), AST.getLangOpts());
+  ASSERT_TRUE(Word);
+  auto Results =
+  locateSymbolTextually(*Word, AST, Index.get(), testPath(TU.Filename));
   EXPECT_THAT(Results,
   UnorderedElementsAre(Sym("uniqueMethodName", T.range("FooLoc")),
Sym("uniqueMethodName", T.range("BarLoc";
@@ -985,6 +992,101 @@
   ElementsAre(Sym("foo", FooWithoutHeader.range(;
 }
 
+TEST(LocateSymbol, NearbyTokenSmoke) {
+  auto T = Annotations(R"cpp(
+// prints e^rr and crashes
+void die(const char* [[err]]);
+  )cpp");
+  auto AST = TestTU::withCode(T.code()).build();
+  // We don't pass an index, so can't hit index-based fallback.
+  EXPECT_THAT(locateSymbolAt(AST, T.point()),
+  ElementsAre(Sym("err", T.range(;
+}
+
+TEST(LocateSymbol, NearbyIdentifier) {
+  const char *Tests[] = {
+  R"cpp(
+  // regular identifiers (won't trigger)
+  int hello;
+  int y = he^llo;
+)cpp",
+  R"cpp(
+  // disabled preprocessor sections
+  int [[hello]];
+  #if 0
+  int y = ^hello;
+  #endif
+)cpp",
+  R"cpp(
+  // comments
+  // he^llo, world
+  int [[hello]];
+)cpp",
+  R"cpp(
+  // not triggered by string literals
+  int hello;
+  const char* greeting = "h^ello, world";
+)cpp",
+
+  R"cpp(
+  // can refer to macro invocations
+  #define INT int
+  [[INT]] x;
+  // I^NT
+)cpp",
+
+  R"cpp(
+  // can refer to macro invocations (even if they expand to nothing)
+  #define EMPTY
+  [[EMPTY]] int x;
+  // E^MPTY
+)cpp",
+
+  R"cpp(
+  // prefer nearest occurrence, backwards is worse than forwards
+  int hello;
+  int x = hello;
+  // h^ello
+  int y = [[hello]];
+  int z = hello;
+)cpp",
+
+  R"cpp(
+  // short identifiers find near results
+  int [[hi]];
+  // h^i
+)cpp",
+  R"cpp(
+  // short identifiers don't find far results
+  int hi;
+
+
+
+  // h^i
+)cpp",
+  };
+  for (const char *Test : Tests) {
+Annotations T(Test);
+auto AST = TestTU::withCode(T.code()).build();
+const auto  = AST.getSourceManager();
+llvm::Optional Nearby;
+auto Word =
+SpelledWord::touching(cantFail(sourceLocationInMainFile(SM, T.point())),
+  AST.getTokens(), AST.getLangOpts());
+if (!Word) {
+  ADD_FAILURE() << "No word at point! " << Test;
+  continue;
+}
+if (const auto *Tok = findNearbyIdentifier(*Word, AST.getTokens()))
+  Nearby = halfOpenToRange(SM, CharSourceRange::getCharRange(
+   Tok->location(), Tok->endLocation()));
+if (T.ranges().empty())
+  EXPECT_THAT(Nearby, Eq(llvm::None)) << Test;
+else
+  EXPECT_EQ(Nearby, T.range()) << Test;
+  }
+}
+
 TEST(FindReferences, WithinAST) {
   const char *Tests[] = {
   R"cpp(// Local variable
Index: clang-tools-extra/clangd/unittests/SourceCodeTests.cpp
===
--- clang-tools-extra/clangd/unittests/SourceCodeTests.cpp
+++ 

[PATCH] D75479: [clangd] go-to-def on names in comments etc that are used nearby.

2020-04-22 Thread Sam McCall via Phabricator via cfe-commits
sammccall updated this revision to Diff 259295.
sammccall marked 15 inline comments as done.
sammccall added a comment.

address review comments


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D75479/new/

https://reviews.llvm.org/D75479

Files:
  clang-tools-extra/clangd/SourceCode.cpp
  clang-tools-extra/clangd/SourceCode.h
  clang-tools-extra/clangd/XRefs.cpp
  clang-tools-extra/clangd/XRefs.h
  clang-tools-extra/clangd/unittests/SourceCodeTests.cpp
  clang-tools-extra/clangd/unittests/XRefsTests.cpp

Index: clang-tools-extra/clangd/unittests/XRefsTests.cpp
===
--- clang-tools-extra/clangd/unittests/XRefsTests.cpp
+++ clang-tools-extra/clangd/unittests/XRefsTests.cpp
@@ -685,10 +685,15 @@
 
 auto AST = TU.build();
 auto Index = TU.index();
-auto Results = locateSymbolNamedTextuallyAt(
-AST, Index.get(),
+auto Word = SpelledWord::touching(
 cantFail(sourceLocationInMainFile(AST.getSourceManager(), T.point())),
-testPath(TU.Filename));
+AST.getTokens(), AST.getLangOpts());
+if (!Word) {
+  ADD_FAILURE() << "No word touching point!" << Test;
+  continue;
+}
+auto Results =
+locateSymbolTextually(*Word, AST, Index.get(), testPath(TU.Filename));
 
 if (!WantDecl) {
   EXPECT_THAT(Results, IsEmpty()) << Test;
@@ -788,10 +793,12 @@
   auto TU = TestTU::withCode(T.code());
   auto AST = TU.build();
   auto Index = TU.index();
-  auto Results = locateSymbolNamedTextuallyAt(
-  AST, Index.get(),
+  auto Word = SpelledWord::touching(
   cantFail(sourceLocationInMainFile(AST.getSourceManager(), T.point())),
-  testPath(TU.Filename));
+  AST.getTokens(), AST.getLangOpts());
+  ASSERT_TRUE(Word);
+  auto Results =
+  locateSymbolTextually(*Word, AST, Index.get(), testPath(TU.Filename));
   EXPECT_THAT(Results,
   UnorderedElementsAre(Sym("uniqueMethodName", T.range("FooLoc")),
Sym("uniqueMethodName", T.range("BarLoc";
@@ -985,6 +992,101 @@
   ElementsAre(Sym("foo", FooWithoutHeader.range(;
 }
 
+TEST(LocateSymbol, NearbyTokenSmoke) {
+  auto T = Annotations(R"cpp(
+// prints e^rr and crashes
+void die(const char* [[err]]);
+  )cpp");
+  auto AST = TestTU::withCode(T.code()).build();
+  // We don't pass an index, so can't hit index-based fallback.
+  EXPECT_THAT(locateSymbolAt(AST, T.point()),
+  ElementsAre(Sym("err", T.range(;
+}
+
+TEST(LocateSymbol, NearbyIdentifier) {
+  const char *Tests[] = {
+  R"cpp(
+  // regular identifiers (won't trigger)
+  int hello;
+  int y = he^llo;
+)cpp",
+  R"cpp(
+  // disabled preprocessor sections
+  int [[hello]];
+  #if 0
+  int y = ^hello;
+  #endif
+)cpp",
+  R"cpp(
+  // comments
+  // he^llo, world
+  int [[hello]];
+)cpp",
+  R"cpp(
+  // not triggered by string literals
+  int hello;
+  const char* greeting = "h^ello, world";
+)cpp",
+
+  R"cpp(
+  // can refer to macro invocations
+  #define INT int
+  [[INT]] x;
+  // I^NT
+)cpp",
+
+  R"cpp(
+  // can refer to macro invocations (even if they expand to nothing)
+  #define EMPTY
+  [[EMPTY]] int x;
+  // E^MPTY
+)cpp",
+
+  R"cpp(
+  // prefer nearest occurrence, backwards is worse than forwards
+  int hello;
+  int x = hello;
+  // h^ello
+  int y = [[hello]];
+  int z = hello;
+)cpp",
+
+  R"cpp(
+  // short identifiers find near results
+  int [[hi]];
+  // h^i
+)cpp",
+  R"cpp(
+  // short identifiers don't find far results
+  int hi;
+
+
+
+  // h^i
+)cpp",
+  };
+  for (const char *Test : Tests) {
+Annotations T(Test);
+auto AST = TestTU::withCode(T.code()).build();
+const auto  = AST.getSourceManager();
+llvm::Optional Nearby;
+auto Word =
+SpelledWord::touching(cantFail(sourceLocationInMainFile(SM, T.point())),
+  AST.getTokens(), AST.getLangOpts());
+if (!Word) {
+  ADD_FAILURE() << "No word at point! " << Test;
+  continue;
+}
+if (const auto *Tok = findNearbyIdentifier(*Word, AST.getTokens()))
+  Nearby = halfOpenToRange(SM, CharSourceRange::getCharRange(
+   Tok->location(), Tok->endLocation()));
+if (T.ranges().empty())
+  EXPECT_THAT(Nearby, Eq(llvm::None)) << Test;
+else
+  EXPECT_EQ(Nearby, T.range()) << Test;
+  }
+}
+
 TEST(FindReferences, WithinAST) {
   const char *Tests[] = {
   R"cpp(// Local variable
Index: clang-tools-extra/clangd/unittests/SourceCodeTests.cpp
===
--- clang-tools-extra/clangd/unittests/SourceCodeTests.cpp
+++ clang-tools-extra/clangd/unittests/SourceCodeTests.cpp
@@ -12,6 +12,7 @@
 

[PATCH] D75479: [clangd] go-to-def on names in comments etc that are used nearby.

2020-04-21 Thread Nathan Ridge via Phabricator via cfe-commits
nridge accepted this revision.
nridge added a comment.
This revision is now accepted and ready to land.

Thanks, Sam, this looks great!




Comment at: clang-tools-extra/clangd/SourceCode.cpp:883
 
+static bool isLikelyIdentifier(llvm::StringRef Word, StringRef Before,
+   StringRef After) {

nit: qualify StringRef or not consistently



Comment at: clang-tools-extra/clangd/SourceCode.cpp:891
+return true;
+  // Doxygen tags like \c foo indicate identifiers.
+  // Don't search too far back.

It's interesting to note that clang has a lexer and parser for doxygen comments 
(see e.g. `RawComment::parse()`), so we could conceivably do something more 
structured, but it's probably not worth the effort.



Comment at: clang-tools-extra/clangd/SourceCode.cpp:938
+  TB.expandedTokens(SM.getMacroArgExpandedLocation(T.location()));
+  if (!Expanded.empty() && Expanded.size() == 1 &&
+  Expanded.front().text(SM) == Result.Text)

`Expanded.size() == 1` implies `!Expanded.empty()`



Comment at: clang-tools-extra/clangd/SourceCode.h:244
+  // - Text is identifier-like (e.g. "foo_bar")
+  // - Text is surrounded by quotes (e.g. Foo in "// returns `Foo`")
+  bool LikelyIdentifier = false;

quotes --> backticks?



Comment at: clang-tools-extra/clangd/XRefs.cpp:451
+assert(SM.getFileID(Loc) == File && "spelled token in wrong file?");
+unsigned Line = SM.getLineNumber(File, SM.getFileOffset(Loc));
+if (Line > WordLine)

Is this any different from `getSpellingLineNumber(Loc)`?



Comment at: clang-tools-extra/clangd/unittests/SourceCodeTests.cpp:392
+  EXPECT_TRUE(word("// bar::[[f^oo]] ").LikelyIdentifier);
+  EXPECT_TRUE(word("// [[f^oo]]::bar ").LikelyIdentifier);
+}

Maybe test the initialism thing with `EXPECT_FALSE(word("// [[H^TTP]] 
").LikelyIdentifier);`



Comment at: clang-tools-extra/clangd/unittests/XRefsTests.cpp:1028
+  int [[hello]];
+  const char* greeting = "h^ello, world";
+)cpp",

What's the rationale for supporting string literals for the nearby-ident 
heuristic, but not the index heuristic?



Comment at: clang-tools-extra/clangd/unittests/XRefsTests.cpp:1032
+  R"cpp(
+  // can refer to macro invocations (even if they expand to nothing)
+  #define INT int

(Did you mean to write a test case where the macro invocation expands to 
nothing?)


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D75479/new/

https://reviews.llvm.org/D75479



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D75479: [clangd] go-to-def on names in comments etc that are used nearby.

2020-04-16 Thread Sam McCall via Phabricator via cfe-commits
sammccall updated this revision to Diff 258193.
sammccall added a comment.

address comments


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D75479/new/

https://reviews.llvm.org/D75479

Files:
  clang-tools-extra/clangd/SourceCode.cpp
  clang-tools-extra/clangd/SourceCode.h
  clang-tools-extra/clangd/XRefs.cpp
  clang-tools-extra/clangd/XRefs.h
  clang-tools-extra/clangd/unittests/SourceCodeTests.cpp
  clang-tools-extra/clangd/unittests/XRefsTests.cpp

Index: clang-tools-extra/clangd/unittests/XRefsTests.cpp
===
--- clang-tools-extra/clangd/unittests/XRefsTests.cpp
+++ clang-tools-extra/clangd/unittests/XRefsTests.cpp
@@ -685,10 +685,15 @@
 
 auto AST = TU.build();
 auto Index = TU.index();
-auto Results = locateSymbolNamedTextuallyAt(
-AST, Index.get(),
+auto Word = SpelledWord::touching(
 cantFail(sourceLocationInMainFile(AST.getSourceManager(), T.point())),
-testPath(TU.Filename));
+AST.getTokens(), AST.getLangOpts());
+if (!Word) {
+  ADD_FAILURE() << "No word touching point!" << Test;
+  continue;
+}
+auto Results =
+locateSymbolTextually(*Word, AST, Index.get(), testPath(TU.Filename));
 
 if (!WantDecl) {
   EXPECT_THAT(Results, IsEmpty()) << Test;
@@ -788,10 +793,12 @@
   auto TU = TestTU::withCode(T.code());
   auto AST = TU.build();
   auto Index = TU.index();
-  auto Results = locateSymbolNamedTextuallyAt(
-  AST, Index.get(),
+  auto Word = SpelledWord::touching(
   cantFail(sourceLocationInMainFile(AST.getSourceManager(), T.point())),
-  testPath(TU.Filename));
+  AST.getTokens(), AST.getLangOpts());
+  ASSERT_TRUE(Word);
+  auto Results =
+  locateSymbolTextually(*Word, AST, Index.get(), testPath(TU.Filename));
   EXPECT_THAT(Results,
   UnorderedElementsAre(Sym("uniqueMethodName", T.range("FooLoc")),
Sym("uniqueMethodName", T.range("BarLoc";
@@ -985,6 +992,94 @@
   ElementsAre(Sym("foo", FooWithoutHeader.range(;
 }
 
+TEST(LocateSymbol, NearbyTokenSmoke) {
+  auto T = Annotations(R"cpp(
+// prints e^rr and crashes
+void die(const char* [[err]]);
+  )cpp");
+  auto AST = TestTU::withCode(T.code()).build();
+  // We don't pass an index, so can't hit index-based fallback.
+  EXPECT_THAT(locateSymbolAt(AST, T.point()),
+  ElementsAre(Sym("err", T.range(;
+}
+
+TEST(LocateSymbol, NearbyIdentifier) {
+  const char *Tests[] = {
+  R"cpp(
+  // regular identifiers (won't trigger)
+  int hello;
+  int y = he^llo;
+)cpp",
+  R"cpp(
+  // disabled preprocessor sections
+  int [[hello]];
+  #if 0
+  int y = ^hello;
+  #endif
+)cpp",
+  R"cpp(
+  // comments
+  // he^llo, world
+  int [[hello]];
+)cpp",
+  R"cpp(
+  // string literals
+  int [[hello]];
+  const char* greeting = "h^ello, world";
+)cpp",
+
+  R"cpp(
+  // can refer to macro invocations (even if they expand to nothing)
+  #define INT int
+  [[INT]] x;
+  // I^NT
+)cpp",
+
+  R"cpp(
+  // prefer nearest occurrence, backwards is worse than forwards
+  int hello;
+  int x = hello;
+  // h^ello
+  int y = [[hello]];
+  int z = hello;
+)cpp",
+
+  R"cpp(
+  // short identifiers find near results
+  int [[hi]];
+  // h^i
+)cpp",
+  R"cpp(
+  // short identifiers don't find far results
+  int hi;
+
+
+
+  // h^i
+)cpp",
+  };
+  for (const char *Test : Tests) {
+Annotations T(Test);
+auto AST = TestTU::withCode(T.code()).build();
+const auto  = AST.getSourceManager();
+llvm::Optional Nearby;
+auto Word =
+SpelledWord::touching(cantFail(sourceLocationInMainFile(SM, T.point())),
+  AST.getTokens(), AST.getLangOpts());
+if (!Word) {
+  ADD_FAILURE() << "No word at point! " << Test;
+  continue;
+}
+if (const auto *Tok = findNearbyIdentifier(*Word, AST.getTokens()))
+  Nearby = halfOpenToRange(SM, CharSourceRange::getCharRange(
+   Tok->location(), Tok->endLocation()));
+if (T.ranges().empty())
+  EXPECT_THAT(Nearby, Eq(llvm::None)) << Test;
+else
+  EXPECT_EQ(Nearby, T.range()) << Test;
+  }
+}
+
 TEST(FindReferences, WithinAST) {
   const char *Tests[] = {
   R"cpp(// Local variable
Index: clang-tools-extra/clangd/unittests/SourceCodeTests.cpp
===
--- clang-tools-extra/clangd/unittests/SourceCodeTests.cpp
+++ clang-tools-extra/clangd/unittests/SourceCodeTests.cpp
@@ -12,6 +12,7 @@
 #include "TestTU.h"
 #include "clang/Basic/LangOptions.h"
 #include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/TokenKinds.h"
 #include "clang/Format/Format.h"
 #include 

[PATCH] D75479: [clangd] go-to-def on names in comments etc that are used nearby.

2020-04-16 Thread Sam McCall via Phabricator via cfe-commits
sammccall marked 3 inline comments as done.
sammccall added a comment.

OK, I think this is probably finally good for another round - it's pretty well 
merged with the index-based version, and the common parts are pulled out to 
SourceCode and tested.
I couldn't resist adding support for backtick-identifiers and a few others 
while there...

This doesn't do the lookup using concentric semantic ranges described in the 
bug. I think what it does is probably good enough for now and probably won't 
have time to rebuild that part really soon...


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D75479/new/

https://reviews.llvm.org/D75479



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D75479: [clangd] go-to-def on names in comments etc that are used nearby.

2020-04-16 Thread Sam McCall via Phabricator via cfe-commits
sammccall updated this revision to Diff 258187.
sammccall added a comment.

Add tests for SpelledWord


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D75479/new/

https://reviews.llvm.org/D75479

Files:
  clang-tools-extra/clangd/SourceCode.cpp
  clang-tools-extra/clangd/SourceCode.h
  clang-tools-extra/clangd/XRefs.cpp
  clang-tools-extra/clangd/XRefs.h
  clang-tools-extra/clangd/unittests/SourceCodeTests.cpp
  clang-tools-extra/clangd/unittests/XRefsTests.cpp

Index: clang-tools-extra/clangd/unittests/XRefsTests.cpp
===
--- clang-tools-extra/clangd/unittests/XRefsTests.cpp
+++ clang-tools-extra/clangd/unittests/XRefsTests.cpp
@@ -685,10 +685,15 @@
 
 auto AST = TU.build();
 auto Index = TU.index();
-auto Results = locateSymbolNamedTextuallyAt(
-AST, Index.get(),
+auto Word = SpelledWord::touching(
 cantFail(sourceLocationInMainFile(AST.getSourceManager(), T.point())),
-testPath(TU.Filename));
+AST.getTokens(), AST.getLangOpts());
+if (!Word) {
+  ADD_FAILURE() << "No word touching point!" << Test;
+  continue;
+}
+auto Results =
+locateSymbolTextually(*Word, AST, Index.get(), testPath(TU.Filename));
 
 if (!WantDecl) {
   EXPECT_THAT(Results, IsEmpty()) << Test;
@@ -788,10 +793,12 @@
   auto TU = TestTU::withCode(T.code());
   auto AST = TU.build();
   auto Index = TU.index();
-  auto Results = locateSymbolNamedTextuallyAt(
-  AST, Index.get(),
+  auto Word = SpelledWord::touching(
   cantFail(sourceLocationInMainFile(AST.getSourceManager(), T.point())),
-  testPath(TU.Filename));
+  AST.getTokens(), AST.getLangOpts());
+  ASSERT_TRUE(Word);
+  auto Results =
+  locateSymbolTextually(*Word, AST, Index.get(), testPath(TU.Filename));
   EXPECT_THAT(Results,
   UnorderedElementsAre(Sym("uniqueMethodName", T.range("FooLoc")),
Sym("uniqueMethodName", T.range("BarLoc";
@@ -985,6 +992,94 @@
   ElementsAre(Sym("foo", FooWithoutHeader.range(;
 }
 
+TEST(LocateSymbol, NearbyTokenSmoke) {
+  auto T = Annotations(R"cpp(
+// prints e^rr and crashes
+void die(const char* [[err]]);
+  )cpp");
+  auto AST = TestTU::withCode(T.code()).build();
+  // We don't pass an index, so can't hit index-based fallback.
+  EXPECT_THAT(locateSymbolAt(AST, T.point()),
+  ElementsAre(Sym("err", T.range(;
+}
+
+TEST(LocateSymbol, NearbyIdentifier) {
+  const char *Tests[] = {
+  R"cpp(
+  // regular identifiers (won't trigger)
+  int hello;
+  int y = he^llo;
+)cpp",
+  R"cpp(
+  // disabled preprocessor sections
+  int [[hello]];
+  #if 0
+  int y = ^hello;
+  #endif
+)cpp",
+  R"cpp(
+  // comments
+  // he^llo, world
+  int [[hello]];
+)cpp",
+  R"cpp(
+  // string literals
+  int [[hello]];
+  const char* greeting = "h^ello, world";
+)cpp",
+
+  R"cpp(
+  // can refer to macro invocations (even if they expand to nothing)
+  #define INT int
+  [[INT]] x;
+  // I^NT
+)cpp",
+
+  R"cpp(
+  // prefer nearest occurrence
+  int hello;
+  int x = hello;
+  // h^ello
+  int y = [[hello]];
+  int z = hello;
+)cpp",
+
+  R"cpp(
+  // short identifiers find near results
+  int [[hi]];
+  // h^i
+)cpp",
+  R"cpp(
+  // short identifiers don't find far results
+  int hi;
+
+
+
+  // h^i
+)cpp",
+  };
+  for (const char *Test : Tests) {
+Annotations T(Test);
+auto AST = TestTU::withCode(T.code()).build();
+const auto  = AST.getSourceManager();
+llvm::Optional Nearby;
+auto Word =
+SpelledWord::touching(cantFail(sourceLocationInMainFile(SM, T.point())),
+  AST.getTokens(), AST.getLangOpts());
+if (!Word) {
+  ADD_FAILURE() << "No word at point! " << Test;
+  continue;
+}
+if (const auto *Tok = findNearbyIdentifier(*Word, AST.getTokens()))
+  Nearby = halfOpenToRange(SM, CharSourceRange::getCharRange(
+   Tok->location(), Tok->endLocation()));
+if (T.ranges().empty())
+  EXPECT_THAT(Nearby, Eq(llvm::None)) << Test;
+else
+  EXPECT_THAT(Nearby, T.range()) << Test;
+  }
+}
+
 TEST(FindReferences, WithinAST) {
   const char *Tests[] = {
   R"cpp(// Local variable
Index: clang-tools-extra/clangd/unittests/SourceCodeTests.cpp
===
--- clang-tools-extra/clangd/unittests/SourceCodeTests.cpp
+++ clang-tools-extra/clangd/unittests/SourceCodeTests.cpp
@@ -12,6 +12,7 @@
 #include "TestTU.h"
 #include "clang/Basic/LangOptions.h"
 #include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/TokenKinds.h"
 #include "clang/Format/Format.h"
 #include "llvm/Support/Error.h"
 #include 

[PATCH] D75479: [clangd] go-to-def on names in comments etc that are used nearby.

2020-04-16 Thread Sam McCall via Phabricator via cfe-commits
sammccall updated this revision to Diff 258097.
sammccall added a comment.

Merge with existing heuristic, extracting SpelledWord struct for the analysis.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D75479/new/

https://reviews.llvm.org/D75479

Files:
  clang-tools-extra/clangd/SourceCode.cpp
  clang-tools-extra/clangd/SourceCode.h
  clang-tools-extra/clangd/XRefs.cpp
  clang-tools-extra/clangd/XRefs.h
  clang-tools-extra/clangd/unittests/XRefsTests.cpp

Index: clang-tools-extra/clangd/unittests/XRefsTests.cpp
===
--- clang-tools-extra/clangd/unittests/XRefsTests.cpp
+++ clang-tools-extra/clangd/unittests/XRefsTests.cpp
@@ -685,10 +685,15 @@
 
 auto AST = TU.build();
 auto Index = TU.index();
-auto Results = locateSymbolNamedTextuallyAt(
-AST, Index.get(),
+auto Word = SpelledWord::touching(
 cantFail(sourceLocationInMainFile(AST.getSourceManager(), T.point())),
-testPath(TU.Filename));
+AST.getTokens(), AST.getLangOpts());
+if (!Word) {
+  ADD_FAILURE() << "No word touching point!" << Test;
+  continue;
+}
+auto Results =
+locateSymbolTextually(*Word, AST, Index.get(), testPath(TU.Filename));
 
 if (!WantDecl) {
   EXPECT_THAT(Results, IsEmpty()) << Test;
@@ -788,10 +793,12 @@
   auto TU = TestTU::withCode(T.code());
   auto AST = TU.build();
   auto Index = TU.index();
-  auto Results = locateSymbolNamedTextuallyAt(
-  AST, Index.get(),
+  auto Word = SpelledWord::touching(
   cantFail(sourceLocationInMainFile(AST.getSourceManager(), T.point())),
-  testPath(TU.Filename));
+  AST.getTokens(), AST.getLangOpts());
+  ASSERT_TRUE(Word);
+  auto Results =
+  locateSymbolTextually(*Word, AST, Index.get(), testPath(TU.Filename));
   EXPECT_THAT(Results,
   UnorderedElementsAre(Sym("uniqueMethodName", T.range("FooLoc")),
Sym("uniqueMethodName", T.range("BarLoc";
@@ -985,6 +992,94 @@
   ElementsAre(Sym("foo", FooWithoutHeader.range(;
 }
 
+TEST(LocateSymbol, NearbyTokenSmoke) {
+  auto T = Annotations(R"cpp(
+// prints e^rr and crashes
+void die(const char* [[err]]);
+  )cpp");
+  auto AST = TestTU::withCode(T.code()).build();
+  // We don't pass an index, so can't hit index-based fallback.
+  EXPECT_THAT(locateSymbolAt(AST, T.point()),
+  ElementsAre(Sym("err", T.range(;
+}
+
+TEST(LocateSymbol, NearbyIdentifier) {
+  const char *Tests[] = {
+  R"cpp(
+  // regular identifiers (won't trigger)
+  int hello;
+  int y = he^llo;
+)cpp",
+  R"cpp(
+  // disabled preprocessor sections
+  int [[hello]];
+  #if 0
+  int y = ^hello;
+  #endif
+)cpp",
+  R"cpp(
+  // comments
+  // he^llo, world
+  int [[hello]];
+)cpp",
+  R"cpp(
+  // string literals
+  int [[hello]];
+  const char* greeting = "h^ello, world";
+)cpp",
+
+  R"cpp(
+  // can refer to macro invocations (even if they expand to nothing)
+  #define INT int
+  [[INT]] x;
+  // I^NT
+)cpp",
+
+  R"cpp(
+  // prefer nearest occurrence
+  int hello;
+  int x = hello;
+  // h^ello
+  int y = [[hello]];
+  int z = hello;
+)cpp",
+
+  R"cpp(
+  // short identifiers find near results
+  int [[hi]];
+  // h^i
+)cpp",
+  R"cpp(
+  // short identifiers don't find far results
+  int hi;
+
+
+
+  // h^i
+)cpp",
+  };
+  for (const char *Test : Tests) {
+Annotations T(Test);
+auto AST = TestTU::withCode(T.code()).build();
+const auto  = AST.getSourceManager();
+llvm::Optional Nearby;
+auto Word =
+SpelledWord::touching(cantFail(sourceLocationInMainFile(SM, T.point())),
+  AST.getTokens(), AST.getLangOpts());
+if (!Word) {
+  ADD_FAILURE() << "No word at point! " << Test;
+  continue;
+}
+if (const auto *Tok = findNearbyIdentifier(*Word, AST.getTokens()))
+  Nearby = halfOpenToRange(SM, CharSourceRange::getCharRange(
+   Tok->location(), Tok->endLocation()));
+if (T.ranges().empty())
+  EXPECT_THAT(Nearby, Eq(llvm::None)) << Test;
+else
+  EXPECT_THAT(Nearby, T.range()) << Test;
+  }
+}
+
 TEST(FindReferences, WithinAST) {
   const char *Tests[] = {
   R"cpp(// Local variable
Index: clang-tools-extra/clangd/XRefs.h
===
--- clang-tools-extra/clangd/XRefs.h
+++ clang-tools-extra/clangd/XRefs.h
@@ -16,6 +16,7 @@
 #include "FormattedString.h"
 #include "Path.h"
 #include "Protocol.h"
+#include "SourceCode.h"
 #include "index/Index.h"
 #include "index/SymbolLocation.h"
 #include "clang/AST/Type.h"
@@ -26,6 +27,10 @@
 #include 
 
 namespace clang {
+namespace syntax {
+class Token;
+class TokenBuffer;
+} 

[PATCH] D75479: [clangd] go-to-def on names in comments etc that are used nearby.

2020-04-16 Thread Sam McCall via Phabricator via cfe-commits
sammccall updated this revision to Diff 257989.
sammccall added a comment.

rebase - finally getting back to this


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D75479/new/

https://reviews.llvm.org/D75479

Files:
  clang-tools-extra/clangd/XRefs.cpp
  clang-tools-extra/clangd/XRefs.h
  clang-tools-extra/clangd/unittests/XRefsTests.cpp

Index: clang-tools-extra/clangd/unittests/XRefsTests.cpp
===
--- clang-tools-extra/clangd/unittests/XRefsTests.cpp
+++ clang-tools-extra/clangd/unittests/XRefsTests.cpp
@@ -985,6 +985,88 @@
   ElementsAre(Sym("foo", FooWithoutHeader.range(;
 }
 
+TEST(LocateSymbol, NearbyTokenSmoke) {
+  auto T = Annotations(R"cpp(
+// prints e^rr and crashes
+void die(const char* [[err]]);
+  )cpp");
+  auto AST = TestTU::withCode(T.code()).build();
+  // We don't pass an index, so can't hit index-based fallback.
+  EXPECT_THAT(locateSymbolAt(AST, T.point()),
+  ElementsAre(Sym("err", T.range(;
+}
+
+TEST(LocateSymbol, NearbyIdentifier) {
+  const char *Tests[] = {
+  R"cpp(
+  // regular identifiers (won't trigger)
+  int hello;
+  int y = he^llo;
+)cpp",
+  R"cpp(
+  // disabled preprocessor sections
+  int [[hello]];
+  #if 0
+  int y = ^hello;
+  #endif
+)cpp",
+  R"cpp(
+  // comments
+  // he^llo, world
+  int [[hello]];
+)cpp",
+  R"cpp(
+  // string literals
+  int [[hello]];
+  const char* greeting = "h^ello, world";
+)cpp",
+
+  R"cpp(
+  // can refer to macro invocations (even if they expand to nothing)
+  #define INT int
+  [[INT]] x;
+  // I^NT
+)cpp",
+
+  R"cpp(
+  // prefer nearest occurrence
+  int hello;
+  int x = hello;
+  // h^ello
+  int y = [[hello]];
+  int z = hello;
+)cpp",
+
+  R"cpp(
+  // short identifiers find near results
+  int [[hi]];
+  // h^i
+)cpp",
+  R"cpp(
+  // short identifiers don't find far results
+  int hi;
+
+
+
+  // h^i
+)cpp",
+  };
+  for (const char *Test : Tests) {
+Annotations T(Test);
+auto AST = TestTU::withCode(T.code()).build();
+const auto  = AST.getSourceManager();
+llvm::Optional Nearby;
+if (const auto *Tok = findNearbyIdentifier(
+cantFail(sourceLocationInMainFile(SM, T.point())), AST.getTokens()))
+  Nearby = halfOpenToRange(SM, CharSourceRange::getCharRange(
+   Tok->location(), Tok->endLocation()));
+if (T.ranges().empty())
+  EXPECT_THAT(Nearby, Eq(llvm::None)) << Test;
+else
+  EXPECT_THAT(Nearby, T.range()) << Test;
+  }
+}
+
 TEST(FindReferences, WithinAST) {
   const char *Tests[] = {
   R"cpp(// Local variable
Index: clang-tools-extra/clangd/XRefs.h
===
--- clang-tools-extra/clangd/XRefs.h
+++ clang-tools-extra/clangd/XRefs.h
@@ -26,6 +26,10 @@
 #include 
 
 namespace clang {
+namespace syntax {
+class Token;
+class TokenBuffer;
+} // namespace syntax
 namespace clangd {
 class ParsedAST;
 
@@ -64,6 +68,13 @@
  SourceLocation Loc,
  const std::string );
 
+// If SpellingLoc points at a "word" that does not correspond to an expanded
+// token (e.g. in a comment, a string, or a PP disabled region), then try to
+// find a close occurrence of that word that does.
+// (This is for internal use by locateSymbolAt, and is exposed for testing).
+const syntax::Token *findNearbyIdentifier(SourceLocation SpellingLoc,
+  const syntax::TokenBuffer );
+
 /// Get all document links
 std::vector getDocumentLinks(ParsedAST );
 
Index: clang-tools-extra/clangd/XRefs.cpp
===
--- clang-tools-extra/clangd/XRefs.cpp
+++ clang-tools-extra/clangd/XRefs.cpp
@@ -48,6 +48,7 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/Error.h"
+#include "llvm/Support/MathExtras.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/raw_ostream.h"
 
@@ -315,6 +316,12 @@
   return Result;
 }
 
+bool tokenSpelledAt(SourceLocation SpellingLoc, const syntax::TokenBuffer ) {
+  auto ExpandedTokens = TB.expandedTokens(
+  TB.sourceManager().getMacroArgExpandedLocation(SpellingLoc));
+  return !ExpandedTokens.empty();
+}
+
 llvm::StringRef wordTouching(llvm::StringRef Code, unsigned Offset) {
   unsigned B = Offset, E = Offset;
   while (B > 0 && isIdentifierBody(Code[B - 1]))
@@ -481,6 +488,84 @@
   return Results;
 }
 
+const syntax::Token *findNearbyIdentifier(SourceLocation SpellingLoc,
+  const syntax::TokenBuffer ) {
+  const SourceManager  = TB.sourceManager();
+  auto Pos = SM.getDecomposedLoc(SpellingLoc);
+  llvm::StringRef Code = 

[PATCH] D75479: [clangd] go-to-def on names in comments etc that are used nearby.

2020-03-06 Thread Sam McCall via Phabricator via cfe-commits
sammccall marked an inline comment as done.
sammccall added a comment.

> I'll make a proposal on https://github.com/clangd/clangd/issues/241 for a bit 
> more visibility.

OK, I wrote a bunch of stuff there (twice, laptop crashed, grr...)

I'll put this briefly on hold to see what you/others think there - e.g. the 
real-identifier bit is best decided with the final plan in mind.




Comment at: clang-tools-extra/clangd/unittests/XRefsTests.cpp:871
+TEST(LocateSymbol, NearbyIdentifier) {
+  const char *Tests[] = {
+R"cpp(

nridge wrote:
> Tangentially related, but what do you think about the issue I raised in [this 
> mailing list 
> thread](https://lists.llvm.org/pipermail/clangd-dev/2019-November/000579.html)
>  about testcase style?
Posted a reply on the thread. TL;DR:
 - I hate that too
 - the style brings several benefits
 - I don't know what would be better that's available
 - but we could probably build something


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D75479/new/

https://reviews.llvm.org/D75479



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D75479: [clangd] go-to-def on names in comments etc that are used nearby.

2020-03-06 Thread Sam McCall via Phabricator via cfe-commits
sammccall added a comment.

In D75479#1908774 , @nridge wrote:

> This method only returns `Bar::uniqueMethodName()` because it's closer in 
> terms of distance


Yeah, missing data is definitely bad:

- not finding results makes the feature feel flaky or unreliable
- finding *partial* results can be misleading and makes the feature 
untrustworthy

> Is that perhaps a reason to adjust the order in which we try the approaches 
> (i.e. use this one as a fallback if index lookup fails)?

Well, it's even easier to construct examples where the index approach is 
missing some data (most symbols are not indexable) or gives wrong results (name 
collisions are common) which is probably worse.
So I'm not convinced by this that the index is more accurate.

> Or should we try to allow multiple results with this approach as well?

It's an interesting idea, but two immediate risks:

- it's clearly worse for things like comment navigation local variables
- it helps your example only quite narrowly: all the targets need to be in the 
current file

We have several building blocks and various ways for how to put them together.
I think we should go back to the use cases (e.g. dependent code) and work out 
how each is best served, my intuition is that pretty different. Discussing how 
the building blocks should relate to each other in the general case may run in 
circles a bit :-) I'll make a proposal on 
https://github.com/clangd/clangd/issues/241 for a bit more visibility.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D75479/new/

https://reviews.llvm.org/D75479



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D75479: [clangd] go-to-def on names in comments etc that are used nearby.

2020-03-05 Thread Nathan Ridge via Phabricator via cfe-commits
nridge added a comment.

Here's a test case that gives a less desirable result with this approach than 
D72874 :

  struct Foo {
void uniqueMethodName();
  };
  struct Bar {
void uniqueMethodName();
  };
  
  // Will call u^niqueMethodName() on t.
  template 
  void f(T t);

This method only returns `Bar::uniqueMethodName()` because it's closer in terms 
of distance, whereas D72874  returns both 
methods.

Is that perhaps a reason to adjust the order in which we try the approaches 
(i.e. use this one as a fallback if index lookup fails)? Or should we try to 
allow multiple results with this approach as well?


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D75479/new/

https://reviews.llvm.org/D75479



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D75479: [clangd] go-to-def on names in comments etc that are used nearby.

2020-03-03 Thread Nathan Ridge via Phabricator via cfe-commits
nridge added a comment.

Thanks for putting this together, this is really neat and will indeed 
complement D72874  nicely!

I have one high-level comment about the interface, the rest are nits.




Comment at: clang-tools-extra/clangd/XRefs.cpp:337
+  const SourceManager  = TB.sourceManager();
+  auto Pos = SM.getDecomposedLoc(SpellingLoc);
+  llvm::StringRef Code = SM.getBufferData(Pos.first);

nit: I think separate names for the `FileID` and the position would read better



Comment at: clang-tools-extra/clangd/XRefs.cpp:369
+  auto Consider = [&](const syntax::Token ) {
+if(!(Tok.kind() == tok::identifier && Tok.text(SM) == Word))
+  return false;

nit: space after `if`



Comment at: clang-tools-extra/clangd/XRefs.h:56
 
+// If SpellingLoc points at a "word" that does not correspond to an expanded
+// token (e.g. in a comment, a string, or a PP disabled region), then try to

Do we want to bake this condition into the interface of this function, or would 
it be more appropriate to just tell the caller whether it's a real identifier 
token or not?

In particular, given our [plan for handling some dependent 
cases](https://reviews.llvm.org/D72874#1901722), the caller may want to do 
something along the lines of `if (notRealIdentifier || isDependent) { /* use 
the textual heuristic */ }`.



Comment at: clang-tools-extra/clangd/unittests/XRefsTests.cpp:871
+TEST(LocateSymbol, NearbyIdentifier) {
+  const char *Tests[] = {
+R"cpp(

Tangentially related, but what do you think about the issue I raised in [this 
mailing list 
thread](https://lists.llvm.org/pipermail/clangd-dev/2019-November/000579.html) 
about testcase style?



Comment at: clang-tools-extra/clangd/unittests/XRefsTests.cpp:903
+R"cpp(
+  // prefer nearest occurrence
+  int hello;

// (taking into account the penalty for going backwards)



Comment at: clang-tools-extra/clangd/unittests/XRefsTests.cpp:930
+llvm::Optional Nearby;
+if (const auto*Tok = findNearbyIdentifier(
+cantFail(sourceLocationInMainFile(SM, T.point())), AST.getTokens()))

nit: space after `*`



Comment at: clang-tools-extra/clangd/unittests/XRefsTests.cpp:937
+else
+  EXPECT_THAT(Nearby, T.range()) << Test;
+  }

`EXPECT_EQ`?


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D75479/new/

https://reviews.llvm.org/D75479



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D75479: [clangd] go-to-def on names in comments etc that are used nearby.

2020-03-02 Thread Sam McCall via Phabricator via cfe-commits
sammccall created this revision.
sammccall added a reviewer: nridge.
Herald added subscribers: cfe-commits, usaxena95, kadircet, arphaman, jkorous, 
MaskRay, ilya-biryukov.
Herald added a project: clang.

This is intended as a companion to (and is inspired by) D72874 
 which attempts to
resolve these cases using the index.
The intent is we'd try this strategy after the AST-based approach but before the
index-based (I think local usages would be more reliable than index matches).


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D75479

Files:
  clang-tools-extra/clangd/XRefs.cpp
  clang-tools-extra/clangd/XRefs.h
  clang-tools-extra/clangd/unittests/XRefsTests.cpp

Index: clang-tools-extra/clangd/unittests/XRefsTests.cpp
===
--- clang-tools-extra/clangd/unittests/XRefsTests.cpp
+++ clang-tools-extra/clangd/unittests/XRefsTests.cpp
@@ -38,6 +38,7 @@
 namespace {
 
 using ::testing::ElementsAre;
+using ::testing::Eq;
 using ::testing::IsEmpty;
 using ::testing::Matcher;
 using ::testing::UnorderedElementsAreArray;
@@ -342,7 +343,7 @@
   R"cpp(// Symbol concatenated inside macro (not supported)
int *pi;
#define POINTER(X) p ## X;
-   int i = *POINTER(^i);
+   int x = *POINTER(^i);
   )cpp",
 
   R"cpp(// Forward class declaration
@@ -855,6 +856,88 @@
   ElementsAre(Sym("foo", FooWithoutHeader.range(;
 }
 
+TEST(LocateSymbol, NearbyTokenSmoke) {
+  auto T = Annotations(R"cpp(
+// prints e^rr and crashes
+void die(const char* [[err]]);
+  )cpp");
+  auto AST = TestTU::withCode(T.code()).build();
+  // We don't pass an index, so can't hit index-based fallback.
+  EXPECT_THAT(locateSymbolAt(AST, T.point()),
+  ElementsAre(Sym("err", T.range(;
+}
+
+TEST(LocateSymbol, NearbyIdentifier) {
+  const char *Tests[] = {
+R"cpp(
+  // regular identifiers (won't trigger)
+  int hello;
+  int y = he^llo;
+)cpp",
+R"cpp(
+  // disabled preprocessor sections
+  int [[hello]];
+  #if 0
+  int y = ^hello;
+  #endif
+)cpp",
+R"cpp(
+  // comments
+  // he^llo, world
+  int [[hello]];
+)cpp",
+R"cpp(
+  // string literals
+  int [[hello]];
+  const char* greeting = "h^ello, world";
+)cpp",
+
+R"cpp(
+  // can refer to macro invocations (even if they expand to nothing)
+  #define INT int
+  [[INT]] x;
+  // I^NT
+)cpp",
+
+R"cpp(
+  // prefer nearest occurrence
+  int hello;
+  int x = hello;
+  // h^ello
+  int y = [[hello]];
+  int z = hello;
+)cpp",
+
+R"cpp(
+  // short identifiers find near results
+  int [[hi]];
+  // h^i
+)cpp",
+R"cpp(
+  // short identifiers don't find far results
+  int hi;
+
+
+
+  // h^i
+)cpp",
+  };
+  for (const char* Test : Tests) {
+Annotations T(Test);
+auto AST = TestTU::withCode(T.code()).build();
+const auto  = AST.getSourceManager();
+llvm::Optional Nearby;
+if (const auto*Tok = findNearbyIdentifier(
+cantFail(sourceLocationInMainFile(SM, T.point())), AST.getTokens()))
+  Nearby = halfOpenToRange(SM, CharSourceRange::getCharRange(
+   Tok->location(), Tok->endLocation()));
+if (T.ranges().empty())
+  EXPECT_THAT(Nearby, Eq(llvm::None)) << Test;
+else
+  EXPECT_THAT(Nearby, T.range()) << Test;
+  }
+}
+
 TEST(FindReferences, WithinAST) {
   const char *Tests[] = {
   R"cpp(// Local variable
Index: clang-tools-extra/clangd/XRefs.h
===
--- clang-tools-extra/clangd/XRefs.h
+++ clang-tools-extra/clangd/XRefs.h
@@ -26,6 +26,10 @@
 #include 
 
 namespace clang {
+namespace syntax {
+class Token;
+class TokenBuffer;
+} // namespace syntax
 namespace clangd {
 class ParsedAST;
 
@@ -49,6 +53,13 @@
 std::vector locateSymbolAt(ParsedAST , Position Pos,
   const SymbolIndex *Index = nullptr);
 
+// If SpellingLoc points at a "word" that does not correspond to an expanded
+// token (e.g. in a comment, a string, or a PP disabled region), then try to
+// find a close occurrence of that word that does.
+// (This is for internal use by locateSymbolAt, and is exposed for testing).
+const syntax::Token *findNearbyIdentifier(SourceLocation SpellingLoc,
+  const syntax::TokenBuffer );
+
 /// Get all document links
 std::vector getDocumentLinks(ParsedAST );
 
Index: clang-tools-extra/clangd/XRefs.cpp
===
--- clang-tools-extra/clangd/XRefs.cpp
+++ clang-tools-extra/clangd/XRefs.cpp
@@ -28,6 +28,7 @@
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/Type.h"
+#include "clang/Basic/CharInfo.h"
 #include "clang/Basic/LLVM.h"