[AMDGPU][AsmParser][NFC] Translate parsed MIMG instructions to MCInsts automatically.
[llvm-project.git] / clang-tools-extra / clangd / unittests / SymbolCollectorTests.cpp
bloba2bf1c0baf0b3f0965f255bf06dc59203e334b9c
1 //===-- SymbolCollectorTests.cpp -------------------------------*- C++ -*-===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
9 #include "Annotations.h"
10 #include "TestFS.h"
11 #include "TestTU.h"
12 #include "index/SymbolCollector.h"
13 #include "clang/Basic/FileManager.h"
14 #include "clang/Basic/FileSystemOptions.h"
15 #include "clang/Frontend/CompilerInstance.h"
16 #include "clang/Index/IndexingAction.h"
17 #include "clang/Index/IndexingOptions.h"
18 #include "clang/Tooling/Tooling.h"
19 #include "llvm/ADT/IntrusiveRefCntPtr.h"
20 #include "llvm/ADT/StringRef.h"
21 #include "llvm/Support/MemoryBuffer.h"
22 #include "llvm/Support/VirtualFileSystem.h"
23 #include "llvm/Testing/Support/Error.h"
24 #include "gmock/gmock-matchers.h"
25 #include "gmock/gmock.h"
26 #include "gtest/gtest.h"
28 #include <memory>
29 #include <optional>
30 #include <string>
32 namespace clang {
33 namespace clangd {
34 namespace {
36 using ::testing::_;
37 using ::testing::AllOf;
38 using ::testing::Contains;
39 using ::testing::Each;
40 using ::testing::ElementsAre;
41 using ::testing::Field;
42 using ::testing::IsEmpty;
43 using ::testing::Not;
44 using ::testing::Pair;
45 using ::testing::UnorderedElementsAre;
46 using ::testing::UnorderedElementsAreArray;
48 // GMock helpers for matching Symbol.
49 MATCHER_P(labeled, Label, "") {
50 return (arg.Name + arg.Signature).str() == Label;
52 MATCHER_P(returnType, D, "") { return arg.ReturnType == D; }
53 MATCHER_P(doc, D, "") { return arg.Documentation == D; }
54 MATCHER_P(snippet, S, "") {
55 return (arg.Name + arg.CompletionSnippetSuffix).str() == S;
57 MATCHER_P(qName, Name, "") { return (arg.Scope + arg.Name).str() == Name; }
58 MATCHER_P(hasName, Name, "") { return arg.Name == Name; }
59 MATCHER_P(templateArgs, TemplArgs, "") {
60 return arg.TemplateSpecializationArgs == TemplArgs;
62 MATCHER_P(hasKind, Kind, "") { return arg.SymInfo.Kind == Kind; }
63 MATCHER_P(declURI, P, "") {
64 return StringRef(arg.CanonicalDeclaration.FileURI) == P;
66 MATCHER_P(defURI, P, "") { return StringRef(arg.Definition.FileURI) == P; }
67 MATCHER(includeHeader, "") { return !arg.IncludeHeaders.empty(); }
68 MATCHER_P(includeHeader, P, "") {
69 return (arg.IncludeHeaders.size() == 1) &&
70 (arg.IncludeHeaders.begin()->IncludeHeader == P);
72 MATCHER_P2(IncludeHeaderWithRef, includeHeader, References, "") {
73 return (arg.IncludeHeader == includeHeader) && (arg.References == References);
75 bool rangesMatch(const SymbolLocation &Loc, const Range &R) {
76 return std::make_tuple(Loc.Start.line(), Loc.Start.column(), Loc.End.line(),
77 Loc.End.column()) ==
78 std::make_tuple(R.start.line, R.start.character, R.end.line,
79 R.end.character);
81 MATCHER_P(declRange, Pos, "") {
82 return rangesMatch(arg.CanonicalDeclaration, Pos);
84 MATCHER_P(defRange, Pos, "") { return rangesMatch(arg.Definition, Pos); }
85 MATCHER_P(refCount, R, "") { return int(arg.References) == R; }
86 MATCHER_P(forCodeCompletion, IsIndexedForCodeCompletion, "") {
87 return static_cast<bool>(arg.Flags & Symbol::IndexedForCodeCompletion) ==
88 IsIndexedForCodeCompletion;
90 MATCHER(deprecated, "") { return arg.Flags & Symbol::Deprecated; }
91 MATCHER(implementationDetail, "") {
92 return arg.Flags & Symbol::ImplementationDetail;
94 MATCHER(visibleOutsideFile, "") {
95 return static_cast<bool>(arg.Flags & Symbol::VisibleOutsideFile);
97 MATCHER(refRange, "") {
98 const Ref &Pos = ::testing::get<0>(arg);
99 const Range &Range = ::testing::get<1>(arg);
100 return rangesMatch(Pos.Location, Range);
102 MATCHER_P2(OverriddenBy, Subject, Object, "") {
103 return arg == Relation{Subject.ID, RelationKind::OverriddenBy, Object.ID};
105 ::testing::Matcher<const std::vector<Ref> &>
106 haveRanges(const std::vector<Range> Ranges) {
107 return ::testing::UnorderedPointwise(refRange(), Ranges);
110 class ShouldCollectSymbolTest : public ::testing::Test {
111 public:
112 void build(llvm::StringRef HeaderCode, llvm::StringRef Code = "") {
113 File.HeaderFilename = HeaderName;
114 File.Filename = FileName;
115 File.HeaderCode = std::string(HeaderCode);
116 File.Code = std::string(Code);
117 AST = File.build();
120 // build() must have been called.
121 bool shouldCollect(llvm::StringRef Name, bool Qualified = true) {
122 assert(AST);
123 const NamedDecl &ND =
124 Qualified ? findDecl(*AST, Name) : findUnqualifiedDecl(*AST, Name);
125 const SourceManager &SM = AST->getSourceManager();
126 bool MainFile = isInsideMainFile(ND.getBeginLoc(), SM);
127 return SymbolCollector::shouldCollectSymbol(
128 ND, AST->getASTContext(), SymbolCollector::Options(), MainFile);
131 protected:
132 std::string HeaderName = "f.h";
133 std::string FileName = "f.cpp";
134 TestTU File;
135 std::optional<ParsedAST> AST; // Initialized after build.
138 TEST_F(ShouldCollectSymbolTest, ShouldCollectSymbol) {
139 build(R"(
140 namespace nx {
141 class X{};
142 auto f() { int Local; } // auto ensures function body is parsed.
143 struct { int x; } var;
147 class InMain {};
148 namespace { class InAnonymous {}; }
149 static void g();
150 )");
151 auto AST = File.build();
152 EXPECT_TRUE(shouldCollect("nx"));
153 EXPECT_TRUE(shouldCollect("nx::X"));
154 EXPECT_TRUE(shouldCollect("nx::f"));
155 EXPECT_TRUE(shouldCollect("InMain"));
156 EXPECT_TRUE(shouldCollect("InAnonymous", /*Qualified=*/false));
157 EXPECT_TRUE(shouldCollect("g"));
159 EXPECT_FALSE(shouldCollect("Local", /*Qualified=*/false));
162 TEST_F(ShouldCollectSymbolTest, CollectLocalClassesAndVirtualMethods) {
163 build(R"(
164 namespace nx {
165 auto f() {
166 int Local;
167 auto LocalLambda = [&](){
168 Local++;
169 class ClassInLambda{};
170 return Local;
172 } // auto ensures function body is parsed.
173 auto foo() {
174 class LocalBase {
175 virtual void LocalVirtual();
176 void LocalConcrete();
177 int BaseMember;
180 } // namespace nx
182 "");
183 auto AST = File.build();
184 EXPECT_FALSE(shouldCollect("Local", /*Qualified=*/false));
185 EXPECT_TRUE(shouldCollect("ClassInLambda", /*Qualified=*/false));
186 EXPECT_TRUE(shouldCollect("LocalBase", /*Qualified=*/false));
187 EXPECT_TRUE(shouldCollect("LocalVirtual", /*Qualified=*/false));
188 EXPECT_TRUE(shouldCollect("LocalConcrete", /*Qualified=*/false));
189 EXPECT_FALSE(shouldCollect("BaseMember", /*Qualified=*/false));
190 EXPECT_FALSE(shouldCollect("Local", /*Qualified=*/false));
193 TEST_F(ShouldCollectSymbolTest, NoPrivateProtoSymbol) {
194 HeaderName = "f.proto.h";
195 build(
196 R"(// Generated by the protocol buffer compiler. DO NOT EDIT!
197 namespace nx {
198 class Top_Level {};
199 class TopLevel {};
200 enum Kind {
201 KIND_OK,
202 Kind_Not_Ok,
204 })");
205 EXPECT_TRUE(shouldCollect("nx::TopLevel"));
206 EXPECT_TRUE(shouldCollect("nx::Kind::KIND_OK"));
207 EXPECT_TRUE(shouldCollect("nx::Kind"));
209 EXPECT_FALSE(shouldCollect("nx::Top_Level"));
210 EXPECT_FALSE(shouldCollect("nx::Kind::Kind_Not_Ok"));
213 TEST_F(ShouldCollectSymbolTest, DoubleCheckProtoHeaderComment) {
214 HeaderName = "f.proto.h";
215 build(R"(
216 namespace nx {
217 class Top_Level {};
218 enum Kind {
219 Kind_Fine
222 )");
223 EXPECT_TRUE(shouldCollect("nx::Top_Level"));
224 EXPECT_TRUE(shouldCollect("nx::Kind_Fine"));
227 class SymbolIndexActionFactory : public tooling::FrontendActionFactory {
228 public:
229 SymbolIndexActionFactory(SymbolCollector::Options COpts,
230 CommentHandler *PragmaHandler)
231 : COpts(std::move(COpts)), PragmaHandler(PragmaHandler) {}
233 std::unique_ptr<FrontendAction> create() override {
234 class IndexAction : public ASTFrontendAction {
235 public:
236 IndexAction(std::shared_ptr<index::IndexDataConsumer> DataConsumer,
237 const index::IndexingOptions &Opts,
238 CommentHandler *PragmaHandler)
239 : DataConsumer(std::move(DataConsumer)), Opts(Opts),
240 PragmaHandler(PragmaHandler) {}
242 std::unique_ptr<ASTConsumer>
243 CreateASTConsumer(CompilerInstance &CI, llvm::StringRef InFile) override {
244 if (PragmaHandler)
245 CI.getPreprocessor().addCommentHandler(PragmaHandler);
246 return createIndexingASTConsumer(DataConsumer, Opts,
247 CI.getPreprocessorPtr());
250 bool BeginInvocation(CompilerInstance &CI) override {
251 // Make the compiler parse all comments.
252 CI.getLangOpts().CommentOpts.ParseAllComments = true;
253 return true;
256 private:
257 std::shared_ptr<index::IndexDataConsumer> DataConsumer;
258 index::IndexingOptions Opts;
259 CommentHandler *PragmaHandler;
261 index::IndexingOptions IndexOpts;
262 IndexOpts.SystemSymbolFilter =
263 index::IndexingOptions::SystemSymbolFilterKind::All;
264 IndexOpts.IndexFunctionLocals = true;
265 Collector = std::make_shared<SymbolCollector>(COpts);
266 return std::make_unique<IndexAction>(Collector, std::move(IndexOpts),
267 PragmaHandler);
270 std::shared_ptr<SymbolCollector> Collector;
271 SymbolCollector::Options COpts;
272 CommentHandler *PragmaHandler;
275 class SymbolCollectorTest : public ::testing::Test {
276 public:
277 SymbolCollectorTest()
278 : InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem),
279 TestHeaderName(testPath("symbol.h")),
280 TestFileName(testPath("symbol.cc")) {
281 TestHeaderURI = URI::create(TestHeaderName).toString();
282 TestFileURI = URI::create(TestFileName).toString();
285 // Note that unlike TestTU, no automatic header guard is added.
286 // HeaderCode should start with #pragma once to be treated as modular.
287 bool runSymbolCollector(llvm::StringRef HeaderCode, llvm::StringRef MainCode,
288 const std::vector<std::string> &ExtraArgs = {}) {
289 llvm::IntrusiveRefCntPtr<FileManager> Files(
290 new FileManager(FileSystemOptions(), InMemoryFileSystem));
292 auto Factory = std::make_unique<SymbolIndexActionFactory>(
293 CollectorOpts, PragmaHandler.get());
295 std::vector<std::string> Args = {"symbol_collector", "-fsyntax-only",
296 "-xc++", "-include", TestHeaderName};
297 Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
298 // This allows to override the "-xc++" with something else, i.e.
299 // -xobjective-c++.
300 Args.push_back(TestFileName);
302 tooling::ToolInvocation Invocation(
303 Args, Factory->create(), Files.get(),
304 std::make_shared<PCHContainerOperations>());
306 InMemoryFileSystem->addFile(TestHeaderName, 0,
307 llvm::MemoryBuffer::getMemBuffer(HeaderCode));
308 InMemoryFileSystem->addFile(TestFileName, 0,
309 llvm::MemoryBuffer::getMemBuffer(MainCode));
310 Invocation.run();
311 Symbols = Factory->Collector->takeSymbols();
312 Refs = Factory->Collector->takeRefs();
313 Relations = Factory->Collector->takeRelations();
314 return true;
317 protected:
318 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem;
319 std::string TestHeaderName;
320 std::string TestHeaderURI;
321 std::string TestFileName;
322 std::string TestFileURI;
323 SymbolSlab Symbols;
324 RefSlab Refs;
325 RelationSlab Relations;
326 SymbolCollector::Options CollectorOpts;
327 std::unique_ptr<CommentHandler> PragmaHandler;
330 TEST_F(SymbolCollectorTest, CollectSymbols) {
331 const std::string Header = R"(
332 class Foo {
333 Foo() {}
334 Foo(int a) {}
335 void f();
336 friend void f1();
337 friend class Friend;
338 Foo& operator=(const Foo&);
339 ~Foo();
340 class Nested {
341 void f();
344 class Friend {
347 void f1();
348 inline void f2() {}
349 static const int KInt = 2;
350 const char* kStr = "123";
352 namespace {
353 void ff() {} // ignore
356 void f1() {
357 auto LocalLambda = [&](){
358 class ClassInLambda{};
362 namespace foo {
363 // Type alias
364 typedef int int32;
365 using int32_t = int32;
367 // Variable
368 int v1;
370 // Namespace
371 namespace bar {
372 int v2;
374 // Namespace alias
375 namespace baz = bar;
377 using bar::v2;
378 } // namespace foo
380 runSymbolCollector(Header, /*Main=*/"");
381 EXPECT_THAT(Symbols,
382 UnorderedElementsAreArray(
383 {AllOf(qName("Foo"), forCodeCompletion(true)),
384 AllOf(qName("Foo::Foo"), forCodeCompletion(false)),
385 AllOf(qName("Foo::Foo"), forCodeCompletion(false)),
386 AllOf(qName("Foo::f"), forCodeCompletion(false)),
387 AllOf(qName("Foo::~Foo"), forCodeCompletion(false)),
388 AllOf(qName("Foo::operator="), forCodeCompletion(false)),
389 AllOf(qName("Foo::Nested"), forCodeCompletion(false)),
390 AllOf(qName("Foo::Nested::f"), forCodeCompletion(false)),
391 AllOf(qName("ClassInLambda"), forCodeCompletion(false)),
392 AllOf(qName("Friend"), forCodeCompletion(true)),
393 AllOf(qName("f1"), forCodeCompletion(true)),
394 AllOf(qName("f2"), forCodeCompletion(true)),
395 AllOf(qName("KInt"), forCodeCompletion(true)),
396 AllOf(qName("kStr"), forCodeCompletion(true)),
397 AllOf(qName("foo"), forCodeCompletion(true)),
398 AllOf(qName("foo::bar"), forCodeCompletion(true)),
399 AllOf(qName("foo::int32"), forCodeCompletion(true)),
400 AllOf(qName("foo::int32_t"), forCodeCompletion(true)),
401 AllOf(qName("foo::v1"), forCodeCompletion(true)),
402 AllOf(qName("foo::bar::v2"), forCodeCompletion(true)),
403 AllOf(qName("foo::v2"), forCodeCompletion(true)),
404 AllOf(qName("foo::baz"), forCodeCompletion(true))}));
407 TEST_F(SymbolCollectorTest, FileLocal) {
408 const std::string Header = R"(
409 class Foo {};
410 namespace {
411 class Ignored {};
413 void bar();
415 const std::string Main = R"(
416 class ForwardDecl;
417 void bar() {}
418 static void a();
419 class B {};
420 namespace {
421 void c();
424 runSymbolCollector(Header, Main);
425 EXPECT_THAT(Symbols,
426 UnorderedElementsAre(
427 AllOf(qName("Foo"), visibleOutsideFile()),
428 AllOf(qName("bar"), visibleOutsideFile()),
429 AllOf(qName("a"), Not(visibleOutsideFile())),
430 AllOf(qName("B"), Not(visibleOutsideFile())),
431 AllOf(qName("c"), Not(visibleOutsideFile())),
432 // FIXME: ForwardDecl likely *is* visible outside.
433 AllOf(qName("ForwardDecl"), Not(visibleOutsideFile()))));
436 TEST_F(SymbolCollectorTest, Template) {
437 Annotations Header(R"(
438 // Primary template and explicit specialization are indexed, instantiation
439 // is not.
440 template <class T, class U> struct [[Tmpl]] {T $xdecl[[x]] = 0;};
441 template <> struct $specdecl[[Tmpl]]<int, bool> {};
442 template <class U> struct $partspecdecl[[Tmpl]]<bool, U> {};
443 extern template struct Tmpl<float, bool>;
444 template struct Tmpl<double, bool>;
445 )");
446 runSymbolCollector(Header.code(), /*Main=*/"");
447 EXPECT_THAT(Symbols,
448 UnorderedElementsAre(
449 AllOf(qName("Tmpl"), declRange(Header.range()),
450 forCodeCompletion(true)),
451 AllOf(qName("Tmpl"), declRange(Header.range("specdecl")),
452 forCodeCompletion(false)),
453 AllOf(qName("Tmpl"), declRange(Header.range("partspecdecl")),
454 forCodeCompletion(false)),
455 AllOf(qName("Tmpl::x"), declRange(Header.range("xdecl")),
456 forCodeCompletion(false))));
459 TEST_F(SymbolCollectorTest, templateArgs) {
460 Annotations Header(R"(
461 template <class X> class $barclasstemp[[Bar]] {};
462 template <class T, class U, template<typename> class Z, int Q>
463 struct [[Tmpl]] { T $xdecl[[x]] = 0; };
465 // template-template, non-type and type full spec
466 template <> struct $specdecl[[Tmpl]]<int, bool, Bar, 3> {};
468 // template-template, non-type and type partial spec
469 template <class U, int T> struct $partspecdecl[[Tmpl]]<bool, U, Bar, T> {};
470 // instantiation
471 extern template struct Tmpl<float, bool, Bar, 8>;
472 // instantiation
473 template struct Tmpl<double, bool, Bar, 2>;
475 template <typename ...> class $fooclasstemp[[Foo]] {};
476 // parameter-packs full spec
477 template<> class $parampack[[Foo]]<Bar<int>, int, double> {};
478 // parameter-packs partial spec
479 template<class T> class $parampackpartial[[Foo]]<T, T> {};
481 template <int ...> class $bazclasstemp[[Baz]] {};
482 // non-type parameter-packs full spec
483 template<> class $parampacknontype[[Baz]]<3, 5, 8> {};
484 // non-type parameter-packs partial spec
485 template<int T> class $parampacknontypepartial[[Baz]]<T, T> {};
487 template <template <class> class ...> class $fozclasstemp[[Foz]] {};
488 // template-template parameter-packs full spec
489 template<> class $parampacktempltempl[[Foz]]<Bar, Bar> {};
490 // template-template parameter-packs partial spec
491 template<template <class> class T>
492 class $parampacktempltemplpartial[[Foz]]<T, T> {};
493 )");
494 runSymbolCollector(Header.code(), /*Main=*/"");
495 EXPECT_THAT(
496 Symbols,
497 AllOf(
498 Contains(AllOf(qName("Tmpl"), templateArgs("<int, bool, Bar, 3>"),
499 declRange(Header.range("specdecl")),
500 forCodeCompletion(false))),
501 Contains(AllOf(qName("Tmpl"), templateArgs("<bool, U, Bar, T>"),
502 declRange(Header.range("partspecdecl")),
503 forCodeCompletion(false))),
504 Contains(AllOf(qName("Foo"), templateArgs("<Bar<int>, int, double>"),
505 declRange(Header.range("parampack")),
506 forCodeCompletion(false))),
507 Contains(AllOf(qName("Foo"), templateArgs("<T, T>"),
508 declRange(Header.range("parampackpartial")),
509 forCodeCompletion(false))),
510 Contains(AllOf(qName("Baz"), templateArgs("<3, 5, 8>"),
511 declRange(Header.range("parampacknontype")),
512 forCodeCompletion(false))),
513 Contains(AllOf(qName("Baz"), templateArgs("<T, T>"),
514 declRange(Header.range("parampacknontypepartial")),
515 forCodeCompletion(false))),
516 Contains(AllOf(qName("Foz"), templateArgs("<Bar, Bar>"),
517 declRange(Header.range("parampacktempltempl")),
518 forCodeCompletion(false))),
519 Contains(AllOf(qName("Foz"), templateArgs("<T, T>"),
520 declRange(Header.range("parampacktempltemplpartial")),
521 forCodeCompletion(false)))));
524 TEST_F(SymbolCollectorTest, ObjCSymbols) {
525 const std::string Header = R"(
526 @interface Person
527 - (void)someMethodName:(void*)name1 lastName:(void*)lName;
528 @end
530 @implementation Person
531 - (void)someMethodName:(void*)name1 lastName:(void*)lName{
532 int foo;
533 ^(int param){ int bar; };
535 @end
537 @interface Person (MyCategory)
538 - (void)someMethodName2:(void*)name2;
539 @end
541 @implementation Person (MyCategory)
542 - (void)someMethodName2:(void*)name2 {
543 int foo2;
545 @end
547 @protocol MyProtocol
548 - (void)someMethodName3:(void*)name3;
549 @end
551 TestFileName = testPath("test.m");
552 runSymbolCollector(Header, /*Main=*/"", {"-fblocks", "-xobjective-c++"});
553 EXPECT_THAT(Symbols,
554 UnorderedElementsAre(
555 qName("Person"), qName("Person::someMethodName:lastName:"),
556 AllOf(qName("MyCategory"), forCodeCompletion(false)),
557 qName("Person::someMethodName2:"), qName("MyProtocol"),
558 qName("MyProtocol::someMethodName3:")));
561 TEST_F(SymbolCollectorTest, ObjCPropertyImpl) {
562 const std::string Header = R"(
563 @interface Container
564 @property(nonatomic) int magic;
565 @end
567 @implementation Container
568 @end
570 TestFileName = testPath("test.m");
571 runSymbolCollector(Header, /*Main=*/"", {"-xobjective-c++"});
572 EXPECT_THAT(Symbols, Contains(qName("Container")));
573 EXPECT_THAT(Symbols, Contains(qName("Container::magic")));
574 // FIXME: Results also contain Container::_magic on some platforms.
575 // Figure out why it's platform-dependent.
578 TEST_F(SymbolCollectorTest, ObjCLocations) {
579 Annotations Header(R"(
580 // Declared in header, defined in main.
581 @interface $dogdecl[[Dog]]
582 @end
583 @interface $fluffydecl[[Dog]] (Fluffy)
584 @end
585 )");
586 Annotations Main(R"(
587 @interface Dog ()
588 @end
589 @implementation $dogdef[[Dog]]
590 @end
591 @implementation $fluffydef[[Dog]] (Fluffy)
592 @end
593 // Category with no declaration (only implementation).
594 @implementation $ruff[[Dog]] (Ruff)
595 @end
596 // Implicitly defined interface.
597 @implementation $catdog[[CatDog]]
598 @end
599 )");
600 runSymbolCollector(Header.code(), Main.code(),
601 {"-xobjective-c++", "-Wno-objc-root-class"});
602 EXPECT_THAT(Symbols,
603 UnorderedElementsAre(
604 AllOf(qName("Dog"), declRange(Header.range("dogdecl")),
605 defRange(Main.range("dogdef"))),
606 AllOf(qName("Fluffy"), declRange(Header.range("fluffydecl")),
607 defRange(Main.range("fluffydef"))),
608 AllOf(qName("CatDog"), declRange(Main.range("catdog")),
609 defRange(Main.range("catdog"))),
610 AllOf(qName("Ruff"), declRange(Main.range("ruff")),
611 defRange(Main.range("ruff")))));
614 TEST_F(SymbolCollectorTest, ObjCForwardDecls) {
615 Annotations Header(R"(
616 // Forward declared in header, declared and defined in main.
617 @protocol Barker;
618 @class Dog;
619 // Never fully declared so Clang latches onto this decl.
620 @class $catdogdecl[[CatDog]];
621 )");
622 Annotations Main(R"(
623 @protocol $barkerdecl[[Barker]]
624 - (void)woof;
625 @end
626 @interface $dogdecl[[Dog]]<Barker>
627 - (void)woof;
628 @end
629 @implementation $dogdef[[Dog]]
630 - (void)woof {}
631 @end
632 @implementation $catdogdef[[CatDog]]
633 @end
634 )");
635 runSymbolCollector(Header.code(), Main.code(),
636 {"-xobjective-c++", "-Wno-objc-root-class"});
637 EXPECT_THAT(Symbols,
638 UnorderedElementsAre(
639 AllOf(qName("CatDog"), declRange(Header.range("catdogdecl")),
640 defRange(Main.range("catdogdef"))),
641 AllOf(qName("Dog"), declRange(Main.range("dogdecl")),
642 defRange(Main.range("dogdef"))),
643 AllOf(qName("Barker"), declRange(Main.range("barkerdecl"))),
644 qName("Barker::woof"), qName("Dog::woof")));
647 TEST_F(SymbolCollectorTest, ObjCClassExtensions) {
648 Annotations Header(R"(
649 @interface $catdecl[[Cat]]
650 @end
651 )");
652 Annotations Main(R"(
653 @interface Cat ()
654 - (void)meow;
655 @end
656 @interface Cat ()
657 - (void)pur;
658 @end
659 )");
660 runSymbolCollector(Header.code(), Main.code(),
661 {"-xobjective-c++", "-Wno-objc-root-class"});
662 EXPECT_THAT(Symbols,
663 UnorderedElementsAre(
664 AllOf(qName("Cat"), declRange(Header.range("catdecl"))),
665 qName("Cat::meow"), qName("Cat::pur")));
668 TEST_F(SymbolCollectorTest, ObjCFrameworkIncludeHeader) {
669 CollectorOpts.CollectIncludePath = true;
670 auto FrameworksPath = testPath("Frameworks/");
671 std::string FrameworkHeader = R"(
672 __attribute((objc_root_class))
673 @interface NSObject
674 @end
676 InMemoryFileSystem->addFile(
677 testPath("Frameworks/Foundation.framework/Headers/NSObject.h"), 0,
678 llvm::MemoryBuffer::getMemBuffer(FrameworkHeader));
679 std::string PrivateFrameworkHeader = R"(
680 #import <Foundation/NSObject.h>
682 @interface PrivateClass : NSObject
683 @end
685 InMemoryFileSystem->addFile(
686 testPath(
687 "Frameworks/Foundation.framework/PrivateHeaders/NSObject+Private.h"),
688 0, llvm::MemoryBuffer::getMemBuffer(PrivateFrameworkHeader));
690 std::string Header = R"(
691 #import <Foundation/NSObject+Private.h>
692 #import <Foundation/NSObject.h>
694 @interface Container : NSObject
695 @end
697 std::string Main = "";
698 TestFileName = testPath("test.m");
699 runSymbolCollector(Header, Main, {"-F", FrameworksPath, "-xobjective-c++"});
700 EXPECT_THAT(
701 Symbols,
702 UnorderedElementsAre(
703 AllOf(qName("NSObject"), includeHeader("\"Foundation/NSObject.h\"")),
704 AllOf(qName("PrivateClass"),
705 includeHeader("\"Foundation/NSObject+Private.h\"")),
706 AllOf(qName("Container"))));
708 // After adding the umbrella headers, we should use that spelling instead.
709 std::string UmbrellaHeader = R"(
710 #import <Foundation/NSObject.h>
712 InMemoryFileSystem->addFile(
713 testPath("Frameworks/Foundation.framework/Headers/Foundation.h"), 0,
714 llvm::MemoryBuffer::getMemBuffer(UmbrellaHeader));
715 std::string PrivateUmbrellaHeader = R"(
716 #import <Foundation/NSObject+Private.h>
718 InMemoryFileSystem->addFile(
719 testPath("Frameworks/Foundation.framework/PrivateHeaders/"
720 "Foundation_Private.h"),
721 0, llvm::MemoryBuffer::getMemBuffer(PrivateUmbrellaHeader));
722 runSymbolCollector(Header, Main, {"-F", FrameworksPath, "-xobjective-c++"});
723 EXPECT_THAT(Symbols,
724 UnorderedElementsAre(
725 AllOf(qName("NSObject"),
726 includeHeader("\"Foundation/Foundation.h\"")),
727 AllOf(qName("PrivateClass"),
728 includeHeader("\"Foundation/Foundation_Private.h\"")),
729 AllOf(qName("Container"))));
731 runSymbolCollector(Header, Main,
732 {"-iframework", FrameworksPath, "-xobjective-c++"});
733 EXPECT_THAT(
734 Symbols,
735 UnorderedElementsAre(
736 AllOf(qName("NSObject"), includeHeader("<Foundation/Foundation.h>")),
737 AllOf(qName("PrivateClass"),
738 includeHeader("<Foundation/Foundation_Private.h>")),
739 AllOf(qName("Container"))));
742 TEST_F(SymbolCollectorTest, Locations) {
743 Annotations Header(R"cpp(
744 // Declared in header, defined in main.
745 extern int $xdecl[[X]];
746 class $clsdecl[[Cls]];
747 void $printdecl[[print]]();
749 // Declared in header, defined nowhere.
750 extern int $zdecl[[Z]];
752 void $foodecl[[fo\
753 o]]();
754 )cpp");
755 Annotations Main(R"cpp(
756 int $xdef[[X]] = 42;
757 class $clsdef[[Cls]] {};
758 void $printdef[[print]]() {}
760 // Declared/defined in main only.
761 int $ydecl[[Y]];
762 )cpp");
763 runSymbolCollector(Header.code(), Main.code());
764 EXPECT_THAT(Symbols,
765 UnorderedElementsAre(
766 AllOf(qName("X"), declRange(Header.range("xdecl")),
767 defRange(Main.range("xdef"))),
768 AllOf(qName("Cls"), declRange(Header.range("clsdecl")),
769 defRange(Main.range("clsdef"))),
770 AllOf(qName("print"), declRange(Header.range("printdecl")),
771 defRange(Main.range("printdef"))),
772 AllOf(qName("Z"), declRange(Header.range("zdecl"))),
773 AllOf(qName("foo"), declRange(Header.range("foodecl"))),
774 AllOf(qName("Y"), declRange(Main.range("ydecl")))));
777 TEST_F(SymbolCollectorTest, Refs) {
778 Annotations Header(R"(
779 #define MACRO(X) (X + 1)
780 class Foo {
781 public:
782 Foo() {}
783 Foo(int);
785 class Bar;
786 void func();
788 namespace NS {} // namespace ref is ignored
789 )");
790 Annotations Main(R"(
791 class $bar[[Bar]] {};
793 void $func[[func]]();
795 void fff() {
796 $foo[[Foo]] foo;
797 $bar[[Bar]] bar;
798 $func[[func]]();
799 int abc = 0;
800 $foo[[Foo]] foo2 = abc;
801 abc = $macro[[MACRO]](1);
803 )");
804 Annotations SymbolsOnlyInMainCode(R"(
805 #define FUNC(X) (X+1)
806 int a;
807 void b() {}
808 static const int c = FUNC(1);
809 class d {};
810 )");
811 CollectorOpts.RefFilter = RefKind::All;
812 CollectorOpts.CollectMacro = true;
813 runSymbolCollector(Header.code(),
814 (Main.code() + SymbolsOnlyInMainCode.code()).str());
815 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Foo").ID,
816 haveRanges(Main.ranges("foo")))));
817 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Bar").ID,
818 haveRanges(Main.ranges("bar")))));
819 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "func").ID,
820 haveRanges(Main.ranges("func")))));
821 EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(Symbols, "NS").ID, _))));
822 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "MACRO").ID,
823 haveRanges(Main.ranges("macro")))));
824 // - (a, b) externally visible and should have refs.
825 // - (c, FUNC) externally invisible and had no refs collected.
826 auto MainSymbols =
827 TestTU::withHeaderCode(SymbolsOnlyInMainCode.code()).headerSymbols();
828 EXPECT_THAT(Refs, Contains(Pair(findSymbol(MainSymbols, "a").ID, _)));
829 EXPECT_THAT(Refs, Contains(Pair(findSymbol(MainSymbols, "b").ID, _)));
830 EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(MainSymbols, "c").ID, _))));
831 EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(MainSymbols, "FUNC").ID, _))));
833 // Run the collector again with CollectMainFileRefs = true.
834 // We need to recreate InMemoryFileSystem because runSymbolCollector()
835 // calls MemoryBuffer::getMemBuffer(), which makes the buffers unusable
836 // after runSymbolCollector() exits.
837 InMemoryFileSystem = new llvm::vfs::InMemoryFileSystem();
838 CollectorOpts.CollectMainFileRefs = true;
839 runSymbolCollector(Header.code(),
840 (Main.code() + SymbolsOnlyInMainCode.code()).str());
841 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "a").ID, _)));
842 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "b").ID, _)));
843 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "c").ID, _)));
844 // However, references to main-file macros are not collected.
845 EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(Symbols, "FUNC").ID, _))));
848 TEST_F(SymbolCollectorTest, RefContainers) {
849 Annotations Code(R"cpp(
850 int $toplevel1[[f1]](int);
851 void f2() {
852 (void) $ref1a[[f1]](1);
853 auto fptr = &$ref1b[[f1]];
855 int $toplevel2[[v1]] = $ref2[[f1]](2);
856 void f3(int arg = $ref3[[f1]](3));
857 struct S1 {
858 int $classscope1[[member1]] = $ref4[[f1]](4);
859 int $classscope2[[member2]] = 42;
861 constexpr int f4(int x) { return x + 1; }
862 template <int I = $ref5[[f4]](0)> struct S2 {};
863 S2<$ref6[[f4]](0)> v2;
864 S2<$ref7a[[f4]](0)> f5(S2<$ref7b[[f4]](0)>);
865 namespace N {
866 void $namespacescope1[[f6]]();
867 int $namespacescope2[[v3]];
869 )cpp");
870 CollectorOpts.RefFilter = RefKind::All;
871 CollectorOpts.CollectMainFileRefs = true;
872 runSymbolCollector("", Code.code());
873 auto FindRefWithRange = [&](Range R) -> std::optional<Ref> {
874 for (auto &Entry : Refs) {
875 for (auto &Ref : Entry.second) {
876 if (rangesMatch(Ref.Location, R))
877 return Ref;
880 return std::nullopt;
882 auto Container = [&](llvm::StringRef RangeName) {
883 auto Ref = FindRefWithRange(Code.range(RangeName));
884 EXPECT_TRUE(bool(Ref));
885 return Ref->Container;
887 EXPECT_EQ(Container("ref1a"),
888 findSymbol(Symbols, "f2").ID); // function body (call)
889 EXPECT_EQ(Container("ref1b"),
890 findSymbol(Symbols, "f2").ID); // function body (address-of)
891 EXPECT_EQ(Container("ref2"),
892 findSymbol(Symbols, "v1").ID); // variable initializer
893 EXPECT_EQ(Container("ref3"),
894 findSymbol(Symbols, "f3").ID); // function parameter default value
895 EXPECT_EQ(Container("ref4"),
896 findSymbol(Symbols, "S1::member1").ID); // member initializer
897 EXPECT_EQ(Container("ref5"),
898 findSymbol(Symbols, "S2").ID); // template parameter default value
899 EXPECT_EQ(Container("ref6"),
900 findSymbol(Symbols, "v2").ID); // type of variable
901 EXPECT_EQ(Container("ref7a"),
902 findSymbol(Symbols, "f5").ID); // return type of function
903 EXPECT_EQ(Container("ref7b"),
904 findSymbol(Symbols, "f5").ID); // parameter type of function
906 EXPECT_FALSE(Container("classscope1").isNull());
907 EXPECT_FALSE(Container("namespacescope1").isNull());
909 EXPECT_EQ(Container("toplevel1"), Container("toplevel2"));
910 EXPECT_EQ(Container("classscope1"), Container("classscope2"));
911 EXPECT_EQ(Container("namespacescope1"), Container("namespacescope2"));
913 EXPECT_NE(Container("toplevel1"), Container("namespacescope1"));
914 EXPECT_NE(Container("toplevel1"), Container("classscope1"));
915 EXPECT_NE(Container("classscope1"), Container("namespacescope1"));
918 TEST_F(SymbolCollectorTest, MacroRefInHeader) {
919 Annotations Header(R"(
920 #define $foo[[FOO]](X) (X + 1)
921 #define $bar[[BAR]](X) (X + 2)
923 // Macro defined multiple times.
924 #define $ud1[[UD]] 1
925 int ud_1 = $ud1[[UD]];
926 #undef UD
928 #define $ud2[[UD]] 2
929 int ud_2 = $ud2[[UD]];
930 #undef UD
932 // Macros from token concatenations not included.
933 #define $concat[[CONCAT]](X) X##A()
934 #define $prepend[[PREPEND]](X) MACRO##X()
935 #define $macroa[[MACROA]]() 123
936 int B = $concat[[CONCAT]](MACRO);
937 int D = $prepend[[PREPEND]](A);
939 void fff() {
940 int abc = $foo[[FOO]](1) + $bar[[BAR]]($foo[[FOO]](1));
942 )");
943 CollectorOpts.RefFilter = RefKind::All;
944 CollectorOpts.RefsInHeaders = true;
945 // Need this to get the SymbolID for macros for tests.
946 CollectorOpts.CollectMacro = true;
948 runSymbolCollector(Header.code(), "");
950 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "FOO").ID,
951 haveRanges(Header.ranges("foo")))));
952 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "BAR").ID,
953 haveRanges(Header.ranges("bar")))));
954 // No unique ID for multiple symbols named UD. Check for ranges only.
955 EXPECT_THAT(Refs, Contains(Pair(_, haveRanges(Header.ranges("ud1")))));
956 EXPECT_THAT(Refs, Contains(Pair(_, haveRanges(Header.ranges("ud2")))));
957 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "CONCAT").ID,
958 haveRanges(Header.ranges("concat")))));
959 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "PREPEND").ID,
960 haveRanges(Header.ranges("prepend")))));
961 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "MACROA").ID,
962 haveRanges(Header.ranges("macroa")))));
965 TEST_F(SymbolCollectorTest, MacroRefWithoutCollectingSymbol) {
966 Annotations Header(R"(
967 #define $foo[[FOO]](X) (X + 1)
968 int abc = $foo[[FOO]](1);
969 )");
970 CollectorOpts.RefFilter = RefKind::All;
971 CollectorOpts.RefsInHeaders = true;
972 CollectorOpts.CollectMacro = false;
973 runSymbolCollector(Header.code(), "");
974 EXPECT_THAT(Refs, Contains(Pair(_, haveRanges(Header.ranges("foo")))));
977 TEST_F(SymbolCollectorTest, MacrosWithRefFilter) {
978 Annotations Header("#define $macro[[MACRO]](X) (X + 1)");
979 Annotations Main("void foo() { int x = $macro[[MACRO]](1); }");
980 CollectorOpts.RefFilter = RefKind::Unknown;
981 runSymbolCollector(Header.code(), Main.code());
982 EXPECT_THAT(Refs, IsEmpty());
985 TEST_F(SymbolCollectorTest, SpelledReferences) {
986 struct {
987 llvm::StringRef Header;
988 llvm::StringRef Main;
989 llvm::StringRef TargetSymbolName;
990 } TestCases[] = {
992 R"cpp(
993 struct Foo;
994 #define MACRO Foo
995 )cpp",
996 R"cpp(
997 struct $spelled[[Foo]] {
998 $spelled[[Foo]]();
999 ~$spelled[[Foo]]();
1001 $spelled[[Foo]] Variable1;
1002 $implicit[[MACRO]] Variable2;
1003 )cpp",
1004 "Foo",
1007 R"cpp(
1008 class Foo {
1009 public:
1010 Foo() = default;
1012 )cpp",
1013 R"cpp(
1014 void f() { Foo $implicit[[f]]; f = $spelled[[Foo]]();}
1015 )cpp",
1016 "Foo::Foo" /// constructor.
1018 { // Unclean identifiers
1019 R"cpp(
1020 struct Foo {};
1021 )cpp",
1022 R"cpp(
1023 $spelled[[Fo\
1024 o]] f{};
1025 )cpp",
1026 "Foo",
1029 CollectorOpts.RefFilter = RefKind::All;
1030 CollectorOpts.RefsInHeaders = false;
1031 for (const auto& T : TestCases) {
1032 SCOPED_TRACE(T.Header + "\n---\n" + T.Main);
1033 Annotations Header(T.Header);
1034 Annotations Main(T.Main);
1035 // Reset the file system.
1036 InMemoryFileSystem = new llvm::vfs::InMemoryFileSystem;
1037 runSymbolCollector(Header.code(), Main.code());
1039 const auto SpelledRanges = Main.ranges("spelled");
1040 const auto ImplicitRanges = Main.ranges("implicit");
1041 RefSlab::Builder SpelledSlabBuilder, ImplicitSlabBuilder;
1042 const auto TargetID = findSymbol(Symbols, T.TargetSymbolName).ID;
1043 for (const auto &SymbolAndRefs : Refs) {
1044 const auto ID = SymbolAndRefs.first;
1045 if (ID != TargetID)
1046 continue;
1047 for (const auto &Ref : SymbolAndRefs.second)
1048 if ((Ref.Kind & RefKind::Spelled) != RefKind::Unknown)
1049 SpelledSlabBuilder.insert(ID, Ref);
1050 else
1051 ImplicitSlabBuilder.insert(ID, Ref);
1053 const auto SpelledRefs = std::move(SpelledSlabBuilder).build(),
1054 ImplicitRefs = std::move(ImplicitSlabBuilder).build();
1055 EXPECT_EQ(SpelledRanges.empty(), SpelledRefs.empty());
1056 EXPECT_EQ(ImplicitRanges.empty(), ImplicitRefs.empty());
1057 if (!SpelledRanges.empty())
1058 EXPECT_THAT(SpelledRefs,
1059 Contains(Pair(TargetID, haveRanges(SpelledRanges))));
1060 if (!ImplicitRanges.empty())
1061 EXPECT_THAT(ImplicitRefs,
1062 Contains(Pair(TargetID, haveRanges(ImplicitRanges))));
1066 TEST_F(SymbolCollectorTest, NameReferences) {
1067 CollectorOpts.RefFilter = RefKind::All;
1068 CollectorOpts.RefsInHeaders = true;
1069 Annotations Header(R"(
1070 class [[Foo]] {
1071 public:
1072 [[Foo]]() {}
1073 ~[[Foo]]() {}
1075 )");
1076 CollectorOpts.RefFilter = RefKind::All;
1077 runSymbolCollector(Header.code(), "");
1078 // When we find references for class Foo, we expect to see all
1079 // constructor/destructor references.
1080 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Foo").ID,
1081 haveRanges(Header.ranges()))));
1084 TEST_F(SymbolCollectorTest, RefsOnMacros) {
1085 // Refs collected from SymbolCollector behave in the same way as
1086 // AST-based xrefs.
1087 CollectorOpts.RefFilter = RefKind::All;
1088 CollectorOpts.RefsInHeaders = true;
1089 Annotations Header(R"(
1090 #define TYPE(X) X
1091 #define FOO Foo
1092 #define CAT(X, Y) X##Y
1093 class [[Foo]] {};
1094 void test() {
1095 TYPE([[Foo]]) foo;
1096 [[FOO]] foo2;
1097 TYPE(TYPE([[Foo]])) foo3;
1098 [[CAT]](Fo, o) foo4;
1100 )");
1101 CollectorOpts.RefFilter = RefKind::All;
1102 runSymbolCollector(Header.code(), "");
1103 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Foo").ID,
1104 haveRanges(Header.ranges()))));
1107 TEST_F(SymbolCollectorTest, HeaderAsMainFile) {
1108 CollectorOpts.RefFilter = RefKind::All;
1109 Annotations Header(R"(
1110 class $Foo[[Foo]] {};
1112 void $Func[[Func]]() {
1113 $Foo[[Foo]] fo;
1115 )");
1116 // We should collect refs to main-file symbols in all cases:
1118 // 1. The main file is normal .cpp file.
1119 TestFileName = testPath("foo.cpp");
1120 runSymbolCollector("", Header.code());
1121 EXPECT_THAT(Refs,
1122 UnorderedElementsAre(Pair(findSymbol(Symbols, "Foo").ID,
1123 haveRanges(Header.ranges("Foo"))),
1124 Pair(findSymbol(Symbols, "Func").ID,
1125 haveRanges(Header.ranges("Func")))));
1127 // 2. Run the .h file as main file.
1128 TestFileName = testPath("foo.h");
1129 runSymbolCollector("", Header.code(),
1130 /*ExtraArgs=*/{"-xobjective-c++-header"});
1131 EXPECT_THAT(Symbols, UnorderedElementsAre(qName("Foo"), qName("Func")));
1132 EXPECT_THAT(Refs,
1133 UnorderedElementsAre(Pair(findSymbol(Symbols, "Foo").ID,
1134 haveRanges(Header.ranges("Foo"))),
1135 Pair(findSymbol(Symbols, "Func").ID,
1136 haveRanges(Header.ranges("Func")))));
1138 // 3. Run the .hh file as main file (without "-x c++-header").
1139 TestFileName = testPath("foo.hh");
1140 runSymbolCollector("", Header.code());
1141 EXPECT_THAT(Symbols, UnorderedElementsAre(qName("Foo"), qName("Func")));
1142 EXPECT_THAT(Refs,
1143 UnorderedElementsAre(Pair(findSymbol(Symbols, "Foo").ID,
1144 haveRanges(Header.ranges("Foo"))),
1145 Pair(findSymbol(Symbols, "Func").ID,
1146 haveRanges(Header.ranges("Func")))));
1149 TEST_F(SymbolCollectorTest, RefsInHeaders) {
1150 CollectorOpts.RefFilter = RefKind::All;
1151 CollectorOpts.RefsInHeaders = true;
1152 CollectorOpts.CollectMacro = true;
1153 Annotations Header(R"(
1154 #define $macro[[MACRO]](x) (x+1)
1155 class $foo[[Foo]] {};
1156 )");
1157 runSymbolCollector(Header.code(), "");
1158 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Foo").ID,
1159 haveRanges(Header.ranges("foo")))));
1160 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "MACRO").ID,
1161 haveRanges(Header.ranges("macro")))));
1164 TEST_F(SymbolCollectorTest, BaseOfRelations) {
1165 std::string Header = R"(
1166 class Base {};
1167 class Derived : public Base {};
1169 runSymbolCollector(Header, /*Main=*/"");
1170 const Symbol &Base = findSymbol(Symbols, "Base");
1171 const Symbol &Derived = findSymbol(Symbols, "Derived");
1172 EXPECT_THAT(Relations,
1173 Contains(Relation{Base.ID, RelationKind::BaseOf, Derived.ID}));
1176 TEST_F(SymbolCollectorTest, OverrideRelationsSimpleInheritance) {
1177 std::string Header = R"cpp(
1178 class A {
1179 virtual void foo();
1181 class B : public A {
1182 void foo() override; // A::foo
1183 virtual void bar();
1185 class C : public B {
1186 void bar() override; // B::bar
1188 class D: public C {
1189 void foo() override; // B::foo
1190 void bar() override; // C::bar
1192 )cpp";
1193 runSymbolCollector(Header, /*Main=*/"");
1194 const Symbol &AFoo = findSymbol(Symbols, "A::foo");
1195 const Symbol &BFoo = findSymbol(Symbols, "B::foo");
1196 const Symbol &DFoo = findSymbol(Symbols, "D::foo");
1198 const Symbol &BBar = findSymbol(Symbols, "B::bar");
1199 const Symbol &CBar = findSymbol(Symbols, "C::bar");
1200 const Symbol &DBar = findSymbol(Symbols, "D::bar");
1202 std::vector<Relation> Result;
1203 for (const Relation &R : Relations)
1204 if (R.Predicate == RelationKind::OverriddenBy)
1205 Result.push_back(R);
1206 EXPECT_THAT(Result, UnorderedElementsAre(
1207 OverriddenBy(AFoo, BFoo), OverriddenBy(BBar, CBar),
1208 OverriddenBy(BFoo, DFoo), OverriddenBy(CBar, DBar)));
1211 TEST_F(SymbolCollectorTest, OverrideRelationsMultipleInheritance) {
1212 std::string Header = R"cpp(
1213 class A {
1214 virtual void foo();
1216 class B {
1217 virtual void bar();
1219 class C : public B {
1220 void bar() override; // B::bar
1221 virtual void baz();
1223 class D : public A, C {
1224 void foo() override; // A::foo
1225 void bar() override; // C::bar
1226 void baz() override; // C::baz
1228 )cpp";
1229 runSymbolCollector(Header, /*Main=*/"");
1230 const Symbol &AFoo = findSymbol(Symbols, "A::foo");
1231 const Symbol &BBar = findSymbol(Symbols, "B::bar");
1232 const Symbol &CBar = findSymbol(Symbols, "C::bar");
1233 const Symbol &CBaz = findSymbol(Symbols, "C::baz");
1234 const Symbol &DFoo = findSymbol(Symbols, "D::foo");
1235 const Symbol &DBar = findSymbol(Symbols, "D::bar");
1236 const Symbol &DBaz = findSymbol(Symbols, "D::baz");
1238 std::vector<Relation> Result;
1239 for (const Relation &R : Relations)
1240 if (R.Predicate == RelationKind::OverriddenBy)
1241 Result.push_back(R);
1242 EXPECT_THAT(Result, UnorderedElementsAre(
1243 OverriddenBy(BBar, CBar), OverriddenBy(AFoo, DFoo),
1244 OverriddenBy(CBar, DBar), OverriddenBy(CBaz, DBaz)));
1247 TEST_F(SymbolCollectorTest, CountReferences) {
1248 const std::string Header = R"(
1249 class W;
1250 class X {};
1251 class Y;
1252 class Z {}; // not used anywhere
1253 Y* y = nullptr; // used in header doesn't count
1254 #define GLOBAL_Z(name) Z name;
1256 const std::string Main = R"(
1257 W* w = nullptr;
1258 W* w2 = nullptr; // only one usage counts
1259 X x();
1260 class V;
1261 class Y{}; // definition doesn't count as a reference
1262 V* v = nullptr;
1263 GLOBAL_Z(z); // Not a reference to Z, we don't spell the type.
1265 CollectorOpts.CountReferences = true;
1266 runSymbolCollector(Header, Main);
1267 EXPECT_THAT(
1268 Symbols,
1269 UnorderedElementsAreArray(
1270 {AllOf(qName("W"), refCount(1)), AllOf(qName("X"), refCount(1)),
1271 AllOf(qName("Y"), refCount(0)), AllOf(qName("Z"), refCount(0)),
1272 AllOf(qName("y"), refCount(0)), AllOf(qName("z"), refCount(0)),
1273 AllOf(qName("x"), refCount(0)), AllOf(qName("w"), refCount(0)),
1274 AllOf(qName("w2"), refCount(0)), AllOf(qName("V"), refCount(1)),
1275 AllOf(qName("v"), refCount(0))}));
1278 TEST_F(SymbolCollectorTest, SymbolRelativeNoFallback) {
1279 runSymbolCollector("class Foo {};", /*Main=*/"");
1280 EXPECT_THAT(Symbols, UnorderedElementsAre(
1281 AllOf(qName("Foo"), declURI(TestHeaderURI))));
1284 TEST_F(SymbolCollectorTest, SymbolRelativeWithFallback) {
1285 TestHeaderName = "x.h";
1286 TestFileName = "x.cpp";
1287 TestHeaderURI = URI::create(testPath(TestHeaderName)).toString();
1288 CollectorOpts.FallbackDir = testRoot();
1289 runSymbolCollector("class Foo {};", /*Main=*/"");
1290 EXPECT_THAT(Symbols, UnorderedElementsAre(
1291 AllOf(qName("Foo"), declURI(TestHeaderURI))));
1294 TEST_F(SymbolCollectorTest, UnittestURIScheme) {
1295 // Use test URI scheme from URITests.cpp
1296 TestHeaderName = testPath("x.h");
1297 TestFileName = testPath("x.cpp");
1298 runSymbolCollector("class Foo {};", /*Main=*/"");
1299 EXPECT_THAT(Symbols, UnorderedElementsAre(
1300 AllOf(qName("Foo"), declURI("unittest:///x.h"))));
1303 TEST_F(SymbolCollectorTest, IncludeEnums) {
1304 const std::string Header = R"(
1305 enum {
1308 enum Color {
1309 Green
1311 enum class Color2 {
1312 Yellow
1314 namespace ns {
1315 enum {
1316 Black
1319 class Color3 {
1320 enum {
1321 Blue
1325 runSymbolCollector(Header, /*Main=*/"");
1326 EXPECT_THAT(Symbols,
1327 UnorderedElementsAre(
1328 AllOf(qName("Red"), forCodeCompletion(true)),
1329 AllOf(qName("Color"), forCodeCompletion(true)),
1330 AllOf(qName("Green"), forCodeCompletion(true)),
1331 AllOf(qName("Color2"), forCodeCompletion(true)),
1332 AllOf(qName("Color2::Yellow"), forCodeCompletion(true)),
1333 AllOf(qName("ns"), forCodeCompletion(true)),
1334 AllOf(qName("ns::Black"), forCodeCompletion(true)),
1335 AllOf(qName("Color3"), forCodeCompletion(true)),
1336 AllOf(qName("Color3::Blue"), forCodeCompletion(true))));
1339 TEST_F(SymbolCollectorTest, NamelessSymbols) {
1340 const std::string Header = R"(
1341 struct {
1342 int a;
1343 } Foo;
1345 runSymbolCollector(Header, /*Main=*/"");
1346 EXPECT_THAT(Symbols, UnorderedElementsAre(qName("Foo"),
1347 qName("(anonymous struct)::a")));
1350 TEST_F(SymbolCollectorTest, SymbolFormedFromRegisteredSchemeFromMacro) {
1352 Annotations Header(R"(
1353 #define FF(name) \
1354 class name##_Test {};
1356 $expansion[[FF]](abc);
1358 #define FF2() \
1359 class $spelling[[Test]] {};
1361 FF2();
1362 )");
1364 runSymbolCollector(Header.code(), /*Main=*/"");
1365 EXPECT_THAT(Symbols,
1366 UnorderedElementsAre(
1367 AllOf(qName("abc_Test"), declRange(Header.range("expansion")),
1368 declURI(TestHeaderURI)),
1369 AllOf(qName("Test"), declRange(Header.range("spelling")),
1370 declURI(TestHeaderURI))));
1373 TEST_F(SymbolCollectorTest, SymbolFormedByCLI) {
1374 Annotations Header(R"(
1375 #ifdef NAME
1376 class $expansion[[NAME]] {};
1377 #endif
1378 )");
1379 runSymbolCollector(Header.code(), /*Main=*/"", /*ExtraArgs=*/{"-DNAME=name"});
1380 EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(
1381 qName("name"), declRange(Header.range("expansion")),
1382 declURI(TestHeaderURI))));
1385 TEST_F(SymbolCollectorTest, SymbolsInMainFile) {
1386 const std::string Main = R"(
1387 class Foo {};
1388 void f1();
1389 inline void f2() {}
1391 namespace {
1392 void ff() {}
1394 namespace foo {
1395 namespace {
1396 class Bar {};
1399 void main_f() {}
1400 void f1() {}
1402 runSymbolCollector(/*Header=*/"", Main);
1403 EXPECT_THAT(Symbols, UnorderedElementsAre(
1404 qName("Foo"), qName("f1"), qName("f2"), qName("ff"),
1405 qName("foo"), qName("foo::Bar"), qName("main_f")));
1408 TEST_F(SymbolCollectorTest, Documentation) {
1409 const std::string Header = R"(
1410 // doc Foo
1411 class Foo {
1412 // doc f
1413 int f();
1416 CollectorOpts.StoreAllDocumentation = false;
1417 runSymbolCollector(Header, /* Main */ "");
1418 EXPECT_THAT(Symbols,
1419 UnorderedElementsAre(
1420 AllOf(qName("Foo"), doc("doc Foo"), forCodeCompletion(true)),
1421 AllOf(qName("Foo::f"), doc(""), returnType(""),
1422 forCodeCompletion(false))));
1424 CollectorOpts.StoreAllDocumentation = true;
1425 runSymbolCollector(Header, /* Main */ "");
1426 EXPECT_THAT(Symbols,
1427 UnorderedElementsAre(
1428 AllOf(qName("Foo"), doc("doc Foo"), forCodeCompletion(true)),
1429 AllOf(qName("Foo::f"), doc("doc f"), returnType(""),
1430 forCodeCompletion(false))));
1433 TEST_F(SymbolCollectorTest, ClassMembers) {
1434 const std::string Header = R"(
1435 class Foo {
1436 void f() {}
1437 void g();
1438 static void sf() {}
1439 static void ssf();
1440 static int x;
1443 const std::string Main = R"(
1444 void Foo::g() {}
1445 void Foo::ssf() {}
1447 runSymbolCollector(Header, Main);
1448 EXPECT_THAT(
1449 Symbols,
1450 UnorderedElementsAre(
1451 qName("Foo"),
1452 AllOf(qName("Foo::f"), returnType(""), forCodeCompletion(false)),
1453 AllOf(qName("Foo::g"), returnType(""), forCodeCompletion(false)),
1454 AllOf(qName("Foo::sf"), returnType(""), forCodeCompletion(false)),
1455 AllOf(qName("Foo::ssf"), returnType(""), forCodeCompletion(false)),
1456 AllOf(qName("Foo::x"), returnType(""), forCodeCompletion(false))));
1459 TEST_F(SymbolCollectorTest, Scopes) {
1460 const std::string Header = R"(
1461 namespace na {
1462 class Foo {};
1463 namespace nb {
1464 class Bar {};
1468 runSymbolCollector(Header, /*Main=*/"");
1469 EXPECT_THAT(Symbols,
1470 UnorderedElementsAre(qName("na"), qName("na::nb"),
1471 qName("na::Foo"), qName("na::nb::Bar")));
1474 TEST_F(SymbolCollectorTest, ExternC) {
1475 const std::string Header = R"(
1476 extern "C" { class Foo {}; }
1477 namespace na {
1478 extern "C" { class Bar {}; }
1481 runSymbolCollector(Header, /*Main=*/"");
1482 EXPECT_THAT(Symbols, UnorderedElementsAre(qName("na"), qName("Foo"),
1483 qName("na::Bar")));
1486 TEST_F(SymbolCollectorTest, SkipInlineNamespace) {
1487 const std::string Header = R"(
1488 namespace na {
1489 inline namespace nb {
1490 class Foo {};
1493 namespace na {
1494 // This is still inlined.
1495 namespace nb {
1496 class Bar {};
1500 runSymbolCollector(Header, /*Main=*/"");
1501 EXPECT_THAT(Symbols,
1502 UnorderedElementsAre(qName("na"), qName("na::nb"),
1503 qName("na::Foo"), qName("na::Bar")));
1506 TEST_F(SymbolCollectorTest, SymbolWithDocumentation) {
1507 const std::string Header = R"(
1508 namespace nx {
1509 /// Foo comment.
1510 int ff(int x, double y) { return 0; }
1513 runSymbolCollector(Header, /*Main=*/"");
1514 EXPECT_THAT(
1515 Symbols,
1516 UnorderedElementsAre(
1517 qName("nx"), AllOf(qName("nx::ff"), labeled("ff(int x, double y)"),
1518 returnType("int"), doc("Foo comment."))));
1521 TEST_F(SymbolCollectorTest, snippet) {
1522 const std::string Header = R"(
1523 namespace nx {
1524 void f() {}
1525 int ff(int x, double y) { return 0; }
1528 runSymbolCollector(Header, /*Main=*/"");
1529 EXPECT_THAT(Symbols,
1530 UnorderedElementsAre(
1531 qName("nx"),
1532 AllOf(qName("nx::f"), labeled("f()"), snippet("f()")),
1533 AllOf(qName("nx::ff"), labeled("ff(int x, double y)"),
1534 snippet("ff(${1:int x}, ${2:double y})"))));
1537 TEST_F(SymbolCollectorTest, IncludeHeaderSameAsFileURI) {
1538 CollectorOpts.CollectIncludePath = true;
1539 runSymbolCollector("#pragma once\nclass Foo {};", /*Main=*/"");
1540 EXPECT_THAT(Symbols, UnorderedElementsAre(
1541 AllOf(qName("Foo"), declURI(TestHeaderURI))));
1542 EXPECT_THAT(Symbols.begin()->IncludeHeaders,
1543 UnorderedElementsAre(IncludeHeaderWithRef(TestHeaderURI, 1u)));
1546 TEST_F(SymbolCollectorTest, CanonicalSTLHeader) {
1547 CollectorOpts.CollectIncludePath = true;
1548 CanonicalIncludes Includes;
1549 auto Language = LangOptions();
1550 Language.CPlusPlus = true;
1551 Includes.addSystemHeadersMapping(Language);
1552 CollectorOpts.Includes = &Includes;
1553 runSymbolCollector(
1554 R"cpp(
1555 namespace std {
1556 class string {};
1557 // Move overloads have special handling.
1558 template <typename _T> T&& move(_T&& __value);
1559 template <typename _I, typename _O> _O move(_I, _I, _O);
1561 )cpp",
1562 /*Main=*/"");
1563 EXPECT_THAT(
1564 Symbols,
1565 UnorderedElementsAre(
1566 qName("std"),
1567 AllOf(qName("std::string"), declURI(TestHeaderURI),
1568 includeHeader("<string>")),
1569 // Parameter names are demangled.
1570 AllOf(labeled("move(T &&value)"), includeHeader("<utility>")),
1571 AllOf(labeled("move(I, I, O)"), includeHeader("<algorithm>"))));
1574 TEST_F(SymbolCollectorTest, IWYUPragma) {
1575 CollectorOpts.CollectIncludePath = true;
1576 CanonicalIncludes Includes;
1577 PragmaHandler = collectIWYUHeaderMaps(&Includes);
1578 CollectorOpts.Includes = &Includes;
1579 const std::string Header = R"(
1580 // IWYU pragma: private, include the/good/header.h
1581 class Foo {};
1583 runSymbolCollector(Header, /*Main=*/"");
1584 EXPECT_THAT(Symbols, UnorderedElementsAre(
1585 AllOf(qName("Foo"), declURI(TestHeaderURI),
1586 includeHeader("\"the/good/header.h\""))));
1589 TEST_F(SymbolCollectorTest, IWYUPragmaWithDoubleQuotes) {
1590 CollectorOpts.CollectIncludePath = true;
1591 CanonicalIncludes Includes;
1592 PragmaHandler = collectIWYUHeaderMaps(&Includes);
1593 CollectorOpts.Includes = &Includes;
1594 const std::string Header = R"(
1595 // IWYU pragma: private, include "the/good/header.h"
1596 class Foo {};
1598 runSymbolCollector(Header, /*Main=*/"");
1599 EXPECT_THAT(Symbols, UnorderedElementsAre(
1600 AllOf(qName("Foo"), declURI(TestHeaderURI),
1601 includeHeader("\"the/good/header.h\""))));
1604 TEST_F(SymbolCollectorTest, SkipIncFileWhenCanonicalizeHeaders) {
1605 auto IncFile = testPath("test.inc");
1606 auto IncURI = URI::create(IncFile).toString();
1607 InMemoryFileSystem->addFile(IncFile, 0,
1608 llvm::MemoryBuffer::getMemBuffer("class X {};"));
1609 llvm::IntrusiveRefCntPtr<FileManager> Files(
1610 new FileManager(FileSystemOptions(), InMemoryFileSystem));
1611 std::string HeaderCode = "#include \"test.inc\"\nclass Y {};";
1612 InMemoryFileSystem->addFile(TestHeaderName, 0,
1613 llvm::MemoryBuffer::getMemBuffer(HeaderCode));
1614 auto File = Files->getFileRef(TestHeaderName);
1615 ASSERT_THAT_EXPECTED(File, llvm::Succeeded());
1616 CanonicalIncludes Includes;
1617 Includes.addMapping(*File, "<canonical>");
1618 CollectorOpts.CollectIncludePath = true;
1619 CollectorOpts.Includes = &Includes;
1620 runSymbolCollector(HeaderCode, /*Main=*/"",
1621 /*ExtraArgs=*/{"-I", testRoot()});
1622 EXPECT_THAT(Symbols,
1623 UnorderedElementsAre(AllOf(qName("X"), declURI(IncURI),
1624 includeHeader("<canonical>")),
1625 AllOf(qName("Y"), declURI(TestHeaderURI),
1626 includeHeader("<canonical>"))));
1629 TEST_F(SymbolCollectorTest, MainFileIsHeaderWhenSkipIncFile) {
1630 CollectorOpts.CollectIncludePath = true;
1631 // To make this case as hard as possible, we won't tell clang main is a
1632 // header. No extension, no -x c++-header.
1633 TestFileName = testPath("no_ext_main");
1634 TestFileURI = URI::create(TestFileName).toString();
1635 auto IncFile = testPath("test.inc");
1636 auto IncURI = URI::create(IncFile).toString();
1637 InMemoryFileSystem->addFile(IncFile, 0,
1638 llvm::MemoryBuffer::getMemBuffer("class X {};"));
1639 runSymbolCollector("", R"cpp(
1640 // Can't use #pragma once in a main file clang doesn't think is a header.
1641 #ifndef MAIN_H_
1642 #define MAIN_H_
1643 #include "test.inc"
1644 #endif
1645 )cpp",
1646 /*ExtraArgs=*/{"-I", testRoot()});
1647 EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(qName("X"), declURI(IncURI),
1648 includeHeader(TestFileURI))));
1651 TEST_F(SymbolCollectorTest, IncFileInNonHeader) {
1652 CollectorOpts.CollectIncludePath = true;
1653 TestFileName = testPath("main.cc");
1654 TestFileURI = URI::create(TestFileName).toString();
1655 auto IncFile = testPath("test.inc");
1656 auto IncURI = URI::create(IncFile).toString();
1657 InMemoryFileSystem->addFile(IncFile, 0,
1658 llvm::MemoryBuffer::getMemBuffer("class X {};"));
1659 runSymbolCollector("", R"cpp(
1660 #include "test.inc"
1661 )cpp",
1662 /*ExtraArgs=*/{"-I", testRoot()});
1663 EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(qName("X"), declURI(IncURI),
1664 Not(includeHeader()))));
1667 // Features that depend on header-guards are fragile. Header guards are only
1668 // recognized when the file ends, so we have to defer checking for them.
1669 TEST_F(SymbolCollectorTest, HeaderGuardDetected) {
1670 CollectorOpts.CollectIncludePath = true;
1671 CollectorOpts.CollectMacro = true;
1672 runSymbolCollector(R"cpp(
1673 #ifndef HEADER_GUARD_
1674 #define HEADER_GUARD_
1676 // Symbols are seen before the header guard is complete.
1677 #define MACRO
1678 int decl();
1680 #endif // Header guard is recognized here.
1681 )cpp",
1682 "");
1683 EXPECT_THAT(Symbols, Not(Contains(qName("HEADER_GUARD_"))));
1684 EXPECT_THAT(Symbols, Each(includeHeader()));
1687 TEST_F(SymbolCollectorTest, NonModularHeader) {
1688 auto TU = TestTU::withHeaderCode("int x();");
1689 EXPECT_THAT(TU.headerSymbols(), ElementsAre(includeHeader()));
1691 // Files missing include guards aren't eligible for insertion.
1692 TU.ImplicitHeaderGuard = false;
1693 EXPECT_THAT(TU.headerSymbols(), ElementsAre(Not(includeHeader())));
1695 // We recognize some patterns of trying to prevent insertion.
1696 TU = TestTU::withHeaderCode(R"cpp(
1697 #ifndef SECRET
1698 #error "This file isn't safe to include directly"
1699 #endif
1700 int x();
1701 )cpp");
1702 TU.ExtraArgs.push_back("-DSECRET"); // *we're* able to include it.
1703 EXPECT_THAT(TU.headerSymbols(), ElementsAre(Not(includeHeader())));
1706 TEST_F(SymbolCollectorTest, AvoidUsingFwdDeclsAsCanonicalDecls) {
1707 CollectorOpts.CollectIncludePath = true;
1708 Annotations Header(R"(
1709 #pragma once
1710 // Forward declarations of TagDecls.
1711 class C;
1712 struct S;
1713 union U;
1715 // Canonical declarations.
1716 class $cdecl[[C]] {};
1717 struct $sdecl[[S]] {};
1718 union $udecl[[U]] {int $xdecl[[x]]; bool $ydecl[[y]];};
1719 )");
1720 runSymbolCollector(Header.code(), /*Main=*/"");
1721 EXPECT_THAT(
1722 Symbols,
1723 UnorderedElementsAre(
1724 AllOf(qName("C"), declURI(TestHeaderURI),
1725 declRange(Header.range("cdecl")), includeHeader(TestHeaderURI),
1726 defURI(TestHeaderURI), defRange(Header.range("cdecl"))),
1727 AllOf(qName("S"), declURI(TestHeaderURI),
1728 declRange(Header.range("sdecl")), includeHeader(TestHeaderURI),
1729 defURI(TestHeaderURI), defRange(Header.range("sdecl"))),
1730 AllOf(qName("U"), declURI(TestHeaderURI),
1731 declRange(Header.range("udecl")), includeHeader(TestHeaderURI),
1732 defURI(TestHeaderURI), defRange(Header.range("udecl"))),
1733 AllOf(qName("U::x"), declURI(TestHeaderURI),
1734 declRange(Header.range("xdecl")), defURI(TestHeaderURI),
1735 defRange(Header.range("xdecl"))),
1736 AllOf(qName("U::y"), declURI(TestHeaderURI),
1737 declRange(Header.range("ydecl")), defURI(TestHeaderURI),
1738 defRange(Header.range("ydecl")))));
1741 TEST_F(SymbolCollectorTest, ClassForwardDeclarationIsCanonical) {
1742 CollectorOpts.CollectIncludePath = true;
1743 runSymbolCollector(/*Header=*/"#pragma once\nclass X;",
1744 /*Main=*/"class X {};");
1745 EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(
1746 qName("X"), declURI(TestHeaderURI),
1747 includeHeader(TestHeaderURI), defURI(TestFileURI))));
1750 TEST_F(SymbolCollectorTest, UTF16Character) {
1751 // ö is 2-bytes.
1752 Annotations Header(/*Header=*/"class [[pörk]] {};");
1753 runSymbolCollector(Header.code(), /*Main=*/"");
1754 EXPECT_THAT(Symbols, UnorderedElementsAre(
1755 AllOf(qName("pörk"), declRange(Header.range()))));
1758 TEST_F(SymbolCollectorTest, DoNotIndexSymbolsInFriendDecl) {
1759 Annotations Header(R"(
1760 namespace nx {
1761 class $z[[Z]] {};
1762 class X {
1763 friend class Y;
1764 friend class Z;
1765 friend void foo();
1766 friend void $bar[[bar]]() {}
1768 class $y[[Y]] {};
1769 void $foo[[foo]]();
1771 )");
1772 runSymbolCollector(Header.code(), /*Main=*/"");
1774 EXPECT_THAT(Symbols,
1775 UnorderedElementsAre(
1776 qName("nx"), qName("nx::X"),
1777 AllOf(qName("nx::Y"), declRange(Header.range("y"))),
1778 AllOf(qName("nx::Z"), declRange(Header.range("z"))),
1779 AllOf(qName("nx::foo"), declRange(Header.range("foo"))),
1780 AllOf(qName("nx::bar"), declRange(Header.range("bar")))));
1783 TEST_F(SymbolCollectorTest, ReferencesInFriendDecl) {
1784 const std::string Header = R"(
1785 class X;
1786 class Y;
1788 const std::string Main = R"(
1789 class C {
1790 friend ::X;
1791 friend class Y;
1794 CollectorOpts.CountReferences = true;
1795 runSymbolCollector(Header, Main);
1796 EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(qName("X"), refCount(1)),
1797 AllOf(qName("Y"), refCount(1)),
1798 AllOf(qName("C"), refCount(0))));
1801 TEST_F(SymbolCollectorTest, Origin) {
1802 CollectorOpts.Origin = SymbolOrigin::Static;
1803 runSymbolCollector("class Foo {};", /*Main=*/"");
1804 EXPECT_THAT(Symbols, UnorderedElementsAre(
1805 Field(&Symbol::Origin, SymbolOrigin::Static)));
1806 runSymbolCollector("#define FOO", /*Main=*/"");
1807 EXPECT_THAT(Symbols, UnorderedElementsAre(
1808 Field(&Symbol::Origin, SymbolOrigin::Static)));
1811 TEST_F(SymbolCollectorTest, CollectMacros) {
1812 CollectorOpts.CollectIncludePath = true;
1813 Annotations Header(R"(
1814 #pragma once
1815 #define X 1
1816 #define $mac[[MAC]](x) int x
1817 #define $used[[USED]](y) float y;
1819 MAC(p);
1820 )");
1822 Annotations Main(R"(
1823 #define $main[[MAIN]] 1
1824 USED(t);
1825 )");
1826 CollectorOpts.CountReferences = true;
1827 CollectorOpts.CollectMacro = true;
1828 runSymbolCollector(Header.code(), Main.code());
1829 EXPECT_THAT(
1830 Symbols,
1831 UnorderedElementsAre(
1832 qName("p"), qName("t"),
1833 AllOf(qName("X"), declURI(TestHeaderURI),
1834 includeHeader(TestHeaderURI)),
1835 AllOf(labeled("MAC(x)"), refCount(0),
1837 declRange(Header.range("mac")), visibleOutsideFile()),
1838 AllOf(labeled("USED(y)"), refCount(1),
1839 declRange(Header.range("used")), visibleOutsideFile()),
1840 AllOf(labeled("MAIN"), refCount(0), declRange(Main.range("main")),
1841 Not(visibleOutsideFile()))));
1844 TEST_F(SymbolCollectorTest, DeprecatedSymbols) {
1845 const std::string Header = R"(
1846 void TestClangc() __attribute__((deprecated("", "")));
1847 void TestClangd();
1849 runSymbolCollector(Header, /**/ "");
1850 EXPECT_THAT(Symbols, UnorderedElementsAre(
1851 AllOf(qName("TestClangc"), deprecated()),
1852 AllOf(qName("TestClangd"), Not(deprecated()))));
1855 TEST_F(SymbolCollectorTest, implementationDetail) {
1856 const std::string Header = R"(
1857 #define DECL_NAME(x, y) x##_##y##_Decl
1858 #define DECL(x, y) class DECL_NAME(x, y) {};
1859 DECL(X, Y); // X_Y_Decl
1861 class Public {};
1863 runSymbolCollector(Header, /**/ "");
1864 EXPECT_THAT(Symbols,
1865 UnorderedElementsAre(
1866 AllOf(qName("X_Y_Decl"), implementationDetail()),
1867 AllOf(qName("Public"), Not(implementationDetail()))));
1870 TEST_F(SymbolCollectorTest, UsingDecl) {
1871 const char *Header = R"(
1872 void foo();
1873 namespace std {
1874 using ::foo;
1875 })";
1876 runSymbolCollector(Header, /**/ "");
1877 EXPECT_THAT(Symbols, Contains(qName("std::foo")));
1880 TEST_F(SymbolCollectorTest, CBuiltins) {
1881 // In C, printf in stdio.h is a redecl of an implicit builtin.
1882 const char *Header = R"(
1883 extern int printf(const char*, ...);
1885 runSymbolCollector(Header, /**/ "", {"-xc"});
1886 EXPECT_THAT(Symbols, Contains(qName("printf")));
1889 TEST_F(SymbolCollectorTest, InvalidSourceLoc) {
1890 const char *Header = R"(
1891 void operator delete(void*)
1892 __attribute__((__externally_visible__));)";
1893 runSymbolCollector(Header, /**/ "");
1894 EXPECT_THAT(Symbols, Contains(qName("operator delete")));
1897 TEST_F(SymbolCollectorTest, BadUTF8) {
1898 // Extracted from boost/spirit/home/support/char_encoding/iso8859_1.hpp
1899 // This looks like UTF-8 and fools clang, but has high-ISO-8859-1 comments.
1900 const char *Header = "int PUNCT = 0;\n"
1901 "/* \xa1 */ int types[] = { /* \xa1 */PUNCT };";
1902 CollectorOpts.RefFilter = RefKind::All;
1903 CollectorOpts.RefsInHeaders = true;
1904 runSymbolCollector(Header, "");
1905 EXPECT_THAT(Symbols, Contains(AllOf(qName("types"), doc("\xef\xbf\xbd "))));
1906 EXPECT_THAT(Symbols, Contains(qName("PUNCT")));
1907 // Reference is stored, although offset within line is not reliable.
1908 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "PUNCT").ID, _)));
1911 TEST_F(SymbolCollectorTest, MacrosInHeaders) {
1912 CollectorOpts.CollectMacro = true;
1913 TestFileName = testPath("test.h");
1914 runSymbolCollector("", "#define X");
1915 EXPECT_THAT(Symbols,
1916 UnorderedElementsAre(AllOf(qName("X"), forCodeCompletion(true))));
1919 // Regression test for a crash-bug we used to have.
1920 TEST_F(SymbolCollectorTest, UndefOfModuleMacro) {
1921 auto TU = TestTU::withCode(R"cpp(#include "bar.h")cpp");
1922 TU.AdditionalFiles["bar.h"] = R"cpp(
1923 #include "foo.h"
1924 #undef X
1925 )cpp";
1926 TU.AdditionalFiles["foo.h"] = "#define X 1";
1927 TU.AdditionalFiles["module.map"] = R"cpp(
1928 module foo {
1929 header "foo.h"
1930 export *
1932 )cpp";
1933 TU.ExtraArgs.push_back("-fmodules");
1934 TU.ExtraArgs.push_back("-fmodule-map-file=" + testPath("module.map"));
1935 TU.OverlayRealFileSystemForModules = true;
1937 TU.build();
1938 // We mostly care about not crashing, but verify that we didn't insert garbage
1939 // about X too.
1940 EXPECT_THAT(TU.headerSymbols(), Not(Contains(qName("X"))));
1943 TEST_F(SymbolCollectorTest, NoCrashOnObjCMethodCStyleParam) {
1944 auto TU = TestTU::withCode(R"objc(
1945 @interface Foo
1946 - (void)fun:(bool)foo, bool bar;
1947 @end
1948 )objc");
1949 TU.ExtraArgs.push_back("-xobjective-c++");
1951 TU.build();
1952 // We mostly care about not crashing.
1953 EXPECT_THAT(TU.headerSymbols(),
1954 UnorderedElementsAre(qName("Foo"), qName("Foo::fun:")));
1957 TEST_F(SymbolCollectorTest, Reserved) {
1958 const char *Header = R"cpp(
1959 void __foo();
1960 namespace _X { int secret; }
1961 )cpp";
1963 CollectorOpts.CollectReserved = true;
1964 runSymbolCollector("", Header);
1965 EXPECT_THAT(Symbols, UnorderedElementsAre(qName("__foo"), qName("_X"),
1966 qName("_X::secret")));
1968 CollectorOpts.CollectReserved = false;
1969 runSymbolCollector("", Header); //
1970 EXPECT_THAT(Symbols, IsEmpty());
1973 TEST_F(SymbolCollectorTest, Concepts) {
1974 const char *Header = R"cpp(
1975 template <class T>
1976 concept A = sizeof(T) <= 8;
1977 )cpp";
1978 runSymbolCollector("", Header, {"-std=c++20"});
1979 EXPECT_THAT(Symbols,
1980 UnorderedElementsAre(AllOf(
1981 qName("A"), hasKind(clang::index::SymbolKind::Concept))));
1984 } // namespace
1985 } // namespace clangd
1986 } // namespace clang