1 //===-- SymbolCollectorTests.cpp -------------------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "Annotations.h"
13 #include "clang-include-cleaner/Record.h"
14 #include "index/SymbolCollector.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"
40 using ::testing::AllOf
;
41 using ::testing::Contains
;
42 using ::testing::Each
;
43 using ::testing::ElementsAre
;
44 using ::testing::Field
;
45 using ::testing::IsEmpty
;
47 using ::testing::Pair
;
48 using ::testing::UnorderedElementsAre
;
49 using ::testing::UnorderedElementsAreArray
;
51 // GMock helpers for matching Symbol.
52 MATCHER_P(labeled
, Label
, "") {
53 return (arg
.Name
+ arg
.Signature
).str() == Label
;
55 MATCHER_P(returnType
, D
, "") { return arg
.ReturnType
== D
; }
56 MATCHER_P(doc
, D
, "") { return arg
.Documentation
== D
; }
57 MATCHER_P(snippet
, S
, "") {
58 return (arg
.Name
+ arg
.CompletionSnippetSuffix
).str() == S
;
60 MATCHER_P(qName
, Name
, "") { return (arg
.Scope
+ arg
.Name
).str() == Name
; }
61 MATCHER_P(hasName
, Name
, "") { return arg
.Name
== Name
; }
62 MATCHER_P(templateArgs
, TemplArgs
, "") {
63 return arg
.TemplateSpecializationArgs
== TemplArgs
;
65 MATCHER_P(hasKind
, Kind
, "") { return arg
.SymInfo
.Kind
== Kind
; }
66 MATCHER_P(declURI
, P
, "") {
67 return StringRef(arg
.CanonicalDeclaration
.FileURI
) == P
;
69 MATCHER_P(defURI
, P
, "") { return StringRef(arg
.Definition
.FileURI
) == P
; }
70 MATCHER(includeHeader
, "") { return !arg
.IncludeHeaders
.empty(); }
71 MATCHER_P(includeHeader
, P
, "") {
72 return (arg
.IncludeHeaders
.size() == 1) &&
73 (arg
.IncludeHeaders
.begin()->IncludeHeader
== P
);
75 MATCHER_P2(IncludeHeaderWithRef
, includeHeader
, References
, "") {
76 return (arg
.IncludeHeader
== includeHeader
) && (arg
.References
== References
);
78 bool 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
,
84 MATCHER_P(declRange
, Pos
, "") {
85 return rangesMatch(arg
.CanonicalDeclaration
, Pos
);
87 MATCHER_P(defRange
, Pos
, "") { return rangesMatch(arg
.Definition
, Pos
); }
88 MATCHER_P(refCount
, R
, "") { return int(arg
.References
) == R
; }
89 MATCHER_P(forCodeCompletion
, IsIndexedForCodeCompletion
, "") {
90 return static_cast<bool>(arg
.Flags
& Symbol::IndexedForCodeCompletion
) ==
91 IsIndexedForCodeCompletion
;
93 MATCHER(deprecated
, "") { return arg
.Flags
& Symbol::Deprecated
; }
94 MATCHER(implementationDetail
, "") {
95 return arg
.Flags
& Symbol::ImplementationDetail
;
97 MATCHER(visibleOutsideFile
, "") {
98 return static_cast<bool>(arg
.Flags
& Symbol::VisibleOutsideFile
);
100 MATCHER(refRange
, "") {
101 const Ref
&Pos
= ::testing::get
<0>(arg
);
102 const Range
&Range
= ::testing::get
<1>(arg
);
103 return rangesMatch(Pos
.Location
, Range
);
105 MATCHER_P2(OverriddenBy
, Subject
, Object
, "") {
106 return arg
== Relation
{Subject
.ID
, RelationKind::OverriddenBy
, Object
.ID
};
108 MATCHER(isSpelled
, "") {
109 return static_cast<bool>(arg
.Kind
& RefKind::Spelled
);
111 ::testing::Matcher
<const std::vector
<Ref
> &>
112 haveRanges(const std::vector
<Range
> Ranges
) {
113 return ::testing::UnorderedPointwise(refRange(), Ranges
);
116 class ShouldCollectSymbolTest
: public ::testing::Test
{
118 void build(llvm::StringRef HeaderCode
, llvm::StringRef Code
= "") {
119 File
.HeaderFilename
= HeaderName
;
120 File
.Filename
= FileName
;
121 File
.HeaderCode
= std::string(HeaderCode
);
122 File
.Code
= std::string(Code
);
126 // build() must have been called.
127 bool shouldCollect(llvm::StringRef Name
, bool Qualified
= true) {
129 const NamedDecl
&ND
=
130 Qualified
? findDecl(*AST
, Name
) : findUnqualifiedDecl(*AST
, Name
);
131 const SourceManager
&SM
= AST
->getSourceManager();
132 bool MainFile
= isInsideMainFile(ND
.getBeginLoc(), SM
);
133 return SymbolCollector::shouldCollectSymbol(
134 ND
, AST
->getASTContext(), SymbolCollector::Options(), MainFile
);
138 std::string HeaderName
= "f.h";
139 std::string FileName
= "f.cpp";
141 std::optional
<ParsedAST
> AST
; // Initialized after build.
144 TEST_F(ShouldCollectSymbolTest
, ShouldCollectSymbol
) {
148 auto f() { int Local; } // auto ensures function body is parsed.
149 struct { int x; } var;
154 namespace { class InAnonymous {}; }
157 auto AST
= File
.build();
158 EXPECT_TRUE(shouldCollect("nx"));
159 EXPECT_TRUE(shouldCollect("nx::X"));
160 EXPECT_TRUE(shouldCollect("nx::f"));
161 EXPECT_TRUE(shouldCollect("InMain"));
162 EXPECT_TRUE(shouldCollect("InAnonymous", /*Qualified=*/false));
163 EXPECT_TRUE(shouldCollect("g"));
165 EXPECT_FALSE(shouldCollect("Local", /*Qualified=*/false));
168 TEST_F(ShouldCollectSymbolTest
, CollectLocalClassesAndVirtualMethods
) {
173 auto LocalLambda = [&](){
175 class ClassInLambda{};
178 } // auto ensures function body is parsed.
181 virtual void LocalVirtual();
182 void LocalConcrete();
189 auto AST
= File
.build();
190 EXPECT_FALSE(shouldCollect("Local", /*Qualified=*/false));
191 EXPECT_TRUE(shouldCollect("ClassInLambda", /*Qualified=*/false));
192 EXPECT_TRUE(shouldCollect("LocalBase", /*Qualified=*/false));
193 EXPECT_TRUE(shouldCollect("LocalVirtual", /*Qualified=*/false));
194 EXPECT_TRUE(shouldCollect("LocalConcrete", /*Qualified=*/false));
195 EXPECT_FALSE(shouldCollect("BaseMember", /*Qualified=*/false));
196 EXPECT_FALSE(shouldCollect("Local", /*Qualified=*/false));
199 TEST_F(ShouldCollectSymbolTest
, NoPrivateProtoSymbol
) {
200 HeaderName
= "f.proto.h";
202 R
"(// Generated by the protocol buffer compiler. DO NOT EDIT!
211 EXPECT_TRUE(shouldCollect("nx::TopLevel"));
212 EXPECT_TRUE(shouldCollect("nx::Kind::KIND_OK"));
213 EXPECT_TRUE(shouldCollect("nx::Kind"));
215 EXPECT_FALSE(shouldCollect("nx::Top_Level"));
216 EXPECT_FALSE(shouldCollect("nx::Kind::Kind_Not_Ok"));
219 TEST_F(ShouldCollectSymbolTest
, DoubleCheckProtoHeaderComment
) {
220 HeaderName
= "f.proto.h";
229 EXPECT_TRUE(shouldCollect("nx::Top_Level"));
230 EXPECT_TRUE(shouldCollect("nx::Kind_Fine"));
233 class SymbolIndexActionFactory
: public tooling::FrontendActionFactory
{
235 SymbolIndexActionFactory(SymbolCollector::Options COpts
)
236 : COpts(std::move(COpts
)) {}
238 std::unique_ptr
<FrontendAction
> create() override
{
239 class IndexAction
: public ASTFrontendAction
{
241 IndexAction(std::shared_ptr
<index::IndexDataConsumer
> DataConsumer
,
242 const index::IndexingOptions
&Opts
,
243 std::shared_ptr
<include_cleaner::PragmaIncludes
> PI
)
244 : DataConsumer(std::move(DataConsumer
)), Opts(Opts
),
247 std::unique_ptr
<ASTConsumer
>
248 CreateASTConsumer(CompilerInstance
&CI
, llvm::StringRef InFile
) override
{
250 return createIndexingASTConsumer(DataConsumer
, Opts
,
251 CI
.getPreprocessorPtr());
254 bool BeginInvocation(CompilerInstance
&CI
) override
{
255 // Make the compiler parse all comments.
256 CI
.getLangOpts().CommentOpts
.ParseAllComments
= true;
261 std::shared_ptr
<index::IndexDataConsumer
> DataConsumer
;
262 index::IndexingOptions Opts
;
263 std::shared_ptr
<include_cleaner::PragmaIncludes
> PI
;
265 index::IndexingOptions IndexOpts
;
266 IndexOpts
.SystemSymbolFilter
=
267 index::IndexingOptions::SystemSymbolFilterKind::All
;
268 IndexOpts
.IndexFunctionLocals
= true;
269 std::shared_ptr
<include_cleaner::PragmaIncludes
> PI
=
270 std::make_shared
<include_cleaner::PragmaIncludes
>();
271 COpts
.PragmaIncludes
= PI
.get();
272 Collector
= std::make_shared
<SymbolCollector
>(COpts
);
273 return std::make_unique
<IndexAction
>(Collector
, std::move(IndexOpts
),
277 std::shared_ptr
<SymbolCollector
> Collector
;
278 SymbolCollector::Options COpts
;
281 class SymbolCollectorTest
: public ::testing::Test
{
283 SymbolCollectorTest()
284 : InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem
),
285 TestHeaderName(testPath("symbol.h")),
286 TestFileName(testPath("symbol.cc")) {
287 TestHeaderURI
= URI::create(TestHeaderName
).toString();
288 TestFileURI
= URI::create(TestFileName
).toString();
291 // Note that unlike TestTU, no automatic header guard is added.
292 // HeaderCode should start with #pragma once to be treated as modular.
293 bool runSymbolCollector(llvm::StringRef HeaderCode
, llvm::StringRef MainCode
,
294 const std::vector
<std::string
> &ExtraArgs
= {}) {
295 llvm::IntrusiveRefCntPtr
<FileManager
> Files(
296 new FileManager(FileSystemOptions(), InMemoryFileSystem
));
298 auto Factory
= std::make_unique
<SymbolIndexActionFactory
>(CollectorOpts
);
300 std::vector
<std::string
> Args
= {"symbol_collector", "-fsyntax-only",
301 "-xc++", "-include", TestHeaderName
};
302 Args
.insert(Args
.end(), ExtraArgs
.begin(), ExtraArgs
.end());
303 // This allows to override the "-xc++" with something else, i.e.
305 Args
.push_back(TestFileName
);
307 tooling::ToolInvocation
Invocation(
308 Args
, Factory
->create(), Files
.get(),
309 std::make_shared
<PCHContainerOperations
>());
311 // Multiple calls to runSymbolCollector with different contents will fail
312 // to update the filesystem! Why are we sharing one across tests, anyway?
313 EXPECT_TRUE(InMemoryFileSystem
->addFile(
314 TestHeaderName
, 0, llvm::MemoryBuffer::getMemBuffer(HeaderCode
)));
315 EXPECT_TRUE(InMemoryFileSystem
->addFile(
316 TestFileName
, 0, llvm::MemoryBuffer::getMemBuffer(MainCode
)));
318 Symbols
= Factory
->Collector
->takeSymbols();
319 Refs
= Factory
->Collector
->takeRefs();
320 Relations
= Factory
->Collector
->takeRelations();
325 llvm::IntrusiveRefCntPtr
<llvm::vfs::InMemoryFileSystem
> InMemoryFileSystem
;
326 std::string TestHeaderName
;
327 std::string TestHeaderURI
;
328 std::string TestFileName
;
329 std::string TestFileURI
;
332 RelationSlab Relations
;
333 SymbolCollector::Options CollectorOpts
;
336 TEST_F(SymbolCollectorTest
, CollectSymbols
) {
337 const std::string Header
= R
"(
344 Foo& operator=(const Foo&);
355 static const int KInt = 2;
356 const char* kStr = "123";
359 void ff() {} // ignore
363 auto LocalLambda = [&](){
364 class ClassInLambda{};
371 using int32_t = int32;
386 runSymbolCollector(Header
, /*Main=*/"");
388 UnorderedElementsAreArray(
389 {AllOf(qName("Foo"), forCodeCompletion(true)),
390 AllOf(qName("Foo::Foo"), forCodeCompletion(false)),
391 AllOf(qName("Foo::Foo"), forCodeCompletion(false)),
392 AllOf(qName("Foo::f"), forCodeCompletion(false)),
393 AllOf(qName("Foo::~Foo"), forCodeCompletion(false)),
394 AllOf(qName("Foo::operator="), forCodeCompletion(false)),
395 AllOf(qName("Foo::Nested"), forCodeCompletion(false)),
396 AllOf(qName("Foo::Nested::f"), forCodeCompletion(false)),
397 AllOf(qName("ClassInLambda"), forCodeCompletion(false)),
398 AllOf(qName("Friend"), forCodeCompletion(true)),
399 AllOf(qName("f1"), forCodeCompletion(true)),
400 AllOf(qName("f2"), forCodeCompletion(true)),
401 AllOf(qName("KInt"), forCodeCompletion(true)),
402 AllOf(qName("kStr"), forCodeCompletion(true)),
403 AllOf(qName("foo"), forCodeCompletion(true)),
404 AllOf(qName("foo::bar"), forCodeCompletion(true)),
405 AllOf(qName("foo::int32"), forCodeCompletion(true)),
406 AllOf(qName("foo::int32_t"), forCodeCompletion(true)),
407 AllOf(qName("foo::v1"), forCodeCompletion(true)),
408 AllOf(qName("foo::bar::v2"), forCodeCompletion(true)),
409 AllOf(qName("foo::v2"), forCodeCompletion(true)),
410 AllOf(qName("foo::baz"), forCodeCompletion(true))}));
413 TEST_F(SymbolCollectorTest
, FileLocal
) {
414 const std::string Header
= R
"(
421 const std::string Main
= R
"(
430 runSymbolCollector(Header
, Main
);
432 UnorderedElementsAre(
433 AllOf(qName("Foo"), visibleOutsideFile()),
434 AllOf(qName("bar"), visibleOutsideFile()),
435 AllOf(qName("a"), Not(visibleOutsideFile())),
436 AllOf(qName("B"), Not(visibleOutsideFile())),
437 AllOf(qName("c"), Not(visibleOutsideFile())),
438 // FIXME: ForwardDecl likely *is* visible outside.
439 AllOf(qName("ForwardDecl"), Not(visibleOutsideFile()))));
442 TEST_F(SymbolCollectorTest
, Template
) {
443 Annotations
Header(R
"(
444 // Primary template and explicit specialization are indexed, instantiation
446 template <class T, class U> struct [[Tmpl]] {T $xdecl[[x]] = 0;};
447 template <> struct $specdecl[[Tmpl]]<int, bool> {};
448 template <class U> struct $partspecdecl[[Tmpl]]<bool, U> {};
449 extern template struct Tmpl<float, bool>;
450 template struct Tmpl<double, bool>;
452 runSymbolCollector(Header
.code(), /*Main=*/"");
454 UnorderedElementsAre(
455 AllOf(qName("Tmpl"), declRange(Header
.range()),
456 forCodeCompletion(true)),
457 AllOf(qName("Tmpl"), declRange(Header
.range("specdecl")),
458 forCodeCompletion(false)),
459 AllOf(qName("Tmpl"), declRange(Header
.range("partspecdecl")),
460 forCodeCompletion(false)),
461 AllOf(qName("Tmpl::x"), declRange(Header
.range("xdecl")),
462 forCodeCompletion(false))));
465 TEST_F(SymbolCollectorTest
, templateArgs
) {
466 Annotations
Header(R
"(
467 template <class X> class $barclasstemp[[Bar]] {};
468 template <class T, class U, template<typename> class Z, int Q>
469 struct [[Tmpl]] { T $xdecl[[x]] = 0; };
471 // template-template, non-type and type full spec
472 template <> struct $specdecl[[Tmpl]]<int, bool, Bar, 3> {};
474 // template-template, non-type and type partial spec
475 template <class U, int T> struct $partspecdecl[[Tmpl]]<bool, U, Bar, T> {};
477 extern template struct Tmpl<float, bool, Bar, 8>;
479 template struct Tmpl<double, bool, Bar, 2>;
481 template <typename ...> class $fooclasstemp[[Foo]] {};
482 // parameter-packs full spec
483 template<> class $parampack[[Foo]]<Bar<int>, int, double> {};
484 // parameter-packs partial spec
485 template<class T> class $parampackpartial[[Foo]]<T, T> {};
487 template <int ...> class $bazclasstemp[[Baz]] {};
488 // non-type parameter-packs full spec
489 template<> class $parampacknontype[[Baz]]<3, 5, 8> {};
490 // non-type parameter-packs partial spec
491 template<int T> class $parampacknontypepartial[[Baz]]<T, T> {};
493 template <template <class> class ...> class $fozclasstemp[[Foz]] {};
494 // template-template parameter-packs full spec
495 template<> class $parampacktempltempl[[Foz]]<Bar, Bar> {};
496 // template-template parameter-packs partial spec
497 template<template <class> class T>
498 class $parampacktempltemplpartial[[Foz]]<T, T> {};
500 runSymbolCollector(Header
.code(), /*Main=*/"");
504 Contains(AllOf(qName("Tmpl"), templateArgs("<int, bool, Bar, 3>"),
505 declRange(Header
.range("specdecl")),
506 forCodeCompletion(false))),
507 Contains(AllOf(qName("Tmpl"), templateArgs("<bool, U, Bar, T>"),
508 declRange(Header
.range("partspecdecl")),
509 forCodeCompletion(false))),
510 Contains(AllOf(qName("Foo"), templateArgs("<Bar<int>, int, double>"),
511 declRange(Header
.range("parampack")),
512 forCodeCompletion(false))),
513 Contains(AllOf(qName("Foo"), templateArgs("<T, T>"),
514 declRange(Header
.range("parampackpartial")),
515 forCodeCompletion(false))),
516 Contains(AllOf(qName("Baz"), templateArgs("<3, 5, 8>"),
517 declRange(Header
.range("parampacknontype")),
518 forCodeCompletion(false))),
519 Contains(AllOf(qName("Baz"), templateArgs("<T, T>"),
520 declRange(Header
.range("parampacknontypepartial")),
521 forCodeCompletion(false))),
522 Contains(AllOf(qName("Foz"), templateArgs("<Bar, Bar>"),
523 declRange(Header
.range("parampacktempltempl")),
524 forCodeCompletion(false))),
525 Contains(AllOf(qName("Foz"), templateArgs("<T, T>"),
526 declRange(Header
.range("parampacktempltemplpartial")),
527 forCodeCompletion(false)))));
530 TEST_F(SymbolCollectorTest
, ObjCRefs
) {
531 Annotations
Header(R
"(
533 - (void)$talk[[talk]];
534 - (void)$say[[say]]:(id)something;
536 @interface Person (Category)
537 - (void)categoryMethod;
538 - (void)multiArg:(id)a method:(id)b;
542 @implementation Person
543 - (void)$talk[[talk]] {}
544 - (void)$say[[say]]:(id)something {}
547 void fff(Person *p) {
551 [p multiArg:0 method:0];
554 CollectorOpts
.RefFilter
= RefKind::All
;
555 CollectorOpts
.CollectMainFileRefs
= true;
556 TestFileName
= testPath("test.m");
557 runSymbolCollector(Header
.code(), Main
.code(),
558 {"-fblocks", "-xobjective-c++", "-Wno-objc-root-class"});
559 EXPECT_THAT(Refs
, Contains(Pair(findSymbol(Symbols
, "Person::talk").ID
,
560 haveRanges(Main
.ranges("talk")))));
561 EXPECT_THAT(Refs
, Contains(Pair(findSymbol(Symbols
, "Person::say:").ID
,
562 haveRanges(Main
.ranges("say")))));
564 Contains(Pair(findSymbol(Symbols
, "Person::categoryMethod").ID
,
565 ElementsAre(isSpelled()))));
567 Contains(Pair(findSymbol(Symbols
, "Person::multiArg:method:").ID
,
568 ElementsAre(isSpelled()))));
571 TEST_F(SymbolCollectorTest
, ObjCSymbols
) {
572 const std::string Header
= R
"(
574 - (void)someMethodName:(void*)name1 lastName:(void*)lName;
577 @implementation Person
578 - (void)someMethodName:(void*)name1 lastName:(void*)lName{
580 ^(int param){ int bar; };
584 @interface Person (MyCategory)
585 - (void)someMethodName2:(void*)name2;
588 @implementation Person (MyCategory)
589 - (void)someMethodName2:(void*)name2 {
595 - (void)someMethodName3:(void*)name3;
598 TestFileName
= testPath("test.m");
599 runSymbolCollector(Header
, /*Main=*/"", {"-fblocks", "-xobjective-c++"});
601 UnorderedElementsAre(
602 qName("Person"), qName("Person::someMethodName:lastName:"),
603 AllOf(qName("MyCategory"), forCodeCompletion(false)),
604 qName("Person::someMethodName2:"), qName("MyProtocol"),
605 qName("MyProtocol::someMethodName3:")));
608 TEST_F(SymbolCollectorTest
, ObjCPropertyImpl
) {
609 const std::string Header
= R
"(
611 @property(nonatomic) int magic;
614 @implementation Container
617 TestFileName
= testPath("test.m");
618 runSymbolCollector(Header
, /*Main=*/"", {"-xobjective-c++"});
619 EXPECT_THAT(Symbols
, Contains(qName("Container")));
620 EXPECT_THAT(Symbols
, Contains(qName("Container::magic")));
621 // FIXME: Results also contain Container::_magic on some platforms.
622 // Figure out why it's platform-dependent.
625 TEST_F(SymbolCollectorTest
, ObjCLocations
) {
626 Annotations
Header(R
"(
627 // Declared in header, defined in main.
628 @interface $dogdecl[[Dog]]
630 @interface $fluffydecl[[Dog]] (Fluffy)
636 @implementation $dogdef[[Dog]]
638 @implementation $fluffydef[[Dog]] (Fluffy)
640 // Category with no declaration (only implementation).
641 @implementation $ruff[[Dog]] (Ruff)
643 // Implicitly defined interface.
644 @implementation $catdog[[CatDog]]
647 runSymbolCollector(Header
.code(), Main
.code(),
648 {"-xobjective-c++", "-Wno-objc-root-class"});
650 UnorderedElementsAre(
651 AllOf(qName("Dog"), declRange(Header
.range("dogdecl")),
652 defRange(Main
.range("dogdef"))),
653 AllOf(qName("Fluffy"), declRange(Header
.range("fluffydecl")),
654 defRange(Main
.range("fluffydef"))),
655 AllOf(qName("CatDog"), declRange(Main
.range("catdog")),
656 defRange(Main
.range("catdog"))),
657 AllOf(qName("Ruff"), declRange(Main
.range("ruff")),
658 defRange(Main
.range("ruff")))));
661 TEST_F(SymbolCollectorTest
, ObjCForwardDecls
) {
662 Annotations
Header(R
"(
663 // Forward declared in header, declared and defined in main.
666 // Never fully declared so Clang latches onto this decl.
667 @class $catdogdecl[[CatDog]];
670 @protocol $barkerdecl[[Barker]]
673 @interface $dogdecl[[Dog]]<Barker>
676 @implementation $dogdef[[Dog]]
679 @implementation $catdogdef[[CatDog]]
682 runSymbolCollector(Header
.code(), Main
.code(),
683 {"-xobjective-c++", "-Wno-objc-root-class"});
685 UnorderedElementsAre(
686 AllOf(qName("CatDog"), declRange(Header
.range("catdogdecl")),
687 defRange(Main
.range("catdogdef"))),
688 AllOf(qName("Dog"), declRange(Main
.range("dogdecl")),
689 defRange(Main
.range("dogdef"))),
690 AllOf(qName("Barker"), declRange(Main
.range("barkerdecl"))),
691 qName("Barker::woof"), qName("Dog::woof")));
694 TEST_F(SymbolCollectorTest
, ObjCClassExtensions
) {
695 Annotations
Header(R
"(
696 @interface $catdecl[[Cat]]
707 runSymbolCollector(Header
.code(), Main
.code(),
708 {"-xobjective-c++", "-Wno-objc-root-class"});
710 UnorderedElementsAre(
711 AllOf(qName("Cat"), declRange(Header
.range("catdecl"))),
712 qName("Cat::meow"), qName("Cat::pur")));
715 TEST_F(SymbolCollectorTest
, ObjCFrameworkIncludeHeader
) {
716 CollectorOpts
.CollectIncludePath
= true;
717 auto FrameworksPath
= testPath("Frameworks/");
718 std::string FrameworkHeader
= R
"(
719 __attribute((objc_root_class))
723 InMemoryFileSystem
->addFile(
724 testPath("Frameworks/Foundation.framework/Headers/NSObject.h"), 0,
725 llvm::MemoryBuffer::getMemBuffer(FrameworkHeader
));
726 std::string PrivateFrameworkHeader
= R
"(
727 #import <Foundation/NSObject.h>
729 @interface PrivateClass : NSObject
732 InMemoryFileSystem
->addFile(
734 "Frameworks/Foundation.framework/PrivateHeaders/NSObject+Private.h"),
735 0, llvm::MemoryBuffer::getMemBuffer(PrivateFrameworkHeader
));
737 std::string Header
= R
"(
738 #import <Foundation/NSObject+Private.h>
739 #import <Foundation/NSObject.h>
741 @interface Container : NSObject
744 std::string Main
= "";
745 TestFileName
= testPath("test.m");
746 runSymbolCollector(Header
, Main
, {"-F", FrameworksPath
, "-xobjective-c++"});
749 UnorderedElementsAre(
750 AllOf(qName("NSObject"), includeHeader("<Foundation/NSObject.h>")),
751 AllOf(qName("PrivateClass"),
752 includeHeader("<Foundation/NSObject+Private.h>")),
753 AllOf(qName("Container"))));
755 // After adding the umbrella headers, we should use that spelling instead.
756 std::string UmbrellaHeader
= R
"(
757 #import <Foundation/NSObject.h>
759 InMemoryFileSystem
->addFile(
760 testPath("Frameworks/Foundation.framework/Headers/Foundation.h"), 0,
761 llvm::MemoryBuffer::getMemBuffer(UmbrellaHeader
));
762 std::string PrivateUmbrellaHeader
= R
"(
763 #import <Foundation/NSObject+Private.h>
765 InMemoryFileSystem
->addFile(
766 testPath("Frameworks/Foundation.framework/PrivateHeaders/"
767 "Foundation_Private.h"),
768 0, llvm::MemoryBuffer::getMemBuffer(PrivateUmbrellaHeader
));
769 runSymbolCollector(Header
, Main
, {"-F", FrameworksPath
, "-xobjective-c++"});
772 UnorderedElementsAre(
773 AllOf(qName("NSObject"), includeHeader("<Foundation/Foundation.h>")),
774 AllOf(qName("PrivateClass"),
775 includeHeader("<Foundation/Foundation_Private.h>")),
776 AllOf(qName("Container"))));
778 runSymbolCollector(Header
, Main
,
779 {"-iframework", FrameworksPath
, "-xobjective-c++"});
782 UnorderedElementsAre(
783 AllOf(qName("NSObject"), includeHeader("<Foundation/Foundation.h>")),
784 AllOf(qName("PrivateClass"),
785 includeHeader("<Foundation/Foundation_Private.h>")),
786 AllOf(qName("Container"))));
789 TEST_F(SymbolCollectorTest
, Locations
) {
790 Annotations
Header(R
"cpp(
791 // Declared in header, defined in main.
792 extern int $xdecl[[X]];
793 class $clsdecl[[Cls]];
794 void $printdecl[[print]]();
796 // Declared in header, defined nowhere.
797 extern int $zdecl[[Z]];
802 Annotations
Main(R
"cpp(
804 class $clsdef[[Cls]] {};
805 void $printdef[[print]]() {}
807 // Declared/defined in main only.
810 runSymbolCollector(Header
.code(), Main
.code());
812 UnorderedElementsAre(
813 AllOf(qName("X"), declRange(Header
.range("xdecl")),
814 defRange(Main
.range("xdef"))),
815 AllOf(qName("Cls"), declRange(Header
.range("clsdecl")),
816 defRange(Main
.range("clsdef"))),
817 AllOf(qName("print"), declRange(Header
.range("printdecl")),
818 defRange(Main
.range("printdef"))),
819 AllOf(qName("Z"), declRange(Header
.range("zdecl"))),
820 AllOf(qName("foo"), declRange(Header
.range("foodecl"))),
821 AllOf(qName("Y"), declRange(Main
.range("ydecl")))));
824 TEST_F(SymbolCollectorTest
, Refs
) {
825 Annotations
Header(R
"(
826 #define MACRO(X) (X + 1)
835 namespace NS {} // namespace ref is ignored
838 class $bar[[Bar]] {};
840 void $func[[func]]();
847 $foo[[Foo]] foo2 = abc;
848 abc = $macro[[MACRO]](1);
851 Annotations
SymbolsOnlyInMainCode(R
"(
852 #define FUNC(X) (X+1)
855 static const int c = FUNC(1);
858 CollectorOpts
.RefFilter
= RefKind::All
;
859 CollectorOpts
.CollectMacro
= true;
860 runSymbolCollector(Header
.code(),
861 (Main
.code() + SymbolsOnlyInMainCode
.code()).str());
862 EXPECT_THAT(Refs
, Contains(Pair(findSymbol(Symbols
, "Foo").ID
,
863 haveRanges(Main
.ranges("foo")))));
864 EXPECT_THAT(Refs
, Contains(Pair(findSymbol(Symbols
, "Bar").ID
,
865 haveRanges(Main
.ranges("bar")))));
866 EXPECT_THAT(Refs
, Contains(Pair(findSymbol(Symbols
, "func").ID
,
867 haveRanges(Main
.ranges("func")))));
868 EXPECT_THAT(Refs
, Not(Contains(Pair(findSymbol(Symbols
, "NS").ID
, _
))));
869 EXPECT_THAT(Refs
, Contains(Pair(findSymbol(Symbols
, "MACRO").ID
,
870 haveRanges(Main
.ranges("macro")))));
871 // - (a, b) externally visible and should have refs.
872 // - (c, FUNC) externally invisible and had no refs collected.
874 TestTU::withHeaderCode(SymbolsOnlyInMainCode
.code()).headerSymbols();
875 EXPECT_THAT(Refs
, Contains(Pair(findSymbol(MainSymbols
, "a").ID
, _
)));
876 EXPECT_THAT(Refs
, Contains(Pair(findSymbol(MainSymbols
, "b").ID
, _
)));
877 EXPECT_THAT(Refs
, Not(Contains(Pair(findSymbol(MainSymbols
, "c").ID
, _
))));
878 EXPECT_THAT(Refs
, Not(Contains(Pair(findSymbol(MainSymbols
, "FUNC").ID
, _
))));
880 // Run the collector again with CollectMainFileRefs = true.
881 // We need to recreate InMemoryFileSystem because runSymbolCollector()
882 // calls MemoryBuffer::getMemBuffer(), which makes the buffers unusable
883 // after runSymbolCollector() exits.
884 InMemoryFileSystem
= new llvm::vfs::InMemoryFileSystem();
885 CollectorOpts
.CollectMainFileRefs
= true;
886 runSymbolCollector(Header
.code(),
887 (Main
.code() + SymbolsOnlyInMainCode
.code()).str());
888 EXPECT_THAT(Refs
, Contains(Pair(findSymbol(Symbols
, "a").ID
, _
)));
889 EXPECT_THAT(Refs
, Contains(Pair(findSymbol(Symbols
, "b").ID
, _
)));
890 EXPECT_THAT(Refs
, Contains(Pair(findSymbol(Symbols
, "c").ID
, _
)));
891 // However, references to main-file macros are not collected.
892 EXPECT_THAT(Refs
, Not(Contains(Pair(findSymbol(Symbols
, "FUNC").ID
, _
))));
895 TEST_F(SymbolCollectorTest
, RefContainers
) {
896 Annotations
Code(R
"cpp(
897 int $toplevel1[[f1]](int);
899 (void) $ref1a[[f1]](1);
900 auto fptr = &$ref1b[[f1]];
902 int $toplevel2[[v1]] = $ref2[[f1]](2);
903 void f3(int arg = $ref3[[f1]](3));
905 int $classscope1[[member1]] = $ref4[[f1]](4);
906 int $classscope2[[member2]] = 42;
908 constexpr int f4(int x) { return x + 1; }
909 template <int I = $ref5[[f4]](0)> struct S2 {};
910 S2<$ref6[[f4]](0)> v2;
911 S2<$ref7a[[f4]](0)> f5(S2<$ref7b[[f4]](0)>);
913 void $namespacescope1[[f6]]();
914 int $namespacescope2[[v3]];
917 CollectorOpts
.RefFilter
= RefKind::All
;
918 CollectorOpts
.CollectMainFileRefs
= true;
919 runSymbolCollector("", Code
.code());
920 auto FindRefWithRange
= [&](Range R
) -> std::optional
<Ref
> {
921 for (auto &Entry
: Refs
) {
922 for (auto &Ref
: Entry
.second
) {
923 if (rangesMatch(Ref
.Location
, R
))
929 auto Container
= [&](llvm::StringRef RangeName
) {
930 auto Ref
= FindRefWithRange(Code
.range(RangeName
));
931 EXPECT_TRUE(bool(Ref
));
932 return Ref
->Container
;
934 EXPECT_EQ(Container("ref1a"),
935 findSymbol(Symbols
, "f2").ID
); // function body (call)
936 EXPECT_EQ(Container("ref1b"),
937 findSymbol(Symbols
, "f2").ID
); // function body (address-of)
938 EXPECT_EQ(Container("ref2"),
939 findSymbol(Symbols
, "v1").ID
); // variable initializer
940 EXPECT_EQ(Container("ref3"),
941 findSymbol(Symbols
, "f3").ID
); // function parameter default value
942 EXPECT_EQ(Container("ref4"),
943 findSymbol(Symbols
, "S1::member1").ID
); // member initializer
944 EXPECT_EQ(Container("ref5"),
945 findSymbol(Symbols
, "S2").ID
); // template parameter default value
946 EXPECT_EQ(Container("ref6"),
947 findSymbol(Symbols
, "v2").ID
); // type of variable
948 EXPECT_EQ(Container("ref7a"),
949 findSymbol(Symbols
, "f5").ID
); // return type of function
950 EXPECT_EQ(Container("ref7b"),
951 findSymbol(Symbols
, "f5").ID
); // parameter type of function
953 EXPECT_FALSE(Container("classscope1").isNull());
954 EXPECT_FALSE(Container("namespacescope1").isNull());
956 EXPECT_EQ(Container("toplevel1"), Container("toplevel2"));
957 EXPECT_EQ(Container("classscope1"), Container("classscope2"));
958 EXPECT_EQ(Container("namespacescope1"), Container("namespacescope2"));
960 EXPECT_NE(Container("toplevel1"), Container("namespacescope1"));
961 EXPECT_NE(Container("toplevel1"), Container("classscope1"));
962 EXPECT_NE(Container("classscope1"), Container("namespacescope1"));
965 TEST_F(SymbolCollectorTest
, MacroRefInHeader
) {
966 Annotations
Header(R
"(
967 #define $foo[[FOO]](X) (X + 1)
968 #define $bar[[BAR]](X) (X + 2)
970 // Macro defined multiple times.
972 int ud_1 = $ud1[[UD]];
976 int ud_2 = $ud2[[UD]];
979 // Macros from token concatenations not included.
980 #define $concat[[CONCAT]](X) X##A()
981 #define $prepend[[PREPEND]](X) MACRO##X()
982 #define $macroa[[MACROA]]() 123
983 int B = $concat[[CONCAT]](MACRO);
984 int D = $prepend[[PREPEND]](A);
987 int abc = $foo[[FOO]](1) + $bar[[BAR]]($foo[[FOO]](1));
990 CollectorOpts
.RefFilter
= RefKind::All
;
991 CollectorOpts
.RefsInHeaders
= true;
992 // Need this to get the SymbolID for macros for tests.
993 CollectorOpts
.CollectMacro
= true;
995 runSymbolCollector(Header
.code(), "");
997 EXPECT_THAT(Refs
, Contains(Pair(findSymbol(Symbols
, "FOO").ID
,
998 haveRanges(Header
.ranges("foo")))));
999 EXPECT_THAT(Refs
, Contains(Pair(findSymbol(Symbols
, "BAR").ID
,
1000 haveRanges(Header
.ranges("bar")))));
1001 // No unique ID for multiple symbols named UD. Check for ranges only.
1002 EXPECT_THAT(Refs
, Contains(Pair(_
, haveRanges(Header
.ranges("ud1")))));
1003 EXPECT_THAT(Refs
, Contains(Pair(_
, haveRanges(Header
.ranges("ud2")))));
1004 EXPECT_THAT(Refs
, Contains(Pair(findSymbol(Symbols
, "CONCAT").ID
,
1005 haveRanges(Header
.ranges("concat")))));
1006 EXPECT_THAT(Refs
, Contains(Pair(findSymbol(Symbols
, "PREPEND").ID
,
1007 haveRanges(Header
.ranges("prepend")))));
1008 EXPECT_THAT(Refs
, Contains(Pair(findSymbol(Symbols
, "MACROA").ID
,
1009 haveRanges(Header
.ranges("macroa")))));
1012 TEST_F(SymbolCollectorTest
, MacroRefWithoutCollectingSymbol
) {
1013 Annotations
Header(R
"(
1014 #define $foo[[FOO]](X) (X + 1)
1015 int abc = $foo[[FOO]](1);
1017 CollectorOpts
.RefFilter
= RefKind::All
;
1018 CollectorOpts
.RefsInHeaders
= true;
1019 CollectorOpts
.CollectMacro
= false;
1020 runSymbolCollector(Header
.code(), "");
1021 EXPECT_THAT(Refs
, Contains(Pair(_
, haveRanges(Header
.ranges("foo")))));
1024 TEST_F(SymbolCollectorTest
, MacrosWithRefFilter
) {
1025 Annotations
Header("#define $macro[[MACRO]](X) (X + 1)");
1026 Annotations
Main("void foo() { int x = $macro[[MACRO]](1); }");
1027 CollectorOpts
.RefFilter
= RefKind::Unknown
;
1028 runSymbolCollector(Header
.code(), Main
.code());
1029 EXPECT_THAT(Refs
, IsEmpty());
1032 TEST_F(SymbolCollectorTest
, SpelledReferences
) {
1034 llvm::StringRef Header
;
1035 llvm::StringRef Main
;
1036 llvm::StringRef TargetSymbolName
;
1044 struct $spelled[[Foo]] {
1048 $spelled[[Foo]] Variable1;
1049 $implicit[[MACRO]] Variable2;
1061 void f() { Foo $implicit[[f]]; f = $spelled[[Foo]]();}
1063 "Foo::Foo" /// constructor.
1065 { // Unclean identifiers
1076 CollectorOpts
.RefFilter
= RefKind::All
;
1077 CollectorOpts
.RefsInHeaders
= false;
1078 for (const auto& T
: TestCases
) {
1079 SCOPED_TRACE(T
.Header
+ "\n---\n" + T
.Main
);
1080 Annotations
Header(T
.Header
);
1081 Annotations
Main(T
.Main
);
1082 // Reset the file system.
1083 InMemoryFileSystem
= new llvm::vfs::InMemoryFileSystem
;
1084 runSymbolCollector(Header
.code(), Main
.code());
1086 const auto SpelledRanges
= Main
.ranges("spelled");
1087 const auto ImplicitRanges
= Main
.ranges("implicit");
1088 RefSlab::Builder SpelledSlabBuilder
, ImplicitSlabBuilder
;
1089 const auto TargetID
= findSymbol(Symbols
, T
.TargetSymbolName
).ID
;
1090 for (const auto &SymbolAndRefs
: Refs
) {
1091 const auto ID
= SymbolAndRefs
.first
;
1094 for (const auto &Ref
: SymbolAndRefs
.second
)
1095 if ((Ref
.Kind
& RefKind::Spelled
) != RefKind::Unknown
)
1096 SpelledSlabBuilder
.insert(ID
, Ref
);
1098 ImplicitSlabBuilder
.insert(ID
, Ref
);
1100 const auto SpelledRefs
= std::move(SpelledSlabBuilder
).build(),
1101 ImplicitRefs
= std::move(ImplicitSlabBuilder
).build();
1102 EXPECT_EQ(SpelledRanges
.empty(), SpelledRefs
.empty());
1103 EXPECT_EQ(ImplicitRanges
.empty(), ImplicitRefs
.empty());
1104 if (!SpelledRanges
.empty())
1105 EXPECT_THAT(SpelledRefs
,
1106 Contains(Pair(TargetID
, haveRanges(SpelledRanges
))));
1107 if (!ImplicitRanges
.empty())
1108 EXPECT_THAT(ImplicitRefs
,
1109 Contains(Pair(TargetID
, haveRanges(ImplicitRanges
))));
1113 TEST_F(SymbolCollectorTest
, NameReferences
) {
1114 CollectorOpts
.RefFilter
= RefKind::All
;
1115 CollectorOpts
.RefsInHeaders
= true;
1116 Annotations
Header(R
"(
1123 CollectorOpts
.RefFilter
= RefKind::All
;
1124 runSymbolCollector(Header
.code(), "");
1125 // When we find references for class Foo, we expect to see all
1126 // constructor/destructor references.
1127 EXPECT_THAT(Refs
, Contains(Pair(findSymbol(Symbols
, "Foo").ID
,
1128 haveRanges(Header
.ranges()))));
1131 TEST_F(SymbolCollectorTest
, RefsOnMacros
) {
1132 // Refs collected from SymbolCollector behave in the same way as
1134 CollectorOpts
.RefFilter
= RefKind::All
;
1135 CollectorOpts
.RefsInHeaders
= true;
1136 Annotations
Header(R
"(
1139 #define CAT(X, Y) X##Y
1144 TYPE(TYPE([[Foo]])) foo3;
1145 [[CAT]](Fo, o) foo4;
1148 CollectorOpts
.RefFilter
= RefKind::All
;
1149 runSymbolCollector(Header
.code(), "");
1150 EXPECT_THAT(Refs
, Contains(Pair(findSymbol(Symbols
, "Foo").ID
,
1151 haveRanges(Header
.ranges()))));
1154 TEST_F(SymbolCollectorTest
, HeaderAsMainFile
) {
1155 CollectorOpts
.RefFilter
= RefKind::All
;
1156 Annotations
Header(R
"(
1157 class $Foo[[Foo]] {};
1159 void $Func[[Func]]() {
1163 // We should collect refs to main-file symbols in all cases:
1165 // 1. The main file is normal .cpp file.
1166 TestFileName
= testPath("foo.cpp");
1167 runSymbolCollector("", Header
.code());
1169 UnorderedElementsAre(Pair(findSymbol(Symbols
, "Foo").ID
,
1170 haveRanges(Header
.ranges("Foo"))),
1171 Pair(findSymbol(Symbols
, "Func").ID
,
1172 haveRanges(Header
.ranges("Func")))));
1174 // 2. Run the .h file as main file.
1175 TestFileName
= testPath("foo.h");
1176 runSymbolCollector("", Header
.code(),
1177 /*ExtraArgs=*/{"-xobjective-c++-header"});
1178 EXPECT_THAT(Symbols
, UnorderedElementsAre(qName("Foo"), qName("Func")));
1180 UnorderedElementsAre(Pair(findSymbol(Symbols
, "Foo").ID
,
1181 haveRanges(Header
.ranges("Foo"))),
1182 Pair(findSymbol(Symbols
, "Func").ID
,
1183 haveRanges(Header
.ranges("Func")))));
1185 // 3. Run the .hh file as main file (without "-x c++-header").
1186 TestFileName
= testPath("foo.hh");
1187 runSymbolCollector("", Header
.code());
1188 EXPECT_THAT(Symbols
, UnorderedElementsAre(qName("Foo"), qName("Func")));
1190 UnorderedElementsAre(Pair(findSymbol(Symbols
, "Foo").ID
,
1191 haveRanges(Header
.ranges("Foo"))),
1192 Pair(findSymbol(Symbols
, "Func").ID
,
1193 haveRanges(Header
.ranges("Func")))));
1196 TEST_F(SymbolCollectorTest
, RefsInHeaders
) {
1197 CollectorOpts
.RefFilter
= RefKind::All
;
1198 CollectorOpts
.RefsInHeaders
= true;
1199 CollectorOpts
.CollectMacro
= true;
1200 Annotations
Header(R
"(
1201 #define $macro[[MACRO]](x) (x+1)
1202 class $foo[[Foo]] {};
1204 runSymbolCollector(Header
.code(), "");
1205 EXPECT_THAT(Refs
, Contains(Pair(findSymbol(Symbols
, "Foo").ID
,
1206 haveRanges(Header
.ranges("foo")))));
1207 EXPECT_THAT(Refs
, Contains(Pair(findSymbol(Symbols
, "MACRO").ID
,
1208 haveRanges(Header
.ranges("macro")))));
1211 TEST_F(SymbolCollectorTest
, BaseOfRelations
) {
1212 std::string Header
= R
"(
1214 class Derived : public Base {};
1216 runSymbolCollector(Header
, /*Main=*/"");
1217 const Symbol
&Base
= findSymbol(Symbols
, "Base");
1218 const Symbol
&Derived
= findSymbol(Symbols
, "Derived");
1219 EXPECT_THAT(Relations
,
1220 Contains(Relation
{Base
.ID
, RelationKind::BaseOf
, Derived
.ID
}));
1223 TEST_F(SymbolCollectorTest
, OverrideRelationsSimpleInheritance
) {
1224 std::string Header
= R
"cpp(
1228 class B : public A {
1229 void foo() override; // A::foo
1232 class C : public B {
1233 void bar() override; // B::bar
1236 void foo() override; // B::foo
1237 void bar() override; // C::bar
1240 runSymbolCollector(Header
, /*Main=*/"");
1241 const Symbol
&AFoo
= findSymbol(Symbols
, "A::foo");
1242 const Symbol
&BFoo
= findSymbol(Symbols
, "B::foo");
1243 const Symbol
&DFoo
= findSymbol(Symbols
, "D::foo");
1245 const Symbol
&BBar
= findSymbol(Symbols
, "B::bar");
1246 const Symbol
&CBar
= findSymbol(Symbols
, "C::bar");
1247 const Symbol
&DBar
= findSymbol(Symbols
, "D::bar");
1249 std::vector
<Relation
> Result
;
1250 for (const Relation
&R
: Relations
)
1251 if (R
.Predicate
== RelationKind::OverriddenBy
)
1252 Result
.push_back(R
);
1253 EXPECT_THAT(Result
, UnorderedElementsAre(
1254 OverriddenBy(AFoo
, BFoo
), OverriddenBy(BBar
, CBar
),
1255 OverriddenBy(BFoo
, DFoo
), OverriddenBy(CBar
, DBar
)));
1258 TEST_F(SymbolCollectorTest
, OverrideRelationsMultipleInheritance
) {
1259 std::string Header
= R
"cpp(
1266 class C : public B {
1267 void bar() override; // B::bar
1270 class D : public A, C {
1271 void foo() override; // A::foo
1272 void bar() override; // C::bar
1273 void baz() override; // C::baz
1276 runSymbolCollector(Header
, /*Main=*/"");
1277 const Symbol
&AFoo
= findSymbol(Symbols
, "A::foo");
1278 const Symbol
&BBar
= findSymbol(Symbols
, "B::bar");
1279 const Symbol
&CBar
= findSymbol(Symbols
, "C::bar");
1280 const Symbol
&CBaz
= findSymbol(Symbols
, "C::baz");
1281 const Symbol
&DFoo
= findSymbol(Symbols
, "D::foo");
1282 const Symbol
&DBar
= findSymbol(Symbols
, "D::bar");
1283 const Symbol
&DBaz
= findSymbol(Symbols
, "D::baz");
1285 std::vector
<Relation
> Result
;
1286 for (const Relation
&R
: Relations
)
1287 if (R
.Predicate
== RelationKind::OverriddenBy
)
1288 Result
.push_back(R
);
1289 EXPECT_THAT(Result
, UnorderedElementsAre(
1290 OverriddenBy(BBar
, CBar
), OverriddenBy(AFoo
, DFoo
),
1291 OverriddenBy(CBar
, DBar
), OverriddenBy(CBaz
, DBaz
)));
1294 TEST_F(SymbolCollectorTest
, CountReferences
) {
1295 const std::string Header
= R
"(
1299 class Z {}; // not used anywhere
1300 Y* y = nullptr; // used in header doesn't count
1301 #define GLOBAL_Z(name) Z name;
1303 const std::string Main
= R
"(
1305 W* w2 = nullptr; // only one usage counts
1308 class Y{}; // definition doesn't count as a reference
1310 GLOBAL_Z(z); // Not a reference to Z, we don't spell the type.
1312 CollectorOpts
.CountReferences
= true;
1313 runSymbolCollector(Header
, Main
);
1316 UnorderedElementsAreArray(
1317 {AllOf(qName("W"), refCount(1)), AllOf(qName("X"), refCount(1)),
1318 AllOf(qName("Y"), refCount(0)), AllOf(qName("Z"), refCount(0)),
1319 AllOf(qName("y"), refCount(0)), AllOf(qName("z"), refCount(0)),
1320 AllOf(qName("x"), refCount(0)), AllOf(qName("w"), refCount(0)),
1321 AllOf(qName("w2"), refCount(0)), AllOf(qName("V"), refCount(1)),
1322 AllOf(qName("v"), refCount(0))}));
1325 TEST_F(SymbolCollectorTest
, SymbolRelativeNoFallback
) {
1326 runSymbolCollector("class Foo {};", /*Main=*/"");
1327 EXPECT_THAT(Symbols
, UnorderedElementsAre(
1328 AllOf(qName("Foo"), declURI(TestHeaderURI
))));
1331 TEST_F(SymbolCollectorTest
, SymbolRelativeWithFallback
) {
1332 TestHeaderName
= "x.h";
1333 TestFileName
= "x.cpp";
1334 TestHeaderURI
= URI::create(testPath(TestHeaderName
)).toString();
1335 CollectorOpts
.FallbackDir
= testRoot();
1336 runSymbolCollector("class Foo {};", /*Main=*/"");
1337 EXPECT_THAT(Symbols
, UnorderedElementsAre(
1338 AllOf(qName("Foo"), declURI(TestHeaderURI
))));
1341 TEST_F(SymbolCollectorTest
, UnittestURIScheme
) {
1342 // Use test URI scheme from URITests.cpp
1343 TestHeaderName
= testPath("x.h");
1344 TestFileName
= testPath("x.cpp");
1345 runSymbolCollector("class Foo {};", /*Main=*/"");
1346 EXPECT_THAT(Symbols
, UnorderedElementsAre(
1347 AllOf(qName("Foo"), declURI("unittest:///x.h"))));
1350 TEST_F(SymbolCollectorTest
, IncludeEnums
) {
1351 const std::string Header
= R
"(
1372 runSymbolCollector(Header
, /*Main=*/"");
1373 EXPECT_THAT(Symbols
,
1374 UnorderedElementsAre(
1375 AllOf(qName("Red"), forCodeCompletion(true)),
1376 AllOf(qName("Color"), forCodeCompletion(true)),
1377 AllOf(qName("Green"), forCodeCompletion(true)),
1378 AllOf(qName("Color2"), forCodeCompletion(true)),
1379 AllOf(qName("Color2::Yellow"), forCodeCompletion(true)),
1380 AllOf(qName("ns"), forCodeCompletion(true)),
1381 AllOf(qName("ns::Black"), forCodeCompletion(true)),
1382 AllOf(qName("Color3"), forCodeCompletion(true)),
1383 AllOf(qName("Color3::Blue"), forCodeCompletion(true))));
1386 TEST_F(SymbolCollectorTest
, NamelessSymbols
) {
1387 const std::string Header
= R
"(
1392 runSymbolCollector(Header
, /*Main=*/"");
1393 EXPECT_THAT(Symbols
, UnorderedElementsAre(qName("Foo"),
1394 qName("(anonymous struct)::a")));
1397 TEST_F(SymbolCollectorTest
, SymbolFormedFromRegisteredSchemeFromMacro
) {
1399 Annotations
Header(R
"(
1401 class name##_Test {};
1403 $expansion[[FF]](abc);
1406 class $spelling[[Test]] {};
1411 runSymbolCollector(Header
.code(), /*Main=*/"");
1412 EXPECT_THAT(Symbols
,
1413 UnorderedElementsAre(
1414 AllOf(qName("abc_Test"), declRange(Header
.range("expansion")),
1415 declURI(TestHeaderURI
)),
1416 AllOf(qName("Test"), declRange(Header
.range("spelling")),
1417 declURI(TestHeaderURI
))));
1420 TEST_F(SymbolCollectorTest
, SymbolFormedByCLI
) {
1421 Annotations
Header(R
"(
1423 class $expansion[[NAME]] {};
1426 runSymbolCollector(Header
.code(), /*Main=*/"", /*ExtraArgs=*/{"-DNAME=name"});
1427 EXPECT_THAT(Symbols
, UnorderedElementsAre(AllOf(
1428 qName("name"), declRange(Header
.range("expansion")),
1429 declURI(TestHeaderURI
))));
1432 TEST_F(SymbolCollectorTest
, SymbolsInMainFile
) {
1433 const std::string Main
= R
"(
1449 runSymbolCollector(/*Header=*/"", Main
);
1450 EXPECT_THAT(Symbols
, UnorderedElementsAre(
1451 qName("Foo"), qName("f1"), qName("f2"), qName("ff"),
1452 qName("foo"), qName("foo::Bar"), qName("main_f")));
1455 TEST_F(SymbolCollectorTest
, Documentation
) {
1456 const std::string Header
= R
"(
1463 CollectorOpts
.StoreAllDocumentation
= false;
1464 runSymbolCollector(Header
, /* Main */ "");
1465 EXPECT_THAT(Symbols
,
1466 UnorderedElementsAre(
1467 AllOf(qName("Foo"), doc("doc Foo"), forCodeCompletion(true)),
1468 AllOf(qName("Foo::f"), doc(""), returnType(""),
1469 forCodeCompletion(false))));
1471 CollectorOpts
.StoreAllDocumentation
= true;
1472 runSymbolCollector(Header
, /* Main */ "");
1473 EXPECT_THAT(Symbols
,
1474 UnorderedElementsAre(
1475 AllOf(qName("Foo"), doc("doc Foo"), forCodeCompletion(true)),
1476 AllOf(qName("Foo::f"), doc("doc f"), returnType(""),
1477 forCodeCompletion(false))));
1480 TEST_F(SymbolCollectorTest
, ClassMembers
) {
1481 const std::string Header
= R
"(
1490 const std::string Main
= R
"(
1494 runSymbolCollector(Header
, Main
);
1497 UnorderedElementsAre(
1499 AllOf(qName("Foo::f"), returnType(""), forCodeCompletion(false)),
1500 AllOf(qName("Foo::g"), returnType(""), forCodeCompletion(false)),
1501 AllOf(qName("Foo::sf"), returnType(""), forCodeCompletion(false)),
1502 AllOf(qName("Foo::ssf"), returnType(""), forCodeCompletion(false)),
1503 AllOf(qName("Foo::x"), returnType(""), forCodeCompletion(false))));
1506 TEST_F(SymbolCollectorTest
, Scopes
) {
1507 const std::string Header
= R
"(
1515 runSymbolCollector(Header
, /*Main=*/"");
1516 EXPECT_THAT(Symbols
,
1517 UnorderedElementsAre(qName("na"), qName("na::nb"),
1518 qName("na::Foo"), qName("na::nb::Bar")));
1521 TEST_F(SymbolCollectorTest
, ExternC
) {
1522 const std::string Header
= R
"(
1523 extern "C
" { class Foo {}; }
1525 extern "C
" { class Bar {}; }
1528 runSymbolCollector(Header
, /*Main=*/"");
1529 EXPECT_THAT(Symbols
, UnorderedElementsAre(qName("na"), qName("Foo"),
1533 TEST_F(SymbolCollectorTest
, SkipInlineNamespace
) {
1534 const std::string Header
= R
"(
1536 inline namespace nb {
1541 // This is still inlined.
1547 runSymbolCollector(Header
, /*Main=*/"");
1548 EXPECT_THAT(Symbols
,
1549 UnorderedElementsAre(qName("na"), qName("na::nb"),
1550 qName("na::Foo"), qName("na::Bar")));
1553 TEST_F(SymbolCollectorTest
, SymbolWithDocumentation
) {
1554 const std::string Header
= R
"(
1557 int ff(int x, double y) { return 0; }
1560 runSymbolCollector(Header
, /*Main=*/"");
1563 UnorderedElementsAre(
1564 qName("nx"), AllOf(qName("nx::ff"), labeled("ff(int x, double y)"),
1565 returnType("int"), doc("Foo comment."))));
1568 TEST_F(SymbolCollectorTest
, snippet
) {
1569 const std::string Header
= R
"(
1572 int ff(int x, double y) { return 0; }
1575 runSymbolCollector(Header
, /*Main=*/"");
1576 EXPECT_THAT(Symbols
,
1577 UnorderedElementsAre(
1579 AllOf(qName("nx::f"), labeled("f()"), snippet("f()")),
1580 AllOf(qName("nx::ff"), labeled("ff(int x, double y)"),
1581 snippet("ff(${1:int x}, ${2:double y})"))));
1584 TEST_F(SymbolCollectorTest
, IncludeHeaderSameAsFileURI
) {
1585 CollectorOpts
.CollectIncludePath
= true;
1586 runSymbolCollector("#pragma once\nclass Foo {};", /*Main=*/"");
1587 EXPECT_THAT(Symbols
, UnorderedElementsAre(
1588 AllOf(qName("Foo"), declURI(TestHeaderURI
))));
1589 EXPECT_THAT(Symbols
.begin()->IncludeHeaders
,
1590 UnorderedElementsAre(IncludeHeaderWithRef(TestHeaderURI
, 1u)));
1593 TEST_F(SymbolCollectorTest
, CanonicalSTLHeader
) {
1594 CollectorOpts
.CollectIncludePath
= true;
1599 // Move overloads have special handling.
1600 template <typename _T> T&& move(_T&& __value);
1601 template <typename _I, typename _O> _O move(_I, _I, _O);
1602 template <typename _T, typename _O, typename _I> _O move(
1609 UnorderedElementsAre(
1611 AllOf(qName("std::string"), declURI(TestHeaderURI
),
1612 includeHeader("<string>")),
1613 // Parameter names are demangled.
1614 AllOf(labeled("move(T &&value)"), includeHeader("<utility>")),
1615 AllOf(labeled("move(I, I, O)"), includeHeader("<algorithm>")),
1616 AllOf(labeled("move(T &&, O, O, I)"), includeHeader("<algorithm>"))));
1619 TEST_F(SymbolCollectorTest
, IWYUPragma
) {
1620 CollectorOpts
.CollectIncludePath
= true;
1621 const std::string Header
= R
"(
1622 // IWYU pragma: private, include the/good/header.h
1625 runSymbolCollector(Header
, /*Main=*/"");
1626 EXPECT_THAT(Symbols
, UnorderedElementsAre(
1627 AllOf(qName("Foo"), declURI(TestHeaderURI
),
1628 includeHeader("\"the/good/header.h\""))));
1631 TEST_F(SymbolCollectorTest
, IWYUPragmaWithDoubleQuotes
) {
1632 CollectorOpts
.CollectIncludePath
= true;
1633 const std::string Header
= R
"(
1634 // IWYU pragma: private, include "the
/good
/header
.h
"
1637 runSymbolCollector(Header
, /*Main=*/"");
1638 EXPECT_THAT(Symbols
, UnorderedElementsAre(
1639 AllOf(qName("Foo"), declURI(TestHeaderURI
),
1640 includeHeader("\"the/good/header.h\""))));
1643 TEST_F(SymbolCollectorTest
, IWYUPragmaExport
) {
1644 CollectorOpts
.CollectIncludePath
= true;
1645 const std::string Header
= R
"cpp(#pragma once
1646 #include "exporter
.h
"
1648 auto ExporterFile
= testPath("exporter.h");
1649 InMemoryFileSystem
->addFile(
1650 ExporterFile
, 0, llvm::MemoryBuffer::getMemBuffer(R
"cpp(#pragma once
1651 #include "private.h
" // IWYU pragma: export
1653 auto PrivateFile
= testPath("private.h");
1654 InMemoryFileSystem
->addFile(
1655 PrivateFile
, 0, llvm::MemoryBuffer::getMemBuffer("class Foo {};"));
1656 runSymbolCollector(Header
, /*Main=*/"",
1657 /*ExtraArgs=*/{"-I", testRoot()});
1658 EXPECT_THAT(Symbols
, UnorderedElementsAre(AllOf(
1660 includeHeader(URI::create(ExporterFile
).toString()),
1661 declURI(URI::create(PrivateFile
).toString()))));
1664 TEST_F(SymbolCollectorTest
, MainFileIsHeaderWhenSkipIncFile
) {
1665 CollectorOpts
.CollectIncludePath
= true;
1666 // To make this case as hard as possible, we won't tell clang main is a
1667 // header. No extension, no -x c++-header.
1668 TestFileName
= testPath("no_ext_main");
1669 TestFileURI
= URI::create(TestFileName
).toString();
1670 auto IncFile
= testPath("test.inc");
1671 auto IncURI
= URI::create(IncFile
).toString();
1672 InMemoryFileSystem
->addFile(IncFile
, 0,
1673 llvm::MemoryBuffer::getMemBuffer("class X {};"));
1674 runSymbolCollector("", R
"cpp(
1675 // Can't use #pragma once in a main file clang doesn't think is a header.
1681 /*ExtraArgs=*/{"-I", testRoot()});
1682 EXPECT_THAT(Symbols
, UnorderedElementsAre(AllOf(qName("X"), declURI(IncURI
),
1683 includeHeader(TestFileURI
))));
1686 TEST_F(SymbolCollectorTest
, IncFileInNonHeader
) {
1687 CollectorOpts
.CollectIncludePath
= true;
1688 TestFileName
= testPath("main.cc");
1689 TestFileURI
= URI::create(TestFileName
).toString();
1690 auto IncFile
= testPath("test.inc");
1691 auto IncURI
= URI::create(IncFile
).toString();
1692 InMemoryFileSystem
->addFile(IncFile
, 0,
1693 llvm::MemoryBuffer::getMemBuffer("class X {};"));
1694 runSymbolCollector("", R
"cpp(
1697 /*ExtraArgs=*/{"-I", testRoot()});
1698 EXPECT_THAT(Symbols
, UnorderedElementsAre(AllOf(qName("X"), declURI(IncURI
),
1699 Not(includeHeader()))));
1702 // Features that depend on header-guards are fragile. Header guards are only
1703 // recognized when the file ends, so we have to defer checking for them.
1704 TEST_F(SymbolCollectorTest
, HeaderGuardDetected
) {
1705 CollectorOpts
.CollectIncludePath
= true;
1706 CollectorOpts
.CollectMacro
= true;
1707 runSymbolCollector(R
"cpp(
1708 #ifndef HEADER_GUARD_
1709 #define HEADER_GUARD_
1711 // Symbols are seen before the header guard is complete.
1715 #endif // Header guard is recognized here.
1718 EXPECT_THAT(Symbols
, Not(Contains(qName("HEADER_GUARD_"))));
1719 EXPECT_THAT(Symbols
, Each(includeHeader()));
1722 TEST_F(SymbolCollectorTest
, NonModularHeader
) {
1723 auto TU
= TestTU::withHeaderCode("int x();");
1724 EXPECT_THAT(TU
.headerSymbols(), ElementsAre(includeHeader()));
1726 // Files missing include guards aren't eligible for insertion.
1727 TU
.ImplicitHeaderGuard
= false;
1728 EXPECT_THAT(TU
.headerSymbols(), ElementsAre(Not(includeHeader())));
1730 // We recognize some patterns of trying to prevent insertion.
1731 TU
= TestTU::withHeaderCode(R
"cpp(
1733 #error "This file isn
't safe to include directly"
1737 TU.ExtraArgs.push_back("-DSECRET"); // *we're
* able to include it
.
1738 EXPECT_THAT(TU
.headerSymbols(), ElementsAre(Not(includeHeader())));
1741 TEST_F(SymbolCollectorTest
, AvoidUsingFwdDeclsAsCanonicalDecls
) {
1742 CollectorOpts
.CollectIncludePath
= true;
1743 Annotations
Header(R
"(
1745 // Forward declarations of TagDecls.
1750 // Canonical declarations.
1751 class $cdecl[[C]] {};
1752 struct $sdecl[[S]] {};
1753 union $udecl[[U]] {int $xdecl[[x]]; bool $ydecl[[y]];};
1755 runSymbolCollector(Header
.code(), /*Main=*/"");
1758 UnorderedElementsAre(
1759 AllOf(qName("C"), declURI(TestHeaderURI
),
1760 declRange(Header
.range("cdecl")), includeHeader(TestHeaderURI
),
1761 defURI(TestHeaderURI
), defRange(Header
.range("cdecl"))),
1762 AllOf(qName("S"), declURI(TestHeaderURI
),
1763 declRange(Header
.range("sdecl")), includeHeader(TestHeaderURI
),
1764 defURI(TestHeaderURI
), defRange(Header
.range("sdecl"))),
1765 AllOf(qName("U"), declURI(TestHeaderURI
),
1766 declRange(Header
.range("udecl")), includeHeader(TestHeaderURI
),
1767 defURI(TestHeaderURI
), defRange(Header
.range("udecl"))),
1768 AllOf(qName("U::x"), declURI(TestHeaderURI
),
1769 declRange(Header
.range("xdecl")), defURI(TestHeaderURI
),
1770 defRange(Header
.range("xdecl"))),
1771 AllOf(qName("U::y"), declURI(TestHeaderURI
),
1772 declRange(Header
.range("ydecl")), defURI(TestHeaderURI
),
1773 defRange(Header
.range("ydecl")))));
1776 TEST_F(SymbolCollectorTest
, ClassForwardDeclarationIsCanonical
) {
1777 CollectorOpts
.CollectIncludePath
= true;
1778 runSymbolCollector(/*Header=*/"#pragma once\nclass X;",
1779 /*Main=*/"class X {};");
1780 EXPECT_THAT(Symbols
, UnorderedElementsAre(AllOf(
1781 qName("X"), declURI(TestHeaderURI
),
1782 includeHeader(TestHeaderURI
), defURI(TestFileURI
))));
1785 TEST_F(SymbolCollectorTest
, UTF16Character
) {
1787 Annotations
Header(/*Header=*/"class [[pörk]] {};");
1788 runSymbolCollector(Header
.code(), /*Main=*/"");
1789 EXPECT_THAT(Symbols
, UnorderedElementsAre(
1790 AllOf(qName("pörk"), declRange(Header
.range()))));
1793 TEST_F(SymbolCollectorTest
, DoNotIndexSymbolsInFriendDecl
) {
1794 Annotations
Header(R
"(
1801 friend void $bar[[bar]]() {}
1807 runSymbolCollector(Header
.code(), /*Main=*/"");
1809 EXPECT_THAT(Symbols
,
1810 UnorderedElementsAre(
1811 qName("nx"), qName("nx::X"),
1812 AllOf(qName("nx::Y"), declRange(Header
.range("y"))),
1813 AllOf(qName("nx::Z"), declRange(Header
.range("z"))),
1814 AllOf(qName("nx::foo"), declRange(Header
.range("foo"))),
1815 AllOf(qName("nx::bar"), declRange(Header
.range("bar")))));
1818 TEST_F(SymbolCollectorTest
, ReferencesInFriendDecl
) {
1819 const std::string Header
= R
"(
1823 const std::string Main
= R
"(
1829 CollectorOpts
.CountReferences
= true;
1830 runSymbolCollector(Header
, Main
);
1831 EXPECT_THAT(Symbols
, UnorderedElementsAre(AllOf(qName("X"), refCount(1)),
1832 AllOf(qName("Y"), refCount(1)),
1833 AllOf(qName("C"), refCount(0))));
1836 TEST_F(SymbolCollectorTest
, Origin
) {
1837 CollectorOpts
.Origin
= SymbolOrigin::Static
;
1838 runSymbolCollector("class Foo {};", /*Main=*/"");
1839 EXPECT_THAT(Symbols
, UnorderedElementsAre(
1840 Field(&Symbol::Origin
, SymbolOrigin::Static
)));
1841 InMemoryFileSystem
= new llvm::vfs::InMemoryFileSystem
;
1842 CollectorOpts
.CollectMacro
= true;
1843 runSymbolCollector("#define FOO", /*Main=*/"");
1844 EXPECT_THAT(Symbols
, UnorderedElementsAre(
1845 Field(&Symbol::Origin
, SymbolOrigin::Static
)));
1848 TEST_F(SymbolCollectorTest
, CollectMacros
) {
1849 CollectorOpts
.CollectIncludePath
= true;
1850 Annotations
Header(R
"(
1853 #define $mac[[MAC]](x) int x
1854 #define $used[[USED]](y) float y;
1859 Annotations
Main(R
"(
1860 #define $main[[MAIN]] 1
1863 CollectorOpts
.CountReferences
= true;
1864 CollectorOpts
.CollectMacro
= true;
1865 runSymbolCollector(Header
.code(), Main
.code());
1868 UnorderedElementsAre(
1869 qName("p"), qName("t"),
1870 AllOf(qName("X"), declURI(TestHeaderURI
),
1871 includeHeader(TestHeaderURI
)),
1872 AllOf(labeled("MAC(x)"), refCount(0),
1874 declRange(Header
.range("mac")), visibleOutsideFile()),
1875 AllOf(labeled("USED(y)"), refCount(1),
1876 declRange(Header
.range("used")), visibleOutsideFile()),
1877 AllOf(labeled("MAIN"), refCount(0), declRange(Main
.range("main")),
1878 Not(visibleOutsideFile()))));
1881 TEST_F(SymbolCollectorTest
, DeprecatedSymbols
) {
1882 const std::string Header
= R
"(
1883 void TestClangc() __attribute__((deprecated("", "")));
1886 runSymbolCollector(Header
, /**/ "");
1887 EXPECT_THAT(Symbols
, UnorderedElementsAre(
1888 AllOf(qName("TestClangc"), deprecated()),
1889 AllOf(qName("TestClangd"), Not(deprecated()))));
1892 TEST_F(SymbolCollectorTest
, implementationDetail
) {
1893 const std::string Header
= R
"(
1894 #define DECL_NAME(x, y) x##_##y##_Decl
1895 #define DECL(x, y) class DECL_NAME(x, y) {};
1896 DECL(X, Y); // X_Y_Decl
1900 runSymbolCollector(Header
, /**/ "");
1901 EXPECT_THAT(Symbols
,
1902 UnorderedElementsAre(
1903 AllOf(qName("X_Y_Decl"), implementationDetail()),
1904 AllOf(qName("Public"), Not(implementationDetail()))));
1907 TEST_F(SymbolCollectorTest
, UsingDecl
) {
1908 const char *Header
= R
"(
1913 runSymbolCollector(Header
, /**/ "");
1914 EXPECT_THAT(Symbols
, Contains(qName("std::foo")));
1917 TEST_F(SymbolCollectorTest
, CBuiltins
) {
1918 // In C, printf in stdio.h is a redecl of an implicit builtin.
1919 const char *Header
= R
"(
1920 extern int printf(const char*, ...);
1922 runSymbolCollector(Header
, /**/ "", {"-xc"});
1923 EXPECT_THAT(Symbols
, Contains(qName("printf")));
1926 TEST_F(SymbolCollectorTest
, InvalidSourceLoc
) {
1927 const char *Header
= R
"(
1928 void operator delete(void*)
1929 __attribute__((__externally_visible__));)";
1930 runSymbolCollector(Header
, /**/ "");
1931 EXPECT_THAT(Symbols
, Contains(qName("operator delete")));
1934 TEST_F(SymbolCollectorTest
, BadUTF8
) {
1935 // Extracted from boost/spirit/home/support/char_encoding/iso8859_1.hpp
1936 // This looks like UTF-8 and fools clang, but has high-ISO-8859-1 comments.
1937 const char *Header
= "int PUNCT = 0;\n"
1938 "/* \xa1 */ int types[] = { /* \xa1 */PUNCT };";
1939 CollectorOpts
.RefFilter
= RefKind::All
;
1940 CollectorOpts
.RefsInHeaders
= true;
1941 runSymbolCollector(Header
, "");
1942 EXPECT_THAT(Symbols
, Contains(AllOf(qName("types"), doc("\xef\xbf\xbd "))));
1943 EXPECT_THAT(Symbols
, Contains(qName("PUNCT")));
1944 // Reference is stored, although offset within line is not reliable.
1945 EXPECT_THAT(Refs
, Contains(Pair(findSymbol(Symbols
, "PUNCT").ID
, _
)));
1948 TEST_F(SymbolCollectorTest
, MacrosInHeaders
) {
1949 CollectorOpts
.CollectMacro
= true;
1950 TestFileName
= testPath("test.h");
1951 runSymbolCollector("", "#define X");
1952 EXPECT_THAT(Symbols
,
1953 UnorderedElementsAre(AllOf(qName("X"), forCodeCompletion(true))));
1956 // Regression test for a crash-bug we used to have.
1957 TEST_F(SymbolCollectorTest
, UndefOfModuleMacro
) {
1958 auto TU
= TestTU::withCode(R
"cpp(#include "bar
.h
")cpp");
1959 TU
.AdditionalFiles
["bar.h"] = R
"cpp(
1963 TU
.AdditionalFiles
["foo.h"] = "#define X 1";
1964 TU
.AdditionalFiles
["module.modulemap"] = R
"cpp(
1970 TU
.ExtraArgs
.push_back("-fmodules");
1971 TU
.ExtraArgs
.push_back("-fmodule-map-file=" + testPath("module.modulemap"));
1972 TU
.OverlayRealFileSystemForModules
= true;
1975 // We mostly care about not crashing, but verify that we didn't insert garbage
1977 EXPECT_THAT(TU
.headerSymbols(), Not(Contains(qName("X"))));
1980 TEST_F(SymbolCollectorTest
, NoCrashOnObjCMethodCStyleParam
) {
1981 auto TU
= TestTU::withCode(R
"objc(
1983 - (void)fun:(bool)foo, bool bar;
1986 TU
.ExtraArgs
.push_back("-xobjective-c++");
1989 // We mostly care about not crashing.
1990 EXPECT_THAT(TU
.headerSymbols(),
1991 UnorderedElementsAre(qName("Foo"), qName("Foo::fun:")));
1994 TEST_F(SymbolCollectorTest
, Reserved
) {
1995 const char *Header
= R
"cpp(
1998 namespace _X { int secret; }
2001 CollectorOpts
.CollectReserved
= true;
2002 runSymbolCollector(Header
, "");
2003 EXPECT_THAT(Symbols
, UnorderedElementsAre(qName("__foo"), qName("_X"),
2004 qName("_X::secret")));
2006 CollectorOpts
.CollectReserved
= false;
2007 runSymbolCollector(Header
, "");
2008 EXPECT_THAT(Symbols
, UnorderedElementsAre(qName("__foo"), qName("_X"),
2009 qName("_X::secret")));
2011 // Ugly: for some reason we reuse the test filesystem across tests.
2012 // You can't overwrite the same filename with new content!
2013 InMemoryFileSystem
= new llvm::vfs::InMemoryFileSystem
;
2014 runSymbolCollector("#pragma GCC system_header\n" + std::string(Header
), "");
2015 EXPECT_THAT(Symbols
, IsEmpty());
2018 TEST_F(SymbolCollectorTest
, Concepts
) {
2019 const char *Header
= R
"cpp(
2021 concept A = sizeof(T) <= 8;
2023 runSymbolCollector("", Header
, {"-std=c++20"});
2024 EXPECT_THAT(Symbols
,
2025 UnorderedElementsAre(AllOf(
2026 qName("A"), hasKind(clang::index::SymbolKind::Concept
))));
2029 TEST_F(SymbolCollectorTest
, IncludeHeaderForwardDecls
) {
2030 CollectorOpts
.CollectIncludePath
= true;
2031 const std::string Header
= R
"cpp(#pragma once
2035 auto FullFile
= testPath("full.h");
2036 InMemoryFileSystem
->addFile(FullFile
, 0,
2037 llvm::MemoryBuffer::getMemBuffer(R
"cpp(
2039 struct Foo {};)cpp"));
2040 runSymbolCollector(Header
, /*Main=*/"",
2041 /*ExtraArgs=*/{"-I", testRoot()});
2042 EXPECT_THAT(Symbols
, UnorderedElementsAre(AllOf(
2044 includeHeader(URI::create(FullFile
).toString()))))
2045 << *Symbols
.begin();
2048 } // namespace clangd
2049 } // namespace clang