[TableGen] Add TreePatternNode::children and use it in for loops (NFC) (#119877)
[llvm-project.git] / clang-tools-extra / clangd / unittests / SymbolCollectorTests.cpp
blobe8088cb37fa51c244d141c1bed10ec7ae07996d9
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 "URI.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"
30 #include <memory>
31 #include <optional>
32 #include <string>
33 #include <utility>
35 namespace clang {
36 namespace clangd {
37 namespace {
39 using ::testing::_;
40 using ::testing::AllOf;
41 using ::testing::Contains;
42 using ::testing::Each;
43 using ::testing::ElementsAre;
44 using ::testing::Field;
45 using ::testing::IsEmpty;
46 using ::testing::Not;
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(),
80 Loc.End.column()) ==
81 std::make_tuple(R.start.line, R.start.character, R.end.line,
82 R.end.character);
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 {
117 public:
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);
123 AST = File.build();
126 // build() must have been called.
127 bool shouldCollect(llvm::StringRef Name, bool Qualified = true) {
128 assert(AST);
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);
137 protected:
138 std::string HeaderName = "f.h";
139 std::string FileName = "f.cpp";
140 TestTU File;
141 std::optional<ParsedAST> AST; // Initialized after build.
144 TEST_F(ShouldCollectSymbolTest, ShouldCollectSymbol) {
145 build(R"(
146 namespace nx {
147 class X{};
148 auto f() { int Local; } // auto ensures function body is parsed.
149 struct { int x; } var;
153 class InMain {};
154 namespace { class InAnonymous {}; }
155 static void g();
156 )");
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) {
169 build(R"(
170 namespace nx {
171 auto f() {
172 int Local;
173 auto LocalLambda = [&](){
174 Local++;
175 class ClassInLambda{};
176 return Local;
178 } // auto ensures function body is parsed.
179 auto foo() {
180 class LocalBase {
181 virtual void LocalVirtual();
182 void LocalConcrete();
183 int BaseMember;
186 } // namespace nx
188 "");
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";
201 build(
202 R"(// Generated by the protocol buffer compiler. DO NOT EDIT!
203 namespace nx {
204 enum Outer_Enum : int {
205 Outer_Enum_KIND1,
206 Outer_Enum_Kind_2,
208 bool Outer_Enum_IsValid(int);
210 class Outer_Inner {};
211 class Outer {
212 using Inner = Outer_Inner;
213 using Enum = Outer_Enum;
214 static constexpr Enum KIND1 = Outer_Enum_KIND1;
215 static constexpr Enum Kind_2 = Outer_Enum_Kind_2;
216 static bool Enum_IsValid(int);
217 int &x();
218 void set_x();
219 void _internal_set_x();
221 int &Outer_y();
223 enum Foo {
224 FOO_VAL1,
225 Foo_VAL2,
227 bool Foo_IsValid(int);
228 })");
230 // Make sure all the mangled names for Outer::Enum is discarded.
231 EXPECT_FALSE(shouldCollect("nx::Outer_Enum"));
232 EXPECT_FALSE(shouldCollect("nx::Outer_Enum_KIND1"));
233 EXPECT_FALSE(shouldCollect("nx::Outer_Enum_Kind_2"));
234 EXPECT_FALSE(shouldCollect("nx::Outer_Enum_IsValid"));
235 // But nested aliases are preserved.
236 EXPECT_TRUE(shouldCollect("nx::Outer::Enum"));
237 EXPECT_TRUE(shouldCollect("nx::Outer::KIND1"));
238 EXPECT_TRUE(shouldCollect("nx::Outer::Kind_2"));
239 EXPECT_TRUE(shouldCollect("nx::Outer::Enum_IsValid"));
241 // Check for Outer::Inner.
242 EXPECT_FALSE(shouldCollect("nx::Outer_Inner"));
243 EXPECT_TRUE(shouldCollect("nx::Outer"));
244 EXPECT_TRUE(shouldCollect("nx::Outer::Inner"));
246 // Make sure field related information is preserved, unless it's explicitly
247 // marked with `_internal_`.
248 EXPECT_TRUE(shouldCollect("nx::Outer::x"));
249 EXPECT_TRUE(shouldCollect("nx::Outer::set_x"));
250 EXPECT_FALSE(shouldCollect("nx::Outer::_internal_set_x"));
251 EXPECT_TRUE(shouldCollect("nx::Outer::Outer_y"));
253 // Handling of a top-level enum
254 EXPECT_TRUE(shouldCollect("nx::Foo::FOO_VAL1"));
255 EXPECT_TRUE(shouldCollect("nx::FOO_VAL1"));
256 EXPECT_TRUE(shouldCollect("nx::Foo_IsValid"));
257 // Our heuristic goes wrong here, if the user has a nested name that starts
258 // with parent's name.
259 EXPECT_FALSE(shouldCollect("nx::Foo::Foo_VAL2"));
260 EXPECT_FALSE(shouldCollect("nx::Foo_VAL2"));
263 TEST_F(ShouldCollectSymbolTest, DoubleCheckProtoHeaderComment) {
264 HeaderName = "f.proto.h";
265 build(R"(
266 namespace nx {
267 class Top_Level {};
268 enum Kind {
269 Kind_Fine
272 )");
273 EXPECT_TRUE(shouldCollect("nx::Top_Level"));
274 EXPECT_TRUE(shouldCollect("nx::Kind_Fine"));
277 class SymbolIndexActionFactory : public tooling::FrontendActionFactory {
278 public:
279 SymbolIndexActionFactory(SymbolCollector::Options COpts)
280 : COpts(std::move(COpts)) {}
282 std::unique_ptr<FrontendAction> create() override {
283 class IndexAction : public ASTFrontendAction {
284 public:
285 IndexAction(std::shared_ptr<index::IndexDataConsumer> DataConsumer,
286 const index::IndexingOptions &Opts,
287 std::shared_ptr<include_cleaner::PragmaIncludes> PI)
288 : DataConsumer(std::move(DataConsumer)), Opts(Opts),
289 PI(std::move(PI)) {}
291 std::unique_ptr<ASTConsumer>
292 CreateASTConsumer(CompilerInstance &CI, llvm::StringRef InFile) override {
293 PI->record(CI);
294 return createIndexingASTConsumer(DataConsumer, Opts,
295 CI.getPreprocessorPtr());
298 bool BeginInvocation(CompilerInstance &CI) override {
299 // Make the compiler parse all comments.
300 CI.getLangOpts().CommentOpts.ParseAllComments = true;
301 return true;
304 private:
305 std::shared_ptr<index::IndexDataConsumer> DataConsumer;
306 index::IndexingOptions Opts;
307 std::shared_ptr<include_cleaner::PragmaIncludes> PI;
309 index::IndexingOptions IndexOpts;
310 IndexOpts.SystemSymbolFilter =
311 index::IndexingOptions::SystemSymbolFilterKind::All;
312 IndexOpts.IndexFunctionLocals = true;
313 std::shared_ptr<include_cleaner::PragmaIncludes> PI =
314 std::make_shared<include_cleaner::PragmaIncludes>();
315 COpts.PragmaIncludes = PI.get();
316 Collector = std::make_shared<SymbolCollector>(COpts);
317 return std::make_unique<IndexAction>(Collector, std::move(IndexOpts),
318 std::move(PI));
321 std::shared_ptr<SymbolCollector> Collector;
322 SymbolCollector::Options COpts;
325 class SymbolCollectorTest : public ::testing::Test {
326 public:
327 SymbolCollectorTest()
328 : InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem),
329 TestHeaderName(testPath("symbol.h")),
330 TestFileName(testPath("symbol.cc")) {
331 TestHeaderURI = URI::create(TestHeaderName).toString();
332 TestFileURI = URI::create(TestFileName).toString();
335 // Note that unlike TestTU, no automatic header guard is added.
336 // HeaderCode should start with #pragma once to be treated as modular.
337 bool runSymbolCollector(llvm::StringRef HeaderCode, llvm::StringRef MainCode,
338 const std::vector<std::string> &ExtraArgs = {}) {
339 llvm::IntrusiveRefCntPtr<FileManager> Files(
340 new FileManager(FileSystemOptions(), InMemoryFileSystem));
342 auto Factory = std::make_unique<SymbolIndexActionFactory>(CollectorOpts);
344 std::vector<std::string> Args = {"symbol_collector", "-fsyntax-only",
345 "-xc++", "-include", TestHeaderName};
346 Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
347 // This allows to override the "-xc++" with something else, i.e.
348 // -xobjective-c++.
349 Args.push_back(TestFileName);
351 tooling::ToolInvocation Invocation(
352 Args, Factory->create(), Files.get(),
353 std::make_shared<PCHContainerOperations>());
355 // Multiple calls to runSymbolCollector with different contents will fail
356 // to update the filesystem! Why are we sharing one across tests, anyway?
357 EXPECT_TRUE(InMemoryFileSystem->addFile(
358 TestHeaderName, 0, llvm::MemoryBuffer::getMemBuffer(HeaderCode)));
359 EXPECT_TRUE(InMemoryFileSystem->addFile(
360 TestFileName, 0, llvm::MemoryBuffer::getMemBuffer(MainCode)));
361 Invocation.run();
362 Symbols = Factory->Collector->takeSymbols();
363 Refs = Factory->Collector->takeRefs();
364 Relations = Factory->Collector->takeRelations();
365 return true;
368 protected:
369 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem;
370 std::string TestHeaderName;
371 std::string TestHeaderURI;
372 std::string TestFileName;
373 std::string TestFileURI;
374 SymbolSlab Symbols;
375 RefSlab Refs;
376 RelationSlab Relations;
377 SymbolCollector::Options CollectorOpts;
380 TEST_F(SymbolCollectorTest, CollectSymbols) {
381 const std::string Header = R"(
382 class Foo {
383 Foo() {}
384 Foo(int a) {}
385 void f();
386 friend void f1();
387 friend class Friend;
388 Foo& operator=(const Foo&);
389 ~Foo();
390 class Nested {
391 void f();
394 class Friend {
397 void f1();
398 inline void f2() {}
399 static const int KInt = 2;
400 const char* kStr = "123";
402 namespace {
403 void ff() {} // ignore
406 void f1() {
407 auto LocalLambda = [&](){
408 class ClassInLambda{};
412 namespace foo {
413 // Type alias
414 typedef int int32;
415 using int32_t = int32;
417 // Variable
418 int v1;
420 // Namespace
421 namespace bar {
422 int v2;
424 // Namespace alias
425 namespace baz = bar;
427 using bar::v2;
428 } // namespace foo
430 runSymbolCollector(Header, /*Main=*/"");
431 EXPECT_THAT(Symbols,
432 UnorderedElementsAreArray(
433 {AllOf(qName("Foo"), forCodeCompletion(true)),
434 AllOf(qName("Foo::Foo"), forCodeCompletion(false)),
435 AllOf(qName("Foo::Foo"), forCodeCompletion(false)),
436 AllOf(qName("Foo::f"), forCodeCompletion(false)),
437 AllOf(qName("Foo::~Foo"), forCodeCompletion(false)),
438 AllOf(qName("Foo::operator="), forCodeCompletion(false)),
439 AllOf(qName("Foo::Nested"), forCodeCompletion(false)),
440 AllOf(qName("Foo::Nested::f"), forCodeCompletion(false)),
441 AllOf(qName("ClassInLambda"), forCodeCompletion(false)),
442 AllOf(qName("Friend"), forCodeCompletion(true)),
443 AllOf(qName("f1"), forCodeCompletion(true)),
444 AllOf(qName("f2"), forCodeCompletion(true)),
445 AllOf(qName("KInt"), forCodeCompletion(true)),
446 AllOf(qName("kStr"), forCodeCompletion(true)),
447 AllOf(qName("foo"), forCodeCompletion(true)),
448 AllOf(qName("foo::bar"), forCodeCompletion(true)),
449 AllOf(qName("foo::int32"), forCodeCompletion(true)),
450 AllOf(qName("foo::int32_t"), forCodeCompletion(true)),
451 AllOf(qName("foo::v1"), forCodeCompletion(true)),
452 AllOf(qName("foo::bar::v2"), forCodeCompletion(true)),
453 AllOf(qName("foo::v2"), forCodeCompletion(true)),
454 AllOf(qName("foo::baz"), forCodeCompletion(true))}));
457 TEST_F(SymbolCollectorTest, FileLocal) {
458 const std::string Header = R"(
459 class Foo {};
460 namespace {
461 class Ignored {};
463 void bar();
465 const std::string Main = R"(
466 class ForwardDecl;
467 void bar() {}
468 static void a();
469 class B {};
470 namespace {
471 void c();
474 runSymbolCollector(Header, Main);
475 EXPECT_THAT(Symbols,
476 UnorderedElementsAre(
477 AllOf(qName("Foo"), visibleOutsideFile()),
478 AllOf(qName("bar"), visibleOutsideFile()),
479 AllOf(qName("a"), Not(visibleOutsideFile())),
480 AllOf(qName("B"), Not(visibleOutsideFile())),
481 AllOf(qName("c"), Not(visibleOutsideFile())),
482 // FIXME: ForwardDecl likely *is* visible outside.
483 AllOf(qName("ForwardDecl"), Not(visibleOutsideFile()))));
486 TEST_F(SymbolCollectorTest, Template) {
487 Annotations Header(R"(
488 // Primary template and explicit specialization are indexed, instantiation
489 // is not.
490 template <class T, class U> struct [[Tmpl]] {T $xdecl[[x]] = 0;};
491 template <> struct $specdecl[[Tmpl]]<int, bool> {};
492 template <class U> struct $partspecdecl[[Tmpl]]<bool, U> {};
493 extern template struct Tmpl<float, bool>;
494 template struct Tmpl<double, bool>;
495 )");
496 runSymbolCollector(Header.code(), /*Main=*/"");
497 EXPECT_THAT(Symbols,
498 UnorderedElementsAre(
499 AllOf(qName("Tmpl"), declRange(Header.range()),
500 forCodeCompletion(true)),
501 AllOf(qName("Tmpl"), declRange(Header.range("specdecl")),
502 forCodeCompletion(false)),
503 AllOf(qName("Tmpl"), declRange(Header.range("partspecdecl")),
504 forCodeCompletion(false)),
505 AllOf(qName("Tmpl::x"), declRange(Header.range("xdecl")),
506 forCodeCompletion(false))));
509 TEST_F(SymbolCollectorTest, templateArgs) {
510 Annotations Header(R"(
511 template <class X> class $barclasstemp[[Bar]] {};
512 template <class T, class U, template<typename> class Z, int Q>
513 struct [[Tmpl]] { T $xdecl[[x]] = 0; };
515 // template-template, non-type and type full spec
516 template <> struct $specdecl[[Tmpl]]<int, bool, Bar, 3> {};
518 // template-template, non-type and type partial spec
519 template <class U, int T> struct $partspecdecl[[Tmpl]]<bool, U, Bar, T> {};
520 // instantiation
521 extern template struct Tmpl<float, bool, Bar, 8>;
522 // instantiation
523 template struct Tmpl<double, bool, Bar, 2>;
525 template <typename ...> class $fooclasstemp[[Foo]] {};
526 // parameter-packs full spec
527 template<> class $parampack[[Foo]]<Bar<int>, int, double> {};
528 // parameter-packs partial spec
529 template<class T> class $parampackpartial[[Foo]]<T, T> {};
531 template <int ...> class $bazclasstemp[[Baz]] {};
532 // non-type parameter-packs full spec
533 template<> class $parampacknontype[[Baz]]<3, 5, 8> {};
534 // non-type parameter-packs partial spec
535 template<int T> class $parampacknontypepartial[[Baz]]<T, T> {};
537 template <template <class> class ...> class $fozclasstemp[[Foz]] {};
538 // template-template parameter-packs full spec
539 template<> class $parampacktempltempl[[Foz]]<Bar, Bar> {};
540 // template-template parameter-packs partial spec
541 template<template <class> class T>
542 class $parampacktempltemplpartial[[Foz]]<T, T> {};
543 )");
544 runSymbolCollector(Header.code(), /*Main=*/"");
545 EXPECT_THAT(
546 Symbols,
547 AllOf(
548 Contains(AllOf(qName("Tmpl"), templateArgs("<int, bool, Bar, 3>"),
549 declRange(Header.range("specdecl")),
550 forCodeCompletion(false))),
551 Contains(AllOf(qName("Tmpl"), templateArgs("<bool, U, Bar, T>"),
552 declRange(Header.range("partspecdecl")),
553 forCodeCompletion(false))),
554 Contains(AllOf(qName("Foo"), templateArgs("<Bar<int>, int, double>"),
555 declRange(Header.range("parampack")),
556 forCodeCompletion(false))),
557 Contains(AllOf(qName("Foo"), templateArgs("<T, T>"),
558 declRange(Header.range("parampackpartial")),
559 forCodeCompletion(false))),
560 Contains(AllOf(qName("Baz"), templateArgs("<3, 5, 8>"),
561 declRange(Header.range("parampacknontype")),
562 forCodeCompletion(false))),
563 Contains(AllOf(qName("Baz"), templateArgs("<T, T>"),
564 declRange(Header.range("parampacknontypepartial")),
565 forCodeCompletion(false))),
566 Contains(AllOf(qName("Foz"), templateArgs("<Bar, Bar>"),
567 declRange(Header.range("parampacktempltempl")),
568 forCodeCompletion(false))),
569 Contains(AllOf(qName("Foz"), templateArgs("<T, T>"),
570 declRange(Header.range("parampacktempltemplpartial")),
571 forCodeCompletion(false)))));
574 TEST_F(SymbolCollectorTest, ObjCRefs) {
575 Annotations Header(R"(
576 @interface Person
577 - (void)$talk[[talk]];
578 - (void)$say[[say]]:(id)something;
579 @end
580 @interface Person (Category)
581 - (void)categoryMethod;
582 - (void)multiArg:(id)a method:(id)b;
583 @end
584 )");
585 Annotations Main(R"(
586 @implementation Person
587 - (void)$talk[[talk]] {}
588 - (void)$say[[say]]:(id)something {}
589 @end
591 void fff(Person *p) {
592 [p $talk[[talk]]];
593 [p $say[[say]]:0];
594 [p categoryMethod];
595 [p multiArg:0 method:0];
597 )");
598 CollectorOpts.RefFilter = RefKind::All;
599 CollectorOpts.CollectMainFileRefs = true;
600 TestFileName = testPath("test.m");
601 runSymbolCollector(Header.code(), Main.code(),
602 {"-fblocks", "-xobjective-c++", "-Wno-objc-root-class"});
603 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::talk").ID,
604 haveRanges(Main.ranges("talk")))));
605 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::say:").ID,
606 haveRanges(Main.ranges("say")))));
607 EXPECT_THAT(Refs,
608 Contains(Pair(findSymbol(Symbols, "Person::categoryMethod").ID,
609 ElementsAre(isSpelled()))));
610 EXPECT_THAT(Refs,
611 Contains(Pair(findSymbol(Symbols, "Person::multiArg:method:").ID,
612 ElementsAre(isSpelled()))));
615 TEST_F(SymbolCollectorTest, ObjCSymbols) {
616 const std::string Header = R"(
617 @interface Person
618 - (void)someMethodName:(void*)name1 lastName:(void*)lName;
619 @end
621 @implementation Person
622 - (void)someMethodName:(void*)name1 lastName:(void*)lName{
623 int foo;
624 ^(int param){ int bar; };
626 @end
628 @interface Person (MyCategory)
629 - (void)someMethodName2:(void*)name2;
630 @end
632 @implementation Person (MyCategory)
633 - (void)someMethodName2:(void*)name2 {
634 int foo2;
636 @end
638 @protocol MyProtocol
639 - (void)someMethodName3:(void*)name3;
640 @end
642 TestFileName = testPath("test.m");
643 runSymbolCollector(Header, /*Main=*/"", {"-fblocks", "-xobjective-c++"});
644 EXPECT_THAT(Symbols,
645 UnorderedElementsAre(
646 qName("Person"), qName("Person::someMethodName:lastName:"),
647 AllOf(qName("MyCategory"), forCodeCompletion(false)),
648 qName("Person::someMethodName2:"), qName("MyProtocol"),
649 qName("MyProtocol::someMethodName3:")));
652 TEST_F(SymbolCollectorTest, ObjCPropertyImpl) {
653 const std::string Header = R"(
654 @interface Container
655 @property(nonatomic) int magic;
656 @end
658 @implementation Container
659 @end
661 TestFileName = testPath("test.m");
662 runSymbolCollector(Header, /*Main=*/"", {"-xobjective-c++"});
663 EXPECT_THAT(Symbols, Contains(qName("Container")));
664 EXPECT_THAT(Symbols, Contains(qName("Container::magic")));
665 // FIXME: Results also contain Container::_magic on some platforms.
666 // Figure out why it's platform-dependent.
669 TEST_F(SymbolCollectorTest, ObjCLocations) {
670 Annotations Header(R"(
671 // Declared in header, defined in main.
672 @interface $dogdecl[[Dog]]
673 @end
674 @interface $fluffydecl[[Dog]] (Fluffy)
675 @end
676 )");
677 Annotations Main(R"(
678 @interface Dog ()
679 @end
680 @implementation $dogdef[[Dog]]
681 @end
682 @implementation $fluffydef[[Dog]] (Fluffy)
683 @end
684 // Category with no declaration (only implementation).
685 @implementation $ruff[[Dog]] (Ruff)
686 @end
687 // Implicitly defined interface.
688 @implementation $catdog[[CatDog]]
689 @end
690 )");
691 runSymbolCollector(Header.code(), Main.code(),
692 {"-xobjective-c++", "-Wno-objc-root-class"});
693 EXPECT_THAT(Symbols,
694 UnorderedElementsAre(
695 AllOf(qName("Dog"), declRange(Header.range("dogdecl")),
696 defRange(Main.range("dogdef"))),
697 AllOf(qName("Fluffy"), declRange(Header.range("fluffydecl")),
698 defRange(Main.range("fluffydef"))),
699 AllOf(qName("CatDog"), declRange(Main.range("catdog")),
700 defRange(Main.range("catdog"))),
701 AllOf(qName("Ruff"), declRange(Main.range("ruff")),
702 defRange(Main.range("ruff")))));
705 TEST_F(SymbolCollectorTest, ObjCForwardDecls) {
706 Annotations Header(R"(
707 // Forward declared in header, declared and defined in main.
708 @protocol Barker;
709 @class Dog;
710 // Never fully declared so Clang latches onto this decl.
711 @class $catdogdecl[[CatDog]];
712 )");
713 Annotations Main(R"(
714 @protocol $barkerdecl[[Barker]]
715 - (void)woof;
716 @end
717 @interface $dogdecl[[Dog]]<Barker>
718 - (void)woof;
719 @end
720 @implementation $dogdef[[Dog]]
721 - (void)woof {}
722 @end
723 @implementation $catdogdef[[CatDog]]
724 @end
725 )");
726 runSymbolCollector(Header.code(), Main.code(),
727 {"-xobjective-c++", "-Wno-objc-root-class"});
728 EXPECT_THAT(Symbols,
729 UnorderedElementsAre(
730 AllOf(qName("CatDog"), declRange(Header.range("catdogdecl")),
731 defRange(Main.range("catdogdef"))),
732 AllOf(qName("Dog"), declRange(Main.range("dogdecl")),
733 defRange(Main.range("dogdef"))),
734 AllOf(qName("Barker"), declRange(Main.range("barkerdecl"))),
735 qName("Barker::woof"), qName("Dog::woof")));
738 TEST_F(SymbolCollectorTest, ObjCClassExtensions) {
739 Annotations Header(R"(
740 @interface $catdecl[[Cat]]
741 @end
742 )");
743 Annotations Main(R"(
744 @interface Cat ()
745 - (void)meow;
746 @end
747 @interface Cat ()
748 - (void)pur;
749 @end
750 )");
751 runSymbolCollector(Header.code(), Main.code(),
752 {"-xobjective-c++", "-Wno-objc-root-class"});
753 EXPECT_THAT(Symbols,
754 UnorderedElementsAre(
755 AllOf(qName("Cat"), declRange(Header.range("catdecl"))),
756 qName("Cat::meow"), qName("Cat::pur")));
759 TEST_F(SymbolCollectorTest, ObjCFrameworkIncludeHeader) {
760 CollectorOpts.CollectIncludePath = true;
761 auto FrameworksPath = testPath("Frameworks/");
762 std::string FrameworkHeader = R"(
763 __attribute((objc_root_class))
764 @interface NSObject
765 @end
767 InMemoryFileSystem->addFile(
768 testPath("Frameworks/Foundation.framework/Headers/NSObject.h"), 0,
769 llvm::MemoryBuffer::getMemBuffer(FrameworkHeader));
770 std::string PrivateFrameworkHeader = R"(
771 #import <Foundation/NSObject.h>
773 @interface PrivateClass : NSObject
774 @end
776 InMemoryFileSystem->addFile(
777 testPath(
778 "Frameworks/Foundation.framework/PrivateHeaders/NSObject+Private.h"),
779 0, llvm::MemoryBuffer::getMemBuffer(PrivateFrameworkHeader));
781 std::string Header = R"(
782 #import <Foundation/NSObject+Private.h>
783 #import <Foundation/NSObject.h>
785 @interface Container : NSObject
786 @end
788 std::string Main = "";
789 TestFileName = testPath("test.m");
790 runSymbolCollector(Header, Main, {"-F", FrameworksPath, "-xobjective-c++"});
791 EXPECT_THAT(
792 Symbols,
793 UnorderedElementsAre(
794 AllOf(qName("NSObject"), includeHeader("<Foundation/NSObject.h>")),
795 AllOf(qName("PrivateClass"),
796 includeHeader("<Foundation/NSObject+Private.h>")),
797 AllOf(qName("Container"))));
799 // After adding the umbrella headers, we should use that spelling instead.
800 std::string UmbrellaHeader = R"(
801 #import <Foundation/NSObject.h>
803 InMemoryFileSystem->addFile(
804 testPath("Frameworks/Foundation.framework/Headers/Foundation.h"), 0,
805 llvm::MemoryBuffer::getMemBuffer(UmbrellaHeader));
806 std::string PrivateUmbrellaHeader = R"(
807 #import <Foundation/NSObject+Private.h>
809 InMemoryFileSystem->addFile(
810 testPath("Frameworks/Foundation.framework/PrivateHeaders/"
811 "Foundation_Private.h"),
812 0, llvm::MemoryBuffer::getMemBuffer(PrivateUmbrellaHeader));
813 runSymbolCollector(Header, Main, {"-F", FrameworksPath, "-xobjective-c++"});
814 EXPECT_THAT(
815 Symbols,
816 UnorderedElementsAre(
817 AllOf(qName("NSObject"), includeHeader("<Foundation/Foundation.h>")),
818 AllOf(qName("PrivateClass"),
819 includeHeader("<Foundation/Foundation_Private.h>")),
820 AllOf(qName("Container"))));
822 runSymbolCollector(Header, Main,
823 {"-iframework", FrameworksPath, "-xobjective-c++"});
824 EXPECT_THAT(
825 Symbols,
826 UnorderedElementsAre(
827 AllOf(qName("NSObject"), includeHeader("<Foundation/Foundation.h>")),
828 AllOf(qName("PrivateClass"),
829 includeHeader("<Foundation/Foundation_Private.h>")),
830 AllOf(qName("Container"))));
833 TEST_F(SymbolCollectorTest, Locations) {
834 Annotations Header(R"cpp(
835 // Declared in header, defined in main.
836 extern int $xdecl[[X]];
837 class $clsdecl[[Cls]];
838 void $printdecl[[print]]();
840 // Declared in header, defined nowhere.
841 extern int $zdecl[[Z]];
843 void $foodecl[[fo\
844 o]]();
845 )cpp");
846 Annotations Main(R"cpp(
847 int $xdef[[X]] = 42;
848 class $clsdef[[Cls]] {};
849 void $printdef[[print]]() {}
851 // Declared/defined in main only.
852 int $ydecl[[Y]];
853 )cpp");
854 runSymbolCollector(Header.code(), Main.code());
855 EXPECT_THAT(Symbols,
856 UnorderedElementsAre(
857 AllOf(qName("X"), declRange(Header.range("xdecl")),
858 defRange(Main.range("xdef"))),
859 AllOf(qName("Cls"), declRange(Header.range("clsdecl")),
860 defRange(Main.range("clsdef"))),
861 AllOf(qName("print"), declRange(Header.range("printdecl")),
862 defRange(Main.range("printdef"))),
863 AllOf(qName("Z"), declRange(Header.range("zdecl"))),
864 AllOf(qName("foo"), declRange(Header.range("foodecl"))),
865 AllOf(qName("Y"), declRange(Main.range("ydecl")))));
868 TEST_F(SymbolCollectorTest, Refs) {
869 Annotations Header(R"(
870 #define MACRO(X) (X + 1)
871 class Foo {
872 public:
873 Foo() {}
874 Foo(int);
876 class Bar;
877 void func();
879 namespace NS {} // namespace ref is ignored
880 )");
881 Annotations Main(R"(
882 class $bar[[Bar]] {};
884 void $func[[func]]();
886 void fff() {
887 $foo[[Foo]] foo;
888 $bar[[Bar]] bar;
889 $func[[func]]();
890 int abc = 0;
891 $foo[[Foo]] foo2 = abc;
892 abc = $macro[[MACRO]](1);
894 )");
895 Annotations SymbolsOnlyInMainCode(R"(
896 #define FUNC(X) (X+1)
897 int a;
898 void b() {}
899 static const int c = FUNC(1);
900 class d {};
901 )");
902 CollectorOpts.RefFilter = RefKind::All;
903 CollectorOpts.CollectMacro = true;
904 runSymbolCollector(Header.code(),
905 (Main.code() + SymbolsOnlyInMainCode.code()).str());
906 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Foo").ID,
907 haveRanges(Main.ranges("foo")))));
908 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Bar").ID,
909 haveRanges(Main.ranges("bar")))));
910 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "func").ID,
911 haveRanges(Main.ranges("func")))));
912 EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(Symbols, "NS").ID, _))));
913 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "MACRO").ID,
914 haveRanges(Main.ranges("macro")))));
915 // - (a, b) externally visible and should have refs.
916 // - (c, FUNC) externally invisible and had no refs collected.
917 auto MainSymbols =
918 TestTU::withHeaderCode(SymbolsOnlyInMainCode.code()).headerSymbols();
919 EXPECT_THAT(Refs, Contains(Pair(findSymbol(MainSymbols, "a").ID, _)));
920 EXPECT_THAT(Refs, Contains(Pair(findSymbol(MainSymbols, "b").ID, _)));
921 EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(MainSymbols, "c").ID, _))));
922 EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(MainSymbols, "FUNC").ID, _))));
924 // Run the collector again with CollectMainFileRefs = true.
925 // We need to recreate InMemoryFileSystem because runSymbolCollector()
926 // calls MemoryBuffer::getMemBuffer(), which makes the buffers unusable
927 // after runSymbolCollector() exits.
928 InMemoryFileSystem = new llvm::vfs::InMemoryFileSystem();
929 CollectorOpts.CollectMainFileRefs = true;
930 runSymbolCollector(Header.code(),
931 (Main.code() + SymbolsOnlyInMainCode.code()).str());
932 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "a").ID, _)));
933 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "b").ID, _)));
934 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "c").ID, _)));
935 // However, references to main-file macros are not collected.
936 EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(Symbols, "FUNC").ID, _))));
939 TEST_F(SymbolCollectorTest, RefContainers) {
940 Annotations Code(R"cpp(
941 int $toplevel1[[f1]](int);
942 void f2() {
943 (void) $ref1a[[f1]](1);
944 auto fptr = &$ref1b[[f1]];
946 int $toplevel2[[v1]] = $ref2[[f1]](2);
947 void f3(int arg = $ref3[[f1]](3));
948 struct S1 {
949 int $classscope1[[member1]] = $ref4[[f1]](4);
950 int $classscope2[[member2]] = 42;
952 constexpr int f4(int x) { return x + 1; }
953 template <int I = $ref5[[f4]](0)> struct S2 {};
954 S2<$ref6[[f4]](0)> v2;
955 S2<$ref7a[[f4]](0)> f5(S2<$ref7b[[f4]](0)>);
956 namespace N {
957 void $namespacescope1[[f6]]();
958 int $namespacescope2[[v3]];
960 )cpp");
961 CollectorOpts.RefFilter = RefKind::All;
962 CollectorOpts.CollectMainFileRefs = true;
963 runSymbolCollector("", Code.code());
964 auto FindRefWithRange = [&](Range R) -> std::optional<Ref> {
965 for (auto &Entry : Refs) {
966 for (auto &Ref : Entry.second) {
967 if (rangesMatch(Ref.Location, R))
968 return Ref;
971 return std::nullopt;
973 auto Container = [&](llvm::StringRef RangeName) {
974 auto Ref = FindRefWithRange(Code.range(RangeName));
975 EXPECT_TRUE(bool(Ref));
976 return Ref->Container;
978 EXPECT_EQ(Container("ref1a"),
979 findSymbol(Symbols, "f2").ID); // function body (call)
980 EXPECT_EQ(Container("ref1b"),
981 findSymbol(Symbols, "f2").ID); // function body (address-of)
982 EXPECT_EQ(Container("ref2"),
983 findSymbol(Symbols, "v1").ID); // variable initializer
984 EXPECT_EQ(Container("ref3"),
985 findSymbol(Symbols, "f3").ID); // function parameter default value
986 EXPECT_EQ(Container("ref4"),
987 findSymbol(Symbols, "S1::member1").ID); // member initializer
988 EXPECT_EQ(Container("ref5"),
989 findSymbol(Symbols, "S2").ID); // template parameter default value
990 EXPECT_EQ(Container("ref6"),
991 findSymbol(Symbols, "v2").ID); // type of variable
992 EXPECT_EQ(Container("ref7a"),
993 findSymbol(Symbols, "f5").ID); // return type of function
994 EXPECT_EQ(Container("ref7b"),
995 findSymbol(Symbols, "f5").ID); // parameter type of function
997 EXPECT_FALSE(Container("classscope1").isNull());
998 EXPECT_FALSE(Container("namespacescope1").isNull());
1000 EXPECT_EQ(Container("toplevel1"), Container("toplevel2"));
1001 EXPECT_EQ(Container("classscope1"), Container("classscope2"));
1002 EXPECT_EQ(Container("namespacescope1"), Container("namespacescope2"));
1004 EXPECT_NE(Container("toplevel1"), Container("namespacescope1"));
1005 EXPECT_NE(Container("toplevel1"), Container("classscope1"));
1006 EXPECT_NE(Container("classscope1"), Container("namespacescope1"));
1009 TEST_F(SymbolCollectorTest, MacroRefInHeader) {
1010 Annotations Header(R"(
1011 #define $foo[[FOO]](X) (X + 1)
1012 #define $bar[[BAR]](X) (X + 2)
1014 // Macro defined multiple times.
1015 #define $ud1[[UD]] 1
1016 int ud_1 = $ud1[[UD]];
1017 #undef UD
1019 #define $ud2[[UD]] 2
1020 int ud_2 = $ud2[[UD]];
1021 #undef UD
1023 // Macros from token concatenations not included.
1024 #define $concat[[CONCAT]](X) X##A()
1025 #define $prepend[[PREPEND]](X) MACRO##X()
1026 #define $macroa[[MACROA]]() 123
1027 int B = $concat[[CONCAT]](MACRO);
1028 int D = $prepend[[PREPEND]](A);
1030 void fff() {
1031 int abc = $foo[[FOO]](1) + $bar[[BAR]]($foo[[FOO]](1));
1033 )");
1034 CollectorOpts.RefFilter = RefKind::All;
1035 CollectorOpts.RefsInHeaders = true;
1036 // Need this to get the SymbolID for macros for tests.
1037 CollectorOpts.CollectMacro = true;
1039 runSymbolCollector(Header.code(), "");
1041 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "FOO").ID,
1042 haveRanges(Header.ranges("foo")))));
1043 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "BAR").ID,
1044 haveRanges(Header.ranges("bar")))));
1045 // No unique ID for multiple symbols named UD. Check for ranges only.
1046 EXPECT_THAT(Refs, Contains(Pair(_, haveRanges(Header.ranges("ud1")))));
1047 EXPECT_THAT(Refs, Contains(Pair(_, haveRanges(Header.ranges("ud2")))));
1048 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "CONCAT").ID,
1049 haveRanges(Header.ranges("concat")))));
1050 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "PREPEND").ID,
1051 haveRanges(Header.ranges("prepend")))));
1052 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "MACROA").ID,
1053 haveRanges(Header.ranges("macroa")))));
1056 TEST_F(SymbolCollectorTest, MacroRefWithoutCollectingSymbol) {
1057 Annotations Header(R"(
1058 #define $foo[[FOO]](X) (X + 1)
1059 int abc = $foo[[FOO]](1);
1060 )");
1061 CollectorOpts.RefFilter = RefKind::All;
1062 CollectorOpts.RefsInHeaders = true;
1063 CollectorOpts.CollectMacro = false;
1064 runSymbolCollector(Header.code(), "");
1065 EXPECT_THAT(Refs, Contains(Pair(_, haveRanges(Header.ranges("foo")))));
1068 TEST_F(SymbolCollectorTest, MacrosWithRefFilter) {
1069 Annotations Header("#define $macro[[MACRO]](X) (X + 1)");
1070 Annotations Main("void foo() { int x = $macro[[MACRO]](1); }");
1071 CollectorOpts.RefFilter = RefKind::Unknown;
1072 runSymbolCollector(Header.code(), Main.code());
1073 EXPECT_THAT(Refs, IsEmpty());
1076 TEST_F(SymbolCollectorTest, SpelledReferences) {
1077 struct {
1078 llvm::StringRef Header;
1079 llvm::StringRef Main;
1080 llvm::StringRef TargetSymbolName;
1081 } TestCases[] = {
1083 R"cpp(
1084 struct Foo;
1085 #define MACRO Foo
1086 )cpp",
1087 R"cpp(
1088 struct $spelled[[Foo]] {
1089 $spelled[[Foo]]();
1090 ~$spelled[[Foo]]();
1092 $spelled[[Foo]] Variable1;
1093 $implicit[[MACRO]] Variable2;
1094 )cpp",
1095 "Foo",
1098 R"cpp(
1099 class Foo {
1100 public:
1101 Foo() = default;
1103 )cpp",
1104 R"cpp(
1105 void f() { Foo $implicit[[f]]; f = $spelled[[Foo]]();}
1106 )cpp",
1107 "Foo::Foo" /// constructor.
1109 { // Unclean identifiers
1110 R"cpp(
1111 struct Foo {};
1112 )cpp",
1113 R"cpp(
1114 $spelled[[Fo\
1115 o]] f{};
1116 )cpp",
1117 "Foo",
1120 CollectorOpts.RefFilter = RefKind::All;
1121 CollectorOpts.RefsInHeaders = false;
1122 for (const auto& T : TestCases) {
1123 SCOPED_TRACE(T.Header + "\n---\n" + T.Main);
1124 Annotations Header(T.Header);
1125 Annotations Main(T.Main);
1126 // Reset the file system.
1127 InMemoryFileSystem = new llvm::vfs::InMemoryFileSystem;
1128 runSymbolCollector(Header.code(), Main.code());
1130 const auto SpelledRanges = Main.ranges("spelled");
1131 const auto ImplicitRanges = Main.ranges("implicit");
1132 RefSlab::Builder SpelledSlabBuilder, ImplicitSlabBuilder;
1133 const auto TargetID = findSymbol(Symbols, T.TargetSymbolName).ID;
1134 for (const auto &SymbolAndRefs : Refs) {
1135 const auto ID = SymbolAndRefs.first;
1136 if (ID != TargetID)
1137 continue;
1138 for (const auto &Ref : SymbolAndRefs.second)
1139 if ((Ref.Kind & RefKind::Spelled) != RefKind::Unknown)
1140 SpelledSlabBuilder.insert(ID, Ref);
1141 else
1142 ImplicitSlabBuilder.insert(ID, Ref);
1144 const auto SpelledRefs = std::move(SpelledSlabBuilder).build(),
1145 ImplicitRefs = std::move(ImplicitSlabBuilder).build();
1146 EXPECT_EQ(SpelledRanges.empty(), SpelledRefs.empty());
1147 EXPECT_EQ(ImplicitRanges.empty(), ImplicitRefs.empty());
1148 if (!SpelledRanges.empty())
1149 EXPECT_THAT(SpelledRefs,
1150 Contains(Pair(TargetID, haveRanges(SpelledRanges))));
1151 if (!ImplicitRanges.empty())
1152 EXPECT_THAT(ImplicitRefs,
1153 Contains(Pair(TargetID, haveRanges(ImplicitRanges))));
1157 TEST_F(SymbolCollectorTest, NameReferences) {
1158 CollectorOpts.RefFilter = RefKind::All;
1159 CollectorOpts.RefsInHeaders = true;
1160 Annotations Header(R"(
1161 class [[Foo]] {
1162 public:
1163 [[Foo]]() {}
1164 ~[[Foo]]() {}
1166 )");
1167 CollectorOpts.RefFilter = RefKind::All;
1168 runSymbolCollector(Header.code(), "");
1169 // When we find references for class Foo, we expect to see all
1170 // constructor/destructor references.
1171 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Foo").ID,
1172 haveRanges(Header.ranges()))));
1175 TEST_F(SymbolCollectorTest, RefsOnMacros) {
1176 // Refs collected from SymbolCollector behave in the same way as
1177 // AST-based xrefs.
1178 CollectorOpts.RefFilter = RefKind::All;
1179 CollectorOpts.RefsInHeaders = true;
1180 Annotations Header(R"(
1181 #define TYPE(X) X
1182 #define FOO Foo
1183 #define CAT(X, Y) X##Y
1184 class [[Foo]] {};
1185 void test() {
1186 TYPE([[Foo]]) foo;
1187 [[FOO]] foo2;
1188 TYPE(TYPE([[Foo]])) foo3;
1189 [[CAT]](Fo, o) foo4;
1191 )");
1192 CollectorOpts.RefFilter = RefKind::All;
1193 runSymbolCollector(Header.code(), "");
1194 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Foo").ID,
1195 haveRanges(Header.ranges()))));
1198 TEST_F(SymbolCollectorTest, HeaderAsMainFile) {
1199 CollectorOpts.RefFilter = RefKind::All;
1200 Annotations Header(R"(
1201 class $Foo[[Foo]] {};
1203 void $Func[[Func]]() {
1204 $Foo[[Foo]] fo;
1206 )");
1207 // We should collect refs to main-file symbols in all cases:
1209 // 1. The main file is normal .cpp file.
1210 TestFileName = testPath("foo.cpp");
1211 runSymbolCollector("", Header.code());
1212 EXPECT_THAT(Refs,
1213 UnorderedElementsAre(Pair(findSymbol(Symbols, "Foo").ID,
1214 haveRanges(Header.ranges("Foo"))),
1215 Pair(findSymbol(Symbols, "Func").ID,
1216 haveRanges(Header.ranges("Func")))));
1218 // 2. Run the .h file as main file.
1219 TestFileName = testPath("foo.h");
1220 runSymbolCollector("", Header.code(),
1221 /*ExtraArgs=*/{"-xobjective-c++-header"});
1222 EXPECT_THAT(Symbols, UnorderedElementsAre(qName("Foo"), qName("Func")));
1223 EXPECT_THAT(Refs,
1224 UnorderedElementsAre(Pair(findSymbol(Symbols, "Foo").ID,
1225 haveRanges(Header.ranges("Foo"))),
1226 Pair(findSymbol(Symbols, "Func").ID,
1227 haveRanges(Header.ranges("Func")))));
1229 // 3. Run the .hh file as main file (without "-x c++-header").
1230 TestFileName = testPath("foo.hh");
1231 runSymbolCollector("", Header.code());
1232 EXPECT_THAT(Symbols, UnorderedElementsAre(qName("Foo"), qName("Func")));
1233 EXPECT_THAT(Refs,
1234 UnorderedElementsAre(Pair(findSymbol(Symbols, "Foo").ID,
1235 haveRanges(Header.ranges("Foo"))),
1236 Pair(findSymbol(Symbols, "Func").ID,
1237 haveRanges(Header.ranges("Func")))));
1240 TEST_F(SymbolCollectorTest, RefsInHeaders) {
1241 CollectorOpts.RefFilter = RefKind::All;
1242 CollectorOpts.RefsInHeaders = true;
1243 CollectorOpts.CollectMacro = true;
1244 Annotations Header(R"(
1245 #define $macro[[MACRO]](x) (x+1)
1246 class $foo[[Foo]] {};
1247 )");
1248 runSymbolCollector(Header.code(), "");
1249 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Foo").ID,
1250 haveRanges(Header.ranges("foo")))));
1251 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "MACRO").ID,
1252 haveRanges(Header.ranges("macro")))));
1255 TEST_F(SymbolCollectorTest, BaseOfRelations) {
1256 std::string Header = R"(
1257 class Base {};
1258 class Derived : public Base {};
1260 runSymbolCollector(Header, /*Main=*/"");
1261 const Symbol &Base = findSymbol(Symbols, "Base");
1262 const Symbol &Derived = findSymbol(Symbols, "Derived");
1263 EXPECT_THAT(Relations,
1264 Contains(Relation{Base.ID, RelationKind::BaseOf, Derived.ID}));
1267 TEST_F(SymbolCollectorTest, OverrideRelationsSimpleInheritance) {
1268 std::string Header = R"cpp(
1269 class A {
1270 virtual void foo();
1272 class B : public A {
1273 void foo() override; // A::foo
1274 virtual void bar();
1276 class C : public B {
1277 void bar() override; // B::bar
1279 class D: public C {
1280 void foo() override; // B::foo
1281 void bar() override; // C::bar
1283 )cpp";
1284 runSymbolCollector(Header, /*Main=*/"");
1285 const Symbol &AFoo = findSymbol(Symbols, "A::foo");
1286 const Symbol &BFoo = findSymbol(Symbols, "B::foo");
1287 const Symbol &DFoo = findSymbol(Symbols, "D::foo");
1289 const Symbol &BBar = findSymbol(Symbols, "B::bar");
1290 const Symbol &CBar = findSymbol(Symbols, "C::bar");
1291 const Symbol &DBar = findSymbol(Symbols, "D::bar");
1293 std::vector<Relation> Result;
1294 for (const Relation &R : Relations)
1295 if (R.Predicate == RelationKind::OverriddenBy)
1296 Result.push_back(R);
1297 EXPECT_THAT(Result, UnorderedElementsAre(
1298 OverriddenBy(AFoo, BFoo), OverriddenBy(BBar, CBar),
1299 OverriddenBy(BFoo, DFoo), OverriddenBy(CBar, DBar)));
1302 TEST_F(SymbolCollectorTest, OverrideRelationsMultipleInheritance) {
1303 std::string Header = R"cpp(
1304 class A {
1305 virtual void foo();
1307 class B {
1308 virtual void bar();
1310 class C : public B {
1311 void bar() override; // B::bar
1312 virtual void baz();
1314 class D : public A, C {
1315 void foo() override; // A::foo
1316 void bar() override; // C::bar
1317 void baz() override; // C::baz
1319 )cpp";
1320 runSymbolCollector(Header, /*Main=*/"");
1321 const Symbol &AFoo = findSymbol(Symbols, "A::foo");
1322 const Symbol &BBar = findSymbol(Symbols, "B::bar");
1323 const Symbol &CBar = findSymbol(Symbols, "C::bar");
1324 const Symbol &CBaz = findSymbol(Symbols, "C::baz");
1325 const Symbol &DFoo = findSymbol(Symbols, "D::foo");
1326 const Symbol &DBar = findSymbol(Symbols, "D::bar");
1327 const Symbol &DBaz = findSymbol(Symbols, "D::baz");
1329 std::vector<Relation> Result;
1330 for (const Relation &R : Relations)
1331 if (R.Predicate == RelationKind::OverriddenBy)
1332 Result.push_back(R);
1333 EXPECT_THAT(Result, UnorderedElementsAre(
1334 OverriddenBy(BBar, CBar), OverriddenBy(AFoo, DFoo),
1335 OverriddenBy(CBar, DBar), OverriddenBy(CBaz, DBaz)));
1338 TEST_F(SymbolCollectorTest, CountReferences) {
1339 const std::string Header = R"(
1340 class W;
1341 class X {};
1342 class Y;
1343 class Z {}; // not used anywhere
1344 Y* y = nullptr; // used in header doesn't count
1345 #define GLOBAL_Z(name) Z name;
1347 const std::string Main = R"(
1348 W* w = nullptr;
1349 W* w2 = nullptr; // only one usage counts
1350 X x();
1351 class V;
1352 class Y{}; // definition doesn't count as a reference
1353 V* v = nullptr;
1354 GLOBAL_Z(z); // Not a reference to Z, we don't spell the type.
1356 CollectorOpts.CountReferences = true;
1357 runSymbolCollector(Header, Main);
1358 EXPECT_THAT(
1359 Symbols,
1360 UnorderedElementsAreArray(
1361 {AllOf(qName("W"), refCount(1)), AllOf(qName("X"), refCount(1)),
1362 AllOf(qName("Y"), refCount(0)), AllOf(qName("Z"), refCount(0)),
1363 AllOf(qName("y"), refCount(0)), AllOf(qName("z"), refCount(0)),
1364 AllOf(qName("x"), refCount(0)), AllOf(qName("w"), refCount(0)),
1365 AllOf(qName("w2"), refCount(0)), AllOf(qName("V"), refCount(1)),
1366 AllOf(qName("v"), refCount(0))}));
1369 TEST_F(SymbolCollectorTest, SymbolRelativeNoFallback) {
1370 runSymbolCollector("class Foo {};", /*Main=*/"");
1371 EXPECT_THAT(Symbols, UnorderedElementsAre(
1372 AllOf(qName("Foo"), declURI(TestHeaderURI))));
1375 TEST_F(SymbolCollectorTest, SymbolRelativeWithFallback) {
1376 TestHeaderName = "x.h";
1377 TestFileName = "x.cpp";
1378 TestHeaderURI = URI::create(testPath(TestHeaderName)).toString();
1379 CollectorOpts.FallbackDir = testRoot();
1380 runSymbolCollector("class Foo {};", /*Main=*/"");
1381 EXPECT_THAT(Symbols, UnorderedElementsAre(
1382 AllOf(qName("Foo"), declURI(TestHeaderURI))));
1385 TEST_F(SymbolCollectorTest, UnittestURIScheme) {
1386 // Use test URI scheme from URITests.cpp
1387 TestHeaderName = testPath("x.h");
1388 TestFileName = testPath("x.cpp");
1389 runSymbolCollector("class Foo {};", /*Main=*/"");
1390 EXPECT_THAT(Symbols, UnorderedElementsAre(
1391 AllOf(qName("Foo"), declURI("unittest:///x.h"))));
1394 TEST_F(SymbolCollectorTest, IncludeEnums) {
1395 const std::string Header = R"(
1396 enum {
1399 enum Color {
1400 Green
1402 enum class Color2 {
1403 Yellow
1405 namespace ns {
1406 enum {
1407 Black
1410 class Color3 {
1411 enum {
1412 Blue
1416 runSymbolCollector(Header, /*Main=*/"");
1417 EXPECT_THAT(Symbols,
1418 UnorderedElementsAre(
1419 AllOf(qName("Red"), forCodeCompletion(true)),
1420 AllOf(qName("Color"), forCodeCompletion(true)),
1421 AllOf(qName("Green"), forCodeCompletion(true)),
1422 AllOf(qName("Color2"), forCodeCompletion(true)),
1423 AllOf(qName("Color2::Yellow"), forCodeCompletion(true)),
1424 AllOf(qName("ns"), forCodeCompletion(true)),
1425 AllOf(qName("ns::Black"), forCodeCompletion(true)),
1426 AllOf(qName("Color3"), forCodeCompletion(true)),
1427 AllOf(qName("Color3::Blue"), forCodeCompletion(true))));
1430 TEST_F(SymbolCollectorTest, NamelessSymbols) {
1431 const std::string Header = R"(
1432 struct {
1433 int a;
1434 } Foo;
1436 runSymbolCollector(Header, /*Main=*/"");
1437 EXPECT_THAT(Symbols, UnorderedElementsAre(qName("Foo"),
1438 qName("(anonymous struct)::a")));
1441 TEST_F(SymbolCollectorTest, SymbolFormedFromRegisteredSchemeFromMacro) {
1443 Annotations Header(R"(
1444 #define FF(name) \
1445 class name##_Test {};
1447 $expansion[[FF]](abc);
1449 #define FF2() \
1450 class $spelling[[Test]] {};
1452 FF2();
1453 )");
1455 runSymbolCollector(Header.code(), /*Main=*/"");
1456 EXPECT_THAT(Symbols,
1457 UnorderedElementsAre(
1458 AllOf(qName("abc_Test"), declRange(Header.range("expansion")),
1459 declURI(TestHeaderURI)),
1460 AllOf(qName("Test"), declRange(Header.range("spelling")),
1461 declURI(TestHeaderURI))));
1464 TEST_F(SymbolCollectorTest, SymbolFormedByCLI) {
1465 Annotations Header(R"(
1466 #ifdef NAME
1467 class $expansion[[NAME]] {};
1468 #endif
1469 )");
1470 runSymbolCollector(Header.code(), /*Main=*/"", /*ExtraArgs=*/{"-DNAME=name"});
1471 EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(
1472 qName("name"), declRange(Header.range("expansion")),
1473 declURI(TestHeaderURI))));
1476 TEST_F(SymbolCollectorTest, SymbolsInMainFile) {
1477 const std::string Main = R"(
1478 class Foo {};
1479 void f1();
1480 inline void f2() {}
1482 namespace {
1483 void ff() {}
1485 namespace foo {
1486 namespace {
1487 class Bar {};
1490 void main_f() {}
1491 void f1() {}
1493 runSymbolCollector(/*Header=*/"", Main);
1494 EXPECT_THAT(Symbols, UnorderedElementsAre(
1495 qName("Foo"), qName("f1"), qName("f2"), qName("ff"),
1496 qName("foo"), qName("foo::Bar"), qName("main_f")));
1499 TEST_F(SymbolCollectorTest, Documentation) {
1500 const std::string Header = R"(
1501 // doc Foo
1502 class Foo {
1503 // doc f
1504 int f();
1507 CollectorOpts.StoreAllDocumentation = false;
1508 runSymbolCollector(Header, /* Main */ "");
1509 EXPECT_THAT(Symbols,
1510 UnorderedElementsAre(
1511 AllOf(qName("Foo"), doc("doc Foo"), forCodeCompletion(true)),
1512 AllOf(qName("Foo::f"), doc(""), returnType(""),
1513 forCodeCompletion(false))));
1515 CollectorOpts.StoreAllDocumentation = true;
1516 runSymbolCollector(Header, /* Main */ "");
1517 EXPECT_THAT(Symbols,
1518 UnorderedElementsAre(
1519 AllOf(qName("Foo"), doc("doc Foo"), forCodeCompletion(true)),
1520 AllOf(qName("Foo::f"), doc("doc f"), returnType(""),
1521 forCodeCompletion(false))));
1524 TEST_F(SymbolCollectorTest, DocumentationInMain) {
1525 const std::string Header = R"(
1526 // doc Foo
1527 class Foo {
1528 void f();
1531 const std::string Main = R"(
1532 // doc f
1533 void Foo::f() {}
1535 CollectorOpts.StoreAllDocumentation = true;
1536 runSymbolCollector(Header, Main);
1537 EXPECT_THAT(Symbols,
1538 UnorderedElementsAre(
1539 AllOf(qName("Foo"), doc("doc Foo"), forCodeCompletion(true)),
1540 AllOf(qName("Foo::f"), doc("doc f"), returnType(""),
1541 forCodeCompletion(false))));
1544 TEST_F(SymbolCollectorTest, DocumentationAtDeclThenDef) {
1545 const std::string Header = R"(
1546 class Foo {
1547 // doc f decl
1548 void f();
1551 const std::string Main = R"(
1552 // doc f def
1553 void Foo::f() {}
1555 CollectorOpts.StoreAllDocumentation = true;
1556 runSymbolCollector(Header, Main);
1557 EXPECT_THAT(Symbols,
1558 UnorderedElementsAre(AllOf(qName("Foo")),
1559 AllOf(qName("Foo::f"), doc("doc f decl"))));
1562 TEST_F(SymbolCollectorTest, DocumentationAtDefThenDecl) {
1563 const std::string Header = R"(
1564 // doc f def
1565 void f() {}
1567 // doc f decl
1568 void f();
1570 CollectorOpts.StoreAllDocumentation = true;
1571 runSymbolCollector(Header, "" /*Main*/);
1572 EXPECT_THAT(Symbols,
1573 UnorderedElementsAre(AllOf(qName("f"), doc("doc f def"))));
1576 TEST_F(SymbolCollectorTest, ClassMembers) {
1577 const std::string Header = R"(
1578 class Foo {
1579 void f() {}
1580 void g();
1581 static void sf() {}
1582 static void ssf();
1583 static int x;
1586 const std::string Main = R"(
1587 void Foo::g() {}
1588 void Foo::ssf() {}
1590 runSymbolCollector(Header, Main);
1591 EXPECT_THAT(
1592 Symbols,
1593 UnorderedElementsAre(
1594 qName("Foo"),
1595 AllOf(qName("Foo::f"), returnType(""), forCodeCompletion(false)),
1596 AllOf(qName("Foo::g"), returnType(""), forCodeCompletion(false)),
1597 AllOf(qName("Foo::sf"), returnType(""), forCodeCompletion(false)),
1598 AllOf(qName("Foo::ssf"), returnType(""), forCodeCompletion(false)),
1599 AllOf(qName("Foo::x"), returnType(""), forCodeCompletion(false))));
1602 TEST_F(SymbolCollectorTest, Scopes) {
1603 const std::string Header = R"(
1604 namespace na {
1605 class Foo {};
1606 namespace nb {
1607 class Bar {};
1611 runSymbolCollector(Header, /*Main=*/"");
1612 EXPECT_THAT(Symbols,
1613 UnorderedElementsAre(qName("na"), qName("na::nb"),
1614 qName("na::Foo"), qName("na::nb::Bar")));
1617 TEST_F(SymbolCollectorTest, ExternC) {
1618 const std::string Header = R"(
1619 extern "C" { class Foo {}; }
1620 namespace na {
1621 extern "C" { class Bar {}; }
1624 runSymbolCollector(Header, /*Main=*/"");
1625 EXPECT_THAT(Symbols, UnorderedElementsAre(qName("na"), qName("Foo"),
1626 qName("na::Bar")));
1629 TEST_F(SymbolCollectorTest, SkipInlineNamespace) {
1630 const std::string Header = R"(
1631 namespace na {
1632 inline namespace nb {
1633 class Foo {};
1636 namespace na {
1637 // This is still inlined.
1638 namespace nb {
1639 class Bar {};
1643 runSymbolCollector(Header, /*Main=*/"");
1644 EXPECT_THAT(Symbols,
1645 UnorderedElementsAre(qName("na"), qName("na::nb"),
1646 qName("na::Foo"), qName("na::Bar")));
1649 TEST_F(SymbolCollectorTest, SymbolWithDocumentation) {
1650 const std::string Header = R"(
1651 namespace nx {
1652 /// Foo comment.
1653 int ff(int x, double y) { return 0; }
1656 runSymbolCollector(Header, /*Main=*/"");
1657 EXPECT_THAT(
1658 Symbols,
1659 UnorderedElementsAre(
1660 qName("nx"), AllOf(qName("nx::ff"), labeled("ff(int x, double y)"),
1661 returnType("int"), doc("Foo comment."))));
1664 TEST_F(SymbolCollectorTest, snippet) {
1665 const std::string Header = R"(
1666 namespace nx {
1667 void f() {}
1668 int ff(int x, double y) { return 0; }
1671 runSymbolCollector(Header, /*Main=*/"");
1672 EXPECT_THAT(Symbols,
1673 UnorderedElementsAre(
1674 qName("nx"),
1675 AllOf(qName("nx::f"), labeled("f()"), snippet("f()")),
1676 AllOf(qName("nx::ff"), labeled("ff(int x, double y)"),
1677 snippet("ff(${1:int x}, ${2:double y})"))));
1680 TEST_F(SymbolCollectorTest, IncludeHeaderSameAsFileURI) {
1681 CollectorOpts.CollectIncludePath = true;
1682 runSymbolCollector("#pragma once\nclass Foo {};", /*Main=*/"");
1683 EXPECT_THAT(Symbols, UnorderedElementsAre(
1684 AllOf(qName("Foo"), declURI(TestHeaderURI))));
1685 EXPECT_THAT(Symbols.begin()->IncludeHeaders,
1686 UnorderedElementsAre(IncludeHeaderWithRef(TestHeaderURI, 1u)));
1689 TEST_F(SymbolCollectorTest, CanonicalSTLHeader) {
1690 CollectorOpts.CollectIncludePath = true;
1691 runSymbolCollector(
1692 R"cpp(
1693 namespace std {
1694 class string {};
1695 // Move overloads have special handling.
1696 template <typename _T> T&& move(_T&& __value);
1697 template <typename _I, typename _O> _O move(_I, _I, _O);
1698 template <typename _T, typename _O, typename _I> _O move(
1699 _T&&, _O, _O, _I);
1701 )cpp",
1702 /*Main=*/"");
1703 EXPECT_THAT(
1704 Symbols,
1705 UnorderedElementsAre(
1706 qName("std"),
1707 AllOf(qName("std::string"), declURI(TestHeaderURI),
1708 includeHeader("<string>")),
1709 // Parameter names are demangled.
1710 AllOf(labeled("move(T &&value)"), includeHeader("<utility>")),
1711 AllOf(labeled("move(I, I, O)"), includeHeader("<algorithm>")),
1712 AllOf(labeled("move(T &&, O, O, I)"), includeHeader("<algorithm>"))));
1715 TEST_F(SymbolCollectorTest, IWYUPragma) {
1716 CollectorOpts.CollectIncludePath = true;
1717 const std::string Header = R"(
1718 // IWYU pragma: private, include the/good/header.h
1719 class Foo {};
1721 runSymbolCollector(Header, /*Main=*/"");
1722 EXPECT_THAT(Symbols, UnorderedElementsAre(
1723 AllOf(qName("Foo"), declURI(TestHeaderURI),
1724 includeHeader("\"the/good/header.h\""))));
1727 TEST_F(SymbolCollectorTest, IWYUPragmaWithDoubleQuotes) {
1728 CollectorOpts.CollectIncludePath = true;
1729 const std::string Header = R"(
1730 // IWYU pragma: private, include "the/good/header.h"
1731 class Foo {};
1733 runSymbolCollector(Header, /*Main=*/"");
1734 EXPECT_THAT(Symbols, UnorderedElementsAre(
1735 AllOf(qName("Foo"), declURI(TestHeaderURI),
1736 includeHeader("\"the/good/header.h\""))));
1739 TEST_F(SymbolCollectorTest, IWYUPragmaExport) {
1740 CollectorOpts.CollectIncludePath = true;
1741 const std::string Header = R"cpp(#pragma once
1742 #include "exporter.h"
1743 )cpp";
1744 auto ExporterFile = testPath("exporter.h");
1745 InMemoryFileSystem->addFile(
1746 ExporterFile, 0, llvm::MemoryBuffer::getMemBuffer(R"cpp(#pragma once
1747 #include "private.h" // IWYU pragma: export
1748 )cpp"));
1749 auto PrivateFile = testPath("private.h");
1750 InMemoryFileSystem->addFile(
1751 PrivateFile, 0, llvm::MemoryBuffer::getMemBuffer("class Foo {};"));
1752 runSymbolCollector(Header, /*Main=*/"",
1753 /*ExtraArgs=*/{"-I", testRoot()});
1754 EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(
1755 qName("Foo"),
1756 includeHeader(URI::create(ExporterFile).toString()),
1757 declURI(URI::create(PrivateFile).toString()))));
1760 TEST_F(SymbolCollectorTest, MainFileIsHeaderWhenSkipIncFile) {
1761 CollectorOpts.CollectIncludePath = true;
1762 // To make this case as hard as possible, we won't tell clang main is a
1763 // header. No extension, no -x c++-header.
1764 TestFileName = testPath("no_ext_main");
1765 TestFileURI = URI::create(TestFileName).toString();
1766 auto IncFile = testPath("test.inc");
1767 auto IncURI = URI::create(IncFile).toString();
1768 InMemoryFileSystem->addFile(IncFile, 0,
1769 llvm::MemoryBuffer::getMemBuffer("class X {};"));
1770 runSymbolCollector("", R"cpp(
1771 // Can't use #pragma once in a main file clang doesn't think is a header.
1772 #ifndef MAIN_H_
1773 #define MAIN_H_
1774 #include "test.inc"
1775 #endif
1776 )cpp",
1777 /*ExtraArgs=*/{"-I", testRoot()});
1778 EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(qName("X"), declURI(IncURI),
1779 includeHeader(TestFileURI))));
1782 TEST_F(SymbolCollectorTest, IncFileInNonHeader) {
1783 CollectorOpts.CollectIncludePath = true;
1784 TestFileName = testPath("main.cc");
1785 TestFileURI = URI::create(TestFileName).toString();
1786 auto IncFile = testPath("test.inc");
1787 auto IncURI = URI::create(IncFile).toString();
1788 InMemoryFileSystem->addFile(IncFile, 0,
1789 llvm::MemoryBuffer::getMemBuffer("class X {};"));
1790 runSymbolCollector("", R"cpp(
1791 #include "test.inc"
1792 )cpp",
1793 /*ExtraArgs=*/{"-I", testRoot()});
1794 EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(qName("X"), declURI(IncURI),
1795 Not(includeHeader()))));
1798 // Features that depend on header-guards are fragile. Header guards are only
1799 // recognized when the file ends, so we have to defer checking for them.
1800 TEST_F(SymbolCollectorTest, HeaderGuardDetected) {
1801 CollectorOpts.CollectIncludePath = true;
1802 CollectorOpts.CollectMacro = true;
1803 runSymbolCollector(R"cpp(
1804 #ifndef HEADER_GUARD_
1805 #define HEADER_GUARD_
1807 // Symbols are seen before the header guard is complete.
1808 #define MACRO
1809 int decl();
1811 #endif // Header guard is recognized here.
1812 )cpp",
1813 "");
1814 EXPECT_THAT(Symbols, Not(Contains(qName("HEADER_GUARD_"))));
1815 EXPECT_THAT(Symbols, Each(includeHeader()));
1818 TEST_F(SymbolCollectorTest, NonModularHeader) {
1819 auto TU = TestTU::withHeaderCode("int x();");
1820 EXPECT_THAT(TU.headerSymbols(), ElementsAre(includeHeader()));
1822 // Files missing include guards aren't eligible for insertion.
1823 TU.ImplicitHeaderGuard = false;
1824 EXPECT_THAT(TU.headerSymbols(), ElementsAre(Not(includeHeader())));
1826 // We recognize some patterns of trying to prevent insertion.
1827 TU = TestTU::withHeaderCode(R"cpp(
1828 #ifndef SECRET
1829 #error "This file isn't safe to include directly"
1830 #endif
1831 int x();
1832 )cpp");
1833 TU.ExtraArgs.push_back("-DSECRET"); // *we're* able to include it.
1834 EXPECT_THAT(TU.headerSymbols(), ElementsAre(Not(includeHeader())));
1837 TEST_F(SymbolCollectorTest, AvoidUsingFwdDeclsAsCanonicalDecls) {
1838 CollectorOpts.CollectIncludePath = true;
1839 Annotations Header(R"(
1840 #pragma once
1841 // Forward declarations of TagDecls.
1842 class C;
1843 struct S;
1844 union U;
1846 // Canonical declarations.
1847 class $cdecl[[C]] {};
1848 struct $sdecl[[S]] {};
1849 union $udecl[[U]] {int $xdecl[[x]]; bool $ydecl[[y]];};
1850 )");
1851 runSymbolCollector(Header.code(), /*Main=*/"");
1852 EXPECT_THAT(
1853 Symbols,
1854 UnorderedElementsAre(
1855 AllOf(qName("C"), declURI(TestHeaderURI),
1856 declRange(Header.range("cdecl")), includeHeader(TestHeaderURI),
1857 defURI(TestHeaderURI), defRange(Header.range("cdecl"))),
1858 AllOf(qName("S"), declURI(TestHeaderURI),
1859 declRange(Header.range("sdecl")), includeHeader(TestHeaderURI),
1860 defURI(TestHeaderURI), defRange(Header.range("sdecl"))),
1861 AllOf(qName("U"), declURI(TestHeaderURI),
1862 declRange(Header.range("udecl")), includeHeader(TestHeaderURI),
1863 defURI(TestHeaderURI), defRange(Header.range("udecl"))),
1864 AllOf(qName("U::x"), declURI(TestHeaderURI),
1865 declRange(Header.range("xdecl")), defURI(TestHeaderURI),
1866 defRange(Header.range("xdecl"))),
1867 AllOf(qName("U::y"), declURI(TestHeaderURI),
1868 declRange(Header.range("ydecl")), defURI(TestHeaderURI),
1869 defRange(Header.range("ydecl")))));
1872 TEST_F(SymbolCollectorTest, ClassForwardDeclarationIsCanonical) {
1873 CollectorOpts.CollectIncludePath = true;
1874 runSymbolCollector(/*Header=*/"#pragma once\nclass X;",
1875 /*Main=*/"class X {};");
1876 EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(
1877 qName("X"), declURI(TestHeaderURI),
1878 includeHeader(TestHeaderURI), defURI(TestFileURI))));
1881 TEST_F(SymbolCollectorTest, UTF16Character) {
1882 // ö is 2-bytes.
1883 Annotations Header(/*Header=*/"class [[pörk]] {};");
1884 runSymbolCollector(Header.code(), /*Main=*/"");
1885 EXPECT_THAT(Symbols, UnorderedElementsAre(
1886 AllOf(qName("pörk"), declRange(Header.range()))));
1889 TEST_F(SymbolCollectorTest, DoNotIndexSymbolsInFriendDecl) {
1890 Annotations Header(R"(
1891 namespace nx {
1892 class $z[[Z]] {};
1893 class X {
1894 friend class Y;
1895 friend class Z;
1896 friend void foo();
1897 friend void $bar[[bar]]() {}
1899 class $y[[Y]] {};
1900 void $foo[[foo]]();
1902 )");
1903 runSymbolCollector(Header.code(), /*Main=*/"");
1905 EXPECT_THAT(Symbols,
1906 UnorderedElementsAre(
1907 qName("nx"), qName("nx::X"),
1908 AllOf(qName("nx::Y"), declRange(Header.range("y"))),
1909 AllOf(qName("nx::Z"), declRange(Header.range("z"))),
1910 AllOf(qName("nx::foo"), declRange(Header.range("foo"))),
1911 AllOf(qName("nx::bar"), declRange(Header.range("bar")))));
1914 TEST_F(SymbolCollectorTest, ReferencesInFriendDecl) {
1915 const std::string Header = R"(
1916 class X;
1917 class Y;
1919 const std::string Main = R"(
1920 class C {
1921 friend ::X;
1922 friend class Y;
1925 CollectorOpts.CountReferences = true;
1926 runSymbolCollector(Header, Main);
1927 EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(qName("X"), refCount(1)),
1928 AllOf(qName("Y"), refCount(1)),
1929 AllOf(qName("C"), refCount(0))));
1932 TEST_F(SymbolCollectorTest, Origin) {
1933 CollectorOpts.Origin = SymbolOrigin::Static;
1934 runSymbolCollector("class Foo {};", /*Main=*/"");
1935 EXPECT_THAT(Symbols, UnorderedElementsAre(
1936 Field(&Symbol::Origin, SymbolOrigin::Static)));
1937 InMemoryFileSystem = new llvm::vfs::InMemoryFileSystem;
1938 CollectorOpts.CollectMacro = true;
1939 runSymbolCollector("#define FOO", /*Main=*/"");
1940 EXPECT_THAT(Symbols, UnorderedElementsAre(
1941 Field(&Symbol::Origin, SymbolOrigin::Static)));
1944 TEST_F(SymbolCollectorTest, CollectMacros) {
1945 CollectorOpts.CollectIncludePath = true;
1946 Annotations Header(R"(
1947 #pragma once
1948 #define X 1
1949 #define $mac[[MAC]](x) int x
1950 #define $used[[USED]](y) float y;
1952 MAC(p);
1953 )");
1955 Annotations Main(R"(
1956 #define $main[[MAIN]] 1
1957 USED(t);
1958 )");
1959 CollectorOpts.CountReferences = true;
1960 CollectorOpts.CollectMacro = true;
1961 runSymbolCollector(Header.code(), Main.code());
1962 EXPECT_THAT(
1963 Symbols,
1964 UnorderedElementsAre(
1965 qName("p"), qName("t"),
1966 AllOf(qName("X"), declURI(TestHeaderURI),
1967 includeHeader(TestHeaderURI)),
1968 AllOf(labeled("MAC(x)"), refCount(0),
1970 declRange(Header.range("mac")), visibleOutsideFile()),
1971 AllOf(labeled("USED(y)"), refCount(1),
1972 declRange(Header.range("used")), visibleOutsideFile()),
1973 AllOf(labeled("MAIN"), refCount(0), declRange(Main.range("main")),
1974 Not(visibleOutsideFile()))));
1977 TEST_F(SymbolCollectorTest, DeprecatedSymbols) {
1978 const std::string Header = R"(
1979 void TestClangc() __attribute__((deprecated("", "")));
1980 void TestClangd();
1982 runSymbolCollector(Header, /**/ "");
1983 EXPECT_THAT(Symbols, UnorderedElementsAre(
1984 AllOf(qName("TestClangc"), deprecated()),
1985 AllOf(qName("TestClangd"), Not(deprecated()))));
1988 TEST_F(SymbolCollectorTest, implementationDetail) {
1989 const std::string Header = R"(
1990 #define DECL_NAME(x, y) x##_##y##_Decl
1991 #define DECL(x, y) class DECL_NAME(x, y) {};
1992 DECL(X, Y); // X_Y_Decl
1994 class Public {};
1996 runSymbolCollector(Header, /**/ "");
1997 EXPECT_THAT(Symbols,
1998 UnorderedElementsAre(
1999 AllOf(qName("X_Y_Decl"), implementationDetail()),
2000 AllOf(qName("Public"), Not(implementationDetail()))));
2003 TEST_F(SymbolCollectorTest, UsingDecl) {
2004 const char *Header = R"(
2005 void foo();
2006 namespace std {
2007 using ::foo;
2008 })";
2009 runSymbolCollector(Header, /**/ "");
2010 EXPECT_THAT(Symbols, Contains(qName("std::foo")));
2013 TEST_F(SymbolCollectorTest, CBuiltins) {
2014 // In C, printf in stdio.h is a redecl of an implicit builtin.
2015 const char *Header = R"(
2016 extern int printf(const char*, ...);
2018 runSymbolCollector(Header, /**/ "", {"-xc"});
2019 EXPECT_THAT(Symbols, Contains(qName("printf")));
2022 TEST_F(SymbolCollectorTest, InvalidSourceLoc) {
2023 const char *Header = R"(
2024 void operator delete(void*)
2025 __attribute__((__externally_visible__));)";
2026 runSymbolCollector(Header, /**/ "");
2027 EXPECT_THAT(Symbols, Contains(qName("operator delete")));
2030 TEST_F(SymbolCollectorTest, BadUTF8) {
2031 // Extracted from boost/spirit/home/support/char_encoding/iso8859_1.hpp
2032 // This looks like UTF-8 and fools clang, but has high-ISO-8859-1 comments.
2033 const char *Header = "int PUNCT = 0;\n"
2034 "/* \xa1 */ int types[] = { /* \xa1 */PUNCT };";
2035 CollectorOpts.RefFilter = RefKind::All;
2036 CollectorOpts.RefsInHeaders = true;
2037 runSymbolCollector(Header, "");
2038 EXPECT_THAT(Symbols, Contains(AllOf(qName("types"), doc("\xef\xbf\xbd "))));
2039 EXPECT_THAT(Symbols, Contains(qName("PUNCT")));
2040 // Reference is stored, although offset within line is not reliable.
2041 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "PUNCT").ID, _)));
2044 TEST_F(SymbolCollectorTest, MacrosInHeaders) {
2045 CollectorOpts.CollectMacro = true;
2046 TestFileName = testPath("test.h");
2047 runSymbolCollector("", "#define X");
2048 EXPECT_THAT(Symbols,
2049 UnorderedElementsAre(AllOf(qName("X"), forCodeCompletion(true))));
2052 // Regression test for a crash-bug we used to have.
2053 TEST_F(SymbolCollectorTest, UndefOfModuleMacro) {
2054 auto TU = TestTU::withCode(R"cpp(#include "bar.h")cpp");
2055 TU.AdditionalFiles["bar.h"] = R"cpp(
2056 #include "foo.h"
2057 #undef X
2058 )cpp";
2059 TU.AdditionalFiles["foo.h"] = "#define X 1";
2060 TU.AdditionalFiles["module.modulemap"] = R"cpp(
2061 module foo {
2062 header "foo.h"
2063 export *
2065 )cpp";
2066 TU.ExtraArgs.push_back("-fmodules");
2067 TU.ExtraArgs.push_back("-fmodule-map-file=" + testPath("module.modulemap"));
2068 TU.OverlayRealFileSystemForModules = true;
2070 TU.build();
2071 // We mostly care about not crashing, but verify that we didn't insert garbage
2072 // about X too.
2073 EXPECT_THAT(TU.headerSymbols(), Not(Contains(qName("X"))));
2076 TEST_F(SymbolCollectorTest, NoCrashOnObjCMethodCStyleParam) {
2077 auto TU = TestTU::withCode(R"objc(
2078 @interface Foo
2079 - (void)fun:(bool)foo, bool bar;
2080 @end
2081 )objc");
2082 TU.ExtraArgs.push_back("-xobjective-c++");
2084 TU.build();
2085 // We mostly care about not crashing.
2086 EXPECT_THAT(TU.headerSymbols(),
2087 UnorderedElementsAre(qName("Foo"), qName("Foo::fun:")));
2090 TEST_F(SymbolCollectorTest, Reserved) {
2091 const char *Header = R"cpp(
2092 #pragma once
2093 void __foo();
2094 namespace _X { int secret; }
2095 )cpp";
2097 CollectorOpts.CollectReserved = true;
2098 runSymbolCollector(Header, "");
2099 EXPECT_THAT(Symbols, UnorderedElementsAre(qName("__foo"), qName("_X"),
2100 qName("_X::secret")));
2102 CollectorOpts.CollectReserved = false;
2103 runSymbolCollector(Header, "");
2104 EXPECT_THAT(Symbols, UnorderedElementsAre(qName("__foo"), qName("_X"),
2105 qName("_X::secret")));
2107 // Ugly: for some reason we reuse the test filesystem across tests.
2108 // You can't overwrite the same filename with new content!
2109 InMemoryFileSystem = new llvm::vfs::InMemoryFileSystem;
2110 runSymbolCollector("#pragma GCC system_header\n" + std::string(Header), "");
2111 EXPECT_THAT(Symbols, IsEmpty());
2114 TEST_F(SymbolCollectorTest, Concepts) {
2115 const char *Header = R"cpp(
2116 template <class T>
2117 concept A = sizeof(T) <= 8;
2118 )cpp";
2119 runSymbolCollector("", Header, {"-std=c++20"});
2120 EXPECT_THAT(Symbols,
2121 UnorderedElementsAre(AllOf(
2122 qName("A"), hasKind(clang::index::SymbolKind::Concept))));
2125 TEST_F(SymbolCollectorTest, IncludeHeaderForwardDecls) {
2126 CollectorOpts.CollectIncludePath = true;
2127 const std::string Header = R"cpp(#pragma once
2128 struct Foo;
2129 #include "full.h"
2130 )cpp";
2131 auto FullFile = testPath("full.h");
2132 InMemoryFileSystem->addFile(FullFile, 0,
2133 llvm::MemoryBuffer::getMemBuffer(R"cpp(
2134 #pragma once
2135 struct Foo {};)cpp"));
2136 runSymbolCollector(Header, /*Main=*/"",
2137 /*ExtraArgs=*/{"-I", testRoot()});
2138 EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(
2139 qName("Foo"),
2140 includeHeader(URI::create(FullFile).toString()))))
2141 << *Symbols.begin();
2143 } // namespace
2144 } // namespace clangd
2145 } // namespace clang