hokein updated this revision to Diff 161927.
hokein added a comment.
Herald added a subscriber: kadircet.
Update the patch based on our offline discussion
- only one single clang intefaces implementation, and move finding references
to current symbol collector;
- store references in SymbolSlab;
Repository:
rCTE Clang Tools Extra
https://reviews.llvm.org/D50385
Files:
clangd/index/FileIndex.cpp
clangd/index/Index.cpp
clangd/index/Index.h
clangd/index/SymbolCollector.cpp
clangd/index/SymbolCollector.h
unittests/clangd/SymbolCollectorTests.cpp
Index: unittests/clangd/SymbolCollectorTests.cpp
===================================================================
--- unittests/clangd/SymbolCollectorTests.cpp
+++ unittests/clangd/SymbolCollectorTests.cpp
@@ -74,6 +74,14 @@
MATCHER_P(ForCodeCompletion, IsIndexedForCodeCompletion, "") {
return arg.IsIndexedForCodeCompletion == IsIndexedForCodeCompletion;
}
+MATCHER(OccurrenceRange, "") {
+ const clang::clangd::SymbolOccurrence &Pos = testing::get<0>(arg);
+ const clang::clangd::Range &Range = testing::get<1>(arg);
+ return std::tie(Pos.Location.Start.Line, Pos.Location.Start.Column,
+ Pos.Location.End.Line, Pos.Location.End.Column) ==
+ std::tie(Range.start.line, Range.start.character, Range.end.line,
+ Range.end.character);
+}
namespace clang {
namespace clangd {
@@ -95,7 +103,7 @@
assert(AST.hasValue());
return SymbolCollector::shouldCollectSymbol(
Qualified ? findDecl(*AST, Name) : findAnyDecl(*AST, Name),
- AST->getASTContext(), SymbolCollector::Options());
+ AST->getASTContext());
}
protected:
@@ -162,8 +170,10 @@
class SymbolIndexActionFactory : public tooling::FrontendActionFactory {
public:
SymbolIndexActionFactory(SymbolCollector::Options COpts,
- CommentHandler *PragmaHandler)
- : COpts(std::move(COpts)), PragmaHandler(PragmaHandler) {}
+ CommentHandler *PragmaHandler,
+ index::IndexingOptions IndexOpts)
+ : COpts(std::move(COpts)), PragmaHandler(PragmaHandler),
+ IndexOpts(IndexOpts) {}
clang::FrontendAction *create() override {
class WrappedIndexAction : public WrapperFrontendAction {
@@ -186,18 +196,16 @@
index::IndexingOptions IndexOpts;
CommentHandler *PragmaHandler;
};
- index::IndexingOptions IndexOpts;
- IndexOpts.SystemSymbolFilter =
- index::IndexingOptions::SystemSymbolFilterKind::All;
- IndexOpts.IndexFunctionLocals = false;
Collector = std::make_shared<SymbolCollector>(COpts);
return new WrappedIndexAction(Collector, std::move(IndexOpts),
PragmaHandler);
}
std::shared_ptr<SymbolCollector> Collector;
SymbolCollector::Options COpts;
CommentHandler *PragmaHandler;
+
+ index::IndexingOptions IndexOpts;
};
class SymbolCollectorTest : public ::testing::Test {
@@ -210,13 +218,52 @@
TestFileURI = URI::createFile(TestFileName).toString();
}
+ bool collectSymbols(StringRef HeaderCode, StringRef MainCode,
+ const std::vector<std::string> &ExtraArgs = {}) {
+ index::IndexingOptions IndexOpts;
+ IndexOpts.SystemSymbolFilter =
+ index::IndexingOptions::SystemSymbolFilterKind::All;
+ IndexOpts.IndexFunctionLocals = false;
+ CollectorOpts.SymOpts = &CollectSymOpts;
+ CollectorOpts.OccurrenceOpts = nullptr;
+ return runSymbolCollector(HeaderCode, MainCode, CollectorOpts, IndexOpts,
+ ExtraArgs);
+ }
+
+ bool collectOccurrences(StringRef HeaderCode, StringRef MainCode,
+ const std::vector<std::string> &ExtraArgs = {}) {
+ index::IndexingOptions IndexOpts;
+ IndexOpts.SystemSymbolFilter =
+ index::IndexingOptions::SystemSymbolFilterKind::All;
+ IndexOpts.IndexFunctionLocals = true;
+ CollectorOpts.SymOpts = nullptr;
+ CollectorOpts.OccurrenceOpts = &CollectOccurrenceOpts;
+ return runSymbolCollector(HeaderCode, MainCode, CollectorOpts, IndexOpts,
+ ExtraArgs);
+ }
+
+protected:
+ llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem;
+ std::string TestHeaderName;
+ std::string TestHeaderURI;
+ std::string TestFileName;
+ std::string TestFileURI;
+ SymbolSlab Symbols;
+ SymbolCollector::Options CollectorOpts;
+ SymbolCollector::Options::CollectSymbolOptions CollectSymOpts;
+ SymbolCollector::Options::CollectOccurrenceOptions CollectOccurrenceOpts;
+ std::unique_ptr<CommentHandler> PragmaHandler;
+
+private:
bool runSymbolCollector(StringRef HeaderCode, StringRef MainCode,
+ SymbolCollector::Options Opts,
+ index::IndexingOptions IndexOpts,
const std::vector<std::string> &ExtraArgs = {}) {
llvm::IntrusiveRefCntPtr<FileManager> Files(
new FileManager(FileSystemOptions(), InMemoryFileSystem));
auto Factory = llvm::make_unique<SymbolIndexActionFactory>(
- CollectorOpts, PragmaHandler.get());
+ Opts, PragmaHandler.get(), std::move(IndexOpts));
std::vector<std::string> Args = {
"symbol_collector", "-fsyntax-only", "-xc++",
@@ -239,16 +286,6 @@
Symbols = Factory->Collector->takeSymbols();
return true;
}
-
-protected:
- llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem;
- std::string TestHeaderName;
- std::string TestHeaderURI;
- std::string TestFileName;
- std::string TestFileURI;
- SymbolSlab Symbols;
- SymbolCollector::Options CollectorOpts;
- std::unique_ptr<CommentHandler> PragmaHandler;
};
TEST_F(SymbolCollectorTest, CollectSymbols) {
@@ -300,7 +337,7 @@
using bar::v2;
} // namespace foo
)";
- runSymbolCollector(Header, /*Main=*/"");
+ collectSymbols(Header, /*Main=*/"");
EXPECT_THAT(Symbols,
UnorderedElementsAreArray(
{AllOf(QName("Foo"), ForCodeCompletion(true)),
@@ -334,7 +371,7 @@
extern template struct Tmpl<float>;
template struct Tmpl<double>;
)");
- runSymbolCollector(Header.code(), /*Main=*/"");
+ collectSymbols(Header.code(), /*Main=*/"");
EXPECT_THAT(Symbols,
UnorderedElementsAreArray(
{AllOf(QName("Tmpl"), DeclRange(Header.range())),
@@ -369,7 +406,7 @@
@end
)";
TestFileName = "test.m";
- runSymbolCollector(Header, /*Main=*/"", {"-fblocks", "-xobjective-c++"});
+ collectSymbols(Header, /*Main=*/"", {"-fblocks", "-xobjective-c++"});
EXPECT_THAT(Symbols,
UnorderedElementsAre(
QName("Person"), QName("Person::someMethodName:lastName:"),
@@ -398,7 +435,7 @@
// Declared/defined in main only.
int Y;
)cpp");
- runSymbolCollector(Header.code(), Main.code());
+ collectSymbols(Header.code(), Main.code());
EXPECT_THAT(
Symbols,
UnorderedElementsAre(
@@ -431,17 +468,18 @@
class Y{}; // definition doesn't count as a reference
GLOBAL_Z(z); // Not a reference to Z, we don't spell the type.
)";
- CollectorOpts.CountReferences = true;
- runSymbolCollector(Header, Main);
+
+ CollectSymOpts.CountReferences = true;
+ collectSymbols(Header, Main);
EXPECT_THAT(Symbols,
UnorderedElementsAre(AllOf(QName("W"), Refs(1)),
AllOf(QName("X"), Refs(1)),
AllOf(QName("Y"), Refs(0)),
AllOf(QName("Z"), Refs(0)), QName("y")));
}
TEST_F(SymbolCollectorTest, SymbolRelativeNoFallback) {
- runSymbolCollector("class Foo {};", /*Main=*/"");
+ collectSymbols("class Foo {};", /*Main=*/"");
EXPECT_THAT(Symbols, UnorderedElementsAre(
AllOf(QName("Foo"), DeclURI(TestHeaderURI))));
}
@@ -451,7 +489,7 @@
TestFileName = "x.cpp";
TestHeaderURI = URI::createFile(testPath(TestHeaderName)).toString();
CollectorOpts.FallbackDir = testRoot();
- runSymbolCollector("class Foo {};", /*Main=*/"");
+ collectSymbols("class Foo {};", /*Main=*/"");
EXPECT_THAT(Symbols,
UnorderedElementsAre(AllOf(QName("Foo"), DeclURI(TestHeaderURI))));
}
@@ -461,22 +499,22 @@
CollectorOpts.URISchemes.insert(CollectorOpts.URISchemes.begin(), "unittest");
TestHeaderName = testPath("x.h");
TestFileName = testPath("x.cpp");
- runSymbolCollector("class Foo {};", /*Main=*/"");
+ collectSymbols("class Foo {};", /*Main=*/"");
EXPECT_THAT(Symbols, UnorderedElementsAre(
AllOf(QName("Foo"), DeclURI("unittest:///x.h"))));
}
TEST_F(SymbolCollectorTest, InvalidURIScheme) {
// Use test URI scheme from URITests.cpp
CollectorOpts.URISchemes = {"invalid"};
- runSymbolCollector("class Foo {};", /*Main=*/"");
+ collectSymbols("class Foo {};", /*Main=*/"");
EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("Foo"), DeclURI(""))));
}
TEST_F(SymbolCollectorTest, FallbackToFileURI) {
// Use test URI scheme from URITests.cpp
CollectorOpts.URISchemes = {"invalid", "file"};
- runSymbolCollector("class Foo {};", /*Main=*/"");
+ collectSymbols("class Foo {};", /*Main=*/"");
EXPECT_THAT(Symbols, UnorderedElementsAre(
AllOf(QName("Foo"), DeclURI(TestHeaderURI))));
}
@@ -498,7 +536,7 @@
};
}
)";
- runSymbolCollector(Header, /*Main=*/"");
+ collectSymbols(Header, /*Main=*/"");
EXPECT_THAT(Symbols,
UnorderedElementsAre(
AllOf(QName("Red"), ForCodeCompletion(true)),
@@ -516,7 +554,7 @@
int a;
} Foo;
)";
- runSymbolCollector(Header, /*Main=*/"");
+ collectSymbols(Header, /*Main=*/"");
EXPECT_THAT(Symbols, UnorderedElementsAre(QName("Foo"),
QName("(anonymous struct)::a")));
}
@@ -535,7 +573,7 @@
FF2();
)");
- runSymbolCollector(Header.code(), /*Main=*/"");
+ collectSymbols(Header.code(), /*Main=*/"");
EXPECT_THAT(
Symbols,
UnorderedElementsAre(
@@ -552,8 +590,8 @@
#endif
)");
- runSymbolCollector(Header.code(), /*Main=*/"",
- /*ExtraArgs=*/{"-DNAME=name"});
+ collectSymbols(Header.code(), /*Main=*/"",
+ /*ExtraArgs=*/{"-DNAME=name"});
EXPECT_THAT(Symbols,
UnorderedElementsAre(AllOf(
QName("name"),
@@ -574,7 +612,7 @@
void main_f() {} // ignore
void f1() {}
)";
- runSymbolCollector(Header, Main);
+ collectSymbols(Header, Main);
EXPECT_THAT(Symbols,
UnorderedElementsAre(QName("Foo"), QName("f1"), QName("f2")));
}
@@ -593,7 +631,7 @@
void Foo::g() {}
void Foo::ssf() {}
)";
- runSymbolCollector(Header, Main);
+ collectSymbols(Header, Main);
EXPECT_THAT(Symbols,
UnorderedElementsAre(QName("Foo"), QName("Foo::f"),
QName("Foo::g"), QName("Foo::sf"),
@@ -609,7 +647,7 @@
}
}
)";
- runSymbolCollector(Header, /*Main=*/"");
+ collectSymbols(Header, /*Main=*/"");
EXPECT_THAT(Symbols,
UnorderedElementsAre(QName("na"), QName("na::nb"),
QName("na::Foo"), QName("na::nb::Bar")));
@@ -622,7 +660,7 @@
extern "C" { class Bar {}; }
}
)";
- runSymbolCollector(Header, /*Main=*/"");
+ collectSymbols(Header, /*Main=*/"");
EXPECT_THAT(Symbols, UnorderedElementsAre(QName("na"), QName("Foo"),
QName("na::Bar")));
}
@@ -641,7 +679,7 @@
}
}
)";
- runSymbolCollector(Header, /*Main=*/"");
+ collectSymbols(Header, /*Main=*/"");
EXPECT_THAT(Symbols,
UnorderedElementsAre(QName("na"), QName("na::nb"),
QName("na::Foo"), QName("na::Bar")));
@@ -654,7 +692,7 @@
int ff(int x, double y) { return 0; }
}
)";
- runSymbolCollector(Header, /*Main=*/"");
+ collectSymbols(Header, /*Main=*/"");
EXPECT_THAT(
Symbols,
UnorderedElementsAre(
@@ -669,7 +707,7 @@
int ff(int x, double y) { return 0; }
}
)";
- runSymbolCollector(Header, /*Main=*/"");
+ collectSymbols(Header, /*Main=*/"");
EXPECT_THAT(Symbols,
UnorderedElementsAre(
QName("nx"),
@@ -749,35 +787,35 @@
}
TEST_F(SymbolCollectorTest, IncludeHeaderSameAsFileURI) {
- CollectorOpts.CollectIncludePath = true;
- runSymbolCollector("class Foo {};", /*Main=*/"");
+ CollectSymOpts.CollectIncludePath = true;
+ collectSymbols("class Foo {};", /*Main=*/"");
EXPECT_THAT(Symbols,
UnorderedElementsAre(AllOf(QName("Foo"), DeclURI(TestHeaderURI),
IncludeHeader(TestHeaderURI))));
}
#ifndef _WIN32
TEST_F(SymbolCollectorTest, CanonicalSTLHeader) {
- CollectorOpts.CollectIncludePath = true;
+ CollectSymOpts.CollectIncludePath = true;
CanonicalIncludes Includes;
addSystemHeadersMapping(&Includes);
- CollectorOpts.Includes = &Includes;
+ CollectSymOpts.Includes = &Includes;
// bits/basic_string.h$ should be mapped to <string>
TestHeaderName = "/nasty/bits/basic_string.h";
TestFileName = "/nasty/bits/basic_string.cpp";
TestHeaderURI = URI::createFile(TestHeaderName).toString();
- runSymbolCollector("class string {};", /*Main=*/"");
+ collectSymbols("class string {};", /*Main=*/"");
EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("string"),
DeclURI(TestHeaderURI),
IncludeHeader("<string>"))));
}
#endif
TEST_F(SymbolCollectorTest, STLiosfwd) {
- CollectorOpts.CollectIncludePath = true;
+ CollectSymOpts.CollectIncludePath = true;
CanonicalIncludes Includes;
addSystemHeadersMapping(&Includes);
- CollectorOpts.Includes = &Includes;
+ CollectSymOpts.Includes = &Includes;
// Symbols from <iosfwd> should be mapped individually.
TestHeaderName = testPath("iosfwd");
TestFileName = testPath("iosfwd.cpp");
@@ -789,7 +827,7 @@
class filebuf {};
} // namespace std
)";
- runSymbolCollector(Header, /*Main=*/"");
+ collectSymbols(Header, /*Main=*/"");
EXPECT_THAT(Symbols,
UnorderedElementsAre(
QName("std"),
@@ -800,101 +838,101 @@
}
TEST_F(SymbolCollectorTest, IWYUPragma) {
- CollectorOpts.CollectIncludePath = true;
+ CollectSymOpts.CollectIncludePath = true;
CanonicalIncludes Includes;
PragmaHandler = collectIWYUHeaderMaps(&Includes);
- CollectorOpts.Includes = &Includes;
+ CollectSymOpts.Includes = &Includes;
const std::string Header = R"(
// IWYU pragma: private, include the/good/header.h
class Foo {};
)";
- runSymbolCollector(Header, /*Main=*/"");
+ collectSymbols(Header, /*Main=*/"");
EXPECT_THAT(Symbols, UnorderedElementsAre(
AllOf(QName("Foo"), DeclURI(TestHeaderURI),
IncludeHeader("\"the/good/header.h\""))));
}
TEST_F(SymbolCollectorTest, IWYUPragmaWithDoubleQuotes) {
- CollectorOpts.CollectIncludePath = true;
+ CollectSymOpts.CollectIncludePath = true;
CanonicalIncludes Includes;
PragmaHandler = collectIWYUHeaderMaps(&Includes);
- CollectorOpts.Includes = &Includes;
+ CollectSymOpts.Includes = &Includes;
const std::string Header = R"(
// IWYU pragma: private, include "the/good/header.h"
class Foo {};
)";
- runSymbolCollector(Header, /*Main=*/"");
+ collectSymbols(Header, /*Main=*/"");
EXPECT_THAT(Symbols, UnorderedElementsAre(
AllOf(QName("Foo"), DeclURI(TestHeaderURI),
IncludeHeader("\"the/good/header.h\""))));
}
TEST_F(SymbolCollectorTest, SkipIncFileWhenCanonicalizeHeaders) {
- CollectorOpts.CollectIncludePath = true;
+ CollectSymOpts.CollectIncludePath = true;
CanonicalIncludes Includes;
Includes.addMapping(TestHeaderName, "<canonical>");
- CollectorOpts.Includes = &Includes;
+ CollectSymOpts.Includes = &Includes;
auto IncFile = testPath("test.inc");
auto IncURI = URI::createFile(IncFile).toString();
InMemoryFileSystem->addFile(IncFile, 0,
llvm::MemoryBuffer::getMemBuffer("class X {};"));
- runSymbolCollector("#include \"test.inc\"\nclass Y {};", /*Main=*/"",
- /*ExtraArgs=*/{"-I", testRoot()});
+ collectSymbols("#include \"test.inc\"\nclass Y {};", /*Main=*/"",
+ /*ExtraArgs=*/{"-I", testRoot()});
EXPECT_THAT(Symbols,
UnorderedElementsAre(AllOf(QName("X"), DeclURI(IncURI),
IncludeHeader("<canonical>")),
AllOf(QName("Y"), DeclURI(TestHeaderURI),
IncludeHeader("<canonical>"))));
}
TEST_F(SymbolCollectorTest, MainFileIsHeaderWhenSkipIncFile) {
- CollectorOpts.CollectIncludePath = true;
+ CollectSymOpts.CollectIncludePath = true;
CanonicalIncludes Includes;
- CollectorOpts.Includes = &Includes;
+ CollectSymOpts.Includes = &Includes;
TestFileName = testPath("main.h");
TestFileURI = URI::createFile(TestFileName).toString();
auto IncFile = testPath("test.inc");
auto IncURI = URI::createFile(IncFile).toString();
InMemoryFileSystem->addFile(IncFile, 0,
llvm::MemoryBuffer::getMemBuffer("class X {};"));
- runSymbolCollector("", /*Main=*/"#include \"test.inc\"",
- /*ExtraArgs=*/{"-I", testRoot()});
+ collectSymbols("", /*Main=*/"#include \"test.inc\"",
+ /*ExtraArgs=*/{"-I", testRoot()});
EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), DeclURI(IncURI),
IncludeHeader(TestFileURI))));
}
TEST_F(SymbolCollectorTest, MainFileIsHeaderWithoutExtensionWhenSkipIncFile) {
- CollectorOpts.CollectIncludePath = true;
+ CollectSymOpts.CollectIncludePath = true;
CanonicalIncludes Includes;
- CollectorOpts.Includes = &Includes;
+ CollectSymOpts.Includes = &Includes;
TestFileName = testPath("no_ext_main");
TestFileURI = URI::createFile(TestFileName).toString();
auto IncFile = testPath("test.inc");
auto IncURI = URI::createFile(IncFile).toString();
InMemoryFileSystem->addFile(IncFile, 0,
llvm::MemoryBuffer::getMemBuffer("class X {};"));
- runSymbolCollector("", /*Main=*/"#include \"test.inc\"",
- /*ExtraArgs=*/{"-I", testRoot()});
+ collectSymbols("", /*Main=*/"#include \"test.inc\"",
+ /*ExtraArgs=*/{"-I", testRoot()});
EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), DeclURI(IncURI),
IncludeHeader(TestFileURI))));
}
TEST_F(SymbolCollectorTest, FallbackToIncFileWhenIncludingFileIsCC) {
- CollectorOpts.CollectIncludePath = true;
+ CollectSymOpts.CollectIncludePath = true;
CanonicalIncludes Includes;
- CollectorOpts.Includes = &Includes;
+ CollectSymOpts.Includes = &Includes;
auto IncFile = testPath("test.inc");
auto IncURI = URI::createFile(IncFile).toString();
InMemoryFileSystem->addFile(IncFile, 0,
llvm::MemoryBuffer::getMemBuffer("class X {};"));
- runSymbolCollector("", /*Main=*/"#include \"test.inc\"",
- /*ExtraArgs=*/{"-I", testRoot()});
+ collectSymbols("", /*Main=*/"#include \"test.inc\"",
+ /*ExtraArgs=*/{"-I", testRoot()});
EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), DeclURI(IncURI),
IncludeHeader(IncURI))));
}
TEST_F(SymbolCollectorTest, AvoidUsingFwdDeclsAsCanonicalDecls) {
- CollectorOpts.CollectIncludePath = true;
+ CollectSymOpts.CollectIncludePath = true;
Annotations Header(R"(
// Forward declarations of TagDecls.
class C;
@@ -906,7 +944,7 @@
struct $sdecl[[S]] {};
union $udecl[[U]] {int $xdecl[[x]]; bool $ydecl[[y]];};
)");
- runSymbolCollector(Header.code(), /*Main=*/"");
+ collectSymbols(Header.code(), /*Main=*/"");
EXPECT_THAT(
Symbols,
UnorderedElementsAre(
@@ -928,17 +966,18 @@
}
TEST_F(SymbolCollectorTest, ClassForwardDeclarationIsCanonical) {
- CollectorOpts.CollectIncludePath = true;
- runSymbolCollector(/*Header=*/"class X;", /*Main=*/"class X {};");
+ CollectSymOpts.CollectIncludePath = true;
+
+ collectSymbols(/*Header=*/"class X;", /*Main=*/"class X {};");
EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(
QName("X"), DeclURI(TestHeaderURI),
IncludeHeader(TestHeaderURI), DefURI(TestFileURI))));
}
TEST_F(SymbolCollectorTest, UTF16Character) {
// ö is 2-bytes.
Annotations Header(/*Header=*/"class [[pörk]] {};");
- runSymbolCollector(Header.code(), /*Main=*/"");
+ collectSymbols(Header.code(), /*Main=*/"");
EXPECT_THAT(Symbols, UnorderedElementsAre(
AllOf(QName("pörk"), DeclRange(Header.range()))));
}
@@ -957,7 +996,7 @@
void $foo[[foo]]();
}
)");
- runSymbolCollector(Header.code(), /*Main=*/"");
+ collectSymbols(Header.code(), /*Main=*/"");
EXPECT_THAT(Symbols,
UnorderedElementsAre(
@@ -979,21 +1018,21 @@
friend class Y;
};
)";
- CollectorOpts.CountReferences = true;
- runSymbolCollector(Header, Main);
+ CollectSymOpts.CountReferences = true;
+ collectSymbols(Header, Main);
EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), Refs(1)),
AllOf(QName("Y"), Refs(1))));
}
TEST_F(SymbolCollectorTest, Origin) {
- CollectorOpts.Origin = SymbolOrigin::Static;
- runSymbolCollector("class Foo {};", /*Main=*/"");
+ CollectSymOpts.Origin = SymbolOrigin::Static;
+ collectSymbols("class Foo {};", /*Main=*/"");
EXPECT_THAT(Symbols, UnorderedElementsAre(
Field(&Symbol::Origin, SymbolOrigin::Static)));
}
TEST_F(SymbolCollectorTest, CollectMacros) {
- CollectorOpts.CollectIncludePath = true;
+ CollectSymOpts.CollectIncludePath = true;
Annotations Header(R"(
#define X 1
#define $mac[[MAC]](x) int x
@@ -1005,9 +1044,9 @@
#define MAIN 1 // not indexed
USED(t);
)";
- CollectorOpts.CountReferences = true;
- CollectorOpts.CollectMacro = true;
- runSymbolCollector(Header.code(), Main);
+ CollectSymOpts.CountReferences = true;
+ CollectSymOpts.CollectMacro = true;
+ collectSymbols(Header.code(), Main);
EXPECT_THAT(
Symbols,
UnorderedElementsAre(
@@ -1018,6 +1057,59 @@
AllOf(Labeled("USED(y)"), Refs(1), DeclRange(Header.range("used")))));
}
+TEST_F(SymbolCollectorTest, CollectReference) {
+ const std::string Header(R"(
+ class Foo {
+ public:
+ Foo() {}
+ Foo(int);
+ };
+ class Bar;
+ void func();)");
+
+ Annotations Main(R"(
+ class $bar[[Bar]] {};
+
+ void $func[[func]]();
+
+ void fff() {
+ $foo[[Foo]] foo;
+ $bar[[Bar]] bar;
+ $func[[func]]();
+ int abc = 0;
+ $foo[[Foo]] foo2 = abc;
+ })");
+
+ auto H = TestTU::withHeaderCode(Header);
+ auto HeaderSymbols = H.headerSymbols();
+ auto Foo = findSymbol(HeaderSymbols, "Foo");
+ auto Bar = findSymbol(HeaderSymbols, "Bar");
+ auto Func = findSymbol(HeaderSymbols, "func");
+
+ CollectOccurrenceOpts.Filter = SymbolOccurrenceKind::Declaration |
+ SymbolOccurrenceKind::Definition |
+ SymbolOccurrenceKind::Reference;
+ CollectOccurrenceOpts.IDs = llvm::None;
+ collectOccurrences(Header, Main.code());
+ EXPECT_THAT(
+ Symbols.findOccurrences(Foo.ID),
+ testing::UnorderedPointwise(OccurrenceRange(), Main.ranges("foo")));
+ EXPECT_THAT(
+ Symbols.findOccurrences(Bar.ID),
+ testing::UnorderedPointwise(OccurrenceRange(), Main.ranges("bar")));
+ EXPECT_THAT(
+ Symbols.findOccurrences(Func.ID),
+ testing::UnorderedPointwise(OccurrenceRange(), Main.ranges("func")));
+
+ CollectOccurrenceOpts.IDs = {Foo.ID};
+ collectOccurrences(Header, Main.code());
+ EXPECT_THAT(
+ Symbols.findOccurrences(Foo.ID),
+ testing::UnorderedPointwise(OccurrenceRange(), Main.ranges("foo")));
+ EXPECT_THAT(Symbols.findOccurrences(Bar.ID), testing::IsEmpty());
+ EXPECT_THAT(Symbols.findOccurrences(Func.ID), testing::IsEmpty());
+}
+
} // namespace
} // namespace clangd
} // namespace clang
Index: clangd/index/SymbolCollector.h
===================================================================
--- clangd/index/SymbolCollector.h
+++ clangd/index/SymbolCollector.h
@@ -37,42 +37,56 @@
class SymbolCollector : public index::IndexDataConsumer {
public:
struct Options {
+ struct CollectSymbolOptions {
+ bool CollectIncludePath = false;
+ /// If set, this is used to map symbol #include path to a potentially
+ /// different #include path.
+ const CanonicalIncludes *Includes = nullptr;
+ // Populate the Symbol.References field.
+ bool CountReferences = false;
+ // Every symbol collected will be stamped with this origin.
+ SymbolOrigin Origin = SymbolOrigin::Unknown;
+ /// Collect macros.
+ /// Note that SymbolCollector must be run with preprocessor in order to
+ /// collect macros. For example, `indexTopLevelDecls` will not index any
+ /// macro even if this is true.
+ bool CollectMacro = false;
+ };
+ struct CollectOccurrenceOptions {
+ SymbolOccurrenceKind Filter;
+ // A whitelist symbols which will be collected.
+ // If none, all symbol occurrences will be collected.
+ llvm::Optional<llvm::DenseSet<SymbolID>> IDs = llvm::None;
+ };
+
+ /// Specifies URI schemes that can be used to generate URIs for file paths
+ /// in symbols. The list of schemes will be tried in order until a working
+ /// scheme is found. If no scheme works, symbol location will be dropped.
+ std::vector<std::string> URISchemes = {"file"};
+
/// When symbol paths cannot be resolved to absolute paths (e.g. files in
/// VFS that does not have absolute path), combine the fallback directory
/// with symbols' paths to get absolute paths. This must be an absolute
/// path.
std::string FallbackDir;
- /// Specifies URI schemes that can be used to generate URIs for file paths
- /// in symbols. The list of schemes will be tried in order until a working
- /// scheme is found. If no scheme works, symbol location will be dropped.
- std::vector<std::string> URISchemes = {"file"};
- bool CollectIncludePath = false;
- /// If set, this is used to map symbol #include path to a potentially
- /// different #include path.
- const CanonicalIncludes *Includes = nullptr;
- // Populate the Symbol.References field.
- bool CountReferences = false;
- // Every symbol collected will be stamped with this origin.
- SymbolOrigin Origin = SymbolOrigin::Unknown;
- /// Collect macros.
- /// Note that SymbolCollector must be run with preprocessor in order to
- /// collect macros. For example, `indexTopLevelDecls` will not index any
- /// macro even if this is true.
- bool CollectMacro = false;
+
+ // If not null, SymbolCollector will collect symbols.
+ const CollectSymbolOptions *SymOpts;
+ // If not null, SymbolCollector will collect symbol occurrences.
+ const CollectOccurrenceOptions *OccurrenceOpts;
};
SymbolCollector(Options Opts);
+ ~SymbolCollector();
+
/// Returns true is \p ND should be collected.
/// AST matchers require non-const ASTContext.
- static bool shouldCollectSymbol(const NamedDecl &ND, ASTContext &ASTCtx,
- const Options &Opts);
+ static bool shouldCollectSymbol(const NamedDecl &ND, ASTContext &ASTCtx);
void initialize(ASTContext &Ctx) override;
- void setPreprocessor(std::shared_ptr<Preprocessor> PP) override {
- this->PP = std::move(PP);
- }
+ void setPreprocessor(std::shared_ptr<Preprocessor> PP) override;
bool
handleDeclOccurence(const Decl *D, index::SymbolRoleSet Roles,
@@ -89,25 +103,16 @@
void finish() override;
private:
- const Symbol *addDeclaration(const NamedDecl &, SymbolID);
- void addDefinition(const NamedDecl &, const Symbol &DeclSymbol);
+ Options Opts;
- // All Symbols collected from the AST.
- SymbolSlab::Builder Symbols;
- ASTContext *ASTCtx;
std::shared_ptr<Preprocessor> PP;
- std::shared_ptr<GlobalCodeCompletionAllocator> CompletionAllocator;
- std::unique_ptr<CodeCompletionTUInfo> CompletionTUInfo;
- Options Opts;
- // Symbols referenced from the current TU, flushed on finish().
- llvm::DenseSet<const NamedDecl *> ReferencedDecls;
- llvm::DenseSet<const IdentifierInfo *> ReferencedMacros;
- // Maps canonical declaration provided by clang to canonical declaration for
- // an index symbol, if clangd prefers a different declaration than that
- // provided by clang. For example, friend declaration might be considered
- // canonical by clang but should not be considered canonical in the index
- // unless it's a definition.
- llvm::DenseMap<const Decl *, const Decl *> CanonicalDecls;
+
+ class CollectSymbol;
+ class CollectOccurrence;
+ std::unique_ptr<CollectSymbol> CollectSym;
+ std::unique_ptr<CollectOccurrence> CollectOccur;
+ // All symbols and symbol occurrences collected from the AST.
+ SymbolSlab::Builder Symbols;
};
} // namespace clangd
Index: clangd/index/SymbolCollector.cpp
===================================================================
--- clangd/index/SymbolCollector.cpp
+++ clangd/index/SymbolCollector.cpp
@@ -174,8 +174,9 @@
if (Headers.empty())
return llvm::None;
llvm::StringRef Header = Headers[0];
- if (Opts.Includes) {
- Header = Opts.Includes->mapHeader(Headers, QName);
+ assert(Opts.SymOpts && "SymbolOptions must be set.");
+ if (Opts.SymOpts->Includes) {
+ Header = Opts.SymOpts->Includes->mapHeader(Headers, QName);
if (Header.startswith("<") || Header.startswith("\""))
return Header.str();
}
@@ -224,20 +225,143 @@
match(decl(isExpansionInMainFile()), ND, ND.getASTContext()).empty();
}
+SymbolOccurrenceKind ToOccurrenceKind(index::SymbolRoleSet Roles) {
+ SymbolOccurrenceKind Kind;
+ for (auto Mask :
+ {SymbolOccurrenceKind::Declaration, SymbolOccurrenceKind::Definition,
+ SymbolOccurrenceKind::Reference}) {
+ if (Roles & static_cast<unsigned>(Mask))
+ Kind |= Mask;
+ }
+ return Kind;
+}
+
} // namespace
-SymbolCollector::SymbolCollector(Options Opts) : Opts(std::move(Opts)) {}
+class SymbolCollector::CollectOccurrence {
+public:
+ CollectOccurrence(const SymbolCollector::Options &CollectorOpts,
+ SymbolSlab::Builder *Builder)
+ : CollectorOpts(CollectorOpts), Opts(*CollectorOpts.OccurrenceOpts),
+ Builder(Builder) {}
+
+ void initialize(ASTContext &Ctx) { ASTCtx = &Ctx; }
+
+ void collectDecl(const Decl *D, index::SymbolRoleSet Roles,
+ ArrayRef<index::SymbolRelation> Relations,
+ SourceLocation Loc,
+ index::IndexDataConsumer::ASTNodeInfo ASTNode) {
+ assert(ASTCtx && "ASTContext must be set.");
+ if (D->isImplicit())
+ return;
+
+ // We only collect symbol occurrences in current main file.
+ if (!ASTCtx->getSourceManager().isInMainFile(Loc))
+ return;
+ std::string FileURI;
+ auto AddOccurrence = [&](SourceLocation L, const SymbolID &ID) {
+ if (auto Location =
+ getTokenLocation(Loc, ASTCtx->getSourceManager(), CollectorOpts,
+ ASTCtx->getLangOpts(), FileURI)) {
+ SymbolOccurrence Occurrence;
+ Occurrence.Location = *Location;
+ Occurrence.Kind = ToOccurrenceKind(Roles);
+ Builder->insert(ID, Occurrence);
+ }
+ };
+ if (static_cast<unsigned>(Opts.Filter) & Roles) {
+ if (auto ID = getSymbolID(D)) {
+ if (!Opts.IDs || llvm::is_contained(*Opts.IDs, *ID)) {
+ AddOccurrence(Loc, *ID);
+ }
+ }
+ }
+ }
+
+private:
+ const SymbolCollector::Options &CollectorOpts;
+ const SymbolCollector::Options::CollectOccurrenceOptions &Opts;
+
+ SymbolSlab::Builder *Builder;
+ ASTContext *ASTCtx;
+};
+
+class SymbolCollector::CollectSymbol {
+public:
+ CollectSymbol(const SymbolCollector::Options &CollectorOpts,
+ SymbolSlab::Builder *Builder)
+ : CollectorOpts(CollectorOpts), Opts(*CollectorOpts.SymOpts),
+ Symbols(Builder) {}
+
+ void collectDecl(const Decl *D, index::SymbolRoleSet Roles,
+ ArrayRef<index::SymbolRelation> Relations,
+ SourceLocation Loc,
+ index::IndexDataConsumer::ASTNodeInfo ASTNode);
+
+ void collectMacro(const IdentifierInfo *Name, const MacroInfo *MI,
+ index::SymbolRoleSet Roles, SourceLocation Loc);
+
+ void initialize(ASTContext &Ctx) {
+ ASTCtx = &Ctx;
+ CompletionAllocator = std::make_shared<GlobalCodeCompletionAllocator>();
+ CompletionTUInfo =
+ llvm::make_unique<CodeCompletionTUInfo>(CompletionAllocator);
+ }
+
+ void setPreprocessor(std::shared_ptr<Preprocessor> PP) {
+ this->PP = std::move(PP);
+ }
+
+ void finish();
+
+private:
+ const Symbol *addDeclaration(const NamedDecl &, SymbolID);
+ void addDefinition(const NamedDecl &ND, const Symbol &DeclSym);
+
+ const SymbolCollector::Options &CollectorOpts;
+ const SymbolCollector::Options::CollectSymbolOptions &Opts;
+
+ SymbolSlab::Builder *Symbols;
+ ASTContext *ASTCtx;
+
+ std::shared_ptr<Preprocessor> PP;
+ std::shared_ptr<GlobalCodeCompletionAllocator> CompletionAllocator;
+ std::unique_ptr<CodeCompletionTUInfo> CompletionTUInfo;
+ // Symbols referenced from the current TU, flushed on finish().
+ llvm::DenseSet<const NamedDecl *> ReferencedDecls;
+ llvm::DenseSet<const IdentifierInfo *> ReferencedMacros;
+ // Maps canonical declaration provided by clang to canonical declaration for
+ // an index symbol, if clangd prefers a different declaration than that
+ // provided by clang. For example, friend declaration might be considered
+ // canonical by clang but should not be considered canonical in the index
+ // unless it's a definition.
+ llvm::DenseMap<const Decl *, const Decl *> CanonicalDecls;
+};
+
+SymbolCollector::SymbolCollector(Options CollectorOpts)
+ : Opts(std::move(CollectorOpts)) {
+ if (this->Opts.SymOpts)
+ CollectSym = llvm::make_unique<CollectSymbol>(Opts, &Symbols);
+ if (this->Opts.OccurrenceOpts)
+ CollectOccur = llvm::make_unique<CollectOccurrence>(Opts, &Symbols);
+}
+
+SymbolCollector::~SymbolCollector() {}
void SymbolCollector::initialize(ASTContext &Ctx) {
- ASTCtx = &Ctx;
- CompletionAllocator = std::make_shared<GlobalCodeCompletionAllocator>();
- CompletionTUInfo =
- llvm::make_unique<CodeCompletionTUInfo>(CompletionAllocator);
+ if (CollectSym)
+ CollectSym->initialize(Ctx);
+ if (CollectOccur)
+ CollectOccur->initialize(Ctx);
+}
+
+void SymbolCollector::setPreprocessor(std::shared_ptr<Preprocessor> PP) {
+ if (CollectSym)
+ CollectSym->setPreprocessor(PP);
}
bool SymbolCollector::shouldCollectSymbol(const NamedDecl &ND,
- ASTContext &ASTCtx,
- const Options &Opts) {
+ ASTContext &ASTCtx) {
using namespace clang::ast_matchers;
if (ND.isImplicit())
return false;
@@ -282,7 +406,7 @@
}
// Always return true to continue indexing.
-bool SymbolCollector::handleDeclOccurence(
+void SymbolCollector::CollectSymbol::collectDecl(
const Decl *D, index::SymbolRoleSet Roles,
ArrayRef<index::SymbolRelation> Relations, SourceLocation Loc,
index::IndexDataConsumer::ASTNodeInfo ASTNode) {
@@ -295,15 +419,15 @@
if ((ASTNode.OrigD->getFriendObjectKind() !=
Decl::FriendObjectKind::FOK_None) &&
!(Roles & static_cast<unsigned>(index::SymbolRole::Definition)))
- return true;
+ return;
// A declaration created for a friend declaration should not be used as the
// canonical declaration in the index. Use OrigD instead, unless we've already
// picked a replacement for D
if (D->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None)
D = CanonicalDecls.try_emplace(D, ASTNode.OrigD).first->second;
const NamedDecl *ND = llvm::dyn_cast<NamedDecl>(D);
if (!ND)
- return true;
+ return;
// Mark D as referenced if this is a reference coming from the main file.
// D may not be an interesting symbol, but it's cheaper to check at the end.
@@ -316,16 +440,16 @@
// Don't continue indexing if this is a mere reference.
if (!(Roles & static_cast<unsigned>(index::SymbolRole::Declaration) ||
Roles & static_cast<unsigned>(index::SymbolRole::Definition)))
- return true;
- if (!shouldCollectSymbol(*ND, *ASTCtx, Opts))
- return true;
+ return;
+ if (!shouldCollectSymbol(*ND, *ASTCtx))
+ return;
auto ID = getSymbolID(ND);
if (!ID)
- return true;
+ return;
const NamedDecl &OriginalDecl = *cast<NamedDecl>(ASTNode.OrigD);
- const Symbol *BasicSymbol = Symbols.find(*ID);
+ const Symbol *BasicSymbol = Symbols->find(*ID);
if (!BasicSymbol) // Regardless of role, ND is the canonical declaration.
BasicSymbol = addDeclaration(*ND, std::move(*ID));
else if (isPreferredDeclaration(OriginalDecl, Roles))
@@ -337,24 +461,23 @@
if (Roles & static_cast<unsigned>(index::SymbolRole::Definition))
addDefinition(OriginalDecl, *BasicSymbol);
- return true;
}
-bool SymbolCollector::handleMacroOccurence(const IdentifierInfo *Name,
- const MacroInfo *MI,
- index::SymbolRoleSet Roles,
- SourceLocation Loc) {
+void SymbolCollector::CollectSymbol::collectMacro(const IdentifierInfo *Name,
+ const MacroInfo *MI,
+ index::SymbolRoleSet Roles,
+ SourceLocation Loc) {
if (!Opts.CollectMacro)
- return true;
+ return;
assert(PP.get());
const auto &SM = PP->getSourceManager();
if (SM.isInMainFile(SM.getExpansionLoc(MI->getDefinitionLoc())))
- return true;
+ return;
// Header guards are not interesting in index. Builtin macros don't have
// useful locations and are not needed for code completions.
if (MI->isUsedForHeaderGuard() || MI->isBuiltinMacro())
- return true;
+ return;
// Mark the macro as referenced if this is a reference coming from the main
// file. The macro may not be an interesting symbol, but it's cheaper to check
@@ -367,25 +490,25 @@
// FIXME: remove macro with ID if it is undefined.
if (!(Roles & static_cast<unsigned>(index::SymbolRole::Declaration) ||
Roles & static_cast<unsigned>(index::SymbolRole::Definition)))
- return true;
+ return;
llvm::SmallString<128> USR;
if (index::generateUSRForMacro(Name->getName(), MI->getDefinitionLoc(), SM,
USR))
- return true;
+ return;
SymbolID ID(USR);
// Only collect one instance in case there are multiple.
- if (Symbols.find(ID) != nullptr)
- return true;
+ if (Symbols->find(ID) != nullptr)
+ return;
Symbol S;
S.ID = std::move(ID);
S.Name = Name->getName();
S.IsIndexedForCodeCompletion = true;
S.SymInfo = index::getSymbolInfoForMacro(*MI);
std::string FileURI;
- if (auto DeclLoc = getTokenLocation(MI->getDefinitionLoc(), SM, Opts,
+ if (auto DeclLoc = getTokenLocation(MI->getDefinitionLoc(), SM, CollectorOpts,
PP->getLangOpts(), FileURI))
S.CanonicalDeclaration = *DeclLoc;
@@ -398,27 +521,26 @@
std::string Include;
if (Opts.CollectIncludePath && shouldCollectIncludePath(S.SymInfo.Kind)) {
- if (auto Header =
- getIncludeHeader(Name->getName(), SM,
- SM.getExpansionLoc(MI->getDefinitionLoc()), Opts))
+ if (auto Header = getIncludeHeader(
+ Name->getName(), SM, SM.getExpansionLoc(MI->getDefinitionLoc()),
+ CollectorOpts))
Include = std::move(*Header);
}
S.Signature = Signature;
S.CompletionSnippetSuffix = SnippetSuffix;
Symbol::Details Detail;
Detail.IncludeHeader = Include;
S.Detail = &Detail;
- Symbols.insert(S);
- return true;
+ Symbols->insert(S);
}
-void SymbolCollector::finish() {
+void SymbolCollector::CollectSymbol::finish() {
// At the end of the TU, add 1 to the refcount of all referenced symbols.
auto IncRef = [this](const SymbolID &ID) {
- if (const auto *S = Symbols.find(ID)) {
+ if (const auto *S = Symbols->find(ID)) {
Symbol Inc = *S;
++Inc.References;
- Symbols.insert(Inc);
+ Symbols->insert(Inc);
}
};
for (const NamedDecl *ND : ReferencedDecls) {
@@ -440,8 +562,9 @@
ReferencedMacros.clear();
}
-const Symbol *SymbolCollector::addDeclaration(const NamedDecl &ND,
- SymbolID ID) {
+const Symbol *
+SymbolCollector::CollectSymbol::addDeclaration(const NamedDecl &ND,
+ SymbolID ID) {
auto &Ctx = ND.getASTContext();
auto &SM = Ctx.getSourceManager();
@@ -455,7 +578,7 @@
S.IsIndexedForCodeCompletion = isIndexedForCodeCompletion(ND, Ctx);
S.SymInfo = index::getSymbolInfo(&ND);
std::string FileURI;
- if (auto DeclLoc = getTokenLocation(findNameLoc(&ND), SM, Opts,
+ if (auto DeclLoc = getTokenLocation(findNameLoc(&ND), SM, CollectorOpts,
ASTCtx->getLangOpts(), FileURI))
S.CanonicalDeclaration = *DeclLoc;
@@ -481,7 +604,7 @@
// Use the expansion location to get the #include header since this is
// where the symbol is exposed.
if (auto Header = getIncludeHeader(
- QName, SM, SM.getExpansionLoc(ND.getLocation()), Opts))
+ QName, SM, SM.getExpansionLoc(ND.getLocation()), CollectorOpts))
Include = std::move(*Header);
}
S.Signature = Signature;
@@ -493,24 +616,49 @@
S.Detail = &Detail;
S.Origin = Opts.Origin;
- Symbols.insert(S);
- return Symbols.find(S.ID);
+ Symbols->insert(S);
+ return Symbols->find(S.ID);
}
-void SymbolCollector::addDefinition(const NamedDecl &ND,
- const Symbol &DeclSym) {
+void SymbolCollector::CollectSymbol::addDefinition(const NamedDecl &ND,
+ const Symbol &DeclSym) {
if (DeclSym.Definition)
return;
// If we saw some forward declaration, we end up copying the symbol.
// This is not ideal, but avoids duplicating the "is this a definition" check
// in clang::index. We should only see one definition.
Symbol S = DeclSym;
std::string FileURI;
- if (auto DefLoc = getTokenLocation(findNameLoc(&ND),
- ND.getASTContext().getSourceManager(),
- Opts, ASTCtx->getLangOpts(), FileURI))
+ if (auto DefLoc = getTokenLocation(
+ findNameLoc(&ND), ND.getASTContext().getSourceManager(),
+ CollectorOpts, ASTCtx->getLangOpts(), FileURI))
S.Definition = *DefLoc;
- Symbols.insert(S);
+ Symbols->insert(S);
+}
+
+bool SymbolCollector::handleDeclOccurence(
+ const Decl *D, index::SymbolRoleSet Roles,
+ ArrayRef<index::SymbolRelation> Relations, SourceLocation Loc,
+ index::IndexDataConsumer::ASTNodeInfo ASTNode) {
+ if (CollectSym)
+ CollectSym->collectDecl(D, Roles, Relations, Loc, ASTNode);
+ if (CollectOccur)
+ CollectOccur->collectDecl(D, Roles, Relations, Loc, ASTNode);
+ return true;
+}
+
+bool SymbolCollector::handleMacroOccurence(const IdentifierInfo *Name,
+ const MacroInfo *MI,
+ index::SymbolRoleSet Roles,
+ SourceLocation Loc) {
+ if (CollectSym)
+ CollectSym->collectMacro(Name, MI, Roles, Loc);
+ return true;
+}
+
+void SymbolCollector::finish() {
+ if (CollectSym)
+ CollectSym->finish();
}
} // namespace clangd
Index: clangd/index/Index.h
===================================================================
--- clangd/index/Index.h
+++ clangd/index/Index.h
@@ -31,9 +31,6 @@
uint32_t Line = 0; // 0-based
// Using UTF-16 code units.
uint32_t Column = 0; // 0-based
- bool operator==(const Position& P) const {
- return Line == P.Line && Column == P.Column;
- }
};
// The URI of the source file where a symbol occurs.
@@ -44,11 +41,24 @@
Position End;
explicit operator bool() const { return !FileURI.empty(); }
- bool operator==(const SymbolLocation& Loc) const {
- return std::tie(FileURI, Start, End) ==
- std::tie(Loc.FileURI, Loc.Start, Loc.End);
- }
};
+
+inline bool operator==(const SymbolLocation::Position &L,
+ const SymbolLocation::Position &R) {
+ return std::tie(L.Line, L.Column) == std::tie(R.Line, R.Column);
+}
+inline bool operator<(const SymbolLocation::Position &L,
+ const SymbolLocation::Position &R) {
+ return std::tie(L.Line, L.Column) < std::tie(R.Line, R.Column);
+}
+inline bool operator==(const SymbolLocation &L, const SymbolLocation &R) {
+ return std::tie(L.FileURI, L.Start, L.End) ==
+ std::tie(R.FileURI, R.Start, R.End);
+}
+inline bool operator<(const SymbolLocation &L, const SymbolLocation &R) {
+ return std::tie(L.FileURI, L.Start, L.End) <
+ std::tie(R.FileURI, R.Start, R.End);
+}
llvm::raw_ostream &operator<<(llvm::raw_ostream &, const SymbolLocation &);
// The class identifies a particular C++ symbol (class, function, method, etc).
@@ -234,6 +244,50 @@
// and signals -> score, so it can be reused for Sema completions.
double quality(const Symbol &S);
+// Describes the kind of a symbol occurrence.
+//
+// This is a bitfield which can be combined from different kinds.
+enum class SymbolOccurrenceKind : uint8_t {
+ Unknown = 0,
+ Declaration = static_cast<uint8_t>(index::SymbolRole::Declaration),
+ Definition = static_cast<uint8_t>(index::SymbolRole::Definition),
+ Reference = static_cast<uint8_t>(index::SymbolRole::Reference),
+};
+inline SymbolOccurrenceKind operator|(SymbolOccurrenceKind L,
+ SymbolOccurrenceKind R) {
+ return static_cast<SymbolOccurrenceKind>(static_cast<uint8_t>(L) |
+ static_cast<uint8_t>(R));
+}
+inline SymbolOccurrenceKind &operator|=(SymbolOccurrenceKind &L,
+ SymbolOccurrenceKind R) {
+ return L = L | R;
+}
+inline SymbolOccurrenceKind operator&(SymbolOccurrenceKind A,
+ SymbolOccurrenceKind B) {
+ return static_cast<SymbolOccurrenceKind>(static_cast<uint8_t>(A) &
+ static_cast<uint8_t>(B));
+}
+
+// Represents a symbol occurrence in the source file. It could be a
+// declaration/definition/reference occurrence.
+//
+// WARNING: Location does not own the underlying data - Copies are shallow.
+struct SymbolOccurrence {
+ // The location of the occurrence.
+ SymbolLocation Location;
+ SymbolOccurrenceKind Kind = SymbolOccurrenceKind::Unknown;
+};
+
+inline bool operator<(const SymbolOccurrence &L, const SymbolOccurrence &R) {
+ return std::tie(L.Location, L.Kind) < std::tie(R.Location, R.Kind);
+}
+
+inline bool operator==(const SymbolOccurrence &L, const SymbolOccurrence &R) {
+ return std::tie(L.Location, L.Kind) == std::tie(R.Location, R.Kind);
+}
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+ const SymbolOccurrence &Occurrence);
+
// An immutable symbol container that stores a set of symbols.
// The container will maintain the lifetime of the symbols.
class SymbolSlab {
@@ -251,7 +305,15 @@
// Estimates the total memory usage.
size_t bytes() const {
return sizeof(*this) + Arena.getTotalMemory() +
- Symbols.capacity() * sizeof(Symbol);
+ Symbols.capacity() * sizeof(Symbol) +
+ SymbolOccurrences.getMemorySize();
+ }
+
+ llvm::ArrayRef<SymbolOccurrence> findOccurrences(const SymbolID &ID) const {
+ auto It = SymbolOccurrences.find(ID);
+ if (It == SymbolOccurrences.end())
+ return {};
+ return It->second;
}
// SymbolSlab::Builder is a mutable container that can 'freeze' to SymbolSlab.
@@ -264,6 +326,10 @@
// This is a deep copy: underlying strings will be owned by the slab.
void insert(const Symbol &S);
+ // Adds a symbol occurrence.
+ // This is a deep copy: underlying strings will be owned by the slab.
+ void insert(const SymbolID &ID, SymbolOccurrence Occurrence);
+
// Returns the symbol with an ID, if it exists. Valid until next insert().
const Symbol *find(const SymbolID &ID) {
auto I = SymbolIndex.find(ID);
@@ -280,48 +346,20 @@
std::vector<Symbol> Symbols;
// Values are indices into Symbols vector.
llvm::DenseMap<SymbolID, size_t> SymbolIndex;
+
+ llvm::DenseMap<SymbolID, std::vector<SymbolOccurrence>> SymbolOccurrences;
};
private:
- SymbolSlab(llvm::BumpPtrAllocator Arena, std::vector<Symbol> Symbols)
- : Arena(std::move(Arena)), Symbols(std::move(Symbols)) {}
+ SymbolSlab(
+ llvm::BumpPtrAllocator Arena, std::vector<Symbol> Symbols,
+ llvm::DenseMap<SymbolID, std::vector<SymbolOccurrence>> SymbolOccurrences)
+ : Arena(std::move(Arena)), Symbols(std::move(Symbols)),
+ SymbolOccurrences(std::move(SymbolOccurrences)) {}
llvm::BumpPtrAllocator Arena; // Owns Symbol data that the Symbols do not.
std::vector<Symbol> Symbols; // Sorted by SymbolID to allow lookup.
-};
-
-// Describes the kind of a symbol occurrence.
-//
-// This is a bitfield which can be combined from different kinds.
-enum class SymbolOccurrenceKind : uint8_t {
- Unknown = 0,
- Declaration = static_cast<uint8_t>(index::SymbolRole::Declaration),
- Definition = static_cast<uint8_t>(index::SymbolRole::Definition),
- Reference = static_cast<uint8_t>(index::SymbolRole::Reference),
-};
-inline SymbolOccurrenceKind operator|(SymbolOccurrenceKind L,
- SymbolOccurrenceKind R) {
- return static_cast<SymbolOccurrenceKind>(static_cast<uint8_t>(L) |
- static_cast<uint8_t>(R));
-}
-inline SymbolOccurrenceKind &operator|=(SymbolOccurrenceKind &L,
- SymbolOccurrenceKind R) {
- return L = L | R;
-}
-inline SymbolOccurrenceKind operator&(SymbolOccurrenceKind A,
- SymbolOccurrenceKind B) {
- return static_cast<SymbolOccurrenceKind>(static_cast<uint8_t>(A) &
- static_cast<uint8_t>(B));
-}
-
-// Represents a symbol occurrence in the source file. It could be a
-// declaration/definition/reference occurrence.
-//
-// WARNING: Location does not own the underlying data - Copies are shallow.
-struct SymbolOccurrence {
- // The location of the occurrence.
- SymbolLocation Location;
- SymbolOccurrenceKind Kind = SymbolOccurrenceKind::Unknown;
+ llvm::DenseMap<SymbolID, std::vector<SymbolOccurrence>> SymbolOccurrences;
};
struct FuzzyFindRequest {
Index: clangd/index/Index.cpp
===================================================================
--- clangd/index/Index.cpp
+++ clangd/index/Index.cpp
@@ -114,6 +114,11 @@
own(Copy, UniqueStrings, Arena);
}
}
+void SymbolSlab::Builder::insert(const SymbolID &ID,
+ SymbolOccurrence Occurrence) {
+ Occurrence.Location.FileURI = UniqueStrings.save(Occurrence.Location.FileURI);
+ SymbolOccurrences[ID].push_back(std::move(Occurrence));
+}
SymbolSlab SymbolSlab::Builder::build() && {
Symbols = {Symbols.begin(), Symbols.end()}; // Force shrink-to-fit.
@@ -125,7 +130,39 @@
llvm::UniqueStringSaver Strings(NewArena);
for (auto &S : Symbols)
own(S, Strings, NewArena);
- return SymbolSlab(std::move(NewArena), std::move(Symbols));
+
+ // We may have duplicated symbol occurrences (as some AST nodes have been
+ // visited multiple times). Deduplicate them.
+ for (auto &IDAndOccurrences : SymbolOccurrences) {
+ auto &Occurrences = IDAndOccurrences.getSecond();
+ std::sort(Occurrences.begin(), Occurrences.end(),
+ [](const SymbolOccurrence &L, const SymbolOccurrence &R) {
+ return L < R;
+ });
+ Occurrences.erase(
+ std::unique(Occurrences.begin(), Occurrences.end(),
+ [](const SymbolOccurrence &L, const SymbolOccurrence &R) {
+ return L == R;
+ }),
+ Occurrences.end());
+
+ for (auto &O : Occurrences)
+ O.Location.FileURI = UniqueStrings.save(O.Location.FileURI);
+ }
+
+ return SymbolSlab(std::move(NewArena), std::move(Symbols),
+ std::move(SymbolOccurrences));
+}
+
+raw_ostream &operator<<(raw_ostream &OS, SymbolOccurrenceKind K) {
+ OS << static_cast<unsigned>(K);
+ return OS;
+}
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+ const SymbolOccurrence &Occurrence) {
+ OS << Occurrence.Location << ":" << Occurrence.Kind;
+ return OS;
}
} // namespace clangd
Index: clangd/index/FileIndex.cpp
===================================================================
--- clangd/index/FileIndex.cpp
+++ clangd/index/FileIndex.cpp
@@ -19,16 +19,18 @@
SymbolSlab indexAST(ASTContext &AST, std::shared_ptr<Preprocessor> PP,
llvm::ArrayRef<std::string> URISchemes) {
SymbolCollector::Options CollectorOpts;
+ SymbolCollector::Options::CollectSymbolOptions SymbolOpts;
// FIXME(ioeric): we might also want to collect include headers. We would need
// to make sure all includes are canonicalized (with CanonicalIncludes), which
// is not trivial given the current way of collecting symbols: we only have
// AST at this point, but we also need preprocessor callbacks (e.g.
// CommentHandler for IWYU pragma) to canonicalize includes.
- CollectorOpts.CollectIncludePath = false;
- CollectorOpts.CountReferences = false;
+ SymbolOpts.CollectIncludePath = false;
+ SymbolOpts.CountReferences = false;
if (!URISchemes.empty())
CollectorOpts.URISchemes = URISchemes;
- CollectorOpts.Origin = SymbolOrigin::Dynamic;
+ SymbolOpts.Origin = SymbolOrigin::Dynamic;
+ CollectorOpts.SymOpts = &SymbolOpts;
SymbolCollector Collector(std::move(CollectorOpts));
Collector.setPreprocessor(PP);
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits