14#include "clang-include-cleaner/Record.h"
16#include "clang/Basic/FileManager.h"
17#include "clang/Basic/FileSystemOptions.h"
18#include "clang/Basic/SourceLocation.h"
19#include "clang/Frontend/CompilerInstance.h"
20#include "clang/Index/IndexingAction.h"
21#include "clang/Index/IndexingOptions.h"
22#include "clang/Tooling/Tooling.h"
23#include "llvm/ADT/IntrusiveRefCntPtr.h"
24#include "llvm/ADT/StringRef.h"
25#include "llvm/Support/MemoryBuffer.h"
26#include "llvm/Support/VirtualFileSystem.h"
27#include "gmock/gmock-matchers.h"
28#include "gmock/gmock.h"
29#include "gtest/gtest.h"
41using ::testing::AllOf;
42using ::testing::Contains;
44using ::testing::ElementsAre;
45using ::testing::Field;
46using ::testing::IsEmpty;
49using ::testing::UnorderedElementsAre;
50using ::testing::UnorderedElementsAreArray;
54 return (arg.Name + arg.Signature).str() ==
Label;
56MATCHER_P(returnType, D,
"") {
return arg.ReturnType ==
D; }
57MATCHER_P(doc, D,
"") {
return arg.Documentation ==
D; }
59 return (arg.Name + arg.CompletionSnippetSuffix).str() == S;
61MATCHER_P(qName, Name,
"") {
return (arg.Scope + arg.Name).str() == Name; }
63 return arg.TemplateSpecializationArgs == TemplArgs;
65MATCHER_P(hasKind, Kind,
"") {
return arg.SymInfo.Kind == Kind; }
67 return StringRef(arg.CanonicalDeclaration.FileURI) ==
P;
69MATCHER_P(defURI, P,
"") {
return StringRef(arg.Definition.FileURI) ==
P; }
70MATCHER(includeHeader,
"") {
return !arg.IncludeHeaders.empty(); }
72 return (arg.IncludeHeaders.size() == 1) &&
73 (arg.IncludeHeaders.begin()->IncludeHeader == P);
76 return (arg.IncludeHeader == includeHeader) && (arg.References ==
References);
79 return std::make_tuple(Loc.Start.line(), Loc.Start.column(), Loc.End.line(),
81 std::make_tuple(R.start.line, R.start.character, R.end.line,
85 return rangesMatch(arg.CanonicalDeclaration, Pos);
87MATCHER_P(defRange, Pos,
"") {
return rangesMatch(arg.Definition, Pos); }
88MATCHER_P(refCount, R,
"") {
return int(arg.References) == R; }
89MATCHER_P(forCodeCompletion, IsIndexedForCodeCompletion,
"") {
91 IsIndexedForCodeCompletion;
94MATCHER(implementationDetail,
"") {
97MATCHER(visibleOutsideFile,
"") {
101 const Ref &Pos = ::testing::get<0>(arg);
103 return rangesMatch(Pos.Location,
Range);
111::testing::Matcher<const std::vector<Ref> &>
112haveRanges(
const std::vector<Range> Ranges) {
113 return ::testing::UnorderedPointwise(refRange(), Ranges);
116class ShouldCollectSymbolTest :
public ::testing::Test {
118 void build(llvm::StringRef HeaderCode, llvm::StringRef Code =
"") {
119 File.HeaderFilename = HeaderName;
120 File.Filename = FileName;
121 File.HeaderCode = std::string(HeaderCode);
122 File.Code = std::string(Code);
127 bool shouldCollect(llvm::StringRef Name,
bool Qualified =
true) {
129 const NamedDecl &ND =
131 const SourceManager &SM = AST->getSourceManager();
134 ND, AST->getASTContext(), SymbolCollector::Options(), MainFile);
138 std::string HeaderName =
"f.h";
139 std::string FileName =
"f.cpp";
141 std::optional<ParsedAST> AST;
144TEST_F(ShouldCollectSymbolTest, ShouldCollectSymbol) {
148 auto f() { int Local; } // auto ensures function body is parsed.
149 struct { int x; } var;
154 namespace { class InAnonymous {}; }
158 EXPECT_TRUE(shouldCollect(
"nx"));
159 EXPECT_TRUE(shouldCollect(
"nx::X"));
160 EXPECT_TRUE(shouldCollect(
"nx::f"));
161 EXPECT_TRUE(shouldCollect(
"InMain"));
162 EXPECT_TRUE(shouldCollect(
"InAnonymous",
false));
163 EXPECT_TRUE(shouldCollect(
"g"));
165 EXPECT_FALSE(shouldCollect(
"Local",
false));
168TEST_F(ShouldCollectSymbolTest, CollectLocalClassesAndVirtualMethods) {
173 auto LocalLambda = [&](){
175 class ClassInLambda{};
178 } // auto ensures function body is parsed.
181 virtual void LocalVirtual();
182 void LocalConcrete();
190 EXPECT_FALSE(shouldCollect(
"Local",
false));
191 EXPECT_TRUE(shouldCollect(
"ClassInLambda",
false));
192 EXPECT_TRUE(shouldCollect(
"LocalBase",
false));
193 EXPECT_TRUE(shouldCollect(
"LocalVirtual",
false));
194 EXPECT_TRUE(shouldCollect(
"LocalConcrete",
false));
195 EXPECT_FALSE(shouldCollect(
"BaseMember",
false));
196 EXPECT_FALSE(shouldCollect(
"Local",
false));
199TEST_F(ShouldCollectSymbolTest, NoPrivateProtoSymbol) {
200 HeaderName =
"f.proto.h";
202 R
"(// Generated by the protocol buffer compiler. DO NOT EDIT!
204 enum Outer_Enum : int {
208 bool Outer_Enum_IsValid(int);
210 class Outer_Inner {};
212 using Inner = Outer_Inner;
213 using Enum = Outer_Enum;
214 static constexpr Enum KIND1 = Outer_Enum_KIND1;
215 static constexpr Enum Kind_2 = Outer_Enum_Kind_2;
216 static bool Enum_IsValid(int);
219 void _internal_set_x();
227 bool Foo_IsValid(int);
231 EXPECT_FALSE(shouldCollect(
"nx::Outer_Enum"));
232 EXPECT_FALSE(shouldCollect(
"nx::Outer_Enum_KIND1"));
233 EXPECT_FALSE(shouldCollect(
"nx::Outer_Enum_Kind_2"));
234 EXPECT_FALSE(shouldCollect(
"nx::Outer_Enum_IsValid"));
236 EXPECT_TRUE(shouldCollect(
"nx::Outer::Enum"));
237 EXPECT_TRUE(shouldCollect(
"nx::Outer::KIND1"));
238 EXPECT_TRUE(shouldCollect(
"nx::Outer::Kind_2"));
239 EXPECT_TRUE(shouldCollect(
"nx::Outer::Enum_IsValid"));
242 EXPECT_FALSE(shouldCollect(
"nx::Outer_Inner"));
243 EXPECT_TRUE(shouldCollect(
"nx::Outer"));
244 EXPECT_TRUE(shouldCollect(
"nx::Outer::Inner"));
248 EXPECT_TRUE(shouldCollect(
"nx::Outer::x"));
249 EXPECT_TRUE(shouldCollect(
"nx::Outer::set_x"));
250 EXPECT_FALSE(shouldCollect(
"nx::Outer::_internal_set_x"));
251 EXPECT_TRUE(shouldCollect(
"nx::Outer::Outer_y"));
254 EXPECT_TRUE(shouldCollect(
"nx::Foo::FOO_VAL1"));
255 EXPECT_TRUE(shouldCollect(
"nx::FOO_VAL1"));
256 EXPECT_TRUE(shouldCollect(
"nx::Foo_IsValid"));
259 EXPECT_FALSE(shouldCollect(
"nx::Foo::Foo_VAL2"));
260 EXPECT_FALSE(shouldCollect(
"nx::Foo_VAL2"));
263TEST_F(ShouldCollectSymbolTest, DoubleCheckProtoHeaderComment) {
264 HeaderName =
"f.proto.h";
273 EXPECT_TRUE(shouldCollect("nx::Top_Level"));
274 EXPECT_TRUE(shouldCollect(
"nx::Kind_Fine"));
277class SymbolIndexActionFactory :
public tooling::FrontendActionFactory {
279 SymbolIndexActionFactory(SymbolCollector::Options COpts)
280 : COpts(std::move(COpts)) {}
282 std::unique_ptr<FrontendAction> create()
override {
283 class IndexAction :
public ASTFrontendAction {
285 IndexAction(std::shared_ptr<index::IndexDataConsumer> DataConsumer,
286 const index::IndexingOptions &Opts,
287 std::shared_ptr<include_cleaner::PragmaIncludes> PI)
288 : DataConsumer(std::move(DataConsumer)), Opts(Opts),
291 std::unique_ptr<ASTConsumer>
292 CreateASTConsumer(CompilerInstance &CI, llvm::StringRef InFile)
override {
294 return createIndexingASTConsumer(DataConsumer, Opts,
295 CI.getPreprocessorPtr());
298 bool BeginInvocation(CompilerInstance &CI)
override {
300 CI.getLangOpts().CommentOpts.ParseAllComments =
true;
305 std::shared_ptr<index::IndexDataConsumer> DataConsumer;
306 index::IndexingOptions Opts;
307 std::shared_ptr<include_cleaner::PragmaIncludes> PI;
309 index::IndexingOptions IndexOpts;
310 IndexOpts.SystemSymbolFilter =
311 index::IndexingOptions::SystemSymbolFilterKind::All;
312 IndexOpts.IndexFunctionLocals =
true;
313 std::shared_ptr<include_cleaner::PragmaIncludes> PI =
314 std::make_shared<include_cleaner::PragmaIncludes>();
315 COpts.PragmaIncludes = PI.get();
316 Collector = std::make_shared<SymbolCollector>(COpts);
317 return std::make_unique<IndexAction>(Collector, std::move(IndexOpts),
321 std::shared_ptr<SymbolCollector> Collector;
322 SymbolCollector::Options COpts;
325class SymbolCollectorTest :
public ::testing::Test {
327 SymbolCollectorTest()
328 : InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem),
329 TestHeaderName(
testPath(
"symbol.h")),
330 TestFileName(
testPath(
"symbol.cc")) {
331 TestHeaderURI =
URI::create(TestHeaderName).toString();
332 TestFileURI =
URI::create(TestFileName).toString();
337 bool runSymbolCollector(llvm::StringRef HeaderCode, llvm::StringRef MainCode,
338 const std::vector<std::string> &ExtraArgs = {}) {
339 llvm::IntrusiveRefCntPtr<FileManager> Files(
340 new FileManager(FileSystemOptions(), InMemoryFileSystem));
342 auto Factory = std::make_unique<SymbolIndexActionFactory>(CollectorOpts);
344 std::vector<std::string> Args = {
"symbol_collector",
"-fsyntax-only",
345 "-xc++",
"-include", TestHeaderName};
346 Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
349 Args.push_back(TestFileName);
351 tooling::ToolInvocation Invocation(
352 Args, Factory->create(), Files.get(),
353 std::make_shared<PCHContainerOperations>());
357 EXPECT_TRUE(InMemoryFileSystem->addFile(
358 TestHeaderName, 0, llvm::MemoryBuffer::getMemBuffer(HeaderCode)));
359 EXPECT_TRUE(InMemoryFileSystem->addFile(
360 TestFileName, 0, llvm::MemoryBuffer::getMemBuffer(MainCode)));
362 Symbols = Factory->Collector->takeSymbols();
363 Refs = Factory->Collector->takeRefs();
364 Relations = Factory->Collector->takeRelations();
369 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem;
370 std::string TestHeaderName;
371 std::string TestHeaderURI;
372 std::string TestFileName;
373 std::string TestFileURI;
376 RelationSlab Relations;
377 SymbolCollector::Options CollectorOpts;
380TEST_F(SymbolCollectorTest, CollectSymbols) {
381 const std::string Header = R
"(
388 Foo& operator=(const Foo&);
399 static const int KInt = 2;
400 const char* kStr = "123";
403 void ff() {} // ignore
407 auto LocalLambda = [&](){
408 class ClassInLambda{};
415 using int32_t = int32;
430 runSymbolCollector(Header, "");
432 UnorderedElementsAreArray(
433 {AllOf(qName(
"Foo"), forCodeCompletion(
true)),
434 AllOf(qName(
"Foo::Foo"), forCodeCompletion(
false)),
435 AllOf(qName(
"Foo::Foo"), forCodeCompletion(
false)),
436 AllOf(qName(
"Foo::f"), forCodeCompletion(
false)),
437 AllOf(qName(
"Foo::~Foo"), forCodeCompletion(
false)),
438 AllOf(qName(
"Foo::operator="), forCodeCompletion(
false)),
439 AllOf(qName(
"Foo::Nested"), forCodeCompletion(
false)),
440 AllOf(qName(
"Foo::Nested::f"), forCodeCompletion(
false)),
441 AllOf(qName(
"ClassInLambda"), forCodeCompletion(
false)),
442 AllOf(qName(
"Friend"), forCodeCompletion(
true)),
443 AllOf(qName(
"f1"), forCodeCompletion(
true)),
444 AllOf(qName(
"f2"), forCodeCompletion(
true)),
445 AllOf(qName(
"KInt"), forCodeCompletion(
true)),
446 AllOf(qName(
"kStr"), forCodeCompletion(
true)),
447 AllOf(qName(
"foo"), forCodeCompletion(
true)),
448 AllOf(qName(
"foo::bar"), forCodeCompletion(
true)),
449 AllOf(qName(
"foo::int32"), forCodeCompletion(
true)),
450 AllOf(qName(
"foo::int32_t"), forCodeCompletion(
true)),
451 AllOf(qName(
"foo::v1"), forCodeCompletion(
true)),
452 AllOf(qName(
"foo::bar::v2"), forCodeCompletion(
true)),
453 AllOf(qName(
"foo::v2"), forCodeCompletion(
true)),
454 AllOf(qName(
"foo::baz"), forCodeCompletion(
true))}));
457TEST_F(SymbolCollectorTest, FileLocal) {
458 const std::string Header = R
"(
465 const std::string Main = R
"(
474 runSymbolCollector(Header, Main);
476 UnorderedElementsAre(
477 AllOf(qName("Foo"), visibleOutsideFile()),
478 AllOf(qName(
"bar"), visibleOutsideFile()),
479 AllOf(qName(
"a"), Not(visibleOutsideFile())),
480 AllOf(qName(
"B"), Not(visibleOutsideFile())),
481 AllOf(qName(
"c"), Not(visibleOutsideFile())),
483 AllOf(qName(
"ForwardDecl"), Not(visibleOutsideFile()))));
486TEST_F(SymbolCollectorTest, Template) {
488 // Primary template and explicit specialization are indexed, instantiation
490 template <class T, class U> struct [[Tmpl]] {T $xdecl[[x]] = 0;};
491 template <> struct $specdecl[[Tmpl]]<int, bool> {};
492 template <class U> struct $partspecdecl[[Tmpl]]<bool, U> {};
493 extern template struct Tmpl<float, bool>;
494 template struct Tmpl<double, bool>;
496 runSymbolCollector(Header.code(), "");
498 UnorderedElementsAre(
499 AllOf(qName(
"Tmpl"), declRange(Header.range()),
500 forCodeCompletion(
true)),
501 AllOf(qName(
"Tmpl"), declRange(Header.range(
"specdecl")),
502 forCodeCompletion(
false)),
503 AllOf(qName(
"Tmpl"), declRange(Header.range(
"partspecdecl")),
504 forCodeCompletion(
false)),
505 AllOf(qName(
"Tmpl::x"), declRange(Header.range(
"xdecl")),
506 forCodeCompletion(
false))));
509TEST_F(SymbolCollectorTest, templateArgs) {
511 template <class X> class $barclasstemp[[Bar]] {};
512 template <class T, class U, template<typename> class Z, int Q>
513 struct [[Tmpl]] { T $xdecl[[x]] = 0; };
515 // template-template, non-type and type full spec
516 template <> struct $specdecl[[Tmpl]]<int, bool, Bar, 3> {};
518 // template-template, non-type and type partial spec
519 template <class U, int T> struct $partspecdecl[[Tmpl]]<bool, U, Bar, T> {};
521 extern template struct Tmpl<float, bool, Bar, 8>;
523 template struct Tmpl<double, bool, Bar, 2>;
525 template <typename ...> class $fooclasstemp[[Foo]] {};
526 // parameter-packs full spec
527 template<> class $parampack[[Foo]]<Bar<int>, int, double> {};
528 // parameter-packs partial spec
529 template<class T> class $parampackpartial[[Foo]]<T, T> {};
531 template <int ...> class $bazclasstemp[[Baz]] {};
532 // non-type parameter-packs full spec
533 template<> class $parampacknontype[[Baz]]<3, 5, 8> {};
534 // non-type parameter-packs partial spec
535 template<int T> class $parampacknontypepartial[[Baz]]<T, T> {};
537 template <template <class> class ...> class $fozclasstemp[[Foz]] {};
538 // template-template parameter-packs full spec
539 template<> class $parampacktempltempl[[Foz]]<Bar, Bar> {};
540 // template-template parameter-packs partial spec
541 template<template <class> class T>
542 class $parampacktempltemplpartial[[Foz]]<T, T> {};
544 runSymbolCollector(Header.code(), "");
548 Contains(AllOf(qName(
"Tmpl"), templateArgs(
"<int, bool, Bar, 3>"),
549 declRange(Header.range(
"specdecl")),
550 forCodeCompletion(
false))),
551 Contains(AllOf(qName(
"Tmpl"), templateArgs(
"<bool, U, Bar, T>"),
552 declRange(Header.range(
"partspecdecl")),
553 forCodeCompletion(
false))),
554 Contains(AllOf(qName(
"Foo"), templateArgs(
"<Bar<int>, int, double>"),
555 declRange(Header.range(
"parampack")),
556 forCodeCompletion(
false))),
557 Contains(AllOf(qName(
"Foo"), templateArgs(
"<T, T>"),
558 declRange(Header.range(
"parampackpartial")),
559 forCodeCompletion(
false))),
560 Contains(AllOf(qName(
"Baz"), templateArgs(
"<3, 5, 8>"),
561 declRange(Header.range(
"parampacknontype")),
562 forCodeCompletion(
false))),
563 Contains(AllOf(qName(
"Baz"), templateArgs(
"<T, T>"),
564 declRange(Header.range(
"parampacknontypepartial")),
565 forCodeCompletion(
false))),
566 Contains(AllOf(qName(
"Foz"), templateArgs(
"<Bar, Bar>"),
567 declRange(Header.range(
"parampacktempltempl")),
568 forCodeCompletion(
false))),
569 Contains(AllOf(qName(
"Foz"), templateArgs(
"<T, T>"),
570 declRange(Header.range(
"parampacktempltemplpartial")),
571 forCodeCompletion(
false)))));
574TEST_F(SymbolCollectorTest, ObjCRefs) {
577 - (void)$talk[[talk]];
578 - (void)$say[[say]]:(id)something;
580 @interface Person (Category)
581 - (void)categoryMethod;
582 - (void)multiArg:(id)a method:(id)b;
586 @implementation Person
587 - (void)$talk[[talk]] {}
588 - (void)$say[[say]]:(id)something {}
591 void fff(Person *p) {
595 [p multiArg:0 method:0];
599 CollectorOpts.CollectMainFileRefs = true;
601 runSymbolCollector(Header.code(), Main.code(),
602 {
"-fblocks",
"-xobjective-c++",
"-Wno-objc-root-class"});
604 haveRanges(Main.ranges(
"talk")))));
606 haveRanges(Main.ranges(
"say")))));
609 ElementsAre(isSpelled()))));
612 ElementsAre(isSpelled()))));
615TEST_F(SymbolCollectorTest, ObjCSymbols) {
616 const std::string Header = R
"(
618 - (void)someMethodName:(void*)name1 lastName:(void*)lName;
621 @implementation Person
622 - (void)someMethodName:(void*)name1 lastName:(void*)lName{
624 ^(int param){ int bar; };
628 @interface Person (MyCategory)
629 - (void)someMethodName2:(void*)name2;
632 @implementation Person (MyCategory)
633 - (void)someMethodName2:(void*)name2 {
639 - (void)someMethodName3:(void*)name3;
643 runSymbolCollector(Header,
"", {
"-fblocks",
"-xobjective-c++"});
645 UnorderedElementsAre(
646 qName(
"Person"), qName(
"Person::someMethodName:lastName:"),
647 AllOf(qName(
"MyCategory"), forCodeCompletion(
false)),
648 qName(
"Person::someMethodName2:"), qName(
"MyProtocol"),
649 qName(
"MyProtocol::someMethodName3:")));
652TEST_F(SymbolCollectorTest, ObjCPropertyImpl) {
653 const std::string Header = R
"(
655 @property(nonatomic) int magic;
658 @implementation Container
662 runSymbolCollector(Header,
"", {
"-xobjective-c++"});
663 EXPECT_THAT(
Symbols, Contains(qName(
"Container")));
664 EXPECT_THAT(
Symbols, Contains(qName(
"Container::magic")));
669TEST_F(SymbolCollectorTest, ObjCLocations) {
671 // Declared in header, defined in main.
672 @interface $dogdecl[[Dog]]
674 @interface $fluffydecl[[Dog]] (Fluffy)
680 @implementation $dogdef[[Dog]]
682 @implementation $fluffydef[[Dog]] (Fluffy)
684 // Category with no declaration (only implementation).
685 @implementation $ruff[[Dog]] (Ruff)
687 // Implicitly defined interface.
688 @implementation $catdog[[CatDog]]
691 runSymbolCollector(Header.code(), Main.code(),
692 {"-xobjective-c++",
"-Wno-objc-root-class"});
694 UnorderedElementsAre(
695 AllOf(qName(
"Dog"), declRange(Header.range(
"dogdecl")),
696 defRange(Main.range(
"dogdef"))),
697 AllOf(qName(
"Fluffy"), declRange(Header.range(
"fluffydecl")),
698 defRange(Main.range(
"fluffydef"))),
699 AllOf(qName(
"CatDog"), declRange(Main.range(
"catdog")),
700 defRange(Main.range(
"catdog"))),
701 AllOf(qName(
"Ruff"), declRange(Main.range(
"ruff")),
702 defRange(Main.range(
"ruff")))));
705TEST_F(SymbolCollectorTest, ObjCForwardDecls) {
707 // Forward declared in header, declared and defined in main.
710 // Never fully declared so Clang latches onto this decl.
711 @class $catdogdecl[[CatDog]];
714 @protocol $barkerdecl[[Barker]]
717 @interface $dogdecl[[Dog]]<Barker>
720 @implementation $dogdef[[Dog]]
723 @implementation $catdogdef[[CatDog]]
726 runSymbolCollector(Header.code(), Main.code(),
727 {"-xobjective-c++",
"-Wno-objc-root-class"});
729 UnorderedElementsAre(
730 AllOf(qName(
"CatDog"), declRange(Header.range(
"catdogdecl")),
731 defRange(Main.range(
"catdogdef"))),
732 AllOf(qName(
"Dog"), declRange(Main.range(
"dogdecl")),
733 defRange(Main.range(
"dogdef"))),
734 AllOf(qName(
"Barker"), declRange(Main.range(
"barkerdecl"))),
735 qName(
"Barker::woof"), qName(
"Dog::woof")));
738TEST_F(SymbolCollectorTest, ObjCClassExtensions) {
740 @interface $catdecl[[Cat]]
751 runSymbolCollector(Header.code(), Main.code(),
752 {"-xobjective-c++",
"-Wno-objc-root-class"});
754 UnorderedElementsAre(
755 AllOf(qName(
"Cat"), declRange(Header.range(
"catdecl"))),
756 qName(
"Cat::meow"), qName(
"Cat::pur")));
759TEST_F(SymbolCollectorTest, ObjCFrameworkIncludeHeader) {
760 CollectorOpts.CollectIncludePath =
true;
761 auto FrameworksPath =
testPath(
"Frameworks/");
762 std::string FrameworkHeader = R
"(
763 __attribute((objc_root_class))
767 InMemoryFileSystem->addFile(
768 testPath("Frameworks/Foundation.framework/Headers/NSObject.h"), 0,
769 llvm::MemoryBuffer::getMemBuffer(FrameworkHeader));
770 std::string PrivateFrameworkHeader = R
"(
771 #import <Foundation/NSObject.h>
773 @interface PrivateClass : NSObject
776 InMemoryFileSystem->addFile(
778 "Frameworks/Foundation.framework/PrivateHeaders/NSObject+Private.h"),
779 0, llvm::MemoryBuffer::getMemBuffer(PrivateFrameworkHeader));
781 std::string Header = R
"(
782 #import <Foundation/NSObject+Private.h>
783 #import <Foundation/NSObject.h>
785 @interface Container : NSObject
788 std::string Main = "";
790 runSymbolCollector(Header, Main, {
"-F", FrameworksPath,
"-xobjective-c++"});
793 UnorderedElementsAre(
794 AllOf(qName(
"NSObject"), includeHeader(
"<Foundation/NSObject.h>")),
795 AllOf(qName(
"PrivateClass"),
796 includeHeader(
"<Foundation/NSObject+Private.h>")),
797 AllOf(qName(
"Container"))));
800 std::string UmbrellaHeader = R
"(
801 #import <Foundation/NSObject.h>
803 InMemoryFileSystem->addFile(
804 testPath("Frameworks/Foundation.framework/Headers/Foundation.h"), 0,
805 llvm::MemoryBuffer::getMemBuffer(UmbrellaHeader));
806 std::string PrivateUmbrellaHeader = R
"(
807 #import <Foundation/NSObject+Private.h>
809 InMemoryFileSystem->addFile(
810 testPath("Frameworks/Foundation.framework/PrivateHeaders/"
811 "Foundation_Private.h"),
812 0, llvm::MemoryBuffer::getMemBuffer(PrivateUmbrellaHeader));
813 runSymbolCollector(Header, Main, {
"-F", FrameworksPath,
"-xobjective-c++"});
816 UnorderedElementsAre(
817 AllOf(qName(
"NSObject"), includeHeader(
"<Foundation/Foundation.h>")),
818 AllOf(qName(
"PrivateClass"),
819 includeHeader(
"<Foundation/Foundation_Private.h>")),
820 AllOf(qName(
"Container"))));
822 runSymbolCollector(Header, Main,
823 {
"-iframework", FrameworksPath,
"-xobjective-c++"});
826 UnorderedElementsAre(
827 AllOf(qName(
"NSObject"), includeHeader(
"<Foundation/Foundation.h>")),
828 AllOf(qName(
"PrivateClass"),
829 includeHeader(
"<Foundation/Foundation_Private.h>")),
830 AllOf(qName(
"Container"))));
833TEST_F(SymbolCollectorTest, Locations) {
835 // Declared in header, defined in main.
836 extern int $xdecl[[X]];
837 class $clsdecl[[Cls]];
838 void $printdecl[[print]]();
840 // Declared in header, defined nowhere.
841 extern int $zdecl[[Z]];
848 class $clsdef[[Cls]] {};
849 void $printdef[[print]]() {}
851 // Declared/defined in main only.
854 runSymbolCollector(Header.code(), Main.code());
856 UnorderedElementsAre(
857 AllOf(qName("X"), declRange(Header.range(
"xdecl")),
858 defRange(Main.range(
"xdef"))),
859 AllOf(qName(
"Cls"), declRange(Header.range(
"clsdecl")),
860 defRange(Main.range(
"clsdef"))),
861 AllOf(qName(
"print"), declRange(Header.range(
"printdecl")),
862 defRange(Main.range(
"printdef"))),
863 AllOf(qName(
"Z"), declRange(Header.range(
"zdecl"))),
864 AllOf(qName(
"foo"), declRange(Header.range(
"foodecl"))),
865 AllOf(qName(
"Y"), declRange(Main.range(
"ydecl")))));
868TEST_F(SymbolCollectorTest, Refs) {
870 #define MACRO(X) (X + 1)
879 namespace NS {} // namespace ref is ignored
882 class $bar[[Bar]] {};
884 void $func[[func]]();
891 $foo[[Foo]] foo2 = abc;
892 abc = $macro[[MACRO]](1);
896 #define FUNC(X) (X+1)
899 static const int c = FUNC(1);
903 CollectorOpts.CollectMacro = true;
904 runSymbolCollector(Header.code(),
905 (Main.code() + SymbolsOnlyInMainCode.code()).str());
907 haveRanges(Main.ranges(
"foo")))));
909 haveRanges(Main.ranges(
"bar")))));
911 haveRanges(Main.ranges(
"func")))));
914 haveRanges(Main.ranges(
"macro")))));
919 EXPECT_THAT(Refs, Contains(Pair(
findSymbol(MainSymbols,
"a").ID, _)));
920 EXPECT_THAT(Refs, Contains(Pair(
findSymbol(MainSymbols,
"b").ID, _)));
921 EXPECT_THAT(Refs, Not(Contains(Pair(
findSymbol(MainSymbols,
"c").ID, _))));
922 EXPECT_THAT(Refs, Not(Contains(Pair(
findSymbol(MainSymbols,
"FUNC").ID, _))));
928 InMemoryFileSystem =
new llvm::vfs::InMemoryFileSystem();
929 CollectorOpts.CollectMainFileRefs =
true;
930 runSymbolCollector(Header.code(),
931 (Main.code() + SymbolsOnlyInMainCode.code()).str());
939TEST_F(SymbolCollectorTest, RefContainers) {
941 int $toplevel1[[f1]](int);
943 (void) $ref1a[[f1]](1);
944 auto fptr = &$ref1b[[f1]];
946 int $toplevel2[[v1]] = $ref2[[f1]](2);
947 void f3(int arg = $ref3[[f1]](3));
949 int $classscope1[[member1]] = $ref4[[f1]](4);
950 int $classscope2[[member2]] = 42;
952 constexpr int f4(int x) { return x + 1; }
953 template <int I = $ref5[[f4]](0)> struct S2 {};
954 S2<$ref6[[f4]](0)> v2;
955 S2<$ref7a[[f4]](0)> f5(S2<$ref7b[[f4]](0)>);
957 void $namespacescope1[[f6]]();
958 int $namespacescope2[[v3]];
962 CollectorOpts.CollectMainFileRefs = true;
963 runSymbolCollector(
"", Code.code());
964 auto FindRefWithRange = [&](
Range R) -> std::optional<Ref> {
965 for (
auto &Entry : Refs) {
966 for (
auto &
Ref : Entry.second) {
973 auto Container = [&](llvm::StringRef RangeName) {
974 auto Ref = FindRefWithRange(Code.range(RangeName));
975 EXPECT_TRUE(
bool(
Ref));
978 EXPECT_EQ(Container(
"ref1a"),
980 EXPECT_EQ(Container(
"ref1b"),
982 EXPECT_EQ(Container(
"ref2"),
984 EXPECT_EQ(Container(
"ref3"),
986 EXPECT_EQ(Container(
"ref4"),
988 EXPECT_EQ(Container(
"ref5"),
990 EXPECT_EQ(Container(
"ref6"),
992 EXPECT_EQ(Container(
"ref7a"),
994 EXPECT_EQ(Container(
"ref7b"),
997 EXPECT_FALSE(Container(
"classscope1").isNull());
998 EXPECT_FALSE(Container(
"namespacescope1").isNull());
1000 EXPECT_EQ(Container(
"toplevel1"), Container(
"toplevel2"));
1001 EXPECT_EQ(Container(
"classscope1"), Container(
"classscope2"));
1002 EXPECT_EQ(Container(
"namespacescope1"), Container(
"namespacescope2"));
1004 EXPECT_NE(Container(
"toplevel1"), Container(
"namespacescope1"));
1005 EXPECT_NE(Container(
"toplevel1"), Container(
"classscope1"));
1006 EXPECT_NE(Container(
"classscope1"), Container(
"namespacescope1"));
1009TEST_F(SymbolCollectorTest, MacroRefInHeader) {
1011 #define $foo[[FOO]](X) (X + 1)
1012 #define $bar[[BAR]](X) (X + 2)
1014 // Macro defined multiple times.
1015 #define $ud1[[UD]] 1
1016 int ud_1 = $ud1[[UD]];
1019 #define $ud2[[UD]] 2
1020 int ud_2 = $ud2[[UD]];
1023 // Macros from token concatenations not included.
1024 #define $concat[[CONCAT]](X) X##A()
1025 #define $prepend[[PREPEND]](X) MACRO##X()
1026 #define $macroa[[MACROA]]() 123
1027 int B = $concat[[CONCAT]](MACRO);
1028 int D = $prepend[[PREPEND]](A);
1031 int abc = $foo[[FOO]](1) + $bar[[BAR]]($foo[[FOO]](1));
1035 CollectorOpts.RefsInHeaders = true;
1037 CollectorOpts.CollectMacro =
true;
1039 runSymbolCollector(Header.code(),
"");
1042 haveRanges(Header.ranges(
"foo")))));
1044 haveRanges(Header.ranges(
"bar")))));
1046 EXPECT_THAT(Refs, Contains(Pair(_, haveRanges(Header.ranges(
"ud1")))));
1047 EXPECT_THAT(Refs, Contains(Pair(_, haveRanges(Header.ranges(
"ud2")))));
1049 haveRanges(Header.ranges(
"concat")))));
1051 haveRanges(Header.ranges(
"prepend")))));
1053 haveRanges(Header.ranges(
"macroa")))));
1056TEST_F(SymbolCollectorTest, MacroRefWithoutCollectingSymbol) {
1058 #define $foo[[FOO]](X) (X + 1)
1059 int abc = $foo[[FOO]](1);
1062 CollectorOpts.RefsInHeaders = true;
1063 CollectorOpts.CollectMacro =
false;
1064 runSymbolCollector(Header.code(),
"");
1065 EXPECT_THAT(Refs, Contains(Pair(_, haveRanges(Header.ranges(
"foo")))));
1068TEST_F(SymbolCollectorTest, MacrosWithRefFilter) {
1069 Annotations Header(
"#define $macro[[MACRO]](X) (X + 1)");
1070 Annotations Main(
"void foo() { int x = $macro[[MACRO]](1); }");
1072 runSymbolCollector(Header.code(), Main.code());
1073 EXPECT_THAT(Refs, IsEmpty());
1076TEST_F(SymbolCollectorTest, SpelledReferences) {
1078 llvm::StringRef Header;
1079 llvm::StringRef Main;
1080 llvm::StringRef TargetSymbolName;
1088 struct $spelled[[Foo]] {
1092 $spelled[[Foo]] Variable1;
1093 $implicit[[MACRO]] Variable2;
1105 void f() { Foo $implicit[[f]]; f = $spelled[[Foo]]();}
1121 CollectorOpts.RefsInHeaders =
false;
1122 for (
const auto& T : TestCases) {
1123 SCOPED_TRACE(
T.Header +
"\n---\n" +
T.Main);
1127 InMemoryFileSystem =
new llvm::vfs::InMemoryFileSystem;
1128 runSymbolCollector(Header.code(), Main.code());
1130 const auto SpelledRanges = Main.ranges(
"spelled");
1131 const auto ImplicitRanges = Main.ranges(
"implicit");
1134 for (
const auto &SymbolAndRefs : Refs) {
1135 const auto ID = SymbolAndRefs.first;
1138 for (
const auto &
Ref : SymbolAndRefs.second)
1140 SpelledSlabBuilder.insert(ID,
Ref);
1142 ImplicitSlabBuilder.insert(ID,
Ref);
1144 const auto SpelledRefs = std::move(SpelledSlabBuilder).build(),
1145 ImplicitRefs = std::move(ImplicitSlabBuilder).build();
1146 EXPECT_EQ(SpelledRanges.empty(), SpelledRefs.empty());
1147 EXPECT_EQ(ImplicitRanges.empty(), ImplicitRefs.empty());
1148 if (!SpelledRanges.empty())
1149 EXPECT_THAT(SpelledRefs,
1150 Contains(Pair(TargetID, haveRanges(SpelledRanges))));
1151 if (!ImplicitRanges.empty())
1152 EXPECT_THAT(ImplicitRefs,
1153 Contains(Pair(TargetID, haveRanges(ImplicitRanges))));
1157TEST_F(SymbolCollectorTest, NameReferences) {
1159 CollectorOpts.RefsInHeaders =
true;
1168 runSymbolCollector(Header.code(), "");
1172 haveRanges(Header.ranges()))));
1175TEST_F(SymbolCollectorTest, RefsOnMacros) {
1179 CollectorOpts.RefsInHeaders =
true;
1183 #define CAT(X, Y) X##Y
1188 TYPE(TYPE([[Foo]])) foo3;
1189 [[CAT]](Fo, o) foo4;
1193 runSymbolCollector(Header.code(), "");
1195 haveRanges(Header.ranges()))));
1198TEST_F(SymbolCollectorTest, HeaderAsMainFile) {
1201 class $Foo[[Foo]] {};
1203 void $Func[[Func]]() {
1210 TestFileName =
testPath(
"foo.cpp");
1211 runSymbolCollector(
"", Header.code());
1214 haveRanges(Header.ranges(
"Foo"))),
1216 haveRanges(Header.ranges(
"Func")))));
1220 runSymbolCollector(
"", Header.code(),
1221 {
"-xobjective-c++-header"});
1222 EXPECT_THAT(
Symbols, UnorderedElementsAre(qName(
"Foo"), qName(
"Func")));
1225 haveRanges(Header.ranges(
"Foo"))),
1227 haveRanges(Header.ranges(
"Func")))));
1231 runSymbolCollector(
"", Header.code());
1232 EXPECT_THAT(
Symbols, UnorderedElementsAre(qName(
"Foo"), qName(
"Func")));
1235 haveRanges(Header.ranges(
"Foo"))),
1237 haveRanges(Header.ranges(
"Func")))));
1240TEST_F(SymbolCollectorTest, RefsInHeaders) {
1242 CollectorOpts.RefsInHeaders =
true;
1243 CollectorOpts.CollectMacro =
true;
1245 #define $macro[[MACRO]](x) (x+1)
1246 class $foo[[Foo]] {};
1248 runSymbolCollector(Header.code(), "");
1250 haveRanges(Header.ranges(
"foo")))));
1252 haveRanges(Header.ranges(
"macro")))));
1255TEST_F(SymbolCollectorTest, BaseOfRelations) {
1256 std::string Header = R
"(
1258 class Derived : public Base {};
1260 runSymbolCollector(Header, "");
1263 EXPECT_THAT(Relations,
1267TEST_F(SymbolCollectorTest, OverrideRelationsSimpleInheritance) {
1268 std::string Header = R
"cpp(
1272 class B : public A {
1273 void foo() override; // A::foo
1276 class C : public B {
1277 void bar() override; // B::bar
1280 void foo() override; // B::foo
1281 void bar() override; // C::bar
1284 runSymbolCollector(Header, "");
1293 std::vector<Relation> Result;
1294 for (
const Relation &R : Relations)
1296 Result.push_back(R);
1297 EXPECT_THAT(Result, UnorderedElementsAre(
1302TEST_F(SymbolCollectorTest, OverrideRelationsMultipleInheritance) {
1303 std::string Header = R
"cpp(
1310 class C : public B {
1311 void bar() override; // B::bar
1314 class D : public A, C {
1315 void foo() override; // A::foo
1316 void bar() override; // C::bar
1317 void baz() override; // C::baz
1320 runSymbolCollector(Header, "");
1329 std::vector<Relation> Result;
1330 for (
const Relation &R : Relations)
1332 Result.push_back(R);
1333 EXPECT_THAT(Result, UnorderedElementsAre(
1338TEST_F(SymbolCollectorTest, SymbolTagsWithIndexing) {
1342 std::string Header = R
"cpp(
1345 virtual ~A() = default;
1346 virtual void f1() = 0;
1354 void A::f2() const {}
1356 class B final: public A {
1362 runSymbolCollector(Header, "");
1374 UnorderedElementsAre(
1379TEST_F(SymbolCollectorTest, ObjCOverrideRelationsSimpleInheritance) {
1380 std::string Header = R
"cpp(
1385 - (void)foo; // A::foo
1389 - (void)bar; // B::bar
1392 - (void)foo; // B::foo
1393 - (void)bar; // C::bar
1396 runSymbolCollector(Header, "",
1397 {
"-xobjective-c++",
"-Wno-objc-root-class"});
1406 std::vector<Relation> Result;
1407 for (
const Relation &R : Relations)
1409 Result.push_back(R);
1410 EXPECT_THAT(Result, UnorderedElementsAre(
1415TEST_F(SymbolCollectorTest, CountReferences) {
1416 const std::string Header = R
"(
1420 class Z {}; // not used anywhere
1421 Y* y = nullptr; // used in header doesn't count
1422 #define GLOBAL_Z(name) Z name;
1424 const std::string Main = R
"(
1426 W* w2 = nullptr; // only one usage counts
1429 class Y{}; // definition doesn't count as a reference
1431 GLOBAL_Z(z); // Not a reference to Z, we don't spell the type.
1433 CollectorOpts.CountReferences = true;
1434 runSymbolCollector(Header, Main);
1437 UnorderedElementsAreArray(
1438 {AllOf(qName(
"W"), refCount(1)), AllOf(qName(
"X"), refCount(1)),
1439 AllOf(qName(
"Y"), refCount(0)), AllOf(qName(
"Z"), refCount(0)),
1440 AllOf(qName(
"y"), refCount(0)), AllOf(qName(
"z"), refCount(0)),
1441 AllOf(qName(
"x"), refCount(0)), AllOf(qName(
"w"), refCount(0)),
1442 AllOf(qName(
"w2"), refCount(0)), AllOf(qName(
"V"), refCount(1)),
1443 AllOf(qName(
"v"), refCount(0))}));
1446TEST_F(SymbolCollectorTest, SymbolRelativeNoFallback) {
1447 runSymbolCollector(
"class Foo {};",
"");
1448 EXPECT_THAT(
Symbols, UnorderedElementsAre(
1449 AllOf(qName(
"Foo"), declURI(TestHeaderURI))));
1452TEST_F(SymbolCollectorTest, SymbolRelativeWithFallback) {
1453 TestHeaderName =
"x.h";
1454 TestFileName =
"x.cpp";
1456 CollectorOpts.FallbackDir =
testRoot();
1457 runSymbolCollector(
"class Foo {};",
"");
1458 EXPECT_THAT(
Symbols, UnorderedElementsAre(
1459 AllOf(qName(
"Foo"), declURI(TestHeaderURI))));
1462TEST_F(SymbolCollectorTest, UnittestURIScheme) {
1466 runSymbolCollector(
"class Foo {};",
"");
1467 EXPECT_THAT(
Symbols, UnorderedElementsAre(
1468 AllOf(qName(
"Foo"), declURI(
"unittest:///x.h"))));
1471TEST_F(SymbolCollectorTest, IncludeEnums) {
1472 const std::string Header = R
"(
1493 runSymbolCollector(Header, "");
1495 UnorderedElementsAre(
1496 AllOf(qName(
"Red"), forCodeCompletion(
true)),
1497 AllOf(qName(
"Color"), forCodeCompletion(
true)),
1498 AllOf(qName(
"Green"), forCodeCompletion(
true)),
1499 AllOf(qName(
"Color2"), forCodeCompletion(
true)),
1500 AllOf(qName(
"Color2::Yellow"), forCodeCompletion(
true)),
1501 AllOf(qName(
"ns"), forCodeCompletion(
true)),
1502 AllOf(qName(
"ns::Black"), forCodeCompletion(
true)),
1503 AllOf(qName(
"Color3"), forCodeCompletion(
true)),
1504 AllOf(qName(
"Color3::Blue"), forCodeCompletion(
true))));
1507TEST_F(SymbolCollectorTest, NamelessSymbols) {
1508 const std::string Header = R
"(
1513 runSymbolCollector(Header, "");
1515 UnorderedElementsAre(qName(
"Foo"), qName(
"(unnamed struct)::a")));
1518TEST_F(SymbolCollectorTest, SymbolFormedFromRegisteredSchemeFromMacro) {
1522 class name##_Test {};
1524 $expansion[[FF]](abc);
1527 class $spelling[[Test]] {};
1532 runSymbolCollector(Header.code(), "");
1534 UnorderedElementsAre(
1535 AllOf(qName(
"abc_Test"), declRange(Header.range(
"expansion")),
1536 declURI(TestHeaderURI)),
1537 AllOf(qName(
"Test"), declRange(Header.range(
"spelling")),
1538 declURI(TestHeaderURI))));
1541TEST_F(SymbolCollectorTest, SymbolFormedByCLI) {
1544 class $expansion[[NAME]] {};
1547 runSymbolCollector(Header.code(), "", {
"-DNAME=name"});
1548 EXPECT_THAT(
Symbols, UnorderedElementsAre(AllOf(
1549 qName(
"name"), declRange(Header.range(
"expansion")),
1550 declURI(TestHeaderURI))));
1553TEST_F(SymbolCollectorTest, SymbolsInMainFile) {
1554 const std::string Main = R
"(
1570 runSymbolCollector("", Main);
1571 EXPECT_THAT(
Symbols, UnorderedElementsAre(
1572 qName(
"Foo"), qName(
"f1"), qName(
"f2"), qName(
"ff"),
1573 qName(
"foo"), qName(
"foo::Bar"), qName(
"main_f")));
1576TEST_F(SymbolCollectorTest, Documentation) {
1577 const std::string Header = R
"(
1584 CollectorOpts.StoreAllDocumentation = false;
1585 runSymbolCollector(Header,
"");
1587 UnorderedElementsAre(
1588 AllOf(qName(
"Foo"), doc(
"doc Foo"), forCodeCompletion(
true)),
1589 AllOf(qName(
"Foo::f"), doc(
""), returnType(
""),
1590 forCodeCompletion(
false))));
1592 CollectorOpts.StoreAllDocumentation =
true;
1593 runSymbolCollector(Header,
"");
1595 UnorderedElementsAre(
1596 AllOf(qName(
"Foo"), doc(
"doc Foo"), forCodeCompletion(
true)),
1597 AllOf(qName(
"Foo::f"), doc(
"doc f"), returnType(
""),
1598 forCodeCompletion(
false))));
1601TEST_F(SymbolCollectorTest, DocumentationInMain) {
1602 const std::string Header = R
"(
1608 const std::string Main = R
"(
1612 CollectorOpts.StoreAllDocumentation = true;
1613 runSymbolCollector(Header, Main);
1615 UnorderedElementsAre(
1616 AllOf(qName(
"Foo"), doc(
"doc Foo"), forCodeCompletion(
true)),
1617 AllOf(qName(
"Foo::f"), doc(
"doc f"), returnType(
""),
1618 forCodeCompletion(
false))));
1621TEST_F(SymbolCollectorTest, DocumentationAtDeclThenDef) {
1622 const std::string Header = R
"(
1628 const std::string Main = R
"(
1632 CollectorOpts.StoreAllDocumentation = true;
1633 runSymbolCollector(Header, Main);
1635 UnorderedElementsAre(AllOf(qName(
"Foo")),
1636 AllOf(qName(
"Foo::f"), doc(
"doc f decl"))));
1639TEST_F(SymbolCollectorTest, DocumentationAtDefThenDecl) {
1640 const std::string Header = R
"(
1647 CollectorOpts.StoreAllDocumentation = true;
1648 runSymbolCollector(Header,
"" );
1650 UnorderedElementsAre(AllOf(qName(
"f"), doc(
"doc f def"))));
1653TEST_F(SymbolCollectorTest, ClassMembers) {
1654 const std::string Header = R
"(
1663 const std::string Main = R
"(
1667 runSymbolCollector(Header, Main);
1670 UnorderedElementsAre(
1672 AllOf(qName(
"Foo::f"), returnType(
""), forCodeCompletion(
false)),
1673 AllOf(qName(
"Foo::g"), returnType(
""), forCodeCompletion(
false)),
1674 AllOf(qName(
"Foo::sf"), returnType(
""), forCodeCompletion(
false)),
1675 AllOf(qName(
"Foo::ssf"), returnType(
""), forCodeCompletion(
false)),
1676 AllOf(qName(
"Foo::x"), returnType(
""), forCodeCompletion(
false))));
1679TEST_F(SymbolCollectorTest, Scopes) {
1680 const std::string Header = R
"(
1688 runSymbolCollector(Header, "");
1690 UnorderedElementsAre(qName(
"na"), qName(
"na::nb"),
1691 qName(
"na::Foo"), qName(
"na::nb::Bar")));
1694TEST_F(SymbolCollectorTest, ExternC) {
1695 const std::string Header = R
"(
1696 extern "C" { class Foo {}; }
1698 extern "C" { class Bar {}; }
1701 runSymbolCollector(Header, "");
1702 EXPECT_THAT(
Symbols, UnorderedElementsAre(qName(
"na"), qName(
"Foo"),
1706TEST_F(SymbolCollectorTest, SkipInlineNamespace) {
1707 const std::string Header = R
"(
1709 inline namespace nb {
1714 // This is still inlined.
1720 runSymbolCollector(Header, "");
1722 UnorderedElementsAre(qName(
"na"), qName(
"na::nb"),
1723 qName(
"na::Foo"), qName(
"na::Bar")));
1726TEST_F(SymbolCollectorTest, SymbolWithDocumentation) {
1727 const std::string Header = R
"(
1730 int ff(int x, double y) { return 0; }
1733 runSymbolCollector(Header,
"");
1736 UnorderedElementsAre(
1737 qName(
"nx"), AllOf(qName(
"nx::ff"), labeled(
"ff(int x, double y)"),
1738 returnType(
"int"), doc(
"Foo comment."))));
1741TEST_F(SymbolCollectorTest, snippet) {
1742 const std::string Header = R
"(
1745 int ff(int x, double y) { return 0; }
1748 runSymbolCollector(Header, "");
1750 UnorderedElementsAre(
1752 AllOf(qName(
"nx::f"), labeled(
"f()"), snippet(
"f()")),
1753 AllOf(qName(
"nx::ff"), labeled(
"ff(int x, double y)"),
1754 snippet(
"ff(${1:int x}, ${2:double y})"))));
1757TEST_F(SymbolCollectorTest, IncludeHeaderSameAsFileURI) {
1758 CollectorOpts.CollectIncludePath =
true;
1759 runSymbolCollector(
"#pragma once\nclass Foo {};",
"");
1760 EXPECT_THAT(
Symbols, UnorderedElementsAre(
1761 AllOf(qName(
"Foo"), declURI(TestHeaderURI))));
1762 EXPECT_THAT(
Symbols.begin()->IncludeHeaders,
1763 UnorderedElementsAre(IncludeHeaderWithRef(TestHeaderURI, 1u)));
1766TEST_F(SymbolCollectorTest, CanonicalSTLHeader) {
1767 CollectorOpts.CollectIncludePath =
true;
1772 // Move overloads have special handling.
1773 template <typename _T> T&& move(_T&& __value);
1774 template <typename _I, typename _O> _O move(_I, _I, _O);
1775 template <typename _T, typename _O, typename _I> _O move(
1782 UnorderedElementsAre(
1784 AllOf(qName(
"std::string"), declURI(TestHeaderURI),
1785 includeHeader(
"<string>")),
1787 AllOf(labeled(
"move(T &&value)"), includeHeader(
"<utility>")),
1788 AllOf(labeled(
"move(I, I, O)"), includeHeader(
"<algorithm>")),
1789 AllOf(labeled(
"move(T &&, O, O, I)"), includeHeader(
"<algorithm>"))));
1792TEST_F(SymbolCollectorTest, IWYUPragma) {
1793 CollectorOpts.CollectIncludePath =
true;
1794 const std::string Header = R
"(
1795 // IWYU pragma: private, include the/good/header.h
1798 runSymbolCollector(Header, "");
1799 EXPECT_THAT(
Symbols, UnorderedElementsAre(
1800 AllOf(qName(
"Foo"), declURI(TestHeaderURI),
1801 includeHeader(
"\"the/good/header.h\""))));
1804TEST_F(SymbolCollectorTest, IWYUPragmaWithDoubleQuotes) {
1805 CollectorOpts.CollectIncludePath =
true;
1806 const std::string Header = R
"(
1807 // IWYU pragma: private, include "the/good/header.h"
1810 runSymbolCollector(Header, "");
1811 EXPECT_THAT(
Symbols, UnorderedElementsAre(
1812 AllOf(qName(
"Foo"), declURI(TestHeaderURI),
1813 includeHeader(
"\"the/good/header.h\""))));
1816TEST_F(SymbolCollectorTest, IWYUPragmaExport) {
1817 CollectorOpts.CollectIncludePath =
true;
1818 const std::string Header = R
"cpp(#pragma once
1819 #include "exporter.h"
1821 auto ExporterFile =
testPath(
"exporter.h");
1822 InMemoryFileSystem->addFile(
1823 ExporterFile, 0, llvm::MemoryBuffer::getMemBuffer(R
"cpp(#pragma once
1824 #include "private.h" // IWYU pragma: export
1826 auto PrivateFile =
testPath(
"private.h");
1827 InMemoryFileSystem->addFile(
1828 PrivateFile, 0, llvm::MemoryBuffer::getMemBuffer(
"class Foo {};"));
1829 runSymbolCollector(Header,
"",
1831 EXPECT_THAT(
Symbols, UnorderedElementsAre(AllOf(
1837TEST_F(SymbolCollectorTest, MainFileIsHeaderWhenSkipIncFile) {
1838 CollectorOpts.CollectIncludePath =
true;
1841 TestFileName =
testPath(
"no_ext_main");
1842 TestFileURI =
URI::create(TestFileName).toString();
1843 auto IncFile =
testPath(
"test.inc");
1845 InMemoryFileSystem->addFile(IncFile, 0,
1846 llvm::MemoryBuffer::getMemBuffer(
"class X {};"));
1847 runSymbolCollector(
"", R
"cpp(
1848 // Can't use #pragma once in a main file clang doesn't think is a header.
1855 EXPECT_THAT(
Symbols, UnorderedElementsAre(AllOf(qName(
"X"), declURI(IncURI),
1856 includeHeader(TestFileURI))));
1859TEST_F(SymbolCollectorTest, IncFileInNonHeader) {
1860 CollectorOpts.CollectIncludePath =
true;
1861 TestFileName =
testPath(
"main.cc");
1862 TestFileURI =
URI::create(TestFileName).toString();
1863 auto IncFile =
testPath(
"test.inc");
1865 InMemoryFileSystem->addFile(IncFile, 0,
1866 llvm::MemoryBuffer::getMemBuffer(
"class X {};"));
1867 runSymbolCollector(
"", R
"cpp(
1871 EXPECT_THAT(
Symbols, UnorderedElementsAre(AllOf(qName(
"X"), declURI(IncURI),
1872 Not(includeHeader()))));
1877TEST_F(SymbolCollectorTest, HeaderGuardDetected) {
1878 CollectorOpts.CollectIncludePath =
true;
1879 CollectorOpts.CollectMacro =
true;
1880 runSymbolCollector(R
"cpp(
1881 #ifndef HEADER_GUARD_
1882 #define HEADER_GUARD_
1884 // Symbols are seen before the header guard is complete.
1888 #endif // Header guard is recognized here.
1891 EXPECT_THAT(
Symbols, Not(Contains(qName(
"HEADER_GUARD_"))));
1892 EXPECT_THAT(
Symbols, Each(includeHeader()));
1895TEST_F(SymbolCollectorTest, NonModularHeader) {
1897 EXPECT_THAT(TU.headerSymbols(), ElementsAre(includeHeader()));
1900 TU.ImplicitHeaderGuard =
false;
1901 EXPECT_THAT(TU.headerSymbols(), ElementsAre(Not(includeHeader())));
1906#error "This file isn't safe to include directly"
1910 TU.ExtraArgs.push_back("-DSECRET");
1911 EXPECT_THAT(TU.headerSymbols(), ElementsAre(Not(includeHeader())));
1914TEST_F(SymbolCollectorTest, AvoidUsingFwdDeclsAsCanonicalDecls) {
1915 CollectorOpts.CollectIncludePath =
true;
1918 // Forward declarations of TagDecls.
1923 // Canonical declarations.
1924 class $cdecl[[C]] {};
1925 struct $sdecl[[S]] {};
1926 union $udecl[[U]] {int $xdecl[[x]]; bool $ydecl[[y]];};
1928 runSymbolCollector(Header.code(), "");
1931 UnorderedElementsAre(
1932 AllOf(qName(
"C"), declURI(TestHeaderURI),
1933 declRange(Header.range(
"cdecl")), includeHeader(TestHeaderURI),
1934 defURI(TestHeaderURI), defRange(Header.range(
"cdecl"))),
1935 AllOf(qName(
"S"), declURI(TestHeaderURI),
1936 declRange(Header.range(
"sdecl")), includeHeader(TestHeaderURI),
1937 defURI(TestHeaderURI), defRange(Header.range(
"sdecl"))),
1938 AllOf(qName(
"U"), declURI(TestHeaderURI),
1939 declRange(Header.range(
"udecl")), includeHeader(TestHeaderURI),
1940 defURI(TestHeaderURI), defRange(Header.range(
"udecl"))),
1941 AllOf(qName(
"U::x"), declURI(TestHeaderURI),
1942 declRange(Header.range(
"xdecl")), defURI(TestHeaderURI),
1943 defRange(Header.range(
"xdecl"))),
1944 AllOf(qName(
"U::y"), declURI(TestHeaderURI),
1945 declRange(Header.range(
"ydecl")), defURI(TestHeaderURI),
1946 defRange(Header.range(
"ydecl")))));
1949TEST_F(SymbolCollectorTest, ClassForwardDeclarationIsCanonical) {
1950 CollectorOpts.CollectIncludePath =
true;
1951 runSymbolCollector(
"#pragma once\nclass X;",
1953 EXPECT_THAT(
Symbols, UnorderedElementsAre(AllOf(
1954 qName(
"X"), declURI(TestHeaderURI),
1955 includeHeader(TestHeaderURI), defURI(TestFileURI))));
1958TEST_F(SymbolCollectorTest, UTF16Character) {
1961 runSymbolCollector(Header.code(),
"");
1962 EXPECT_THAT(
Symbols, UnorderedElementsAre(
1963 AllOf(qName(
"pörk"), declRange(Header.range()))));
1966TEST_F(SymbolCollectorTest, DoNotIndexSymbolsInFriendDecl) {
1974 friend void $bar[[bar]]() {}
1980 runSymbolCollector(Header.code(), "");
1983 UnorderedElementsAre(
1984 qName(
"nx"), qName(
"nx::X"),
1985 AllOf(qName(
"nx::Y"), declRange(Header.range(
"y"))),
1986 AllOf(qName(
"nx::Z"), declRange(Header.range(
"z"))),
1987 AllOf(qName(
"nx::foo"), declRange(Header.range(
"foo"))),
1988 AllOf(qName(
"nx::bar"), declRange(Header.range(
"bar")))));
1991TEST_F(SymbolCollectorTest, ReferencesInFriendDecl) {
1992 const std::string Header = R
"(
1996 const std::string Main = R
"(
2002 CollectorOpts.CountReferences = true;
2003 runSymbolCollector(Header, Main);
2004 EXPECT_THAT(
Symbols, UnorderedElementsAre(AllOf(qName(
"X"), refCount(1)),
2005 AllOf(qName(
"Y"), refCount(1)),
2006 AllOf(qName(
"C"), refCount(0))));
2009TEST_F(SymbolCollectorTest, Origin) {
2011 runSymbolCollector(
"class Foo {};",
"");
2012 EXPECT_THAT(
Symbols, UnorderedElementsAre(
2014 InMemoryFileSystem =
new llvm::vfs::InMemoryFileSystem;
2015 CollectorOpts.CollectMacro =
true;
2016 runSymbolCollector(
"#define FOO",
"");
2017 EXPECT_THAT(
Symbols, UnorderedElementsAre(
2021TEST_F(SymbolCollectorTest, CollectMacros) {
2022 CollectorOpts.CollectIncludePath =
true;
2026 #define $mac[[MAC]](x) int x
2027 #define $used[[USED]](y) float y;
2033 #define $main[[MAIN]] 1
2036 CollectorOpts.CountReferences = true;
2037 CollectorOpts.CollectMacro =
true;
2038 runSymbolCollector(Header.code(), Main.code());
2041 UnorderedElementsAre(
2042 qName(
"p"), qName(
"t"),
2043 AllOf(qName(
"X"), declURI(TestHeaderURI),
2044 includeHeader(TestHeaderURI)),
2045 AllOf(labeled(
"MAC(x)"), refCount(0),
2047 declRange(Header.range(
"mac")), visibleOutsideFile()),
2048 AllOf(labeled(
"USED(y)"), refCount(1),
2049 declRange(Header.range(
"used")), visibleOutsideFile()),
2050 AllOf(labeled(
"MAIN"), refCount(0), declRange(Main.range(
"main")),
2051 Not(visibleOutsideFile()))));
2054TEST_F(SymbolCollectorTest, DeprecatedSymbols) {
2055 const std::string Header = R
"(
2056 void TestClangc() __attribute__((deprecated("", "")));
2059 runSymbolCollector(Header, "");
2060 EXPECT_THAT(
Symbols, UnorderedElementsAre(
2061 AllOf(qName(
"TestClangc"), deprecated()),
2062 AllOf(qName(
"TestClangd"), Not(deprecated()))));
2065TEST_F(SymbolCollectorTest, implementationDetail) {
2066 const std::string Header = R
"(
2067 #define DECL_NAME(x, y) x##_##y##_Decl
2068 #define DECL(x, y) class DECL_NAME(x, y) {};
2069 DECL(X, Y); // X_Y_Decl
2073 runSymbolCollector(Header, "");
2075 UnorderedElementsAre(
2076 AllOf(qName(
"X_Y_Decl"), implementationDetail()),
2077 AllOf(qName(
"Public"), Not(implementationDetail()))));
2080TEST_F(SymbolCollectorTest, UsingDecl) {
2081 const char *Header = R
"(
2086 runSymbolCollector(Header, "");
2087 EXPECT_THAT(
Symbols, Contains(qName(
"std::foo")));
2090TEST_F(SymbolCollectorTest, CBuiltins) {
2092 const char *Header = R
"(
2093 extern int printf(const char*, ...);
2095 runSymbolCollector(Header, "", {
"-xc"});
2096 EXPECT_THAT(
Symbols, Contains(qName(
"printf")));
2099TEST_F(SymbolCollectorTest, InvalidSourceLoc) {
2100 const char *Header = R
"(
2101 void operator delete(void*)
2102 __attribute__((__externally_visible__));)";
2103 runSymbolCollector(Header, "");
2104 EXPECT_THAT(
Symbols, Contains(qName(
"operator delete")));
2107TEST_F(SymbolCollectorTest, BadUTF8) {
2110 const char *Header =
"int PUNCT = 0;\n"
2111 "/* \xa1 */ int types[] = { /* \xa1 */PUNCT };";
2113 CollectorOpts.RefsInHeaders =
true;
2114 runSymbolCollector(Header,
"");
2115 EXPECT_THAT(
Symbols, Contains(AllOf(qName(
"types"), doc(
"\xef\xbf\xbd "))));
2116 EXPECT_THAT(
Symbols, Contains(qName(
"PUNCT")));
2121TEST_F(SymbolCollectorTest, MacrosInHeaders) {
2122 CollectorOpts.CollectMacro =
true;
2124 runSymbolCollector(
"",
"#define X");
2126 UnorderedElementsAre(AllOf(qName(
"X"), forCodeCompletion(
true))));
2130TEST_F(SymbolCollectorTest, UndefOfModuleMacro) {
2132 TU.AdditionalFiles["bar.h"] = R
"cpp(
2136 TU.AdditionalFiles["foo.h"] =
"#define X 1";
2137 TU.AdditionalFiles[
"module.modulemap"] = R
"cpp(
2143 TU.ExtraArgs.push_back("-fmodules");
2144 TU.ExtraArgs.push_back(
"-fmodule-map-file=" +
testPath(
"module.modulemap"));
2145 TU.OverlayRealFileSystemForModules =
true;
2150 EXPECT_THAT(TU.headerSymbols(), Not(Contains(qName(
"X"))));
2153TEST_F(SymbolCollectorTest, NoCrashOnObjCMethodCStyleParam) {
2156 - (void)fun:(bool)foo, bool bar;
2159 TU.ExtraArgs.push_back("-xobjective-c++");
2163 EXPECT_THAT(TU.headerSymbols(),
2164 UnorderedElementsAre(qName(
"Foo"), qName(
"Foo::fun:")));
2167TEST_F(SymbolCollectorTest, Reserved) {
2168 const char *Header = R
"cpp(
2171 namespace _X { int secret; }
2174 CollectorOpts.CollectReserved = true;
2175 runSymbolCollector(Header,
"");
2176 EXPECT_THAT(
Symbols, UnorderedElementsAre(qName(
"__foo"), qName(
"_X"),
2177 qName(
"_X::secret")));
2179 CollectorOpts.CollectReserved =
false;
2180 runSymbolCollector(Header,
"");
2181 EXPECT_THAT(
Symbols, UnorderedElementsAre(qName(
"__foo"), qName(
"_X"),
2182 qName(
"_X::secret")));
2186 InMemoryFileSystem =
new llvm::vfs::InMemoryFileSystem;
2187 runSymbolCollector(
"#pragma GCC system_header\n" + std::string(Header),
"");
2188 EXPECT_THAT(
Symbols, IsEmpty());
2191TEST_F(SymbolCollectorTest, ReservedSymbolInIntrinsicHeader) {
2192 const char *Header = R
"cpp(
2197 TestHeaderName = "xintrin.h";
2199 InMemoryFileSystem =
new llvm::vfs::InMemoryFileSystem;
2200 CollectorOpts.FallbackDir =
testRoot();
2201 runSymbolCollector(
"#pragma GCC system_header\n" + std::string(Header),
"");
2202 EXPECT_THAT(
Symbols, UnorderedElementsAre(qName(
"__foo")));
2205TEST_F(SymbolCollectorTest, Concepts) {
2206 const char *Header = R
"cpp(
2208 concept A = sizeof(T) <= 8;
2210 runSymbolCollector("", Header, {
"-std=c++20"});
2212 UnorderedElementsAre(AllOf(
2213 qName(
"A"), hasKind(clang::index::SymbolKind::Concept))));
2216TEST_F(SymbolCollectorTest, IncludeHeaderForwardDecls) {
2217 CollectorOpts.CollectIncludePath =
true;
2218 const std::string Header = R
"cpp(#pragma once
2222 auto FullFile =
testPath(
"full.h");
2223 InMemoryFileSystem->addFile(FullFile, 0,
2224 llvm::MemoryBuffer::getMemBuffer(R
"cpp(
2226struct Foo {};)cpp"));
2227 runSymbolCollector(Header, "",
2229 EXPECT_THAT(
Symbols, UnorderedElementsAre(AllOf(
Same as llvm::Annotations, but adjusts functions to LSP-specific types for positions and ranges.
RefSlab::Builder is a mutable container that can 'freeze' to RefSlab.
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.
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
const NamedDecl & findDecl(ParsedAST &AST, llvm::StringRef QName)
std::vector< SymbolTag > getSymbolTags(const Symbol &S)
Returns the SymbolTag values for the given indexed S.
bool isInsideMainFile(SourceLocation Loc, const SourceManager &SM)
Returns true iff Loc is inside the main file.
MATCHER_P2(hasFlag, Flag, Path, "")
static const char * toString(OffsetEncoding OE)
std::string testPath(PathRef File, llvm::sys::path::Style Style)
const NamedDecl & findUnqualifiedDecl(ParsedAST &AST, llvm::StringRef Name)
const Symbol & findSymbol(const SymbolSlab &Slab, llvm::StringRef QName)
cppcoreguidelines::ProBoundsAvoidUncheckedContainerAccessCheck P
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Represents a symbol occurrence in the source file.
SymbolID Container
The ID of the symbol whose definition contains this reference.
SymbolLocation Location
The source location where the symbol is named.
Represents a relation between two symbols.
Ensure we have enough bits to represent all SymbolTag values.
@ IndexedForCodeCompletion
Whether or not this symbol is meant to be used for the code completion.
@ Deprecated
Indicates if the symbol is deprecated.
@ ImplementationDetail
Symbol is an implementation detail.
@ VisibleOutsideFile
Symbol is visible to other files (not e.g. a static helper function).
SymbolID ID
The ID of the symbol.
SymbolOrigin Origin
Where this symbol came from. Usually an index provides a constant value.
static TestTU withHeaderCode(llvm::StringRef HeaderCode)
SymbolSlab headerSymbols() const
static TestTU withCode(llvm::StringRef Code)