13 #include "clang/Basic/FileManager.h" 14 #include "clang/Basic/FileSystemOptions.h" 15 #include "clang/Frontend/CompilerInstance.h" 16 #include "clang/Index/IndexingAction.h" 17 #include "clang/Tooling/Tooling.h" 18 #include "llvm/ADT/IntrusiveRefCntPtr.h" 19 #include "llvm/ADT/STLExtras.h" 20 #include "llvm/ADT/StringRef.h" 21 #include "llvm/Support/MemoryBuffer.h" 22 #include "llvm/Support/VirtualFileSystem.h" 23 #include "gmock/gmock-matchers.h" 24 #include "gmock/gmock-more-matchers.h" 25 #include "gmock/gmock.h" 26 #include "gtest/gtest.h" 36 using ::testing::AllOf;
37 using ::testing::Contains;
38 using ::testing::Each;
39 using ::testing::ElementsAre;
40 using ::testing::Field;
42 using ::testing::Pair;
43 using ::testing::UnorderedElementsAre;
44 using ::testing::UnorderedElementsAreArray;
48 return (arg.Name + arg.Signature).str() == Label;
51 MATCHER_P(Doc,
D,
"") {
return arg.Documentation ==
D; }
53 return (arg.Name + arg.CompletionSnippetSuffix).str() == S;
57 return arg.TemplateSpecializationArgs == TemplArgs;
60 return StringRef(arg.CanonicalDeclaration.FileURI) == P;
62 MATCHER_P(DefURI, P,
"") {
return StringRef(arg.Definition.FileURI) == P; }
65 return (arg.IncludeHeaders.size() == 1) &&
66 (arg.IncludeHeaders.begin()->IncludeHeader == P);
72 return std::make_tuple(arg.CanonicalDeclaration.Start.line(),
73 arg.CanonicalDeclaration.Start.column(),
74 arg.CanonicalDeclaration.End.line(),
75 arg.CanonicalDeclaration.End.column()) ==
76 std::make_tuple(Pos.start.line, Pos.start.character, Pos.end.line,
80 return std::make_tuple(
81 arg.Definition.Start.line(), arg.Definition.Start.column(),
82 arg.Definition.End.line(), arg.Definition.End.column()) ==
83 std::make_tuple(Pos.start.line, Pos.start.character, Pos.end.line,
86 MATCHER_P(RefCount, R,
"") {
return int(arg.References) == R; }
87 MATCHER_P(ForCodeCompletion, IsIndexedForCodeCompletion,
"") {
89 IsIndexedForCodeCompletion;
92 MATCHER(ImplementationDetail,
"") {
95 MATCHER(VisibleOutsideFile,
"") {
99 const Ref &Pos = ::testing::get<0>(arg);
101 return std::make_tuple(Pos.Location.Start.line(), Pos.Location.Start.column(),
102 Pos.Location.End.line(), Pos.Location.End.column()) ==
103 std::make_tuple(Range.start.line, Range.start.character,
104 Range.end.line, Range.end.character);
106 ::testing::Matcher<const std::vector<Ref> &>
107 HaveRanges(
const std::vector<Range> Ranges) {
108 return ::testing::UnorderedPointwise(RefRange(), Ranges);
111 class ShouldCollectSymbolTest :
public ::testing::Test {
113 void build(llvm::StringRef HeaderCode, llvm::StringRef Code =
"") {
116 File.HeaderCode = HeaderCode;
122 bool shouldCollect(llvm::StringRef
Name,
bool Qualified =
true) {
123 assert(
AST.hasValue());
124 const NamedDecl &ND =
126 const SourceManager &SM =
AST->getSourceManager();
129 ND,
AST->getASTContext(), SymbolCollector::Options(),
MainFile);
136 llvm::Optional<ParsedAST>
AST;
139 TEST_F(ShouldCollectSymbolTest, ShouldCollectSymbol) {
143 auto f() { int Local; } // auto ensures function body is parsed. 144 struct { int x; } var; 149 namespace { class InAnonymous {}; } 153 EXPECT_TRUE(shouldCollect(
"nx"));
154 EXPECT_TRUE(shouldCollect(
"nx::X"));
155 EXPECT_TRUE(shouldCollect(
"nx::f"));
156 EXPECT_TRUE(shouldCollect(
"InMain"));
157 EXPECT_TRUE(shouldCollect(
"InAnonymous",
false));
158 EXPECT_TRUE(shouldCollect(
"g"));
160 EXPECT_FALSE(shouldCollect(
"Local",
false));
163 TEST_F(ShouldCollectSymbolTest, NoPrivateProtoSymbol) {
166 R
"(// Generated by the protocol buffer compiler. DO NOT EDIT! 175 EXPECT_TRUE(shouldCollect("nx::TopLevel"));
176 EXPECT_TRUE(shouldCollect(
"nx::Kind::KIND_OK"));
177 EXPECT_TRUE(shouldCollect(
"nx::Kind"));
179 EXPECT_FALSE(shouldCollect(
"nx::Top_Level"));
180 EXPECT_FALSE(shouldCollect(
"nx::Kind::Kind_Not_Ok"));
183 TEST_F(ShouldCollectSymbolTest, DoubleCheckProtoHeaderComment) {
193 EXPECT_TRUE(shouldCollect("nx::Top_Level"));
194 EXPECT_TRUE(shouldCollect(
"nx::Kind_Fine"));
197 class SymbolIndexActionFactory :
public tooling::FrontendActionFactory {
199 SymbolIndexActionFactory(SymbolCollector::Options
COpts,
203 clang::FrontendAction *create()
override {
204 class WrappedIndexAction :
public WrapperFrontendAction {
206 WrappedIndexAction(std::shared_ptr<SymbolCollector> C,
207 const index::IndexingOptions &Opts,
208 CommentHandler *PragmaHandler)
209 : WrapperFrontendAction(
210 index::createIndexingAction(C, Opts,
nullptr)),
213 std::unique_ptr<ASTConsumer>
214 CreateASTConsumer(CompilerInstance &CI, llvm::StringRef InFile)
override {
216 CI.getPreprocessor().addCommentHandler(PragmaHandler);
217 return WrapperFrontendAction::CreateASTConsumer(CI, InFile);
220 bool BeginInvocation(CompilerInstance &CI)
override {
222 CI.getLangOpts().CommentOpts.ParseAllComments =
true;
223 return WrapperFrontendAction::BeginInvocation(CI);
227 index::IndexingOptions IndexOpts;
230 index::IndexingOptions IndexOpts;
231 IndexOpts.SystemSymbolFilter =
232 index::IndexingOptions::SystemSymbolFilterKind::All;
233 IndexOpts.IndexFunctionLocals =
false;
235 return new WrappedIndexAction(
Collector, std::move(IndexOpts),
244 class SymbolCollectorTest :
public ::testing::Test {
246 SymbolCollectorTest()
256 bool runSymbolCollector(llvm::StringRef HeaderCode, llvm::StringRef MainCode,
257 const std::vector<std::string> &ExtraArgs = {}) {
258 llvm::IntrusiveRefCntPtr<FileManager>
Files(
261 auto Factory = llvm::make_unique<SymbolIndexActionFactory>(
264 std::vector<std::string> Args = {
"symbol_collector",
"-fsyntax-only",
266 Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
271 tooling::ToolInvocation Invocation(
272 Args, Factory->create(), Files.get(),
273 std::make_shared<PCHContainerOperations>());
276 llvm::MemoryBuffer::getMemBuffer(HeaderCode));
278 llvm::MemoryBuffer::getMemBuffer(MainCode));
280 Symbols = Factory->Collector->takeSymbols();
281 Refs = Factory->Collector->takeRefs();
282 Relations = Factory->Collector->takeRelations();
299 TEST_F(SymbolCollectorTest, CollectSymbols) {
300 const std::string Header = R
"( 307 Foo& operator=(const Foo&); 318 static const int KInt = 2; 319 const char* kStr = "123"; 322 void ff() {} // ignore 330 using int32_t = int32; 345 runSymbolCollector(Header, "");
347 UnorderedElementsAreArray(
348 {AllOf(
QName(
"Foo"), ForCodeCompletion(
true)),
349 AllOf(
QName(
"Foo::Foo"), ForCodeCompletion(
false)),
350 AllOf(
QName(
"Foo::Foo"), ForCodeCompletion(
false)),
351 AllOf(
QName(
"Foo::f"), ForCodeCompletion(
false)),
352 AllOf(
QName(
"Foo::~Foo"), ForCodeCompletion(
false)),
353 AllOf(
QName(
"Foo::operator="), ForCodeCompletion(
false)),
354 AllOf(
QName(
"Foo::Nested"), ForCodeCompletion(
false)),
355 AllOf(
QName(
"Foo::Nested::f"), ForCodeCompletion(
false)),
357 AllOf(
QName(
"Friend"), ForCodeCompletion(
true)),
358 AllOf(
QName(
"f1"), ForCodeCompletion(
true)),
359 AllOf(
QName(
"f2"), ForCodeCompletion(
true)),
360 AllOf(
QName(
"KInt"), ForCodeCompletion(
true)),
361 AllOf(
QName(
"kStr"), ForCodeCompletion(
true)),
362 AllOf(
QName(
"foo"), ForCodeCompletion(
true)),
363 AllOf(
QName(
"foo::bar"), ForCodeCompletion(
true)),
364 AllOf(
QName(
"foo::int32"), ForCodeCompletion(
true)),
365 AllOf(
QName(
"foo::int32_t"), ForCodeCompletion(
true)),
366 AllOf(
QName(
"foo::v1"), ForCodeCompletion(
true)),
367 AllOf(
QName(
"foo::bar::v2"), ForCodeCompletion(
true)),
368 AllOf(
QName(
"foo::v2"), ForCodeCompletion(
true)),
369 AllOf(
QName(
"foo::baz"), ForCodeCompletion(
true))}));
372 TEST_F(SymbolCollectorTest, FileLocal) {
373 const std::string Header = R
"( 380 const std::string Main = R
"( 389 runSymbolCollector(Header, Main); 391 UnorderedElementsAre( 392 AllOf(QName("Foo"), VisibleOutsideFile()),
393 AllOf(
QName(
"bar"), VisibleOutsideFile()),
394 AllOf(
QName(
"a"), Not(VisibleOutsideFile())),
395 AllOf(
QName(
"B"), Not(VisibleOutsideFile())),
396 AllOf(
QName(
"c"), Not(VisibleOutsideFile())),
398 AllOf(
QName(
"ForwardDecl"), Not(VisibleOutsideFile()))));
401 TEST_F(SymbolCollectorTest, Template) {
402 Annotations Header(R
"( 403 // Primary template and explicit specialization are indexed, instantiation 405 template <class T, class U> struct [[Tmpl]] {T $xdecl[[x]] = 0;}; 406 template <> struct $specdecl[[Tmpl]]<int, bool> {}; 407 template <class U> struct $partspecdecl[[Tmpl]]<bool, U> {}; 408 extern template struct Tmpl<float, bool>; 409 template struct Tmpl<double, bool>; 411 runSymbolCollector(Header.code(), "");
413 UnorderedElementsAre(
414 AllOf(
QName(
"Tmpl"), DeclRange(Header.range()),
415 ForCodeCompletion(
true)),
416 AllOf(
QName(
"Tmpl"), DeclRange(Header.range(
"specdecl")),
417 ForCodeCompletion(
false)),
418 AllOf(
QName(
"Tmpl"), DeclRange(Header.range(
"partspecdecl")),
419 ForCodeCompletion(
false)),
420 AllOf(
QName(
"Tmpl::x"), DeclRange(Header.range(
"xdecl")),
421 ForCodeCompletion(
false))));
424 TEST_F(SymbolCollectorTest, TemplateArgs) {
425 Annotations Header(R
"( 426 template <class X> class $barclasstemp[[Bar]] {}; 427 template <class T, class U, template<typename> class Z, int Q> 428 struct [[Tmpl]] { T $xdecl[[x]] = 0; }; 430 // template-template, non-type and type full spec 431 template <> struct $specdecl[[Tmpl]]<int, bool, Bar, 3> {}; 433 // template-template, non-type and type partial spec 434 template <class U, int T> struct $partspecdecl[[Tmpl]]<bool, U, Bar, T> {}; 436 extern template struct Tmpl<float, bool, Bar, 8>; 438 template struct Tmpl<double, bool, Bar, 2>; 440 template <typename ...> class $fooclasstemp[[Foo]] {}; 441 // parameter-packs full spec 442 template<> class $parampack[[Foo]]<Bar<int>, int, double> {}; 443 // parameter-packs partial spec 444 template<class T> class $parampackpartial[[Foo]]<T, T> {}; 446 template <int ...> class $bazclasstemp[[Baz]] {}; 447 // non-type parameter-packs full spec 448 template<> class $parampacknontype[[Baz]]<3, 5, 8> {}; 449 // non-type parameter-packs partial spec 450 template<int T> class $parampacknontypepartial[[Baz]]<T, T> {}; 452 template <template <class> class ...> class $fozclasstemp[[Foz]] {}; 453 // template-template parameter-packs full spec 454 template<> class $parampacktempltempl[[Foz]]<Bar, Bar> {}; 455 // template-template parameter-packs partial spec 456 template<template <class> class T> 457 class $parampacktempltemplpartial[[Foz]]<T, T> {}; 459 runSymbolCollector(Header.code(), "");
463 Contains(AllOf(
QName(
"Tmpl"), TemplateArgs(
"<int, bool, Bar, 3>"),
464 DeclRange(Header.range(
"specdecl")),
465 ForCodeCompletion(
false))),
466 Contains(AllOf(
QName(
"Tmpl"), TemplateArgs(
"<bool, U, Bar, T>"),
467 DeclRange(Header.range(
"partspecdecl")),
468 ForCodeCompletion(
false))),
469 Contains(AllOf(
QName(
"Foo"), TemplateArgs(
"<Bar<int>, int, double>"),
470 DeclRange(Header.range(
"parampack")),
471 ForCodeCompletion(
false))),
472 Contains(AllOf(
QName(
"Foo"), TemplateArgs(
"<T, T>"),
473 DeclRange(Header.range(
"parampackpartial")),
474 ForCodeCompletion(
false))),
475 Contains(AllOf(
QName(
"Baz"), TemplateArgs(
"<3, 5, 8>"),
476 DeclRange(Header.range(
"parampacknontype")),
477 ForCodeCompletion(
false))),
478 Contains(AllOf(
QName(
"Baz"), TemplateArgs(
"<T, T>"),
479 DeclRange(Header.range(
"parampacknontypepartial")),
480 ForCodeCompletion(
false))),
481 Contains(AllOf(
QName(
"Foz"), TemplateArgs(
"<Bar, Bar>"),
482 DeclRange(Header.range(
"parampacktempltempl")),
483 ForCodeCompletion(
false))),
484 Contains(AllOf(
QName(
"Foz"), TemplateArgs(
"<T, T>"),
485 DeclRange(Header.range(
"parampacktempltemplpartial")),
486 ForCodeCompletion(
false)))));
489 TEST_F(SymbolCollectorTest, ObjCSymbols) {
490 const std::string Header = R
"( 492 - (void)someMethodName:(void*)name1 lastName:(void*)lName; 495 @implementation Person 496 - (void)someMethodName:(void*)name1 lastName:(void*)lName{ 498 ^(int param){ int bar; }; 502 @interface Person (MyCategory) 503 - (void)someMethodName2:(void*)name2; 506 @implementation Person (MyCategory) 507 - (void)someMethodName2:(void*)name2 { 513 - (void)someMethodName3:(void*)name3; 517 runSymbolCollector(Header,
"", {
"-fblocks",
"-xobjective-c++"});
519 UnorderedElementsAre(
520 QName(
"Person"),
QName(
"Person::someMethodName:lastName:"),
521 QName(
"MyCategory"),
QName(
"Person::someMethodName2:"),
522 QName(
"MyProtocol"),
QName(
"MyProtocol::someMethodName3:")));
525 TEST_F(SymbolCollectorTest, ObjCPropertyImpl) {
526 const std::string Header = R
"( 528 @property(nonatomic) int magic; 531 @implementation Container 535 runSymbolCollector(Header,
"", {
"-xobjective-c++"});
537 EXPECT_THAT(
Symbols, Contains(
QName(
"Container::magic")));
542 TEST_F(SymbolCollectorTest, Locations) {
543 Annotations Header(R
"cpp( 544 // Declared in header, defined in main. 545 extern int $xdecl[[X]]; 546 class $clsdecl[[Cls]]; 547 void $printdecl[[print]](); 549 // Declared in header, defined nowhere. 550 extern int $zdecl[[Z]]; 555 Annotations Main(R"cpp( 557 class $clsdef[[Cls]] {}; 558 void $printdef[[print]]() {} 560 // Declared/defined in main only. 563 runSymbolCollector(Header.code(), Main.code()); 565 UnorderedElementsAre( 566 AllOf(QName("X"), DeclRange(Header.range(
"xdecl")),
567 DefRange(Main.range(
"xdef"))),
568 AllOf(
QName(
"Cls"), DeclRange(Header.range(
"clsdecl")),
569 DefRange(Main.range(
"clsdef"))),
570 AllOf(
QName(
"print"), DeclRange(Header.range(
"printdecl")),
571 DefRange(Main.range(
"printdef"))),
572 AllOf(
QName(
"Z"), DeclRange(Header.range(
"zdecl"))),
573 AllOf(
QName(
"foo"), DeclRange(Header.range(
"foodecl"))),
574 AllOf(
QName(
"Y"), DeclRange(Main.range(
"ydecl")))));
578 Annotations Header(R
"( 585 void $func[[func]](); 587 namespace $ns[[NS]] {} // namespace ref is ignored 590 class $bar[[Bar]] {}; 592 void $func[[func]](); 599 $foo[[Foo]] foo2 = abc; 602 Annotations SymbolsOnlyInMainCode(R"( 605 static const int c = 0; 609 runSymbolCollector(Header.code(), 610 (Main.code() + SymbolsOnlyInMainCode.code()).str()); 614 HaveRanges(Main.ranges(
"foo")))));
616 HaveRanges(Main.ranges(
"bar")))));
618 HaveRanges(Main.ranges(
"func")))));
623 EXPECT_THAT(
Refs, Not(Contains(Pair(
findSymbol(MainSymbols,
"a").ID, _))));
624 EXPECT_THAT(
Refs, Not(Contains(Pair(
findSymbol(MainSymbols,
"b").ID, _))));
625 EXPECT_THAT(
Refs, Not(Contains(Pair(
findSymbol(MainSymbols,
"c").ID, _))));
629 TEST_F(SymbolCollectorTest, HeaderAsMainFile) {
631 Annotations Header(R
"( 632 class $Foo[[Foo]] {}; 634 void $Func[[Func]]() { 641 runSymbolCollector(
"", Header.code());
642 EXPECT_THAT(
Refs, UnorderedElementsAre());
646 runSymbolCollector(
"", Header.code(),
647 {
"-xobjective-c++-header"});
650 HaveRanges(Header.ranges(
"Foo"))),
652 HaveRanges(Header.ranges(
"Func")))));
655 TEST_F(SymbolCollectorTest, RefsInHeaders) {
658 Annotations Header(R
"( 661 runSymbolCollector(Header.code(), "");
663 HaveRanges(Header.ranges()))));
667 std::string Header = R
"( 669 class Derived : public Base {}; 671 runSymbolCollector(Header, "");
675 Contains(Relation{Base.ID, index::SymbolRole::RelationBaseOf,
680 const std::string Header = R
"( 684 class Z {}; // not used anywhere 685 Y* y = nullptr; // used in header doesn't count 686 #define GLOBAL_Z(name) Z name; 688 const std::string Main = R
"( 690 W* w2 = nullptr; // only one usage counts 693 class Y{}; // definition doesn't count as a reference 695 GLOBAL_Z(z); // Not a reference to Z, we don't spell the type. 698 runSymbolCollector(Header, Main);
701 UnorderedElementsAreArray(
702 {AllOf(
QName(
"W"), RefCount(1)), AllOf(
QName(
"X"), RefCount(1)),
703 AllOf(
QName(
"Y"), RefCount(0)), AllOf(
QName(
"Z"), RefCount(0)),
704 AllOf(
QName(
"y"), RefCount(0)), AllOf(
QName(
"z"), RefCount(0)),
705 AllOf(
QName(
"x"), RefCount(0)), AllOf(
QName(
"w"), RefCount(0)),
706 AllOf(
QName(
"w2"), RefCount(0)), AllOf(
QName(
"V"), RefCount(1)),
707 AllOf(
QName(
"v"), RefCount(0))}));
710 TEST_F(SymbolCollectorTest, SymbolRelativeNoFallback) {
711 runSymbolCollector(
"class Foo {};",
"");
712 EXPECT_THAT(
Symbols, UnorderedElementsAre(
716 TEST_F(SymbolCollectorTest, SymbolRelativeWithFallback) {
721 runSymbolCollector(
"class Foo {};",
"");
722 EXPECT_THAT(
Symbols, UnorderedElementsAre(
726 TEST_F(SymbolCollectorTest, UnittestURIScheme) {
730 runSymbolCollector(
"class Foo {};",
"");
731 EXPECT_THAT(
Symbols, UnorderedElementsAre(
732 AllOf(
QName(
"Foo"), DeclURI(
"unittest:///x.h"))));
735 TEST_F(SymbolCollectorTest, IncludeEnums) {
736 const std::string Header = R
"( 752 runSymbolCollector(Header, "");
754 UnorderedElementsAre(
755 AllOf(
QName(
"Red"), ForCodeCompletion(
true)),
756 AllOf(
QName(
"Color"), ForCodeCompletion(
true)),
757 AllOf(
QName(
"Green"), ForCodeCompletion(
true)),
758 AllOf(
QName(
"Color2"), ForCodeCompletion(
true)),
759 AllOf(
QName(
"Color2::Yellow"), ForCodeCompletion(
false)),
760 AllOf(
QName(
"ns"), ForCodeCompletion(
true)),
761 AllOf(
QName(
"ns::Black"), ForCodeCompletion(
true))));
764 TEST_F(SymbolCollectorTest, NamelessSymbols) {
765 const std::string Header = R
"( 770 runSymbolCollector(Header, "");
772 QName(
"(anonymous struct)::a")));
775 TEST_F(SymbolCollectorTest, SymbolFormedFromRegisteredSchemeFromMacro) {
777 Annotations Header(R
"( 779 class name##_Test {}; 781 $expansion[[FF]](abc); 784 class $spelling[[Test]] {}; 789 runSymbolCollector(Header.code(), "");
791 UnorderedElementsAre(
792 AllOf(
QName(
"abc_Test"), DeclRange(Header.range(
"expansion")),
794 AllOf(
QName(
"Test"), DeclRange(Header.range(
"spelling")),
798 TEST_F(SymbolCollectorTest, SymbolFormedByCLI) {
799 Annotations Header(R
"( 801 class $expansion[[NAME]] {}; 804 runSymbolCollector(Header.code(), "", {
"-DNAME=name"});
805 EXPECT_THAT(
Symbols, UnorderedElementsAre(AllOf(
806 QName(
"name"), DeclRange(Header.range(
"expansion")),
810 TEST_F(SymbolCollectorTest, SymbolsInMainFile) {
811 const std::string Main = R
"( 827 runSymbolCollector("", Main);
828 EXPECT_THAT(
Symbols, UnorderedElementsAre(
833 TEST_F(SymbolCollectorTest, Documentation) {
834 const std::string Header = R
"( 842 runSymbolCollector(Header,
"");
844 UnorderedElementsAre(
845 AllOf(
QName(
"Foo"), Doc(
"Doc Foo"), ForCodeCompletion(
true)),
847 ForCodeCompletion(
false))));
850 runSymbolCollector(Header,
"");
852 UnorderedElementsAre(
853 AllOf(
QName(
"Foo"), Doc(
"Doc Foo"), ForCodeCompletion(
true)),
855 ForCodeCompletion(
false))));
858 TEST_F(SymbolCollectorTest, ClassMembers) {
859 const std::string Header = R
"( 868 const std::string Main = R
"( 872 runSymbolCollector(Header, Main); 875 UnorderedElementsAre( 884 TEST_F(SymbolCollectorTest, Scopes) {
885 const std::string Header = R
"( 893 runSymbolCollector(Header, "");
895 UnorderedElementsAre(
QName(
"na"),
QName(
"na::nb"),
899 TEST_F(SymbolCollectorTest, ExternC) {
900 const std::string Header = R
"( 901 extern "C" { class Foo {}; } 903 extern "C" { class Bar {}; } 906 runSymbolCollector(Header, "");
911 TEST_F(SymbolCollectorTest, SkipInlineNamespace) {
912 const std::string Header = R
"( 914 inline namespace nb { 919 // This is still inlined. 925 runSymbolCollector(Header, "");
927 UnorderedElementsAre(
QName(
"na"),
QName(
"na::nb"),
931 TEST_F(SymbolCollectorTest, SymbolWithDocumentation) {
932 const std::string Header = R
"( 935 int ff(int x, double y) { return 0; }
938 runSymbolCollector(Header,
"");
941 UnorderedElementsAre(
942 QName(
"nx"), AllOf(
QName(
"nx::ff"), Labeled(
"ff(int x, double y)"),
947 const std::string Header = R
"( 950 int ff(int x, double y) { return 0; } 953 runSymbolCollector(Header, "");
955 UnorderedElementsAre(
958 AllOf(
QName(
"nx::ff"), Labeled(
"ff(int x, double y)"),
959 Snippet(
"ff(${1:int x}, ${2:double y})"))));
962 TEST_F(SymbolCollectorTest, IncludeHeaderSameAsFileURI) {
964 runSymbolCollector(
"#pragma once\nclass Foo {};",
"");
965 EXPECT_THAT(
Symbols, UnorderedElementsAre(
967 EXPECT_THAT(
Symbols.begin()->IncludeHeaders,
968 UnorderedElementsAre(IncludeHeaderWithRef(
TestHeaderURI, 1u)));
971 TEST_F(SymbolCollectorTest, CanonicalSTLHeader) {
973 CanonicalIncludes Includes;
974 auto Language = LangOptions();
975 Language.CPlusPlus =
true;
978 runSymbolCollector(
"namespace std { class string {}; }",
"");
984 TEST_F(SymbolCollectorTest, IWYUPragma) {
986 CanonicalIncludes Includes;
989 const std::string Header = R
"( 990 // IWYU pragma: private, include the/good/header.h 993 runSymbolCollector(Header, "");
994 EXPECT_THAT(
Symbols, UnorderedElementsAre(
999 TEST_F(SymbolCollectorTest, IWYUPragmaWithDoubleQuotes) {
1001 CanonicalIncludes Includes;
1004 const std::string Header = R
"( 1005 // IWYU pragma: private, include "the/good/header.h" 1008 runSymbolCollector(Header, "");
1009 EXPECT_THAT(
Symbols, UnorderedElementsAre(
1014 TEST_F(SymbolCollectorTest, SkipIncFileWhenCanonicalizeHeaders) {
1016 CanonicalIncludes Includes;
1019 auto IncFile =
testPath(
"test.inc");
1022 llvm::MemoryBuffer::getMemBuffer(
"class X {};"));
1023 runSymbolCollector(
"#include \"test.inc\"\nclass Y {};",
"",
1026 UnorderedElementsAre(AllOf(
QName(
"X"), DeclURI(IncURI),
1032 TEST_F(SymbolCollectorTest, MainFileIsHeaderWhenSkipIncFile) {
1038 auto IncFile =
testPath(
"test.inc");
1041 llvm::MemoryBuffer::getMemBuffer(
"class X {};"));
1042 runSymbolCollector(
"", R
"cpp( 1043 // Can't use #pragma once in a main file clang doesn't think is a header. 1050 EXPECT_THAT(
Symbols, UnorderedElementsAre(AllOf(
QName(
"X"), DeclURI(IncURI),
1054 TEST_F(SymbolCollectorTest, IncFileInNonHeader) {
1058 auto IncFile =
testPath(
"test.inc");
1061 llvm::MemoryBuffer::getMemBuffer(
"class X {};"));
1062 runSymbolCollector(
"", R
"cpp( 1066 EXPECT_THAT(
Symbols, UnorderedElementsAre(AllOf(
QName(
"X"), DeclURI(IncURI),
1072 TEST_F(SymbolCollectorTest, HeaderGuardDetected) {
1075 runSymbolCollector(R
"cpp( 1076 #ifndef HEADER_GUARD_ 1077 #define HEADER_GUARD_ 1079 // Symbols are seen before the header guard is complete. 1083 #endif // Header guard is recognized here. 1086 EXPECT_THAT(
Symbols, Not(Contains(
QName(
"HEADER_GUARD_"))));
1090 TEST_F(SymbolCollectorTest, NonModularHeader) {
1092 EXPECT_THAT(TU.headerSymbols(), ElementsAre(
IncludeHeader()));
1095 TU.ImplicitHeaderGuard =
false;
1096 EXPECT_THAT(TU.headerSymbols(), ElementsAre(Not(
IncludeHeader())));
1101 #error "This file isn't safe to include directly" 1105 TU.ExtraArgs.push_back("-DSECRET");
1106 EXPECT_THAT(TU.headerSymbols(), ElementsAre(Not(
IncludeHeader())));
1109 TEST_F(SymbolCollectorTest, AvoidUsingFwdDeclsAsCanonicalDecls) {
1111 Annotations Header(R
"( 1113 // Forward declarations of TagDecls. 1118 // Canonical declarations. 1119 class $cdecl[[C]] {}; 1120 struct $sdecl[[S]] {}; 1121 union $udecl[[U]] {int $xdecl[[x]]; bool $ydecl[[y]];}; 1123 runSymbolCollector(Header.code(), "");
1126 UnorderedElementsAre(
1138 DefRange(Header.range(
"xdecl"))),
1141 DefRange(Header.range(
"ydecl")))));
1144 TEST_F(SymbolCollectorTest, ClassForwardDeclarationIsCanonical) {
1146 runSymbolCollector(
"#pragma once\nclass X;",
1148 EXPECT_THAT(
Symbols, UnorderedElementsAre(AllOf(
1153 TEST_F(SymbolCollectorTest, UTF16Character) {
1155 Annotations Header(
"class [[pörk]] {};");
1156 runSymbolCollector(Header.code(),
"");
1157 EXPECT_THAT(
Symbols, UnorderedElementsAre(
1158 AllOf(
QName(
"pörk"), DeclRange(Header.range()))));
1161 TEST_F(SymbolCollectorTest, DoNotIndexSymbolsInFriendDecl) {
1162 Annotations Header(R
"( 1169 friend void $bar[[bar]]() {} 1175 runSymbolCollector(Header.code(), "");
1178 UnorderedElementsAre(
1180 AllOf(
QName(
"nx::Y"), DeclRange(Header.range(
"y"))),
1181 AllOf(
QName(
"nx::Z"), DeclRange(Header.range(
"z"))),
1182 AllOf(
QName(
"nx::foo"), DeclRange(Header.range(
"foo"))),
1183 AllOf(
QName(
"nx::bar"), DeclRange(Header.range(
"bar")))));
1186 TEST_F(SymbolCollectorTest, ReferencesInFriendDecl) {
1187 const std::string Header = R
"( 1191 const std::string Main = R
"( 1198 runSymbolCollector(Header, Main);
1199 EXPECT_THAT(
Symbols, UnorderedElementsAre(AllOf(
QName(
"X"), RefCount(1)),
1200 AllOf(
QName(
"Y"), RefCount(1)),
1201 AllOf(
QName(
"C"), RefCount(0))));
1204 TEST_F(SymbolCollectorTest, Origin) {
1206 runSymbolCollector(
"class Foo {};",
"");
1207 EXPECT_THAT(
Symbols, UnorderedElementsAre(
1211 TEST_F(SymbolCollectorTest, CollectMacros) {
1213 Annotations Header(R
"( 1216 #define $mac[[MAC]](x) int x 1217 #define $used[[USED]](y) float y; 1222 Annotations Main(R"( 1223 #define $main[[MAIN]] 1 1228 runSymbolCollector(Header.code(), Main.code());
1231 UnorderedElementsAre(
1235 AllOf(Labeled(
"MAC(x)"), RefCount(0),
1237 DeclRange(Header.range(
"mac")), VisibleOutsideFile()),
1238 AllOf(Labeled(
"USED(y)"), RefCount(1),
1239 DeclRange(Header.range(
"used")), VisibleOutsideFile()),
1240 AllOf(Labeled(
"MAIN"), RefCount(0), DeclRange(Main.range(
"main")),
1241 Not(VisibleOutsideFile()))));
1244 TEST_F(SymbolCollectorTest, DeprecatedSymbols) {
1245 const std::string Header = R
"( 1246 void TestClangc() __attribute__((deprecated("", ""))); 1249 runSymbolCollector(Header, "");
1250 EXPECT_THAT(
Symbols, UnorderedElementsAre(
1251 AllOf(
QName(
"TestClangc"), Deprecated()),
1252 AllOf(
QName(
"TestClangd"), Not(Deprecated()))));
1255 TEST_F(SymbolCollectorTest, ImplementationDetail) {
1256 const std::string Header = R
"( 1257 #define DECL_NAME(x, y) x##_##y##_Decl 1258 #define DECL(x, y) class DECL_NAME(x, y) {}; 1259 DECL(X, Y); // X_Y_Decl 1263 runSymbolCollector(Header, "");
1265 UnorderedElementsAre(
1266 AllOf(
QName(
"X_Y_Decl"), ImplementationDetail()),
1267 AllOf(
QName(
"Public"), Not(ImplementationDetail()))));
1270 TEST_F(SymbolCollectorTest, UsingDecl) {
1271 const char *Header = R
"( 1276 runSymbolCollector(Header, "");
1280 TEST_F(SymbolCollectorTest, CBuiltins) {
1282 const char *Header = R
"( 1283 extern int printf(const char*, ...); 1285 runSymbolCollector(Header, "", {
"-xc"});
1289 TEST_F(SymbolCollectorTest, InvalidSourceLoc) {
1290 const char *Header = R
"( 1291 void operator delete(void*) 1292 __attribute__((__externally_visible__));)"; 1293 runSymbolCollector(Header, "");
1294 EXPECT_THAT(
Symbols, Contains(
QName(
"operator delete")));
std::unique_ptr< CommentHandler > collectIWYUHeaderMaps(CanonicalIncludes *Includes)
Returns a CommentHandler that parses pragma comment on include files to determine when we should incl...
SymbolCollector::Options CollectorOpts
llvm::IntrusiveRefCntPtr< llvm::vfs::InMemoryFileSystem > InMemoryFileSystem
std::string TestHeaderName
bool isInsideMainFile(SourceLocation Loc, const SourceManager &SM)
Returns true iff Loc is inside the main file.
Symbol is visible to other files (not e.g. a static helper function).
void addSystemHeadersMapping(CanonicalIncludes *Includes, const LangOptions &Language)
Adds mapping for system headers and some special symbols (e.g.
const NamedDecl & findUnqualifiedDecl(ParsedAST &AST, llvm::StringRef Name)
std::string TestHeaderURI
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
Symbol is an implementation detail.
SymbolCollector::Options COpts
static TestTU withHeaderCode(llvm::StringRef HeaderCode)
Whether or not this symbol is meant to be used for the code completion.
std::string testPath(PathRef File)
static constexpr llvm::StringLiteral Name
std::shared_ptr< SymbolCollector > Collector
static bool shouldCollectSymbol(const NamedDecl &ND, const ASTContext &ASTCtx, const Options &Opts, bool IsMainFileSymbol)
Returns true is ND should be collected.
static llvm::Expected< URI > create(llvm::StringRef AbsolutePath, llvm::StringRef Scheme)
Creates a URI for a file in the given scheme.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
const Symbol & findSymbol(const SymbolSlab &Slab, llvm::StringRef QName)
CharSourceRange Range
SourceRange for the file name.
std::string IncludeHeader
Indicates if the symbol is deprecated.
std::unique_ptr< GlobalCompilationDatabase > Base
SymbolOrigin Origin
Where this symbol came from. Usually an index provides a constant value.
llvm::StringMap< std::string > Files
const NamedDecl & findDecl(ParsedAST &AST, llvm::StringRef QName)
CommentHandler * PragmaHandler