13#include "clang-include-cleaner/Record.h"
15#include "clang/Basic/FileManager.h"
16#include "clang/Basic/FileSystemOptions.h"
17#include "clang/Basic/SourceLocation.h"
18#include "clang/Frontend/CompilerInstance.h"
19#include "clang/Index/IndexingAction.h"
20#include "clang/Index/IndexingOptions.h"
21#include "clang/Tooling/Tooling.h"
22#include "llvm/ADT/IntrusiveRefCntPtr.h"
23#include "llvm/ADT/StringRef.h"
24#include "llvm/Support/MemoryBuffer.h"
25#include "llvm/Support/VirtualFileSystem.h"
26#include "gmock/gmock-matchers.h"
27#include "gmock/gmock.h"
28#include "gtest/gtest.h"
40using ::testing::AllOf;
41using ::testing::Contains;
43using ::testing::ElementsAre;
44using ::testing::Field;
45using ::testing::IsEmpty;
48using ::testing::UnorderedElementsAre;
49using ::testing::UnorderedElementsAreArray;
53 return (arg.Name + arg.Signature).str() ==
Label;
55MATCHER_P(returnType, D,
"") {
return arg.ReturnType ==
D; }
56MATCHER_P(doc, D,
"") {
return arg.Documentation ==
D; }
58 return (arg.Name + arg.CompletionSnippetSuffix).str() == S;
63 return arg.TemplateSpecializationArgs == TemplArgs;
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);
78bool rangesMatch(
const SymbolLocation &
Loc,
const Range &R) {
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);
108::testing::Matcher<const std::vector<Ref> &>
109haveRanges(
const std::vector<Range> Ranges) {
110 return ::testing::UnorderedPointwise(refRange(), Ranges);
113class ShouldCollectSymbolTest :
public ::testing::Test {
115 void build(llvm::StringRef HeaderCode, llvm::StringRef
Code =
"") {
118 File.HeaderCode = std::string(HeaderCode);
119 File.Code = std::string(
Code);
124 bool shouldCollect(llvm::StringRef
Name,
bool Qualified =
true) {
126 const NamedDecl &ND =
128 const SourceManager &SM = AST->getSourceManager();
131 ND, AST->getASTContext(), SymbolCollector::Options(),
MainFile);
138 std::optional<ParsedAST> AST;
141TEST_F(ShouldCollectSymbolTest, ShouldCollectSymbol) {
145 auto f() { int Local; } // auto ensures function body is parsed.
146 struct { int x; } var;
151 namespace { class InAnonymous {}; }
155 EXPECT_TRUE(shouldCollect(
"nx"));
156 EXPECT_TRUE(shouldCollect(
"nx::X"));
157 EXPECT_TRUE(shouldCollect(
"nx::f"));
158 EXPECT_TRUE(shouldCollect(
"InMain"));
159 EXPECT_TRUE(shouldCollect(
"InAnonymous",
false));
160 EXPECT_TRUE(shouldCollect(
"g"));
162 EXPECT_FALSE(shouldCollect(
"Local",
false));
165TEST_F(ShouldCollectSymbolTest, CollectLocalClassesAndVirtualMethods) {
170 auto LocalLambda = [&](){
172 class ClassInLambda{};
175 } // auto ensures function body is parsed.
178 virtual void LocalVirtual();
179 void LocalConcrete();
187 EXPECT_FALSE(shouldCollect(
"Local",
false));
188 EXPECT_TRUE(shouldCollect(
"ClassInLambda",
false));
189 EXPECT_TRUE(shouldCollect(
"LocalBase",
false));
190 EXPECT_TRUE(shouldCollect(
"LocalVirtual",
false));
191 EXPECT_TRUE(shouldCollect(
"LocalConcrete",
false));
192 EXPECT_FALSE(shouldCollect(
"BaseMember",
false));
193 EXPECT_FALSE(shouldCollect(
"Local",
false));
196TEST_F(ShouldCollectSymbolTest, NoPrivateProtoSymbol) {
199 R
"(// Generated by the protocol buffer compiler. DO NOT EDIT!
208 EXPECT_TRUE(shouldCollect("nx::TopLevel"));
209 EXPECT_TRUE(shouldCollect(
"nx::Kind::KIND_OK"));
210 EXPECT_TRUE(shouldCollect(
"nx::Kind"));
212 EXPECT_FALSE(shouldCollect(
"nx::Top_Level"));
213 EXPECT_FALSE(shouldCollect(
"nx::Kind::Kind_Not_Ok"));
216TEST_F(ShouldCollectSymbolTest, DoubleCheckProtoHeaderComment) {
226 EXPECT_TRUE(shouldCollect("nx::Top_Level"));
227 EXPECT_TRUE(shouldCollect(
"nx::Kind_Fine"));
230class SymbolIndexActionFactory :
public tooling::FrontendActionFactory {
232 SymbolIndexActionFactory(SymbolCollector::Options COpts)
235 std::unique_ptr<FrontendAction> create()
override {
236 class IndexAction :
public ASTFrontendAction {
238 IndexAction(std::shared_ptr<index::IndexDataConsumer> DataConsumer,
239 const index::IndexingOptions &Opts,
240 std::shared_ptr<include_cleaner::PragmaIncludes> PI)
241 : DataConsumer(std::move(DataConsumer)), Opts(Opts),
244 std::unique_ptr<ASTConsumer>
245 CreateASTConsumer(CompilerInstance &
CI, llvm::StringRef InFile)
override {
247 return createIndexingASTConsumer(DataConsumer, Opts,
248 CI.getPreprocessorPtr());
251 bool BeginInvocation(CompilerInstance &
CI)
override {
253 CI.getLangOpts().CommentOpts.ParseAllComments =
true;
258 std::shared_ptr<index::IndexDataConsumer> DataConsumer;
259 index::IndexingOptions Opts;
260 std::shared_ptr<include_cleaner::PragmaIncludes> PI;
262 index::IndexingOptions IndexOpts;
263 IndexOpts.SystemSymbolFilter =
264 index::IndexingOptions::SystemSymbolFilterKind::All;
265 IndexOpts.IndexFunctionLocals =
true;
266 std::shared_ptr<include_cleaner::PragmaIncludes> PI =
267 std::make_shared<include_cleaner::PragmaIncludes>();
268 COpts.PragmaIncludes = PI.get();
269 Collector = std::make_shared<SymbolCollector>(COpts);
270 return std::make_unique<IndexAction>(Collector, std::move(IndexOpts),
274 std::shared_ptr<SymbolCollector> Collector;
275 SymbolCollector::Options
COpts;
278class SymbolCollectorTest :
public ::testing::Test {
280 SymbolCollectorTest()
290 bool runSymbolCollector(llvm::StringRef HeaderCode, llvm::StringRef MainCode,
291 const std::vector<std::string> &ExtraArgs = {}) {
292 llvm::IntrusiveRefCntPtr<FileManager> Files(
293 new FileManager(FileSystemOptions(), InMemoryFileSystem));
295 auto Factory = std::make_unique<SymbolIndexActionFactory>(CollectorOpts);
297 std::vector<std::string>
Args = {
"symbol_collector",
"-fsyntax-only",
299 Args.insert(
Args.end(), ExtraArgs.begin(), ExtraArgs.end());
302 Args.push_back(TestFileName);
304 tooling::ToolInvocation Invocation(
305 Args, Factory->create(), Files.get(),
306 std::make_shared<PCHContainerOperations>());
311 TestHeaderName, 0, llvm::MemoryBuffer::getMemBuffer(HeaderCode)));
313 TestFileName, 0, llvm::MemoryBuffer::getMemBuffer(MainCode)));
315 Symbols = Factory->Collector->takeSymbols();
316 Refs = Factory->Collector->takeRefs();
317 Relations = Factory->Collector->takeRelations();
329 RelationSlab Relations;
333TEST_F(SymbolCollectorTest, CollectSymbols) {
334 const std::string Header = R
"(
341 Foo& operator=(const Foo&);
352 static const int KInt = 2;
353 const char* kStr = "123";
356 void ff() {} // ignore
360 auto LocalLambda = [&](){
361 class ClassInLambda{};
368 using int32_t = int32;
383 runSymbolCollector(Header, "");
385 UnorderedElementsAreArray(
386 {AllOf(qName(
"Foo"), forCodeCompletion(
true)),
387 AllOf(qName(
"Foo::Foo"), forCodeCompletion(
false)),
388 AllOf(qName(
"Foo::Foo"), forCodeCompletion(
false)),
389 AllOf(qName(
"Foo::f"), forCodeCompletion(
false)),
390 AllOf(qName(
"Foo::~Foo"), forCodeCompletion(
false)),
391 AllOf(qName(
"Foo::operator="), forCodeCompletion(
false)),
392 AllOf(qName(
"Foo::Nested"), forCodeCompletion(
false)),
393 AllOf(qName(
"Foo::Nested::f"), forCodeCompletion(
false)),
394 AllOf(qName(
"ClassInLambda"), forCodeCompletion(
false)),
395 AllOf(qName(
"Friend"), forCodeCompletion(
true)),
396 AllOf(qName(
"f1"), forCodeCompletion(
true)),
397 AllOf(qName(
"f2"), forCodeCompletion(
true)),
398 AllOf(qName(
"KInt"), forCodeCompletion(
true)),
399 AllOf(qName(
"kStr"), forCodeCompletion(
true)),
400 AllOf(qName(
"foo"), forCodeCompletion(
true)),
401 AllOf(qName(
"foo::bar"), forCodeCompletion(
true)),
402 AllOf(qName(
"foo::int32"), forCodeCompletion(
true)),
403 AllOf(qName(
"foo::int32_t"), forCodeCompletion(
true)),
404 AllOf(qName(
"foo::v1"), forCodeCompletion(
true)),
405 AllOf(qName(
"foo::bar::v2"), forCodeCompletion(
true)),
406 AllOf(qName(
"foo::v2"), forCodeCompletion(
true)),
407 AllOf(qName(
"foo::baz"), forCodeCompletion(
true))}));
410TEST_F(SymbolCollectorTest, FileLocal) {
411 const std::string Header = R
"(
418 const std::string Main = R
"(
427 runSymbolCollector(Header, Main);
429 UnorderedElementsAre(
430 AllOf(qName("Foo"), visibleOutsideFile()),
431 AllOf(qName(
"bar"), visibleOutsideFile()),
432 AllOf(qName(
"a"), Not(visibleOutsideFile())),
433 AllOf(qName(
"B"), Not(visibleOutsideFile())),
434 AllOf(qName(
"c"), Not(visibleOutsideFile())),
436 AllOf(qName(
"ForwardDecl"), Not(visibleOutsideFile()))));
439TEST_F(SymbolCollectorTest, Template) {
440 Annotations Header(R
"(
441 // Primary template and explicit specialization are indexed, instantiation
443 template <class T, class U> struct [[Tmpl]] {T $xdecl[[x]] = 0;};
444 template <> struct $specdecl[[Tmpl]]<int, bool> {};
445 template <class U> struct $partspecdecl[[Tmpl]]<bool, U> {};
446 extern template struct Tmpl<float, bool>;
447 template struct Tmpl<double, bool>;
449 runSymbolCollector(Header.code(), "");
451 UnorderedElementsAre(
452 AllOf(qName(
"Tmpl"), declRange(Header.range()),
453 forCodeCompletion(
true)),
454 AllOf(qName(
"Tmpl"), declRange(Header.range(
"specdecl")),
455 forCodeCompletion(
false)),
456 AllOf(qName(
"Tmpl"), declRange(Header.range(
"partspecdecl")),
457 forCodeCompletion(
false)),
458 AllOf(qName(
"Tmpl::x"), declRange(Header.range(
"xdecl")),
459 forCodeCompletion(
false))));
462TEST_F(SymbolCollectorTest, templateArgs) {
463 Annotations Header(R
"(
464 template <class X> class $barclasstemp[[Bar]] {};
465 template <class T, class U, template<typename> class Z, int Q>
466 struct [[Tmpl]] { T $xdecl[[x]] = 0; };
468 // template-template, non-type and type full spec
469 template <> struct $specdecl[[Tmpl]]<int, bool, Bar, 3> {};
471 // template-template, non-type and type partial spec
472 template <class U, int T> struct $partspecdecl[[Tmpl]]<bool, U, Bar, T> {};
474 extern template struct Tmpl<float, bool, Bar, 8>;
476 template struct Tmpl<double, bool, Bar, 2>;
478 template <typename ...> class $fooclasstemp[[Foo]] {};
479 // parameter-packs full spec
480 template<> class $parampack[[Foo]]<Bar<int>, int, double> {};
481 // parameter-packs partial spec
482 template<class T> class $parampackpartial[[Foo]]<T, T> {};
484 template <int ...> class $bazclasstemp[[Baz]] {};
485 // non-type parameter-packs full spec
486 template<> class $parampacknontype[[Baz]]<3, 5, 8> {};
487 // non-type parameter-packs partial spec
488 template<int T> class $parampacknontypepartial[[Baz]]<T, T> {};
490 template <template <class> class ...> class $fozclasstemp[[Foz]] {};
491 // template-template parameter-packs full spec
492 template<> class $parampacktempltempl[[Foz]]<Bar, Bar> {};
493 // template-template parameter-packs partial spec
494 template<template <class> class T>
495 class $parampacktempltemplpartial[[Foz]]<T, T> {};
497 runSymbolCollector(Header.code(), "");
501 Contains(AllOf(qName(
"Tmpl"), templateArgs(
"<int, bool, Bar, 3>"),
502 declRange(Header.range(
"specdecl")),
503 forCodeCompletion(
false))),
504 Contains(AllOf(qName(
"Tmpl"), templateArgs(
"<bool, U, Bar, T>"),
505 declRange(Header.range(
"partspecdecl")),
506 forCodeCompletion(
false))),
507 Contains(AllOf(qName(
"Foo"), templateArgs(
"<Bar<int>, int, double>"),
508 declRange(Header.range(
"parampack")),
509 forCodeCompletion(
false))),
510 Contains(AllOf(qName(
"Foo"), templateArgs(
"<T, T>"),
511 declRange(Header.range(
"parampackpartial")),
512 forCodeCompletion(
false))),
513 Contains(AllOf(qName(
"Baz"), templateArgs(
"<3, 5, 8>"),
514 declRange(Header.range(
"parampacknontype")),
515 forCodeCompletion(
false))),
516 Contains(AllOf(qName(
"Baz"), templateArgs(
"<T, T>"),
517 declRange(Header.range(
"parampacknontypepartial")),
518 forCodeCompletion(
false))),
519 Contains(AllOf(qName(
"Foz"), templateArgs(
"<Bar, Bar>"),
520 declRange(Header.range(
"parampacktempltempl")),
521 forCodeCompletion(
false))),
522 Contains(AllOf(qName(
"Foz"), templateArgs(
"<T, T>"),
523 declRange(Header.range(
"parampacktempltemplpartial")),
524 forCodeCompletion(
false)))));
527TEST_F(SymbolCollectorTest, ObjCSymbols) {
528 const std::string Header = R
"(
530 - (void)someMethodName:(void*)name1 lastName:(void*)lName;
533 @implementation Person
534 - (void)someMethodName:(void*)name1 lastName:(void*)lName{
536 ^(int param){ int bar; };
540 @interface Person (MyCategory)
541 - (void)someMethodName2:(void*)name2;
544 @implementation Person (MyCategory)
545 - (void)someMethodName2:(void*)name2 {
551 - (void)someMethodName3:(void*)name3;
555 runSymbolCollector(Header,
"", {
"-fblocks",
"-xobjective-c++"});
557 UnorderedElementsAre(
558 qName(
"Person"), qName(
"Person::someMethodName:lastName:"),
559 AllOf(qName(
"MyCategory"), forCodeCompletion(
false)),
560 qName(
"Person::someMethodName2:"), qName(
"MyProtocol"),
561 qName(
"MyProtocol::someMethodName3:")));
564TEST_F(SymbolCollectorTest, ObjCPropertyImpl) {
565 const std::string Header = R
"(
567 @property(nonatomic) int magic;
570 @implementation Container
574 runSymbolCollector(Header,
"", {
"-xobjective-c++"});
575 EXPECT_THAT(
Symbols, Contains(qName(
"Container")));
576 EXPECT_THAT(
Symbols, Contains(qName(
"Container::magic")));
581TEST_F(SymbolCollectorTest, ObjCLocations) {
582 Annotations Header(R
"(
583 // Declared in header, defined in main.
584 @interface $dogdecl[[Dog]]
586 @interface $fluffydecl[[Dog]] (Fluffy)
592 @implementation $dogdef[[Dog]]
594 @implementation $fluffydef[[Dog]] (Fluffy)
596 // Category with no declaration (only implementation).
597 @implementation $ruff[[Dog]] (Ruff)
599 // Implicitly defined interface.
600 @implementation $catdog[[CatDog]]
603 runSymbolCollector(Header.code(), Main.code(),
604 {"-xobjective-c++",
"-Wno-objc-root-class"});
606 UnorderedElementsAre(
607 AllOf(qName(
"Dog"), declRange(Header.range(
"dogdecl")),
608 defRange(Main.range(
"dogdef"))),
609 AllOf(qName(
"Fluffy"), declRange(Header.range(
"fluffydecl")),
610 defRange(Main.range(
"fluffydef"))),
611 AllOf(qName(
"CatDog"), declRange(Main.range(
"catdog")),
612 defRange(Main.range(
"catdog"))),
613 AllOf(qName(
"Ruff"), declRange(Main.range(
"ruff")),
614 defRange(Main.range(
"ruff")))));
617TEST_F(SymbolCollectorTest, ObjCForwardDecls) {
618 Annotations Header(R
"(
619 // Forward declared in header, declared and defined in main.
622 // Never fully declared so Clang latches onto this decl.
623 @class $catdogdecl[[CatDog]];
626 @protocol $barkerdecl[[Barker]]
629 @interface $dogdecl[[Dog]]<Barker>
632 @implementation $dogdef[[Dog]]
635 @implementation $catdogdef[[CatDog]]
638 runSymbolCollector(Header.code(), Main.code(),
639 {"-xobjective-c++",
"-Wno-objc-root-class"});
641 UnorderedElementsAre(
642 AllOf(qName(
"CatDog"), declRange(Header.range(
"catdogdecl")),
643 defRange(Main.range(
"catdogdef"))),
644 AllOf(qName(
"Dog"), declRange(Main.range(
"dogdecl")),
645 defRange(Main.range(
"dogdef"))),
646 AllOf(qName(
"Barker"), declRange(Main.range(
"barkerdecl"))),
647 qName(
"Barker::woof"), qName(
"Dog::woof")));
650TEST_F(SymbolCollectorTest, ObjCClassExtensions) {
651 Annotations Header(R
"(
652 @interface $catdecl[[Cat]]
663 runSymbolCollector(Header.code(), Main.code(),
664 {"-xobjective-c++",
"-Wno-objc-root-class"});
666 UnorderedElementsAre(
667 AllOf(qName(
"Cat"), declRange(Header.range(
"catdecl"))),
668 qName(
"Cat::meow"), qName(
"Cat::pur")));
671TEST_F(SymbolCollectorTest, ObjCFrameworkIncludeHeader) {
673 auto FrameworksPath =
testPath(
"Frameworks/");
674 std::string FrameworkHeader = R
"(
675 __attribute((objc_root_class))
680 testPath("Frameworks/Foundation.framework/Headers/NSObject.h"), 0,
681 llvm::MemoryBuffer::getMemBuffer(FrameworkHeader));
682 std::string PrivateFrameworkHeader = R
"(
683 #import <Foundation/NSObject.h>
685 @interface PrivateClass : NSObject
690 "Frameworks/Foundation.framework/PrivateHeaders/NSObject+Private.h"),
691 0, llvm::MemoryBuffer::getMemBuffer(PrivateFrameworkHeader));
693 std::string Header = R
"(
694 #import <Foundation/NSObject+Private.h>
695 #import <Foundation/NSObject.h>
697 @interface Container : NSObject
700 std::string Main = "";
702 runSymbolCollector(Header, Main, {
"-F", FrameworksPath,
"-xobjective-c++"});
705 UnorderedElementsAre(
706 AllOf(qName(
"NSObject"), includeHeader(
"<Foundation/NSObject.h>")),
707 AllOf(qName(
"PrivateClass"),
708 includeHeader(
"<Foundation/NSObject+Private.h>")),
709 AllOf(qName(
"Container"))));
712 std::string UmbrellaHeader = R
"(
713 #import <Foundation/NSObject.h>
716 testPath("Frameworks/Foundation.framework/Headers/Foundation.h"), 0,
717 llvm::MemoryBuffer::getMemBuffer(UmbrellaHeader));
718 std::string PrivateUmbrellaHeader = R
"(
719 #import <Foundation/NSObject+Private.h>
722 testPath("Frameworks/Foundation.framework/PrivateHeaders/"
723 "Foundation_Private.h"),
724 0, llvm::MemoryBuffer::getMemBuffer(PrivateUmbrellaHeader));
725 runSymbolCollector(Header, Main, {
"-F", FrameworksPath,
"-xobjective-c++"});
728 UnorderedElementsAre(
729 AllOf(qName(
"NSObject"), includeHeader(
"<Foundation/Foundation.h>")),
730 AllOf(qName(
"PrivateClass"),
731 includeHeader(
"<Foundation/Foundation_Private.h>")),
732 AllOf(qName(
"Container"))));
734 runSymbolCollector(Header, Main,
735 {
"-iframework", FrameworksPath,
"-xobjective-c++"});
738 UnorderedElementsAre(
739 AllOf(qName(
"NSObject"), includeHeader(
"<Foundation/Foundation.h>")),
740 AllOf(qName(
"PrivateClass"),
741 includeHeader(
"<Foundation/Foundation_Private.h>")),
742 AllOf(qName(
"Container"))));
745TEST_F(SymbolCollectorTest, Locations) {
746 Annotations Header(R
"cpp(
747 // Declared in header, defined in main.
748 extern int $xdecl[[X]];
749 class $clsdecl[[Cls]];
750 void $printdecl[[print]]();
752 // Declared in header, defined nowhere.
753 extern int $zdecl[[Z]];
758 Annotations Main(R"cpp(
760 class $clsdef[[Cls]] {};
761 void $printdef[[print]]() {}
763 // Declared/defined in main only.
766 runSymbolCollector(Header.code(), Main.code());
768 UnorderedElementsAre(
769 AllOf(qName("X"), declRange(Header.range(
"xdecl")),
770 defRange(Main.range(
"xdef"))),
771 AllOf(qName(
"Cls"), declRange(Header.range(
"clsdecl")),
772 defRange(Main.range(
"clsdef"))),
773 AllOf(qName(
"print"), declRange(Header.range(
"printdecl")),
774 defRange(Main.range(
"printdef"))),
775 AllOf(qName(
"Z"), declRange(Header.range(
"zdecl"))),
776 AllOf(qName(
"foo"), declRange(Header.range(
"foodecl"))),
777 AllOf(qName(
"Y"), declRange(Main.range(
"ydecl")))));
780TEST_F(SymbolCollectorTest, Refs) {
781 Annotations Header(R
"(
782 #define MACRO(X) (X + 1)
791 namespace NS {} // namespace ref is ignored
794 class $bar[[Bar]] {};
796 void $func[[func]]();
803 $foo[[Foo]] foo2 = abc;
804 abc = $macro[[MACRO]](1);
807 Annotations SymbolsOnlyInMainCode(R"(
808 #define FUNC(X) (X+1)
811 static const int c = FUNC(1);
816 runSymbolCollector(Header.code(),
817 (Main.code() + SymbolsOnlyInMainCode.code()).str());
819 haveRanges(Main.ranges(
"foo")))));
821 haveRanges(Main.ranges(
"bar")))));
823 haveRanges(Main.ranges(
"func")))));
826 haveRanges(Main.ranges(
"macro")))));
831 EXPECT_THAT(Refs, Contains(Pair(
findSymbol(MainSymbols,
"a").
ID, _)));
832 EXPECT_THAT(Refs, Contains(Pair(
findSymbol(MainSymbols,
"b").
ID, _)));
833 EXPECT_THAT(Refs, Not(Contains(Pair(
findSymbol(MainSymbols,
"c").
ID, _))));
834 EXPECT_THAT(Refs, Not(Contains(Pair(
findSymbol(MainSymbols,
"FUNC").
ID, _))));
842 runSymbolCollector(Header.code(),
843 (Main.code() + SymbolsOnlyInMainCode.code()).str());
851TEST_F(SymbolCollectorTest, RefContainers) {
852 Annotations
Code(R
"cpp(
853 int $toplevel1[[f1]](int);
855 (void) $ref1a[[f1]](1);
856 auto fptr = &$ref1b[[f1]];
858 int $toplevel2[[v1]] = $ref2[[f1]](2);
859 void f3(int arg = $ref3[[f1]](3));
861 int $classscope1[[member1]] = $ref4[[f1]](4);
862 int $classscope2[[member2]] = 42;
864 constexpr int f4(int x) { return x + 1; }
865 template <int I = $ref5[[f4]](0)> struct S2 {};
866 S2<$ref6[[f4]](0)> v2;
867 S2<$ref7a[[f4]](0)> f5(S2<$ref7b[[f4]](0)>);
869 void $namespacescope1[[f6]]();
870 int $namespacescope2[[v3]];
875 runSymbolCollector(
"",
Code.code());
876 auto FindRefWithRange = [&](
Range R) -> std::optional<Ref> {
877 for (
auto &
Entry : Refs) {
878 for (
auto &Ref :
Entry.second) {
879 if (rangesMatch(Ref.Location, R))
887 EXPECT_TRUE(
bool(Ref));
888 return Ref->Container;
909 EXPECT_FALSE(
Container(
"classscope1").isNull());
910 EXPECT_FALSE(
Container(
"namespacescope1").isNull());
921TEST_F(SymbolCollectorTest, MacroRefInHeader) {
922 Annotations Header(R
"(
923 #define $foo[[FOO]](X) (X + 1)
924 #define $bar[[BAR]](X) (X + 2)
926 // Macro defined multiple times.
928 int ud_1 = $ud1[[UD]];
932 int ud_2 = $ud2[[UD]];
935 // Macros from token concatenations not included.
936 #define $concat[[CONCAT]](X) X##A()
937 #define $prepend[[PREPEND]](X) MACRO##X()
938 #define $macroa[[MACROA]]() 123
939 int B = $concat[[CONCAT]](MACRO);
940 int D = $prepend[[PREPEND]](A);
943 int abc = $foo[[FOO]](1) + $bar[[BAR]]($foo[[FOO]](1));
951 runSymbolCollector(Header.code(),
"");
954 haveRanges(Header.ranges(
"foo")))));
956 haveRanges(Header.ranges(
"bar")))));
958 EXPECT_THAT(Refs, Contains(Pair(_, haveRanges(Header.ranges(
"ud1")))));
959 EXPECT_THAT(Refs, Contains(Pair(_, haveRanges(Header.ranges(
"ud2")))));
961 haveRanges(Header.ranges(
"concat")))));
963 haveRanges(Header.ranges(
"prepend")))));
965 haveRanges(Header.ranges(
"macroa")))));
968TEST_F(SymbolCollectorTest, MacroRefWithoutCollectingSymbol) {
969 Annotations Header(R
"(
970 #define $foo[[FOO]](X) (X + 1)
971 int abc = $foo[[FOO]](1);
976 runSymbolCollector(Header.code(),
"");
977 EXPECT_THAT(Refs, Contains(Pair(_, haveRanges(Header.ranges(
"foo")))));
980TEST_F(SymbolCollectorTest, MacrosWithRefFilter) {
981 Annotations Header(
"#define $macro[[MACRO]](X) (X + 1)");
982 Annotations Main(
"void foo() { int x = $macro[[MACRO]](1); }");
984 runSymbolCollector(Header.code(), Main.code());
985 EXPECT_THAT(Refs, IsEmpty());
988TEST_F(SymbolCollectorTest, SpelledReferences) {
990 llvm::StringRef Header;
991 llvm::StringRef Main;
992 llvm::StringRef TargetSymbolName;
1000 struct $spelled[[Foo]] {
1004 $spelled[[Foo]] Variable1;
1005 $implicit[[MACRO]] Variable2;
1017 void f() { Foo $implicit[[f]]; f = $spelled[[Foo]]();}
1034 for (
const auto& T : TestCases) {
1035 SCOPED_TRACE(T.Header +
"\n---\n" + T.Main);
1036 Annotations Header(T.Header);
1037 Annotations Main(T.Main);
1040 runSymbolCollector(Header.code(), Main.code());
1042 const auto SpelledRanges = Main.ranges(
"spelled");
1043 const auto ImplicitRanges = Main.ranges(
"implicit");
1044 RefSlab::Builder SpelledSlabBuilder, ImplicitSlabBuilder;
1046 for (
const auto &SymbolAndRefs : Refs) {
1047 const auto ID = SymbolAndRefs.first;
1050 for (
const auto &Ref : SymbolAndRefs.second)
1052 SpelledSlabBuilder.insert(
ID, Ref);
1054 ImplicitSlabBuilder.insert(
ID, Ref);
1056 const auto SpelledRefs = std::move(SpelledSlabBuilder).build(),
1057 ImplicitRefs = std::move(ImplicitSlabBuilder).build();
1058 EXPECT_EQ(SpelledRanges.empty(), SpelledRefs.empty());
1059 EXPECT_EQ(ImplicitRanges.empty(), ImplicitRefs.empty());
1060 if (!SpelledRanges.empty())
1061 EXPECT_THAT(SpelledRefs,
1062 Contains(Pair(TargetID, haveRanges(SpelledRanges))));
1063 if (!ImplicitRanges.empty())
1064 EXPECT_THAT(ImplicitRefs,
1065 Contains(Pair(TargetID, haveRanges(ImplicitRanges))));
1069TEST_F(SymbolCollectorTest, NameReferences) {
1072 Annotations Header(R
"(
1080 runSymbolCollector(Header.code(), "");
1084 haveRanges(Header.ranges()))));
1087TEST_F(SymbolCollectorTest, RefsOnMacros) {
1092 Annotations Header(R
"(
1095 #define CAT(X, Y) X##Y
1100 TYPE(TYPE([[Foo]])) foo3;
1101 [[CAT]](Fo, o) foo4;
1105 runSymbolCollector(Header.code(), "");
1107 haveRanges(Header.ranges()))));
1110TEST_F(SymbolCollectorTest, HeaderAsMainFile) {
1112 Annotations Header(R
"(
1113 class $Foo[[Foo]] {};
1115 void $Func[[Func]]() {
1123 runSymbolCollector(
"", Header.code());
1126 haveRanges(Header.ranges(
"Foo"))),
1128 haveRanges(Header.ranges(
"Func")))));
1132 runSymbolCollector(
"", Header.code(),
1133 {
"-xobjective-c++-header"});
1134 EXPECT_THAT(
Symbols, UnorderedElementsAre(qName(
"Foo"), qName(
"Func")));
1137 haveRanges(Header.ranges(
"Foo"))),
1139 haveRanges(Header.ranges(
"Func")))));
1143 runSymbolCollector(
"", Header.code());
1144 EXPECT_THAT(
Symbols, UnorderedElementsAre(qName(
"Foo"), qName(
"Func")));
1147 haveRanges(Header.ranges(
"Foo"))),
1149 haveRanges(Header.ranges(
"Func")))));
1152TEST_F(SymbolCollectorTest, RefsInHeaders) {
1156 Annotations Header(R
"(
1157 #define $macro[[MACRO]](x) (x+1)
1158 class $foo[[Foo]] {};
1160 runSymbolCollector(Header.code(), "");
1162 haveRanges(Header.ranges(
"foo")))));
1164 haveRanges(Header.ranges(
"macro")))));
1167TEST_F(SymbolCollectorTest, BaseOfRelations) {
1168 std::string Header = R
"(
1170 class Derived : public Base {};
1172 runSymbolCollector(Header, "");
1175 EXPECT_THAT(Relations,
1179TEST_F(SymbolCollectorTest, OverrideRelationsSimpleInheritance) {
1180 std::string Header = R
"cpp(
1184 class B : public A {
1185 void foo() override; // A::foo
1188 class C : public B {
1189 void bar() override; // B::bar
1192 void foo() override; // B::foo
1193 void bar() override; // C::bar
1196 runSymbolCollector(Header, "");
1205 std::vector<Relation> Result;
1206 for (
const Relation &R : Relations)
1208 Result.push_back(R);
1209 EXPECT_THAT(Result, UnorderedElementsAre(
1214TEST_F(SymbolCollectorTest, OverrideRelationsMultipleInheritance) {
1215 std::string Header = R
"cpp(
1222 class C : public B {
1223 void bar() override; // B::bar
1226 class D : public A, C {
1227 void foo() override; // A::foo
1228 void bar() override; // C::bar
1229 void baz() override; // C::baz
1232 runSymbolCollector(Header, "");
1241 std::vector<Relation> Result;
1242 for (
const Relation &R : Relations)
1244 Result.push_back(R);
1245 EXPECT_THAT(Result, UnorderedElementsAre(
1250TEST_F(SymbolCollectorTest, CountReferences) {
1251 const std::string Header = R
"(
1255 class Z {}; // not used anywhere
1256 Y* y = nullptr; // used in header doesn't count
1257 #define GLOBAL_Z(name) Z name;
1259 const std::string Main = R
"(
1261 W* w2 = nullptr; // only one usage counts
1264 class Y{}; // definition doesn't count as a reference
1266 GLOBAL_Z(z); // Not a reference to Z, we don't spell the type.
1269 runSymbolCollector(Header, Main);
1272 UnorderedElementsAreArray(
1273 {AllOf(qName(
"W"), refCount(1)), AllOf(qName(
"X"), refCount(1)),
1274 AllOf(qName(
"Y"), refCount(0)), AllOf(qName(
"Z"), refCount(0)),
1275 AllOf(qName(
"y"), refCount(0)), AllOf(qName(
"z"), refCount(0)),
1276 AllOf(qName(
"x"), refCount(0)), AllOf(qName(
"w"), refCount(0)),
1277 AllOf(qName(
"w2"), refCount(0)), AllOf(qName(
"V"), refCount(1)),
1278 AllOf(qName(
"v"), refCount(0))}));
1281TEST_F(SymbolCollectorTest, SymbolRelativeNoFallback) {
1282 runSymbolCollector(
"class Foo {};",
"");
1283 EXPECT_THAT(
Symbols, UnorderedElementsAre(
1287TEST_F(SymbolCollectorTest, SymbolRelativeWithFallback) {
1292 runSymbolCollector(
"class Foo {};",
"");
1293 EXPECT_THAT(
Symbols, UnorderedElementsAre(
1297TEST_F(SymbolCollectorTest, UnittestURIScheme) {
1301 runSymbolCollector(
"class Foo {};",
"");
1302 EXPECT_THAT(
Symbols, UnorderedElementsAre(
1303 AllOf(qName(
"Foo"), declURI(
"unittest:///x.h"))));
1306TEST_F(SymbolCollectorTest, IncludeEnums) {
1307 const std::string Header = R
"(
1328 runSymbolCollector(Header, "");
1330 UnorderedElementsAre(
1331 AllOf(qName(
"Red"), forCodeCompletion(
true)),
1332 AllOf(qName(
"Color"), forCodeCompletion(
true)),
1333 AllOf(qName(
"Green"), forCodeCompletion(
true)),
1334 AllOf(qName(
"Color2"), forCodeCompletion(
true)),
1335 AllOf(qName(
"Color2::Yellow"), forCodeCompletion(
true)),
1336 AllOf(qName(
"ns"), forCodeCompletion(
true)),
1337 AllOf(qName(
"ns::Black"), forCodeCompletion(
true)),
1338 AllOf(qName(
"Color3"), forCodeCompletion(
true)),
1339 AllOf(qName(
"Color3::Blue"), forCodeCompletion(
true))));
1342TEST_F(SymbolCollectorTest, NamelessSymbols) {
1343 const std::string Header = R
"(
1348 runSymbolCollector(Header, "");
1349 EXPECT_THAT(
Symbols, UnorderedElementsAre(qName(
"Foo"),
1350 qName(
"(anonymous struct)::a")));
1353TEST_F(SymbolCollectorTest, SymbolFormedFromRegisteredSchemeFromMacro) {
1355 Annotations Header(R
"(
1357 class name##_Test {};
1359 $expansion[[FF]](abc);
1362 class $spelling[[Test]] {};
1367 runSymbolCollector(Header.code(), "");
1369 UnorderedElementsAre(
1370 AllOf(qName(
"abc_Test"), declRange(Header.range(
"expansion")),
1372 AllOf(qName(
"Test"), declRange(Header.range(
"spelling")),
1376TEST_F(SymbolCollectorTest, SymbolFormedByCLI) {
1377 Annotations Header(R
"(
1379 class $expansion[[NAME]] {};
1382 runSymbolCollector(Header.code(), "", {
"-DNAME=name"});
1383 EXPECT_THAT(
Symbols, UnorderedElementsAre(AllOf(
1384 qName(
"name"), declRange(Header.range(
"expansion")),
1388TEST_F(SymbolCollectorTest, SymbolsInMainFile) {
1389 const std::string Main = R
"(
1405 runSymbolCollector("", Main);
1406 EXPECT_THAT(
Symbols, UnorderedElementsAre(
1407 qName(
"Foo"), qName(
"f1"), qName(
"f2"), qName(
"ff"),
1408 qName(
"foo"), qName(
"foo::Bar"), qName(
"main_f")));
1411TEST_F(SymbolCollectorTest, Documentation) {
1412 const std::string Header = R
"(
1420 runSymbolCollector(Header,
"");
1422 UnorderedElementsAre(
1423 AllOf(qName(
"Foo"), doc(
"doc Foo"), forCodeCompletion(
true)),
1424 AllOf(qName(
"Foo::f"), doc(
""), returnType(
""),
1425 forCodeCompletion(
false))));
1428 runSymbolCollector(Header,
"");
1430 UnorderedElementsAre(
1431 AllOf(qName(
"Foo"), doc(
"doc Foo"), forCodeCompletion(
true)),
1432 AllOf(qName(
"Foo::f"), doc(
"doc f"), returnType(
""),
1433 forCodeCompletion(
false))));
1436TEST_F(SymbolCollectorTest, ClassMembers) {
1437 const std::string Header = R
"(
1446 const std::string Main = R
"(
1450 runSymbolCollector(Header, Main);
1453 UnorderedElementsAre(
1455 AllOf(qName(
"Foo::f"), returnType(
""), forCodeCompletion(
false)),
1456 AllOf(qName(
"Foo::g"), returnType(
""), forCodeCompletion(
false)),
1457 AllOf(qName(
"Foo::sf"), returnType(
""), forCodeCompletion(
false)),
1458 AllOf(qName(
"Foo::ssf"), returnType(
""), forCodeCompletion(
false)),
1459 AllOf(qName(
"Foo::x"), returnType(
""), forCodeCompletion(
false))));
1462TEST_F(SymbolCollectorTest, Scopes) {
1463 const std::string Header = R
"(
1471 runSymbolCollector(Header, "");
1473 UnorderedElementsAre(qName(
"na"), qName(
"na::nb"),
1474 qName(
"na::Foo"), qName(
"na::nb::Bar")));
1477TEST_F(SymbolCollectorTest, ExternC) {
1478 const std::string Header = R
"(
1479 extern "C" { class Foo {}; }
1481 extern "C" { class Bar {}; }
1484 runSymbolCollector(Header, "");
1485 EXPECT_THAT(
Symbols, UnorderedElementsAre(qName(
"na"), qName(
"Foo"),
1489TEST_F(SymbolCollectorTest, SkipInlineNamespace) {
1490 const std::string Header = R
"(
1492 inline namespace nb {
1497 // This is still inlined.
1503 runSymbolCollector(Header, "");
1505 UnorderedElementsAre(qName(
"na"), qName(
"na::nb"),
1506 qName(
"na::Foo"), qName(
"na::Bar")));
1509TEST_F(SymbolCollectorTest, SymbolWithDocumentation) {
1510 const std::string Header = R
"(
1513 int ff(int x, double y) { return 0; }
1516 runSymbolCollector(Header,
"");
1519 UnorderedElementsAre(
1520 qName(
"nx"), AllOf(qName(
"nx::ff"), labeled(
"ff(int x, double y)"),
1521 returnType(
"int"), doc(
"Foo comment."))));
1524TEST_F(SymbolCollectorTest, snippet) {
1525 const std::string Header = R
"(
1528 int ff(int x, double y) { return 0; }
1531 runSymbolCollector(Header, "");
1533 UnorderedElementsAre(
1535 AllOf(qName(
"nx::f"), labeled(
"f()"), snippet(
"f()")),
1536 AllOf(qName(
"nx::ff"), labeled(
"ff(int x, double y)"),
1537 snippet(
"ff(${1:int x}, ${2:double y})"))));
1540TEST_F(SymbolCollectorTest, IncludeHeaderSameAsFileURI) {
1542 runSymbolCollector(
"#pragma once\nclass Foo {};",
"");
1543 EXPECT_THAT(
Symbols, UnorderedElementsAre(
1545 EXPECT_THAT(
Symbols.begin()->IncludeHeaders,
1546 UnorderedElementsAre(IncludeHeaderWithRef(
TestHeaderURI, 1u)));
1549TEST_F(SymbolCollectorTest, CanonicalSTLHeader) {
1555 // Move overloads have special handling.
1556 template <typename _T> T&& move(_T&& __value);
1557 template <typename _I, typename _O> _O move(_I, _I, _O);
1558 template <typename _T, typename _O, typename _I> _O move(
1565 UnorderedElementsAre(
1568 includeHeader(
"<string>")),
1570 AllOf(labeled(
"move(T &&value)"), includeHeader(
"<utility>")),
1571 AllOf(labeled(
"move(I, I, O)"), includeHeader(
"<algorithm>")),
1572 AllOf(labeled(
"move(T &&, O, O, I)"), includeHeader(
"<algorithm>"))));
1575TEST_F(SymbolCollectorTest, IWYUPragma) {
1577 const std::string Header = R
"(
1578 // IWYU pragma: private, include the/good/header.h
1581 runSymbolCollector(Header, "");
1582 EXPECT_THAT(
Symbols, UnorderedElementsAre(
1584 includeHeader(
"\"the/good/header.h\""))));
1587TEST_F(SymbolCollectorTest, IWYUPragmaWithDoubleQuotes) {
1589 const std::string Header = R
"(
1590 // IWYU pragma: private, include "the/good/header.h"
1593 runSymbolCollector(Header, "");
1594 EXPECT_THAT(
Symbols, UnorderedElementsAre(
1596 includeHeader(
"\"the/good/header.h\""))));
1599TEST_F(SymbolCollectorTest, IWYUPragmaExport) {
1601 const std::string Header = R
"cpp(#pragma once
1602 #include "exporter.h"
1604 auto ExporterFile =
testPath(
"exporter.h");
1606 ExporterFile, 0, llvm::MemoryBuffer::getMemBuffer(R
"cpp(#pragma once
1607 #include "private.h" // IWYU pragma: export
1609 auto PrivateFile =
testPath(
"private.h");
1611 PrivateFile, 0, llvm::MemoryBuffer::getMemBuffer(
"class Foo {};"));
1612 runSymbolCollector(Header,
"",
1614 EXPECT_THAT(
Symbols, UnorderedElementsAre(AllOf(
1620TEST_F(SymbolCollectorTest, MainFileIsHeaderWhenSkipIncFile) {
1626 auto IncFile =
testPath(
"test.inc");
1629 llvm::MemoryBuffer::getMemBuffer(
"class X {};"));
1630 runSymbolCollector(
"", R
"cpp(
1631 // Can't use #pragma once in a main file clang doesn't think is a header.
1638 EXPECT_THAT(
Symbols, UnorderedElementsAre(AllOf(qName(
"X"), declURI(IncURI),
1642TEST_F(SymbolCollectorTest, IncFileInNonHeader) {
1646 auto IncFile =
testPath(
"test.inc");
1649 llvm::MemoryBuffer::getMemBuffer(
"class X {};"));
1650 runSymbolCollector(
"", R
"cpp(
1654 EXPECT_THAT(
Symbols, UnorderedElementsAre(AllOf(qName(
"X"), declURI(IncURI),
1655 Not(includeHeader()))));
1660TEST_F(SymbolCollectorTest, HeaderGuardDetected) {
1663 runSymbolCollector(R
"cpp(
1664 #ifndef HEADER_GUARD_
1665 #define HEADER_GUARD_
1667 // Symbols are seen before the header guard is complete.
1671 #endif // Header guard is recognized here.
1674 EXPECT_THAT(
Symbols, Not(Contains(qName(
"HEADER_GUARD_"))));
1675 EXPECT_THAT(
Symbols, Each(includeHeader()));
1678TEST_F(SymbolCollectorTest, NonModularHeader) {
1680 EXPECT_THAT(TU.headerSymbols(), ElementsAre(includeHeader()));
1683 TU.ImplicitHeaderGuard =
false;
1684 EXPECT_THAT(TU.headerSymbols(), ElementsAre(Not(includeHeader())));
1689#error "This file isn't safe to include directly"
1693 TU.ExtraArgs.push_back("-DSECRET");
1694 EXPECT_THAT(TU.headerSymbols(), ElementsAre(Not(includeHeader())));
1697TEST_F(SymbolCollectorTest, AvoidUsingFwdDeclsAsCanonicalDecls) {
1699 Annotations Header(R
"(
1701 // Forward declarations of TagDecls.
1706 // Canonical declarations.
1707 class $cdecl[[C]] {};
1708 struct $sdecl[[S]] {};
1709 union $udecl[[U]] {int $xdecl[[x]]; bool $ydecl[[y]];};
1711 runSymbolCollector(Header.code(), "");
1714 UnorderedElementsAre(
1716 declRange(Header.range(
"cdecl")), includeHeader(
TestHeaderURI),
1719 declRange(Header.range(
"sdecl")), includeHeader(
TestHeaderURI),
1722 declRange(Header.range(
"udecl")), includeHeader(
TestHeaderURI),
1726 defRange(Header.range(
"xdecl"))),
1729 defRange(Header.range(
"ydecl")))));
1732TEST_F(SymbolCollectorTest, ClassForwardDeclarationIsCanonical) {
1734 runSymbolCollector(
"#pragma once\nclass X;",
1736 EXPECT_THAT(
Symbols, UnorderedElementsAre(AllOf(
1741TEST_F(SymbolCollectorTest, UTF16Character) {
1743 Annotations Header(
"class [[pörk]] {};");
1744 runSymbolCollector(Header.code(),
"");
1745 EXPECT_THAT(
Symbols, UnorderedElementsAre(
1746 AllOf(qName(
"pörk"), declRange(Header.range()))));
1749TEST_F(SymbolCollectorTest, DoNotIndexSymbolsInFriendDecl) {
1750 Annotations Header(R
"(
1757 friend void $bar[[bar]]() {}
1763 runSymbolCollector(Header.code(), "");
1766 UnorderedElementsAre(
1767 qName(
"nx"), qName(
"nx::X"),
1768 AllOf(qName(
"nx::Y"), declRange(Header.range(
"y"))),
1769 AllOf(qName(
"nx::Z"), declRange(Header.range(
"z"))),
1770 AllOf(qName(
"nx::foo"), declRange(Header.range(
"foo"))),
1771 AllOf(qName(
"nx::bar"), declRange(Header.range(
"bar")))));
1774TEST_F(SymbolCollectorTest, ReferencesInFriendDecl) {
1775 const std::string Header = R
"(
1779 const std::string Main = R
"(
1786 runSymbolCollector(Header, Main);
1787 EXPECT_THAT(
Symbols, UnorderedElementsAre(AllOf(qName(
"X"), refCount(1)),
1788 AllOf(qName(
"Y"), refCount(1)),
1789 AllOf(qName(
"C"), refCount(0))));
1792TEST_F(SymbolCollectorTest, Origin) {
1794 runSymbolCollector(
"class Foo {};",
"");
1795 EXPECT_THAT(
Symbols, UnorderedElementsAre(
1799 runSymbolCollector(
"#define FOO",
"");
1800 EXPECT_THAT(
Symbols, UnorderedElementsAre(
1804TEST_F(SymbolCollectorTest, CollectMacros) {
1806 Annotations Header(R
"(
1809 #define $mac[[MAC]](x) int x
1810 #define $used[[USED]](y) float y;
1815 Annotations Main(R"(
1816 #define $main[[MAIN]] 1
1821 runSymbolCollector(Header.code(), Main.code());
1824 UnorderedElementsAre(
1825 qName(
"p"), qName(
"t"),
1828 AllOf(labeled(
"MAC(x)"), refCount(0),
1830 declRange(Header.range(
"mac")), visibleOutsideFile()),
1831 AllOf(labeled(
"USED(y)"), refCount(1),
1832 declRange(Header.range(
"used")), visibleOutsideFile()),
1833 AllOf(labeled(
"MAIN"), refCount(0), declRange(Main.range(
"main")),
1834 Not(visibleOutsideFile()))));
1837TEST_F(SymbolCollectorTest, DeprecatedSymbols) {
1838 const std::string Header = R
"(
1839 void TestClangc() __attribute__((deprecated("", "")));
1842 runSymbolCollector(Header, "");
1843 EXPECT_THAT(
Symbols, UnorderedElementsAre(
1844 AllOf(qName(
"TestClangc"), deprecated()),
1845 AllOf(qName(
"TestClangd"), Not(deprecated()))));
1848TEST_F(SymbolCollectorTest, implementationDetail) {
1849 const std::string Header = R
"(
1850 #define DECL_NAME(x, y) x##_##y##_Decl
1851 #define DECL(x, y) class DECL_NAME(x, y) {};
1852 DECL(X, Y); // X_Y_Decl
1856 runSymbolCollector(Header, "");
1858 UnorderedElementsAre(
1859 AllOf(qName(
"X_Y_Decl"), implementationDetail()),
1860 AllOf(qName(
"Public"), Not(implementationDetail()))));
1863TEST_F(SymbolCollectorTest, UsingDecl) {
1864 const char *Header = R
"(
1869 runSymbolCollector(Header, "");
1870 EXPECT_THAT(
Symbols, Contains(qName(
"std::foo")));
1873TEST_F(SymbolCollectorTest, CBuiltins) {
1875 const char *Header = R
"(
1876 extern int printf(const char*, ...);
1878 runSymbolCollector(Header, "", {
"-xc"});
1879 EXPECT_THAT(
Symbols, Contains(qName(
"printf")));
1882TEST_F(SymbolCollectorTest, InvalidSourceLoc) {
1883 const char *Header = R
"(
1884 void operator delete(void*)
1885 __attribute__((__externally_visible__));)";
1886 runSymbolCollector(Header, "");
1887 EXPECT_THAT(
Symbols, Contains(qName(
"operator delete")));
1890TEST_F(SymbolCollectorTest, BadUTF8) {
1893 const char *Header =
"int PUNCT = 0;\n"
1894 "/* \xa1 */ int types[] = { /* \xa1 */PUNCT };";
1897 runSymbolCollector(Header,
"");
1898 EXPECT_THAT(
Symbols, Contains(AllOf(qName(
"types"), doc(
"\xef\xbf\xbd "))));
1899 EXPECT_THAT(
Symbols, Contains(qName(
"PUNCT")));
1904TEST_F(SymbolCollectorTest, MacrosInHeaders) {
1907 runSymbolCollector(
"",
"#define X");
1909 UnorderedElementsAre(AllOf(qName(
"X"), forCodeCompletion(
true))));
1913TEST_F(SymbolCollectorTest, UndefOfModuleMacro) {
1915 TU.AdditionalFiles["bar.h"] = R
"cpp(
1919 TU.AdditionalFiles["foo.h"] =
"#define X 1";
1920 TU.AdditionalFiles[
"module.map"] = R
"cpp(
1926 TU.ExtraArgs.push_back("-fmodules");
1927 TU.ExtraArgs.push_back(
"-fmodule-map-file=" +
testPath(
"module.map"));
1928 TU.OverlayRealFileSystemForModules =
true;
1933 EXPECT_THAT(TU.headerSymbols(), Not(Contains(qName(
"X"))));
1936TEST_F(SymbolCollectorTest, NoCrashOnObjCMethodCStyleParam) {
1939 - (void)fun:(bool)foo, bool bar;
1942 TU.ExtraArgs.push_back("-xobjective-c++");
1946 EXPECT_THAT(TU.headerSymbols(),
1947 UnorderedElementsAre(qName(
"Foo"), qName(
"Foo::fun:")));
1950TEST_F(SymbolCollectorTest, Reserved) {
1951 const char *Header = R
"cpp(
1954 namespace _X { int secret; }
1958 runSymbolCollector(Header,
"");
1959 EXPECT_THAT(
Symbols, UnorderedElementsAre(qName(
"__foo"), qName(
"_X"),
1960 qName(
"_X::secret")));
1963 runSymbolCollector(Header,
"");
1964 EXPECT_THAT(
Symbols, UnorderedElementsAre(qName(
"__foo"), qName(
"_X"),
1965 qName(
"_X::secret")));
1970 runSymbolCollector(
"#pragma GCC system_header\n" + std::string(Header),
"");
1971 EXPECT_THAT(
Symbols, IsEmpty());
1974TEST_F(SymbolCollectorTest, Concepts) {
1975 const char *Header = R
"cpp(
1977 concept A = sizeof(T) <= 8;
1979 runSymbolCollector("", Header, {
"-std=c++20"});
1981 UnorderedElementsAre(AllOf(
1982 qName(
"A"), hasKind(clang::index::SymbolKind::Concept))));
1985TEST_F(SymbolCollectorTest, IncludeHeaderForwardDecls) {
1987 const std::string Header = R
"cpp(#pragma once
1991 auto FullFile =
testPath(
"full.h");
1993 llvm::MemoryBuffer::getMemBuffer(R
"cpp(
1995struct Foo {};)cpp"));
1996 runSymbolCollector(Header, "",
1998 EXPECT_THAT(
Symbols, UnorderedElementsAre(AllOf(
CharSourceRange Range
SourceRange for the file name.
std::string TestHeaderName
SymbolCollector::Options COpts
std::string TestHeaderURI
llvm::IntrusiveRefCntPtr< llvm::vfs::InMemoryFileSystem > InMemoryFileSystem
SymbolCollector::Options CollectorOpts
std::unique_ptr< CompilerInvocation > CI
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.
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
const NamedDecl & findDecl(ParsedAST &AST, llvm::StringRef QName)
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)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Some operations such as code completion produce a set of candidates.
bool CollectMacro
Collect macros.
std::string FallbackDir
When symbol paths cannot be resolved to absolute paths (e.g.
RefKind RefFilter
The symbol ref kinds that will be collected.
bool StoreAllDocumentation
If set to true, SymbolCollector will collect doc for all symbols.
bool CollectMainFileRefs
Collect references to main-file symbols.
bool RefsInHeaders
If set to true, SymbolCollector will collect all refs (from main file and included headers); otherwis...
bool CollectReserved
Collect symbols with reserved names, like __Vector_base.
@ 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)