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; }
62MATCHER_P(hasName, Name,
"") {
return arg.Name == Name; }
64 return arg.TemplateSpecializationArgs == TemplArgs;
66MATCHER_P(hasKind, Kind,
"") {
return arg.SymInfo.Kind == Kind; }
68 return StringRef(arg.CanonicalDeclaration.FileURI) ==
P;
70MATCHER_P(defURI, P,
"") {
return StringRef(arg.Definition.FileURI) ==
P; }
71MATCHER(includeHeader,
"") {
return !arg.IncludeHeaders.empty(); }
73 return (arg.IncludeHeaders.size() == 1) &&
74 (arg.IncludeHeaders.begin()->IncludeHeader == P);
77 return (arg.IncludeHeader == includeHeader) && (arg.References ==
References);
80 return std::make_tuple(Loc.Start.line(), Loc.Start.column(), Loc.End.line(),
82 std::make_tuple(R.start.line, R.start.character, R.end.line,
86 return rangesMatch(arg.CanonicalDeclaration, Pos);
88MATCHER_P(defRange, Pos,
"") {
return rangesMatch(arg.Definition, Pos); }
89MATCHER_P(refCount, R,
"") {
return int(arg.References) == R; }
90MATCHER_P(forCodeCompletion, IsIndexedForCodeCompletion,
"") {
92 IsIndexedForCodeCompletion;
95MATCHER(implementationDetail,
"") {
98MATCHER(visibleOutsideFile,
"") {
102 const Ref &Pos = ::testing::get<0>(arg);
104 return rangesMatch(Pos.Location,
Range);
112::testing::Matcher<const std::vector<Ref> &>
113haveRanges(
const std::vector<Range> Ranges) {
114 return ::testing::UnorderedPointwise(refRange(), Ranges);
117class ShouldCollectSymbolTest :
public ::testing::Test {
119 void build(llvm::StringRef HeaderCode, llvm::StringRef Code =
"") {
120 File.HeaderFilename = HeaderName;
121 File.Filename = FileName;
122 File.HeaderCode = std::string(HeaderCode);
123 File.Code = std::string(Code);
128 bool shouldCollect(llvm::StringRef Name,
bool Qualified =
true) {
130 const NamedDecl &ND =
132 const SourceManager &SM = AST->getSourceManager();
135 ND, AST->getASTContext(), SymbolCollector::Options(), MainFile);
139 std::string HeaderName =
"f.h";
140 std::string FileName =
"f.cpp";
142 std::optional<ParsedAST> AST;
145TEST_F(ShouldCollectSymbolTest, ShouldCollectSymbol) {
149 auto f() { int Local; } // auto ensures function body is parsed.
150 struct { int x; } var;
155 namespace { class InAnonymous {}; }
159 EXPECT_TRUE(shouldCollect(
"nx"));
160 EXPECT_TRUE(shouldCollect(
"nx::X"));
161 EXPECT_TRUE(shouldCollect(
"nx::f"));
162 EXPECT_TRUE(shouldCollect(
"InMain"));
163 EXPECT_TRUE(shouldCollect(
"InAnonymous",
false));
164 EXPECT_TRUE(shouldCollect(
"g"));
166 EXPECT_FALSE(shouldCollect(
"Local",
false));
169TEST_F(ShouldCollectSymbolTest, CollectLocalClassesAndVirtualMethods) {
174 auto LocalLambda = [&](){
176 class ClassInLambda{};
179 } // auto ensures function body is parsed.
182 virtual void LocalVirtual();
183 void LocalConcrete();
191 EXPECT_FALSE(shouldCollect(
"Local",
false));
192 EXPECT_TRUE(shouldCollect(
"ClassInLambda",
false));
193 EXPECT_TRUE(shouldCollect(
"LocalBase",
false));
194 EXPECT_TRUE(shouldCollect(
"LocalVirtual",
false));
195 EXPECT_TRUE(shouldCollect(
"LocalConcrete",
false));
196 EXPECT_FALSE(shouldCollect(
"BaseMember",
false));
197 EXPECT_FALSE(shouldCollect(
"Local",
false));
200TEST_F(ShouldCollectSymbolTest, NoPrivateProtoSymbol) {
201 HeaderName =
"f.proto.h";
203 R
"(// Generated by the protocol buffer compiler. DO NOT EDIT!
205 enum Outer_Enum : int {
209 bool Outer_Enum_IsValid(int);
211 class Outer_Inner {};
213 using Inner = Outer_Inner;
214 using Enum = Outer_Enum;
215 static constexpr Enum KIND1 = Outer_Enum_KIND1;
216 static constexpr Enum Kind_2 = Outer_Enum_Kind_2;
217 static bool Enum_IsValid(int);
220 void _internal_set_x();
228 bool Foo_IsValid(int);
232 EXPECT_FALSE(shouldCollect(
"nx::Outer_Enum"));
233 EXPECT_FALSE(shouldCollect(
"nx::Outer_Enum_KIND1"));
234 EXPECT_FALSE(shouldCollect(
"nx::Outer_Enum_Kind_2"));
235 EXPECT_FALSE(shouldCollect(
"nx::Outer_Enum_IsValid"));
237 EXPECT_TRUE(shouldCollect(
"nx::Outer::Enum"));
238 EXPECT_TRUE(shouldCollect(
"nx::Outer::KIND1"));
239 EXPECT_TRUE(shouldCollect(
"nx::Outer::Kind_2"));
240 EXPECT_TRUE(shouldCollect(
"nx::Outer::Enum_IsValid"));
243 EXPECT_FALSE(shouldCollect(
"nx::Outer_Inner"));
244 EXPECT_TRUE(shouldCollect(
"nx::Outer"));
245 EXPECT_TRUE(shouldCollect(
"nx::Outer::Inner"));
249 EXPECT_TRUE(shouldCollect(
"nx::Outer::x"));
250 EXPECT_TRUE(shouldCollect(
"nx::Outer::set_x"));
251 EXPECT_FALSE(shouldCollect(
"nx::Outer::_internal_set_x"));
252 EXPECT_TRUE(shouldCollect(
"nx::Outer::Outer_y"));
255 EXPECT_TRUE(shouldCollect(
"nx::Foo::FOO_VAL1"));
256 EXPECT_TRUE(shouldCollect(
"nx::FOO_VAL1"));
257 EXPECT_TRUE(shouldCollect(
"nx::Foo_IsValid"));
260 EXPECT_FALSE(shouldCollect(
"nx::Foo::Foo_VAL2"));
261 EXPECT_FALSE(shouldCollect(
"nx::Foo_VAL2"));
264TEST_F(ShouldCollectSymbolTest, DoubleCheckProtoHeaderComment) {
265 HeaderName =
"f.proto.h";
274 EXPECT_TRUE(shouldCollect("nx::Top_Level"));
275 EXPECT_TRUE(shouldCollect(
"nx::Kind_Fine"));
278class SymbolIndexActionFactory :
public tooling::FrontendActionFactory {
280 SymbolIndexActionFactory(SymbolCollector::Options COpts)
281 : COpts(std::move(COpts)) {}
283 std::unique_ptr<FrontendAction> create()
override {
284 class IndexAction :
public ASTFrontendAction {
286 IndexAction(std::shared_ptr<index::IndexDataConsumer> DataConsumer,
287 const index::IndexingOptions &Opts,
288 std::shared_ptr<include_cleaner::PragmaIncludes> PI)
289 : DataConsumer(std::move(DataConsumer)), Opts(Opts),
292 std::unique_ptr<ASTConsumer>
293 CreateASTConsumer(CompilerInstance &CI, llvm::StringRef InFile)
override {
295 return createIndexingASTConsumer(DataConsumer, Opts,
296 CI.getPreprocessorPtr());
299 bool BeginInvocation(CompilerInstance &CI)
override {
301 CI.getLangOpts().CommentOpts.ParseAllComments =
true;
306 std::shared_ptr<index::IndexDataConsumer> DataConsumer;
307 index::IndexingOptions Opts;
308 std::shared_ptr<include_cleaner::PragmaIncludes> PI;
310 index::IndexingOptions IndexOpts;
311 IndexOpts.SystemSymbolFilter =
312 index::IndexingOptions::SystemSymbolFilterKind::All;
313 IndexOpts.IndexFunctionLocals =
true;
314 std::shared_ptr<include_cleaner::PragmaIncludes> PI =
315 std::make_shared<include_cleaner::PragmaIncludes>();
316 COpts.PragmaIncludes = PI.get();
317 Collector = std::make_shared<SymbolCollector>(COpts);
318 return std::make_unique<IndexAction>(Collector, std::move(IndexOpts),
322 std::shared_ptr<SymbolCollector> Collector;
323 SymbolCollector::Options COpts;
326class SymbolCollectorTest :
public ::testing::Test {
328 SymbolCollectorTest()
329 : InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem),
330 TestHeaderName(
testPath(
"symbol.h")),
331 TestFileName(
testPath(
"symbol.cc")) {
332 TestHeaderURI =
URI::create(TestHeaderName).toString();
333 TestFileURI =
URI::create(TestFileName).toString();
338 bool runSymbolCollector(llvm::StringRef HeaderCode, llvm::StringRef MainCode,
339 const std::vector<std::string> &ExtraArgs = {}) {
340 llvm::IntrusiveRefCntPtr<FileManager> Files(
341 new FileManager(FileSystemOptions(), InMemoryFileSystem));
343 auto Factory = std::make_unique<SymbolIndexActionFactory>(CollectorOpts);
345 std::vector<std::string> Args = {
"symbol_collector",
"-fsyntax-only",
346 "-xc++",
"-include", TestHeaderName};
347 Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
350 Args.push_back(TestFileName);
352 tooling::ToolInvocation Invocation(
353 Args, Factory->create(), Files.get(),
354 std::make_shared<PCHContainerOperations>());
358 EXPECT_TRUE(InMemoryFileSystem->addFile(
359 TestHeaderName, 0, llvm::MemoryBuffer::getMemBuffer(HeaderCode)));
360 EXPECT_TRUE(InMemoryFileSystem->addFile(
361 TestFileName, 0, llvm::MemoryBuffer::getMemBuffer(MainCode)));
363 Symbols = Factory->Collector->takeSymbols();
364 Refs = Factory->Collector->takeRefs();
365 Relations = Factory->Collector->takeRelations();
370 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem;
371 std::string TestHeaderName;
372 std::string TestHeaderURI;
373 std::string TestFileName;
374 std::string TestFileURI;
377 RelationSlab Relations;
378 SymbolCollector::Options CollectorOpts;
381TEST_F(SymbolCollectorTest, CollectSymbols) {
382 const std::string Header = R
"(
389 Foo& operator=(const Foo&);
400 static const int KInt = 2;
401 const char* kStr = "123";
404 void ff() {} // ignore
408 auto LocalLambda = [&](){
409 class ClassInLambda{};
416 using int32_t = int32;
431 runSymbolCollector(Header, "");
433 UnorderedElementsAreArray(
434 {AllOf(qName(
"Foo"), forCodeCompletion(
true)),
435 AllOf(qName(
"Foo::Foo"), forCodeCompletion(
false)),
436 AllOf(qName(
"Foo::Foo"), forCodeCompletion(
false)),
437 AllOf(qName(
"Foo::f"), forCodeCompletion(
false)),
438 AllOf(qName(
"Foo::~Foo"), forCodeCompletion(
false)),
439 AllOf(qName(
"Foo::operator="), forCodeCompletion(
false)),
440 AllOf(qName(
"Foo::Nested"), forCodeCompletion(
false)),
441 AllOf(qName(
"Foo::Nested::f"), forCodeCompletion(
false)),
442 AllOf(qName(
"ClassInLambda"), forCodeCompletion(
false)),
443 AllOf(qName(
"Friend"), forCodeCompletion(
true)),
444 AllOf(qName(
"f1"), forCodeCompletion(
true)),
445 AllOf(qName(
"f2"), forCodeCompletion(
true)),
446 AllOf(qName(
"KInt"), forCodeCompletion(
true)),
447 AllOf(qName(
"kStr"), forCodeCompletion(
true)),
448 AllOf(qName(
"foo"), forCodeCompletion(
true)),
449 AllOf(qName(
"foo::bar"), forCodeCompletion(
true)),
450 AllOf(qName(
"foo::int32"), forCodeCompletion(
true)),
451 AllOf(qName(
"foo::int32_t"), forCodeCompletion(
true)),
452 AllOf(qName(
"foo::v1"), forCodeCompletion(
true)),
453 AllOf(qName(
"foo::bar::v2"), forCodeCompletion(
true)),
454 AllOf(qName(
"foo::v2"), forCodeCompletion(
true)),
455 AllOf(qName(
"foo::baz"), forCodeCompletion(
true))}));
458TEST_F(SymbolCollectorTest, FileLocal) {
459 const std::string Header = R
"(
466 const std::string Main = R
"(
475 runSymbolCollector(Header, Main);
477 UnorderedElementsAre(
478 AllOf(qName("Foo"), visibleOutsideFile()),
479 AllOf(qName(
"bar"), visibleOutsideFile()),
480 AllOf(qName(
"a"), Not(visibleOutsideFile())),
481 AllOf(qName(
"B"), Not(visibleOutsideFile())),
482 AllOf(qName(
"c"), Not(visibleOutsideFile())),
484 AllOf(qName(
"ForwardDecl"), Not(visibleOutsideFile()))));
487TEST_F(SymbolCollectorTest, Template) {
489 // Primary template and explicit specialization are indexed, instantiation
491 template <class T, class U> struct [[Tmpl]] {T $xdecl[[x]] = 0;};
492 template <> struct $specdecl[[Tmpl]]<int, bool> {};
493 template <class U> struct $partspecdecl[[Tmpl]]<bool, U> {};
494 extern template struct Tmpl<float, bool>;
495 template struct Tmpl<double, bool>;
497 runSymbolCollector(Header.code(), "");
499 UnorderedElementsAre(
500 AllOf(qName(
"Tmpl"), declRange(Header.range()),
501 forCodeCompletion(
true)),
502 AllOf(qName(
"Tmpl"), declRange(Header.range(
"specdecl")),
503 forCodeCompletion(
false)),
504 AllOf(qName(
"Tmpl"), declRange(Header.range(
"partspecdecl")),
505 forCodeCompletion(
false)),
506 AllOf(qName(
"Tmpl::x"), declRange(Header.range(
"xdecl")),
507 forCodeCompletion(
false))));
510TEST_F(SymbolCollectorTest, templateArgs) {
512 template <class X> class $barclasstemp[[Bar]] {};
513 template <class T, class U, template<typename> class Z, int Q>
514 struct [[Tmpl]] { T $xdecl[[x]] = 0; };
516 // template-template, non-type and type full spec
517 template <> struct $specdecl[[Tmpl]]<int, bool, Bar, 3> {};
519 // template-template, non-type and type partial spec
520 template <class U, int T> struct $partspecdecl[[Tmpl]]<bool, U, Bar, T> {};
522 extern template struct Tmpl<float, bool, Bar, 8>;
524 template struct Tmpl<double, bool, Bar, 2>;
526 template <typename ...> class $fooclasstemp[[Foo]] {};
527 // parameter-packs full spec
528 template<> class $parampack[[Foo]]<Bar<int>, int, double> {};
529 // parameter-packs partial spec
530 template<class T> class $parampackpartial[[Foo]]<T, T> {};
532 template <int ...> class $bazclasstemp[[Baz]] {};
533 // non-type parameter-packs full spec
534 template<> class $parampacknontype[[Baz]]<3, 5, 8> {};
535 // non-type parameter-packs partial spec
536 template<int T> class $parampacknontypepartial[[Baz]]<T, T> {};
538 template <template <class> class ...> class $fozclasstemp[[Foz]] {};
539 // template-template parameter-packs full spec
540 template<> class $parampacktempltempl[[Foz]]<Bar, Bar> {};
541 // template-template parameter-packs partial spec
542 template<template <class> class T>
543 class $parampacktempltemplpartial[[Foz]]<T, T> {};
545 runSymbolCollector(Header.code(), "");
549 Contains(AllOf(qName(
"Tmpl"), templateArgs(
"<int, bool, Bar, 3>"),
550 declRange(Header.range(
"specdecl")),
551 forCodeCompletion(
false))),
552 Contains(AllOf(qName(
"Tmpl"), templateArgs(
"<bool, U, Bar, T>"),
553 declRange(Header.range(
"partspecdecl")),
554 forCodeCompletion(
false))),
555 Contains(AllOf(qName(
"Foo"), templateArgs(
"<Bar<int>, int, double>"),
556 declRange(Header.range(
"parampack")),
557 forCodeCompletion(
false))),
558 Contains(AllOf(qName(
"Foo"), templateArgs(
"<T, T>"),
559 declRange(Header.range(
"parampackpartial")),
560 forCodeCompletion(
false))),
561 Contains(AllOf(qName(
"Baz"), templateArgs(
"<3, 5, 8>"),
562 declRange(Header.range(
"parampacknontype")),
563 forCodeCompletion(
false))),
564 Contains(AllOf(qName(
"Baz"), templateArgs(
"<T, T>"),
565 declRange(Header.range(
"parampacknontypepartial")),
566 forCodeCompletion(
false))),
567 Contains(AllOf(qName(
"Foz"), templateArgs(
"<Bar, Bar>"),
568 declRange(Header.range(
"parampacktempltempl")),
569 forCodeCompletion(
false))),
570 Contains(AllOf(qName(
"Foz"), templateArgs(
"<T, T>"),
571 declRange(Header.range(
"parampacktempltemplpartial")),
572 forCodeCompletion(
false)))));
575TEST_F(SymbolCollectorTest, ObjCRefs) {
578 - (void)$talk[[talk]];
579 - (void)$say[[say]]:(id)something;
581 @interface Person (Category)
582 - (void)categoryMethod;
583 - (void)multiArg:(id)a method:(id)b;
587 @implementation Person
588 - (void)$talk[[talk]] {}
589 - (void)$say[[say]]:(id)something {}
592 void fff(Person *p) {
596 [p multiArg:0 method:0];
600 CollectorOpts.CollectMainFileRefs = true;
602 runSymbolCollector(Header.code(), Main.code(),
603 {
"-fblocks",
"-xobjective-c++",
"-Wno-objc-root-class"});
605 haveRanges(Main.ranges(
"talk")))));
607 haveRanges(Main.ranges(
"say")))));
610 ElementsAre(isSpelled()))));
613 ElementsAre(isSpelled()))));
616TEST_F(SymbolCollectorTest, ObjCSymbols) {
617 const std::string Header = R
"(
619 - (void)someMethodName:(void*)name1 lastName:(void*)lName;
622 @implementation Person
623 - (void)someMethodName:(void*)name1 lastName:(void*)lName{
625 ^(int param){ int bar; };
629 @interface Person (MyCategory)
630 - (void)someMethodName2:(void*)name2;
633 @implementation Person (MyCategory)
634 - (void)someMethodName2:(void*)name2 {
640 - (void)someMethodName3:(void*)name3;
644 runSymbolCollector(Header,
"", {
"-fblocks",
"-xobjective-c++"});
646 UnorderedElementsAre(
647 qName(
"Person"), qName(
"Person::someMethodName:lastName:"),
648 AllOf(qName(
"MyCategory"), forCodeCompletion(
false)),
649 qName(
"Person::someMethodName2:"), qName(
"MyProtocol"),
650 qName(
"MyProtocol::someMethodName3:")));
653TEST_F(SymbolCollectorTest, ObjCPropertyImpl) {
654 const std::string Header = R
"(
656 @property(nonatomic) int magic;
659 @implementation Container
663 runSymbolCollector(Header,
"", {
"-xobjective-c++"});
664 EXPECT_THAT(
Symbols, Contains(qName(
"Container")));
665 EXPECT_THAT(
Symbols, Contains(qName(
"Container::magic")));
670TEST_F(SymbolCollectorTest, ObjCLocations) {
672 // Declared in header, defined in main.
673 @interface $dogdecl[[Dog]]
675 @interface $fluffydecl[[Dog]] (Fluffy)
681 @implementation $dogdef[[Dog]]
683 @implementation $fluffydef[[Dog]] (Fluffy)
685 // Category with no declaration (only implementation).
686 @implementation $ruff[[Dog]] (Ruff)
688 // Implicitly defined interface.
689 @implementation $catdog[[CatDog]]
692 runSymbolCollector(Header.code(), Main.code(),
693 {"-xobjective-c++",
"-Wno-objc-root-class"});
695 UnorderedElementsAre(
696 AllOf(qName(
"Dog"), declRange(Header.range(
"dogdecl")),
697 defRange(Main.range(
"dogdef"))),
698 AllOf(qName(
"Fluffy"), declRange(Header.range(
"fluffydecl")),
699 defRange(Main.range(
"fluffydef"))),
700 AllOf(qName(
"CatDog"), declRange(Main.range(
"catdog")),
701 defRange(Main.range(
"catdog"))),
702 AllOf(qName(
"Ruff"), declRange(Main.range(
"ruff")),
703 defRange(Main.range(
"ruff")))));
706TEST_F(SymbolCollectorTest, ObjCForwardDecls) {
708 // Forward declared in header, declared and defined in main.
711 // Never fully declared so Clang latches onto this decl.
712 @class $catdogdecl[[CatDog]];
715 @protocol $barkerdecl[[Barker]]
718 @interface $dogdecl[[Dog]]<Barker>
721 @implementation $dogdef[[Dog]]
724 @implementation $catdogdef[[CatDog]]
727 runSymbolCollector(Header.code(), Main.code(),
728 {"-xobjective-c++",
"-Wno-objc-root-class"});
730 UnorderedElementsAre(
731 AllOf(qName(
"CatDog"), declRange(Header.range(
"catdogdecl")),
732 defRange(Main.range(
"catdogdef"))),
733 AllOf(qName(
"Dog"), declRange(Main.range(
"dogdecl")),
734 defRange(Main.range(
"dogdef"))),
735 AllOf(qName(
"Barker"), declRange(Main.range(
"barkerdecl"))),
736 qName(
"Barker::woof"), qName(
"Dog::woof")));
739TEST_F(SymbolCollectorTest, ObjCClassExtensions) {
741 @interface $catdecl[[Cat]]
752 runSymbolCollector(Header.code(), Main.code(),
753 {"-xobjective-c++",
"-Wno-objc-root-class"});
755 UnorderedElementsAre(
756 AllOf(qName(
"Cat"), declRange(Header.range(
"catdecl"))),
757 qName(
"Cat::meow"), qName(
"Cat::pur")));
760TEST_F(SymbolCollectorTest, ObjCFrameworkIncludeHeader) {
761 CollectorOpts.CollectIncludePath =
true;
762 auto FrameworksPath =
testPath(
"Frameworks/");
763 std::string FrameworkHeader = R
"(
764 __attribute((objc_root_class))
768 InMemoryFileSystem->addFile(
769 testPath("Frameworks/Foundation.framework/Headers/NSObject.h"), 0,
770 llvm::MemoryBuffer::getMemBuffer(FrameworkHeader));
771 std::string PrivateFrameworkHeader = R
"(
772 #import <Foundation/NSObject.h>
774 @interface PrivateClass : NSObject
777 InMemoryFileSystem->addFile(
779 "Frameworks/Foundation.framework/PrivateHeaders/NSObject+Private.h"),
780 0, llvm::MemoryBuffer::getMemBuffer(PrivateFrameworkHeader));
782 std::string Header = R
"(
783 #import <Foundation/NSObject+Private.h>
784 #import <Foundation/NSObject.h>
786 @interface Container : NSObject
789 std::string Main = "";
791 runSymbolCollector(Header, Main, {
"-F", FrameworksPath,
"-xobjective-c++"});
794 UnorderedElementsAre(
795 AllOf(qName(
"NSObject"), includeHeader(
"<Foundation/NSObject.h>")),
796 AllOf(qName(
"PrivateClass"),
797 includeHeader(
"<Foundation/NSObject+Private.h>")),
798 AllOf(qName(
"Container"))));
801 std::string UmbrellaHeader = R
"(
802 #import <Foundation/NSObject.h>
804 InMemoryFileSystem->addFile(
805 testPath("Frameworks/Foundation.framework/Headers/Foundation.h"), 0,
806 llvm::MemoryBuffer::getMemBuffer(UmbrellaHeader));
807 std::string PrivateUmbrellaHeader = R
"(
808 #import <Foundation/NSObject+Private.h>
810 InMemoryFileSystem->addFile(
811 testPath("Frameworks/Foundation.framework/PrivateHeaders/"
812 "Foundation_Private.h"),
813 0, llvm::MemoryBuffer::getMemBuffer(PrivateUmbrellaHeader));
814 runSymbolCollector(Header, Main, {
"-F", FrameworksPath,
"-xobjective-c++"});
817 UnorderedElementsAre(
818 AllOf(qName(
"NSObject"), includeHeader(
"<Foundation/Foundation.h>")),
819 AllOf(qName(
"PrivateClass"),
820 includeHeader(
"<Foundation/Foundation_Private.h>")),
821 AllOf(qName(
"Container"))));
823 runSymbolCollector(Header, Main,
824 {
"-iframework", FrameworksPath,
"-xobjective-c++"});
827 UnorderedElementsAre(
828 AllOf(qName(
"NSObject"), includeHeader(
"<Foundation/Foundation.h>")),
829 AllOf(qName(
"PrivateClass"),
830 includeHeader(
"<Foundation/Foundation_Private.h>")),
831 AllOf(qName(
"Container"))));
834TEST_F(SymbolCollectorTest, Locations) {
836 // Declared in header, defined in main.
837 extern int $xdecl[[X]];
838 class $clsdecl[[Cls]];
839 void $printdecl[[print]]();
841 // Declared in header, defined nowhere.
842 extern int $zdecl[[Z]];
849 class $clsdef[[Cls]] {};
850 void $printdef[[print]]() {}
852 // Declared/defined in main only.
855 runSymbolCollector(Header.code(), Main.code());
857 UnorderedElementsAre(
858 AllOf(qName("X"), declRange(Header.range(
"xdecl")),
859 defRange(Main.range(
"xdef"))),
860 AllOf(qName(
"Cls"), declRange(Header.range(
"clsdecl")),
861 defRange(Main.range(
"clsdef"))),
862 AllOf(qName(
"print"), declRange(Header.range(
"printdecl")),
863 defRange(Main.range(
"printdef"))),
864 AllOf(qName(
"Z"), declRange(Header.range(
"zdecl"))),
865 AllOf(qName(
"foo"), declRange(Header.range(
"foodecl"))),
866 AllOf(qName(
"Y"), declRange(Main.range(
"ydecl")))));
869TEST_F(SymbolCollectorTest, Refs) {
871 #define MACRO(X) (X + 1)
880 namespace NS {} // namespace ref is ignored
883 class $bar[[Bar]] {};
885 void $func[[func]]();
892 $foo[[Foo]] foo2 = abc;
893 abc = $macro[[MACRO]](1);
897 #define FUNC(X) (X+1)
900 static const int c = FUNC(1);
904 CollectorOpts.CollectMacro = true;
905 runSymbolCollector(Header.code(),
906 (Main.code() + SymbolsOnlyInMainCode.code()).str());
908 haveRanges(Main.ranges(
"foo")))));
910 haveRanges(Main.ranges(
"bar")))));
912 haveRanges(Main.ranges(
"func")))));
915 haveRanges(Main.ranges(
"macro")))));
920 EXPECT_THAT(Refs, Contains(Pair(
findSymbol(MainSymbols,
"a").ID, _)));
921 EXPECT_THAT(Refs, Contains(Pair(
findSymbol(MainSymbols,
"b").ID, _)));
922 EXPECT_THAT(Refs, Not(Contains(Pair(
findSymbol(MainSymbols,
"c").ID, _))));
923 EXPECT_THAT(Refs, Not(Contains(Pair(
findSymbol(MainSymbols,
"FUNC").ID, _))));
929 InMemoryFileSystem =
new llvm::vfs::InMemoryFileSystem();
930 CollectorOpts.CollectMainFileRefs =
true;
931 runSymbolCollector(Header.code(),
932 (Main.code() + SymbolsOnlyInMainCode.code()).str());
940TEST_F(SymbolCollectorTest, RefContainers) {
942 int $toplevel1[[f1]](int);
944 (void) $ref1a[[f1]](1);
945 auto fptr = &$ref1b[[f1]];
947 int $toplevel2[[v1]] = $ref2[[f1]](2);
948 void f3(int arg = $ref3[[f1]](3));
950 int $classscope1[[member1]] = $ref4[[f1]](4);
951 int $classscope2[[member2]] = 42;
953 constexpr int f4(int x) { return x + 1; }
954 template <int I = $ref5[[f4]](0)> struct S2 {};
955 S2<$ref6[[f4]](0)> v2;
956 S2<$ref7a[[f4]](0)> f5(S2<$ref7b[[f4]](0)>);
958 void $namespacescope1[[f6]]();
959 int $namespacescope2[[v3]];
963 CollectorOpts.CollectMainFileRefs = true;
964 runSymbolCollector(
"", Code.code());
965 auto FindRefWithRange = [&](
Range R) -> std::optional<Ref> {
966 for (
auto &Entry : Refs) {
967 for (
auto &
Ref : Entry.second) {
974 auto Container = [&](llvm::StringRef RangeName) {
975 auto Ref = FindRefWithRange(Code.range(RangeName));
976 EXPECT_TRUE(
bool(
Ref));
979 EXPECT_EQ(Container(
"ref1a"),
981 EXPECT_EQ(Container(
"ref1b"),
983 EXPECT_EQ(Container(
"ref2"),
985 EXPECT_EQ(Container(
"ref3"),
987 EXPECT_EQ(Container(
"ref4"),
989 EXPECT_EQ(Container(
"ref5"),
991 EXPECT_EQ(Container(
"ref6"),
993 EXPECT_EQ(Container(
"ref7a"),
995 EXPECT_EQ(Container(
"ref7b"),
998 EXPECT_FALSE(Container(
"classscope1").isNull());
999 EXPECT_FALSE(Container(
"namespacescope1").isNull());
1001 EXPECT_EQ(Container(
"toplevel1"), Container(
"toplevel2"));
1002 EXPECT_EQ(Container(
"classscope1"), Container(
"classscope2"));
1003 EXPECT_EQ(Container(
"namespacescope1"), Container(
"namespacescope2"));
1005 EXPECT_NE(Container(
"toplevel1"), Container(
"namespacescope1"));
1006 EXPECT_NE(Container(
"toplevel1"), Container(
"classscope1"));
1007 EXPECT_NE(Container(
"classscope1"), Container(
"namespacescope1"));
1010TEST_F(SymbolCollectorTest, MacroRefInHeader) {
1012 #define $foo[[FOO]](X) (X + 1)
1013 #define $bar[[BAR]](X) (X + 2)
1015 // Macro defined multiple times.
1016 #define $ud1[[UD]] 1
1017 int ud_1 = $ud1[[UD]];
1020 #define $ud2[[UD]] 2
1021 int ud_2 = $ud2[[UD]];
1024 // Macros from token concatenations not included.
1025 #define $concat[[CONCAT]](X) X##A()
1026 #define $prepend[[PREPEND]](X) MACRO##X()
1027 #define $macroa[[MACROA]]() 123
1028 int B = $concat[[CONCAT]](MACRO);
1029 int D = $prepend[[PREPEND]](A);
1032 int abc = $foo[[FOO]](1) + $bar[[BAR]]($foo[[FOO]](1));
1036 CollectorOpts.RefsInHeaders = true;
1038 CollectorOpts.CollectMacro =
true;
1040 runSymbolCollector(Header.code(),
"");
1043 haveRanges(Header.ranges(
"foo")))));
1045 haveRanges(Header.ranges(
"bar")))));
1047 EXPECT_THAT(Refs, Contains(Pair(_, haveRanges(Header.ranges(
"ud1")))));
1048 EXPECT_THAT(Refs, Contains(Pair(_, haveRanges(Header.ranges(
"ud2")))));
1050 haveRanges(Header.ranges(
"concat")))));
1052 haveRanges(Header.ranges(
"prepend")))));
1054 haveRanges(Header.ranges(
"macroa")))));
1057TEST_F(SymbolCollectorTest, MacroRefWithoutCollectingSymbol) {
1059 #define $foo[[FOO]](X) (X + 1)
1060 int abc = $foo[[FOO]](1);
1063 CollectorOpts.RefsInHeaders = true;
1064 CollectorOpts.CollectMacro =
false;
1065 runSymbolCollector(Header.code(),
"");
1066 EXPECT_THAT(Refs, Contains(Pair(_, haveRanges(Header.ranges(
"foo")))));
1069TEST_F(SymbolCollectorTest, MacrosWithRefFilter) {
1070 Annotations Header(
"#define $macro[[MACRO]](X) (X + 1)");
1071 Annotations Main(
"void foo() { int x = $macro[[MACRO]](1); }");
1073 runSymbolCollector(Header.code(), Main.code());
1074 EXPECT_THAT(Refs, IsEmpty());
1077TEST_F(SymbolCollectorTest, SpelledReferences) {
1079 llvm::StringRef Header;
1080 llvm::StringRef Main;
1081 llvm::StringRef TargetSymbolName;
1089 struct $spelled[[Foo]] {
1093 $spelled[[Foo]] Variable1;
1094 $implicit[[MACRO]] Variable2;
1106 void f() { Foo $implicit[[f]]; f = $spelled[[Foo]]();}
1122 CollectorOpts.RefsInHeaders =
false;
1123 for (
const auto& T : TestCases) {
1124 SCOPED_TRACE(
T.Header +
"\n---\n" +
T.Main);
1128 InMemoryFileSystem =
new llvm::vfs::InMemoryFileSystem;
1129 runSymbolCollector(Header.code(), Main.code());
1131 const auto SpelledRanges = Main.ranges(
"spelled");
1132 const auto ImplicitRanges = Main.ranges(
"implicit");
1135 for (
const auto &SymbolAndRefs : Refs) {
1136 const auto ID = SymbolAndRefs.first;
1139 for (
const auto &
Ref : SymbolAndRefs.second)
1141 SpelledSlabBuilder.insert(ID,
Ref);
1143 ImplicitSlabBuilder.insert(ID,
Ref);
1145 const auto SpelledRefs = std::move(SpelledSlabBuilder).build(),
1146 ImplicitRefs = std::move(ImplicitSlabBuilder).build();
1147 EXPECT_EQ(SpelledRanges.empty(), SpelledRefs.empty());
1148 EXPECT_EQ(ImplicitRanges.empty(), ImplicitRefs.empty());
1149 if (!SpelledRanges.empty())
1150 EXPECT_THAT(SpelledRefs,
1151 Contains(Pair(TargetID, haveRanges(SpelledRanges))));
1152 if (!ImplicitRanges.empty())
1153 EXPECT_THAT(ImplicitRefs,
1154 Contains(Pair(TargetID, haveRanges(ImplicitRanges))));
1158TEST_F(SymbolCollectorTest, NameReferences) {
1160 CollectorOpts.RefsInHeaders =
true;
1169 runSymbolCollector(Header.code(), "");
1173 haveRanges(Header.ranges()))));
1176TEST_F(SymbolCollectorTest, RefsOnMacros) {
1180 CollectorOpts.RefsInHeaders =
true;
1184 #define CAT(X, Y) X##Y
1189 TYPE(TYPE([[Foo]])) foo3;
1190 [[CAT]](Fo, o) foo4;
1194 runSymbolCollector(Header.code(), "");
1196 haveRanges(Header.ranges()))));
1199TEST_F(SymbolCollectorTest, HeaderAsMainFile) {
1202 class $Foo[[Foo]] {};
1204 void $Func[[Func]]() {
1211 TestFileName =
testPath(
"foo.cpp");
1212 runSymbolCollector(
"", Header.code());
1215 haveRanges(Header.ranges(
"Foo"))),
1217 haveRanges(Header.ranges(
"Func")))));
1221 runSymbolCollector(
"", Header.code(),
1222 {
"-xobjective-c++-header"});
1223 EXPECT_THAT(
Symbols, UnorderedElementsAre(qName(
"Foo"), qName(
"Func")));
1226 haveRanges(Header.ranges(
"Foo"))),
1228 haveRanges(Header.ranges(
"Func")))));
1232 runSymbolCollector(
"", Header.code());
1233 EXPECT_THAT(
Symbols, UnorderedElementsAre(qName(
"Foo"), qName(
"Func")));
1236 haveRanges(Header.ranges(
"Foo"))),
1238 haveRanges(Header.ranges(
"Func")))));
1241TEST_F(SymbolCollectorTest, RefsInHeaders) {
1243 CollectorOpts.RefsInHeaders =
true;
1244 CollectorOpts.CollectMacro =
true;
1246 #define $macro[[MACRO]](x) (x+1)
1247 class $foo[[Foo]] {};
1249 runSymbolCollector(Header.code(), "");
1251 haveRanges(Header.ranges(
"foo")))));
1253 haveRanges(Header.ranges(
"macro")))));
1256TEST_F(SymbolCollectorTest, BaseOfRelations) {
1257 std::string Header = R
"(
1259 class Derived : public Base {};
1261 runSymbolCollector(Header, "");
1264 EXPECT_THAT(Relations,
1268TEST_F(SymbolCollectorTest, OverrideRelationsSimpleInheritance) {
1269 std::string Header = R
"cpp(
1273 class B : public A {
1274 void foo() override; // A::foo
1277 class C : public B {
1278 void bar() override; // B::bar
1281 void foo() override; // B::foo
1282 void bar() override; // C::bar
1285 runSymbolCollector(Header, "");
1294 std::vector<Relation> Result;
1295 for (
const Relation &R : Relations)
1297 Result.push_back(R);
1298 EXPECT_THAT(Result, UnorderedElementsAre(
1303TEST_F(SymbolCollectorTest, OverrideRelationsMultipleInheritance) {
1304 std::string Header = R
"cpp(
1311 class C : public B {
1312 void bar() override; // B::bar
1315 class D : public A, C {
1316 void foo() override; // A::foo
1317 void bar() override; // C::bar
1318 void baz() override; // C::baz
1321 runSymbolCollector(Header, "");
1330 std::vector<Relation> Result;
1331 for (
const Relation &R : Relations)
1333 Result.push_back(R);
1334 EXPECT_THAT(Result, UnorderedElementsAre(
1339TEST_F(SymbolCollectorTest, SymbolTagsWithIndexing) {
1343 std::string Header = R
"cpp(
1346 virtual ~A() = default;
1347 virtual void f1() = 0;
1355 void A::f2() const {}
1357 class B final: public A {
1363 runSymbolCollector(Header, "");
1375 UnorderedElementsAre(
1380TEST_F(SymbolCollectorTest, ObjCOverrideRelationsSimpleInheritance) {
1381 std::string Header = R
"cpp(
1386 - (void)foo; // A::foo
1390 - (void)bar; // B::bar
1393 - (void)foo; // B::foo
1394 - (void)bar; // C::bar
1397 runSymbolCollector(Header, "",
1398 {
"-xobjective-c++",
"-Wno-objc-root-class"});
1407 std::vector<Relation> Result;
1408 for (
const Relation &R : Relations)
1410 Result.push_back(R);
1411 EXPECT_THAT(Result, UnorderedElementsAre(
1416TEST_F(SymbolCollectorTest, CountReferences) {
1417 const std::string Header = R
"(
1421 class Z {}; // not used anywhere
1422 Y* y = nullptr; // used in header doesn't count
1423 #define GLOBAL_Z(name) Z name;
1425 const std::string Main = R
"(
1427 W* w2 = nullptr; // only one usage counts
1430 class Y{}; // definition doesn't count as a reference
1432 GLOBAL_Z(z); // Not a reference to Z, we don't spell the type.
1434 CollectorOpts.CountReferences = true;
1435 runSymbolCollector(Header, Main);
1438 UnorderedElementsAreArray(
1439 {AllOf(qName(
"W"), refCount(1)), AllOf(qName(
"X"), refCount(1)),
1440 AllOf(qName(
"Y"), refCount(0)), AllOf(qName(
"Z"), refCount(0)),
1441 AllOf(qName(
"y"), refCount(0)), AllOf(qName(
"z"), refCount(0)),
1442 AllOf(qName(
"x"), refCount(0)), AllOf(qName(
"w"), refCount(0)),
1443 AllOf(qName(
"w2"), refCount(0)), AllOf(qName(
"V"), refCount(1)),
1444 AllOf(qName(
"v"), refCount(0))}));
1447TEST_F(SymbolCollectorTest, SymbolRelativeNoFallback) {
1448 runSymbolCollector(
"class Foo {};",
"");
1449 EXPECT_THAT(
Symbols, UnorderedElementsAre(
1450 AllOf(qName(
"Foo"), declURI(TestHeaderURI))));
1453TEST_F(SymbolCollectorTest, SymbolRelativeWithFallback) {
1454 TestHeaderName =
"x.h";
1455 TestFileName =
"x.cpp";
1457 CollectorOpts.FallbackDir =
testRoot();
1458 runSymbolCollector(
"class Foo {};",
"");
1459 EXPECT_THAT(
Symbols, UnorderedElementsAre(
1460 AllOf(qName(
"Foo"), declURI(TestHeaderURI))));
1463TEST_F(SymbolCollectorTest, UnittestURIScheme) {
1467 runSymbolCollector(
"class Foo {};",
"");
1468 EXPECT_THAT(
Symbols, UnorderedElementsAre(
1469 AllOf(qName(
"Foo"), declURI(
"unittest:///x.h"))));
1472TEST_F(SymbolCollectorTest, IncludeEnums) {
1473 const std::string Header = R
"(
1494 runSymbolCollector(Header, "");
1496 UnorderedElementsAre(
1497 AllOf(qName(
"Red"), forCodeCompletion(
true)),
1498 AllOf(qName(
"Color"), forCodeCompletion(
true)),
1499 AllOf(qName(
"Green"), forCodeCompletion(
true)),
1500 AllOf(qName(
"Color2"), forCodeCompletion(
true)),
1501 AllOf(qName(
"Color2::Yellow"), forCodeCompletion(
true)),
1502 AllOf(qName(
"ns"), forCodeCompletion(
true)),
1503 AllOf(qName(
"ns::Black"), forCodeCompletion(
true)),
1504 AllOf(qName(
"Color3"), forCodeCompletion(
true)),
1505 AllOf(qName(
"Color3::Blue"), forCodeCompletion(
true))));
1508TEST_F(SymbolCollectorTest, NamelessSymbols) {
1509 const std::string Header = R
"(
1514 runSymbolCollector(Header, "");
1516 UnorderedElementsAre(qName(
"Foo"), qName(
"(unnamed struct)::a")));
1519TEST_F(SymbolCollectorTest, SymbolFormedFromRegisteredSchemeFromMacro) {
1523 class name##_Test {};
1525 $expansion[[FF]](abc);
1528 class $spelling[[Test]] {};
1533 runSymbolCollector(Header.code(), "");
1535 UnorderedElementsAre(
1536 AllOf(qName(
"abc_Test"), declRange(Header.range(
"expansion")),
1537 declURI(TestHeaderURI)),
1538 AllOf(qName(
"Test"), declRange(Header.range(
"spelling")),
1539 declURI(TestHeaderURI))));
1542TEST_F(SymbolCollectorTest, SymbolFormedByCLI) {
1545 class $expansion[[NAME]] {};
1548 runSymbolCollector(Header.code(), "", {
"-DNAME=name"});
1549 EXPECT_THAT(
Symbols, UnorderedElementsAre(AllOf(
1550 qName(
"name"), declRange(Header.range(
"expansion")),
1551 declURI(TestHeaderURI))));
1554TEST_F(SymbolCollectorTest, SymbolsInMainFile) {
1555 const std::string Main = R
"(
1571 runSymbolCollector("", Main);
1572 EXPECT_THAT(
Symbols, UnorderedElementsAre(
1573 qName(
"Foo"), qName(
"f1"), qName(
"f2"), qName(
"ff"),
1574 qName(
"foo"), qName(
"foo::Bar"), qName(
"main_f")));
1577TEST_F(SymbolCollectorTest, Documentation) {
1578 const std::string Header = R
"(
1585 CollectorOpts.StoreAllDocumentation = false;
1586 runSymbolCollector(Header,
"");
1588 UnorderedElementsAre(
1589 AllOf(qName(
"Foo"), doc(
"doc Foo"), forCodeCompletion(
true)),
1590 AllOf(qName(
"Foo::f"), doc(
""), returnType(
""),
1591 forCodeCompletion(
false))));
1593 CollectorOpts.StoreAllDocumentation =
true;
1594 runSymbolCollector(Header,
"");
1596 UnorderedElementsAre(
1597 AllOf(qName(
"Foo"), doc(
"doc Foo"), forCodeCompletion(
true)),
1598 AllOf(qName(
"Foo::f"), doc(
"doc f"), returnType(
""),
1599 forCodeCompletion(
false))));
1602TEST_F(SymbolCollectorTest, DocumentationInMain) {
1603 const std::string Header = R
"(
1609 const std::string Main = R
"(
1613 CollectorOpts.StoreAllDocumentation = true;
1614 runSymbolCollector(Header, Main);
1616 UnorderedElementsAre(
1617 AllOf(qName(
"Foo"), doc(
"doc Foo"), forCodeCompletion(
true)),
1618 AllOf(qName(
"Foo::f"), doc(
"doc f"), returnType(
""),
1619 forCodeCompletion(
false))));
1622TEST_F(SymbolCollectorTest, DocumentationAtDeclThenDef) {
1623 const std::string Header = R
"(
1629 const std::string Main = R
"(
1633 CollectorOpts.StoreAllDocumentation = true;
1634 runSymbolCollector(Header, Main);
1636 UnorderedElementsAre(AllOf(qName(
"Foo")),
1637 AllOf(qName(
"Foo::f"), doc(
"doc f decl"))));
1640TEST_F(SymbolCollectorTest, DocumentationAtDefThenDecl) {
1641 const std::string Header = R
"(
1648 CollectorOpts.StoreAllDocumentation = true;
1649 runSymbolCollector(Header,
"" );
1651 UnorderedElementsAre(AllOf(qName(
"f"), doc(
"doc f def"))));
1654TEST_F(SymbolCollectorTest, ClassMembers) {
1655 const std::string Header = R
"(
1664 const std::string Main = R
"(
1668 runSymbolCollector(Header, Main);
1671 UnorderedElementsAre(
1673 AllOf(qName(
"Foo::f"), returnType(
""), forCodeCompletion(
false)),
1674 AllOf(qName(
"Foo::g"), returnType(
""), forCodeCompletion(
false)),
1675 AllOf(qName(
"Foo::sf"), returnType(
""), forCodeCompletion(
false)),
1676 AllOf(qName(
"Foo::ssf"), returnType(
""), forCodeCompletion(
false)),
1677 AllOf(qName(
"Foo::x"), returnType(
""), forCodeCompletion(
false))));
1680TEST_F(SymbolCollectorTest, Scopes) {
1681 const std::string Header = R
"(
1689 runSymbolCollector(Header, "");
1691 UnorderedElementsAre(qName(
"na"), qName(
"na::nb"),
1692 qName(
"na::Foo"), qName(
"na::nb::Bar")));
1695TEST_F(SymbolCollectorTest, ExternC) {
1696 const std::string Header = R
"(
1697 extern "C" { class Foo {}; }
1699 extern "C" { class Bar {}; }
1702 runSymbolCollector(Header, "");
1703 EXPECT_THAT(
Symbols, UnorderedElementsAre(qName(
"na"), qName(
"Foo"),
1707TEST_F(SymbolCollectorTest, SkipInlineNamespace) {
1708 const std::string Header = R
"(
1710 inline namespace nb {
1715 // This is still inlined.
1721 runSymbolCollector(Header, "");
1723 UnorderedElementsAre(qName(
"na"), qName(
"na::nb"),
1724 qName(
"na::Foo"), qName(
"na::Bar")));
1727TEST_F(SymbolCollectorTest, SymbolWithDocumentation) {
1728 const std::string Header = R
"(
1731 int ff(int x, double y) { return 0; }
1734 runSymbolCollector(Header,
"");
1737 UnorderedElementsAre(
1738 qName(
"nx"), AllOf(qName(
"nx::ff"), labeled(
"ff(int x, double y)"),
1739 returnType(
"int"), doc(
"Foo comment."))));
1742TEST_F(SymbolCollectorTest, snippet) {
1743 const std::string Header = R
"(
1746 int ff(int x, double y) { return 0; }
1749 runSymbolCollector(Header, "");
1751 UnorderedElementsAre(
1753 AllOf(qName(
"nx::f"), labeled(
"f()"), snippet(
"f()")),
1754 AllOf(qName(
"nx::ff"), labeled(
"ff(int x, double y)"),
1755 snippet(
"ff(${1:int x}, ${2:double y})"))));
1758TEST_F(SymbolCollectorTest, IncludeHeaderSameAsFileURI) {
1759 CollectorOpts.CollectIncludePath =
true;
1760 runSymbolCollector(
"#pragma once\nclass Foo {};",
"");
1761 EXPECT_THAT(
Symbols, UnorderedElementsAre(
1762 AllOf(qName(
"Foo"), declURI(TestHeaderURI))));
1763 EXPECT_THAT(
Symbols.begin()->IncludeHeaders,
1764 UnorderedElementsAre(IncludeHeaderWithRef(TestHeaderURI, 1u)));
1767TEST_F(SymbolCollectorTest, CanonicalSTLHeader) {
1768 CollectorOpts.CollectIncludePath =
true;
1773 // Move overloads have special handling.
1774 template <typename _T> T&& move(_T&& __value);
1775 template <typename _I, typename _O> _O move(_I, _I, _O);
1776 template <typename _T, typename _O, typename _I> _O move(
1783 UnorderedElementsAre(
1785 AllOf(qName(
"std::string"), declURI(TestHeaderURI),
1786 includeHeader(
"<string>")),
1788 AllOf(labeled(
"move(T &&value)"), includeHeader(
"<utility>")),
1789 AllOf(labeled(
"move(I, I, O)"), includeHeader(
"<algorithm>")),
1790 AllOf(labeled(
"move(T &&, O, O, I)"), includeHeader(
"<algorithm>"))));
1793TEST_F(SymbolCollectorTest, IWYUPragma) {
1794 CollectorOpts.CollectIncludePath =
true;
1795 const std::string Header = R
"(
1796 // IWYU pragma: private, include the/good/header.h
1799 runSymbolCollector(Header, "");
1800 EXPECT_THAT(
Symbols, UnorderedElementsAre(
1801 AllOf(qName(
"Foo"), declURI(TestHeaderURI),
1802 includeHeader(
"\"the/good/header.h\""))));
1805TEST_F(SymbolCollectorTest, IWYUPragmaWithDoubleQuotes) {
1806 CollectorOpts.CollectIncludePath =
true;
1807 const std::string Header = R
"(
1808 // IWYU pragma: private, include "the/good/header.h"
1811 runSymbolCollector(Header, "");
1812 EXPECT_THAT(
Symbols, UnorderedElementsAre(
1813 AllOf(qName(
"Foo"), declURI(TestHeaderURI),
1814 includeHeader(
"\"the/good/header.h\""))));
1817TEST_F(SymbolCollectorTest, IWYUPragmaExport) {
1818 CollectorOpts.CollectIncludePath =
true;
1819 const std::string Header = R
"cpp(#pragma once
1820 #include "exporter.h"
1822 auto ExporterFile =
testPath(
"exporter.h");
1823 InMemoryFileSystem->addFile(
1824 ExporterFile, 0, llvm::MemoryBuffer::getMemBuffer(R
"cpp(#pragma once
1825 #include "private.h" // IWYU pragma: export
1827 auto PrivateFile =
testPath(
"private.h");
1828 InMemoryFileSystem->addFile(
1829 PrivateFile, 0, llvm::MemoryBuffer::getMemBuffer(
"class Foo {};"));
1830 runSymbolCollector(Header,
"",
1832 EXPECT_THAT(
Symbols, UnorderedElementsAre(AllOf(
1838TEST_F(SymbolCollectorTest, MainFileIsHeaderWhenSkipIncFile) {
1839 CollectorOpts.CollectIncludePath =
true;
1842 TestFileName =
testPath(
"no_ext_main");
1843 TestFileURI =
URI::create(TestFileName).toString();
1844 auto IncFile =
testPath(
"test.inc");
1846 InMemoryFileSystem->addFile(IncFile, 0,
1847 llvm::MemoryBuffer::getMemBuffer(
"class X {};"));
1848 runSymbolCollector(
"", R
"cpp(
1849 // Can't use #pragma once in a main file clang doesn't think is a header.
1856 EXPECT_THAT(
Symbols, UnorderedElementsAre(AllOf(qName(
"X"), declURI(IncURI),
1857 includeHeader(TestFileURI))));
1860TEST_F(SymbolCollectorTest, IncFileInNonHeader) {
1861 CollectorOpts.CollectIncludePath =
true;
1862 TestFileName =
testPath(
"main.cc");
1863 TestFileURI =
URI::create(TestFileName).toString();
1864 auto IncFile =
testPath(
"test.inc");
1866 InMemoryFileSystem->addFile(IncFile, 0,
1867 llvm::MemoryBuffer::getMemBuffer(
"class X {};"));
1868 runSymbolCollector(
"", R
"cpp(
1872 EXPECT_THAT(
Symbols, UnorderedElementsAre(AllOf(qName(
"X"), declURI(IncURI),
1873 Not(includeHeader()))));
1878TEST_F(SymbolCollectorTest, HeaderGuardDetected) {
1879 CollectorOpts.CollectIncludePath =
true;
1880 CollectorOpts.CollectMacro =
true;
1881 runSymbolCollector(R
"cpp(
1882 #ifndef HEADER_GUARD_
1883 #define HEADER_GUARD_
1885 // Symbols are seen before the header guard is complete.
1889 #endif // Header guard is recognized here.
1892 EXPECT_THAT(
Symbols, Not(Contains(qName(
"HEADER_GUARD_"))));
1893 EXPECT_THAT(
Symbols, Each(includeHeader()));
1896TEST_F(SymbolCollectorTest, NonModularHeader) {
1898 EXPECT_THAT(TU.headerSymbols(), ElementsAre(includeHeader()));
1901 TU.ImplicitHeaderGuard =
false;
1902 EXPECT_THAT(TU.headerSymbols(), ElementsAre(Not(includeHeader())));
1907#error "This file isn't safe to include directly"
1911 TU.ExtraArgs.push_back("-DSECRET");
1912 EXPECT_THAT(TU.headerSymbols(), ElementsAre(Not(includeHeader())));
1915TEST_F(SymbolCollectorTest, AvoidUsingFwdDeclsAsCanonicalDecls) {
1916 CollectorOpts.CollectIncludePath =
true;
1919 // Forward declarations of TagDecls.
1924 // Canonical declarations.
1925 class $cdecl[[C]] {};
1926 struct $sdecl[[S]] {};
1927 union $udecl[[U]] {int $xdecl[[x]]; bool $ydecl[[y]];};
1929 runSymbolCollector(Header.code(), "");
1932 UnorderedElementsAre(
1933 AllOf(qName(
"C"), declURI(TestHeaderURI),
1934 declRange(Header.range(
"cdecl")), includeHeader(TestHeaderURI),
1935 defURI(TestHeaderURI), defRange(Header.range(
"cdecl"))),
1936 AllOf(qName(
"S"), declURI(TestHeaderURI),
1937 declRange(Header.range(
"sdecl")), includeHeader(TestHeaderURI),
1938 defURI(TestHeaderURI), defRange(Header.range(
"sdecl"))),
1939 AllOf(qName(
"U"), declURI(TestHeaderURI),
1940 declRange(Header.range(
"udecl")), includeHeader(TestHeaderURI),
1941 defURI(TestHeaderURI), defRange(Header.range(
"udecl"))),
1942 AllOf(qName(
"U::x"), declURI(TestHeaderURI),
1943 declRange(Header.range(
"xdecl")), defURI(TestHeaderURI),
1944 defRange(Header.range(
"xdecl"))),
1945 AllOf(qName(
"U::y"), declURI(TestHeaderURI),
1946 declRange(Header.range(
"ydecl")), defURI(TestHeaderURI),
1947 defRange(Header.range(
"ydecl")))));
1950TEST_F(SymbolCollectorTest, ClassForwardDeclarationIsCanonical) {
1951 CollectorOpts.CollectIncludePath =
true;
1952 runSymbolCollector(
"#pragma once\nclass X;",
1954 EXPECT_THAT(
Symbols, UnorderedElementsAre(AllOf(
1955 qName(
"X"), declURI(TestHeaderURI),
1956 includeHeader(TestHeaderURI), defURI(TestFileURI))));
1959TEST_F(SymbolCollectorTest, UTF16Character) {
1962 runSymbolCollector(Header.code(),
"");
1963 EXPECT_THAT(
Symbols, UnorderedElementsAre(
1964 AllOf(qName(
"pörk"), declRange(Header.range()))));
1967TEST_F(SymbolCollectorTest, DoNotIndexSymbolsInFriendDecl) {
1975 friend void $bar[[bar]]() {}
1981 runSymbolCollector(Header.code(), "");
1984 UnorderedElementsAre(
1985 qName(
"nx"), qName(
"nx::X"),
1986 AllOf(qName(
"nx::Y"), declRange(Header.range(
"y"))),
1987 AllOf(qName(
"nx::Z"), declRange(Header.range(
"z"))),
1988 AllOf(qName(
"nx::foo"), declRange(Header.range(
"foo"))),
1989 AllOf(qName(
"nx::bar"), declRange(Header.range(
"bar")))));
1992TEST_F(SymbolCollectorTest, ReferencesInFriendDecl) {
1993 const std::string Header = R
"(
1997 const std::string Main = R
"(
2003 CollectorOpts.CountReferences = true;
2004 runSymbolCollector(Header, Main);
2005 EXPECT_THAT(
Symbols, UnorderedElementsAre(AllOf(qName(
"X"), refCount(1)),
2006 AllOf(qName(
"Y"), refCount(1)),
2007 AllOf(qName(
"C"), refCount(0))));
2010TEST_F(SymbolCollectorTest, Origin) {
2012 runSymbolCollector(
"class Foo {};",
"");
2013 EXPECT_THAT(
Symbols, UnorderedElementsAre(
2015 InMemoryFileSystem =
new llvm::vfs::InMemoryFileSystem;
2016 CollectorOpts.CollectMacro =
true;
2017 runSymbolCollector(
"#define FOO",
"");
2018 EXPECT_THAT(
Symbols, UnorderedElementsAre(
2022TEST_F(SymbolCollectorTest, CollectMacros) {
2023 CollectorOpts.CollectIncludePath =
true;
2027 #define $mac[[MAC]](x) int x
2028 #define $used[[USED]](y) float y;
2034 #define $main[[MAIN]] 1
2037 CollectorOpts.CountReferences = true;
2038 CollectorOpts.CollectMacro =
true;
2039 runSymbolCollector(Header.code(), Main.code());
2042 UnorderedElementsAre(
2043 qName(
"p"), qName(
"t"),
2044 AllOf(qName(
"X"), declURI(TestHeaderURI),
2045 includeHeader(TestHeaderURI)),
2046 AllOf(labeled(
"MAC(x)"), refCount(0),
2048 declRange(Header.range(
"mac")), visibleOutsideFile()),
2049 AllOf(labeled(
"USED(y)"), refCount(1),
2050 declRange(Header.range(
"used")), visibleOutsideFile()),
2051 AllOf(labeled(
"MAIN"), refCount(0), declRange(Main.range(
"main")),
2052 Not(visibleOutsideFile()))));
2055TEST_F(SymbolCollectorTest, DeprecatedSymbols) {
2056 const std::string Header = R
"(
2057 void TestClangc() __attribute__((deprecated("", "")));
2060 runSymbolCollector(Header, "");
2061 EXPECT_THAT(
Symbols, UnorderedElementsAre(
2062 AllOf(qName(
"TestClangc"), deprecated()),
2063 AllOf(qName(
"TestClangd"), Not(deprecated()))));
2066TEST_F(SymbolCollectorTest, implementationDetail) {
2067 const std::string Header = R
"(
2068 #define DECL_NAME(x, y) x##_##y##_Decl
2069 #define DECL(x, y) class DECL_NAME(x, y) {};
2070 DECL(X, Y); // X_Y_Decl
2074 runSymbolCollector(Header, "");
2076 UnorderedElementsAre(
2077 AllOf(qName(
"X_Y_Decl"), implementationDetail()),
2078 AllOf(qName(
"Public"), Not(implementationDetail()))));
2081TEST_F(SymbolCollectorTest, UsingDecl) {
2082 const char *Header = R
"(
2087 runSymbolCollector(Header, "");
2088 EXPECT_THAT(
Symbols, Contains(qName(
"std::foo")));
2091TEST_F(SymbolCollectorTest, CBuiltins) {
2093 const char *Header = R
"(
2094 extern int printf(const char*, ...);
2096 runSymbolCollector(Header, "", {
"-xc"});
2097 EXPECT_THAT(
Symbols, Contains(qName(
"printf")));
2100TEST_F(SymbolCollectorTest, InvalidSourceLoc) {
2101 const char *Header = R
"(
2102 void operator delete(void*)
2103 __attribute__((__externally_visible__));)";
2104 runSymbolCollector(Header, "");
2105 EXPECT_THAT(
Symbols, Contains(qName(
"operator delete")));
2108TEST_F(SymbolCollectorTest, BadUTF8) {
2111 const char *Header =
"int PUNCT = 0;\n"
2112 "/* \xa1 */ int types[] = { /* \xa1 */PUNCT };";
2114 CollectorOpts.RefsInHeaders =
true;
2115 runSymbolCollector(Header,
"");
2116 EXPECT_THAT(
Symbols, Contains(AllOf(qName(
"types"), doc(
"\xef\xbf\xbd "))));
2117 EXPECT_THAT(
Symbols, Contains(qName(
"PUNCT")));
2122TEST_F(SymbolCollectorTest, MacrosInHeaders) {
2123 CollectorOpts.CollectMacro =
true;
2125 runSymbolCollector(
"",
"#define X");
2127 UnorderedElementsAre(AllOf(qName(
"X"), forCodeCompletion(
true))));
2131TEST_F(SymbolCollectorTest, UndefOfModuleMacro) {
2133 TU.AdditionalFiles["bar.h"] = R
"cpp(
2137 TU.AdditionalFiles["foo.h"] =
"#define X 1";
2138 TU.AdditionalFiles[
"module.modulemap"] = R
"cpp(
2144 TU.ExtraArgs.push_back("-fmodules");
2145 TU.ExtraArgs.push_back(
"-fmodule-map-file=" +
testPath(
"module.modulemap"));
2146 TU.OverlayRealFileSystemForModules =
true;
2151 EXPECT_THAT(TU.headerSymbols(), Not(Contains(qName(
"X"))));
2154TEST_F(SymbolCollectorTest, NoCrashOnObjCMethodCStyleParam) {
2157 - (void)fun:(bool)foo, bool bar;
2160 TU.ExtraArgs.push_back("-xobjective-c++");
2164 EXPECT_THAT(TU.headerSymbols(),
2165 UnorderedElementsAre(qName(
"Foo"), qName(
"Foo::fun:")));
2168TEST_F(SymbolCollectorTest, Reserved) {
2169 const char *Header = R
"cpp(
2172 namespace _X { int secret; }
2175 CollectorOpts.CollectReserved = true;
2176 runSymbolCollector(Header,
"");
2177 EXPECT_THAT(
Symbols, UnorderedElementsAre(qName(
"__foo"), qName(
"_X"),
2178 qName(
"_X::secret")));
2180 CollectorOpts.CollectReserved =
false;
2181 runSymbolCollector(Header,
"");
2182 EXPECT_THAT(
Symbols, UnorderedElementsAre(qName(
"__foo"), qName(
"_X"),
2183 qName(
"_X::secret")));
2187 InMemoryFileSystem =
new llvm::vfs::InMemoryFileSystem;
2188 runSymbolCollector(
"#pragma GCC system_header\n" + std::string(Header),
"");
2189 EXPECT_THAT(
Symbols, IsEmpty());
2192TEST_F(SymbolCollectorTest, ReservedSymbolInIntrinsicHeader) {
2193 const char *Header = R
"cpp(
2198 TestHeaderName = "xintrin.h";
2200 InMemoryFileSystem =
new llvm::vfs::InMemoryFileSystem;
2201 CollectorOpts.FallbackDir =
testRoot();
2202 runSymbolCollector(
"#pragma GCC system_header\n" + std::string(Header),
"");
2203 EXPECT_THAT(
Symbols, UnorderedElementsAre(qName(
"__foo")));
2206TEST_F(SymbolCollectorTest, Concepts) {
2207 const char *Header = R
"cpp(
2209 concept A = sizeof(T) <= 8;
2211 runSymbolCollector("", Header, {
"-std=c++20"});
2213 UnorderedElementsAre(AllOf(
2214 qName(
"A"), hasKind(clang::index::SymbolKind::Concept))));
2217TEST_F(SymbolCollectorTest, IncludeHeaderForwardDecls) {
2218 CollectorOpts.CollectIncludePath =
true;
2219 const std::string Header = R
"cpp(#pragma once
2223 auto FullFile =
testPath(
"full.h");
2224 InMemoryFileSystem->addFile(FullFile, 0,
2225 llvm::MemoryBuffer::getMemBuffer(R
"cpp(
2227struct Foo {};)cpp"));
2228 runSymbolCollector(Header, "",
2230 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)