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 //===----------------------------------------------------------------------===//
8
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/STLExtras.h"
21 #include "llvm/ADT/StringRef.h"
22 #include "llvm/Support/MemoryBuffer.h"
23 #include "llvm/Support/VirtualFileSystem.h"
24 #include "gmock/gmock-matchers.h"
25 #include "gmock/gmock-more-matchers.h"
26 #include "gmock/gmock.h"
27 #include "gtest/gtest.h"
28
29 #include <memory>
30 #include <string>
31
32 namespace clang {
33 namespace clangd {
34 namespace {
35
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;
47
48 // GMock helpers for matching Symbol.
49 MATCHER_P(Labeled, Label, "") {
50 return (arg.Name + arg.Signature).str() == Label;
51 }
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;
56 }
57 MATCHER_P(QName, Name, "") { return (arg.Scope + arg.Name).str() == Name; }
58 MATCHER_P(TemplateArgs, TemplArgs, "") {
59 return arg.TemplateSpecializationArgs == TemplArgs;
60 }
61 MATCHER_P(DeclURI, P, "") {
62 return StringRef(arg.CanonicalDeclaration.FileURI) == P;
63 }
64 MATCHER_P(DefURI, P, "") { return StringRef(arg.Definition.FileURI) == P; }
65 MATCHER(IncludeHeader, "") { return !arg.IncludeHeaders.empty(); }
66 MATCHER_P(IncludeHeader, P, "") {
67 return (arg.IncludeHeaders.size() == 1) &&
68 (arg.IncludeHeaders.begin()->IncludeHeader == P);
69 }
70 MATCHER_P2(IncludeHeaderWithRef, IncludeHeader, References, "") {
71 return (arg.IncludeHeader == IncludeHeader) && (arg.References == References);
72 }
rangesMatch(const SymbolLocation & Loc,const Range & R)73 bool rangesMatch(const SymbolLocation &Loc, const Range &R) {
74 return std::make_tuple(Loc.Start.line(), Loc.Start.column(), Loc.End.line(),
75 Loc.End.column()) ==
76 std::make_tuple(R.start.line, R.start.character, R.end.line,
77 R.end.character);
78 }
79 MATCHER_P(DeclRange, Pos, "") {
80 return rangesMatch(arg.CanonicalDeclaration, Pos);
81 }
82 MATCHER_P(DefRange, Pos, "") { return rangesMatch(arg.Definition, Pos); }
83 MATCHER_P(RefCount, R, "") { return int(arg.References) == R; }
84 MATCHER_P(ForCodeCompletion, IsIndexedForCodeCompletion, "") {
85 return static_cast<bool>(arg.Flags & Symbol::IndexedForCodeCompletion) ==
86 IsIndexedForCodeCompletion;
87 }
88 MATCHER(Deprecated, "") { return arg.Flags & Symbol::Deprecated; }
89 MATCHER(ImplementationDetail, "") {
90 return arg.Flags & Symbol::ImplementationDetail;
91 }
92 MATCHER(VisibleOutsideFile, "") {
93 return static_cast<bool>(arg.Flags & Symbol::VisibleOutsideFile);
94 }
95 MATCHER(RefRange, "") {
96 const Ref &Pos = ::testing::get<0>(arg);
97 const Range &Range = ::testing::get<1>(arg);
98 return rangesMatch(Pos.Location, Range);
99 }
100 MATCHER_P2(OverriddenBy, Subject, Object, "") {
101 return arg == Relation{Subject.ID, RelationKind::OverriddenBy, Object.ID};
102 }
103 ::testing::Matcher<const std::vector<Ref> &>
HaveRanges(const std::vector<Range> Ranges)104 HaveRanges(const std::vector<Range> Ranges) {
105 return ::testing::UnorderedPointwise(RefRange(), Ranges);
106 }
107
108 class ShouldCollectSymbolTest : public ::testing::Test {
109 public:
build(llvm::StringRef HeaderCode,llvm::StringRef Code="")110 void build(llvm::StringRef HeaderCode, llvm::StringRef Code = "") {
111 File.HeaderFilename = HeaderName;
112 File.Filename = FileName;
113 File.HeaderCode = std::string(HeaderCode);
114 File.Code = std::string(Code);
115 AST = File.build();
116 }
117
118 // build() must have been called.
shouldCollect(llvm::StringRef Name,bool Qualified=true)119 bool shouldCollect(llvm::StringRef Name, bool Qualified = true) {
120 assert(AST.hasValue());
121 const NamedDecl &ND =
122 Qualified ? findDecl(*AST, Name) : findUnqualifiedDecl(*AST, Name);
123 const SourceManager &SM = AST->getSourceManager();
124 bool MainFile = isInsideMainFile(ND.getBeginLoc(), SM);
125 return SymbolCollector::shouldCollectSymbol(
126 ND, AST->getASTContext(), SymbolCollector::Options(), MainFile);
127 }
128
129 protected:
130 std::string HeaderName = "f.h";
131 std::string FileName = "f.cpp";
132 TestTU File;
133 llvm::Optional<ParsedAST> AST; // Initialized after build.
134 };
135
TEST_F(ShouldCollectSymbolTest,ShouldCollectSymbol)136 TEST_F(ShouldCollectSymbolTest, ShouldCollectSymbol) {
137 build(R"(
138 namespace nx {
139 class X{};
140 auto f() { int Local; } // auto ensures function body is parsed.
141 struct { int x; } var;
142 }
143 )",
144 R"(
145 class InMain {};
146 namespace { class InAnonymous {}; }
147 static void g();
148 )");
149 auto AST = File.build();
150 EXPECT_TRUE(shouldCollect("nx"));
151 EXPECT_TRUE(shouldCollect("nx::X"));
152 EXPECT_TRUE(shouldCollect("nx::f"));
153 EXPECT_TRUE(shouldCollect("InMain"));
154 EXPECT_TRUE(shouldCollect("InAnonymous", /*Qualified=*/false));
155 EXPECT_TRUE(shouldCollect("g"));
156
157 EXPECT_FALSE(shouldCollect("Local", /*Qualified=*/false));
158 }
159
TEST_F(ShouldCollectSymbolTest,NoPrivateProtoSymbol)160 TEST_F(ShouldCollectSymbolTest, NoPrivateProtoSymbol) {
161 HeaderName = "f.proto.h";
162 build(
163 R"(// Generated by the protocol buffer compiler. DO NOT EDIT!
164 namespace nx {
165 class Top_Level {};
166 class TopLevel {};
167 enum Kind {
168 KIND_OK,
169 Kind_Not_Ok,
170 };
171 })");
172 EXPECT_TRUE(shouldCollect("nx::TopLevel"));
173 EXPECT_TRUE(shouldCollect("nx::Kind::KIND_OK"));
174 EXPECT_TRUE(shouldCollect("nx::Kind"));
175
176 EXPECT_FALSE(shouldCollect("nx::Top_Level"));
177 EXPECT_FALSE(shouldCollect("nx::Kind::Kind_Not_Ok"));
178 }
179
TEST_F(ShouldCollectSymbolTest,DoubleCheckProtoHeaderComment)180 TEST_F(ShouldCollectSymbolTest, DoubleCheckProtoHeaderComment) {
181 HeaderName = "f.proto.h";
182 build(R"(
183 namespace nx {
184 class Top_Level {};
185 enum Kind {
186 Kind_Fine
187 };
188 }
189 )");
190 EXPECT_TRUE(shouldCollect("nx::Top_Level"));
191 EXPECT_TRUE(shouldCollect("nx::Kind_Fine"));
192 }
193
194 class SymbolIndexActionFactory : public tooling::FrontendActionFactory {
195 public:
SymbolIndexActionFactory(SymbolCollector::Options COpts,CommentHandler * PragmaHandler)196 SymbolIndexActionFactory(SymbolCollector::Options COpts,
197 CommentHandler *PragmaHandler)
198 : COpts(std::move(COpts)), PragmaHandler(PragmaHandler) {}
199
create()200 std::unique_ptr<FrontendAction> create() override {
201 class IndexAction : public ASTFrontendAction {
202 public:
203 IndexAction(std::shared_ptr<index::IndexDataConsumer> DataConsumer,
204 const index::IndexingOptions &Opts,
205 CommentHandler *PragmaHandler)
206 : DataConsumer(std::move(DataConsumer)), Opts(Opts),
207 PragmaHandler(PragmaHandler) {}
208
209 std::unique_ptr<ASTConsumer>
210 CreateASTConsumer(CompilerInstance &CI, llvm::StringRef InFile) override {
211 if (PragmaHandler)
212 CI.getPreprocessor().addCommentHandler(PragmaHandler);
213 return createIndexingASTConsumer(DataConsumer, Opts,
214 CI.getPreprocessorPtr());
215 }
216
217 bool BeginInvocation(CompilerInstance &CI) override {
218 // Make the compiler parse all comments.
219 CI.getLangOpts().CommentOpts.ParseAllComments = true;
220 return true;
221 }
222
223 private:
224 std::shared_ptr<index::IndexDataConsumer> DataConsumer;
225 index::IndexingOptions Opts;
226 CommentHandler *PragmaHandler;
227 };
228 index::IndexingOptions IndexOpts;
229 IndexOpts.SystemSymbolFilter =
230 index::IndexingOptions::SystemSymbolFilterKind::All;
231 IndexOpts.IndexFunctionLocals = false;
232 Collector = std::make_shared<SymbolCollector>(COpts);
233 return std::make_unique<IndexAction>(Collector, std::move(IndexOpts),
234 PragmaHandler);
235 }
236
237 std::shared_ptr<SymbolCollector> Collector;
238 SymbolCollector::Options COpts;
239 CommentHandler *PragmaHandler;
240 };
241
242 class SymbolCollectorTest : public ::testing::Test {
243 public:
SymbolCollectorTest()244 SymbolCollectorTest()
245 : InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem),
246 TestHeaderName(testPath("symbol.h")),
247 TestFileName(testPath("symbol.cc")) {
248 TestHeaderURI = URI::create(TestHeaderName).toString();
249 TestFileURI = URI::create(TestFileName).toString();
250 }
251
252 // Note that unlike TestTU, no automatic header guard is added.
253 // HeaderCode should start with #pragma once to be treated as modular.
runSymbolCollector(llvm::StringRef HeaderCode,llvm::StringRef MainCode,const std::vector<std::string> & ExtraArgs={})254 bool runSymbolCollector(llvm::StringRef HeaderCode, llvm::StringRef MainCode,
255 const std::vector<std::string> &ExtraArgs = {}) {
256 llvm::IntrusiveRefCntPtr<FileManager> Files(
257 new FileManager(FileSystemOptions(), InMemoryFileSystem));
258
259 auto Factory = std::make_unique<SymbolIndexActionFactory>(
260 CollectorOpts, PragmaHandler.get());
261
262 std::vector<std::string> Args = {"symbol_collector", "-fsyntax-only",
263 "-xc++", "-include", TestHeaderName};
264 Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
265 // This allows to override the "-xc++" with something else, i.e.
266 // -xobjective-c++.
267 Args.push_back(TestFileName);
268
269 tooling::ToolInvocation Invocation(
270 Args, Factory->create(), Files.get(),
271 std::make_shared<PCHContainerOperations>());
272
273 InMemoryFileSystem->addFile(TestHeaderName, 0,
274 llvm::MemoryBuffer::getMemBuffer(HeaderCode));
275 InMemoryFileSystem->addFile(TestFileName, 0,
276 llvm::MemoryBuffer::getMemBuffer(MainCode));
277 Invocation.run();
278 Symbols = Factory->Collector->takeSymbols();
279 Refs = Factory->Collector->takeRefs();
280 Relations = Factory->Collector->takeRelations();
281 return true;
282 }
283
284 protected:
285 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem;
286 std::string TestHeaderName;
287 std::string TestHeaderURI;
288 std::string TestFileName;
289 std::string TestFileURI;
290 SymbolSlab Symbols;
291 RefSlab Refs;
292 RelationSlab Relations;
293 SymbolCollector::Options CollectorOpts;
294 std::unique_ptr<CommentHandler> PragmaHandler;
295 };
296
TEST_F(SymbolCollectorTest,CollectSymbols)297 TEST_F(SymbolCollectorTest, CollectSymbols) {
298 const std::string Header = R"(
299 class Foo {
300 Foo() {}
301 Foo(int a) {}
302 void f();
303 friend void f1();
304 friend class Friend;
305 Foo& operator=(const Foo&);
306 ~Foo();
307 class Nested {
308 void f();
309 };
310 };
311 class Friend {
312 };
313
314 void f1();
315 inline void f2() {}
316 static const int KInt = 2;
317 const char* kStr = "123";
318
319 namespace {
320 void ff() {} // ignore
321 }
322
323 void f1() {}
324
325 namespace foo {
326 // Type alias
327 typedef int int32;
328 using int32_t = int32;
329
330 // Variable
331 int v1;
332
333 // Namespace
334 namespace bar {
335 int v2;
336 }
337 // Namespace alias
338 namespace baz = bar;
339
340 using bar::v2;
341 } // namespace foo
342 )";
343 runSymbolCollector(Header, /*Main=*/"");
344 EXPECT_THAT(Symbols,
345 UnorderedElementsAreArray(
346 {AllOf(QName("Foo"), ForCodeCompletion(true)),
347 AllOf(QName("Foo::Foo"), ForCodeCompletion(false)),
348 AllOf(QName("Foo::Foo"), ForCodeCompletion(false)),
349 AllOf(QName("Foo::f"), ForCodeCompletion(false)),
350 AllOf(QName("Foo::~Foo"), ForCodeCompletion(false)),
351 AllOf(QName("Foo::operator="), ForCodeCompletion(false)),
352 AllOf(QName("Foo::Nested"), ForCodeCompletion(false)),
353 AllOf(QName("Foo::Nested::f"), ForCodeCompletion(false)),
354
355 AllOf(QName("Friend"), ForCodeCompletion(true)),
356 AllOf(QName("f1"), ForCodeCompletion(true)),
357 AllOf(QName("f2"), ForCodeCompletion(true)),
358 AllOf(QName("KInt"), ForCodeCompletion(true)),
359 AllOf(QName("kStr"), ForCodeCompletion(true)),
360 AllOf(QName("foo"), ForCodeCompletion(true)),
361 AllOf(QName("foo::bar"), ForCodeCompletion(true)),
362 AllOf(QName("foo::int32"), ForCodeCompletion(true)),
363 AllOf(QName("foo::int32_t"), ForCodeCompletion(true)),
364 AllOf(QName("foo::v1"), ForCodeCompletion(true)),
365 AllOf(QName("foo::bar::v2"), ForCodeCompletion(true)),
366 AllOf(QName("foo::v2"), ForCodeCompletion(true)),
367 AllOf(QName("foo::baz"), ForCodeCompletion(true))}));
368 }
369
TEST_F(SymbolCollectorTest,FileLocal)370 TEST_F(SymbolCollectorTest, FileLocal) {
371 const std::string Header = R"(
372 class Foo {};
373 namespace {
374 class Ignored {};
375 }
376 void bar();
377 )";
378 const std::string Main = R"(
379 class ForwardDecl;
380 void bar() {}
381 static void a();
382 class B {};
383 namespace {
384 void c();
385 }
386 )";
387 runSymbolCollector(Header, Main);
388 EXPECT_THAT(Symbols,
389 UnorderedElementsAre(
390 AllOf(QName("Foo"), VisibleOutsideFile()),
391 AllOf(QName("bar"), VisibleOutsideFile()),
392 AllOf(QName("a"), Not(VisibleOutsideFile())),
393 AllOf(QName("B"), Not(VisibleOutsideFile())),
394 AllOf(QName("c"), Not(VisibleOutsideFile())),
395 // FIXME: ForwardDecl likely *is* visible outside.
396 AllOf(QName("ForwardDecl"), Not(VisibleOutsideFile()))));
397 }
398
TEST_F(SymbolCollectorTest,Template)399 TEST_F(SymbolCollectorTest, Template) {
400 Annotations Header(R"(
401 // Primary template and explicit specialization are indexed, instantiation
402 // is not.
403 template <class T, class U> struct [[Tmpl]] {T $xdecl[[x]] = 0;};
404 template <> struct $specdecl[[Tmpl]]<int, bool> {};
405 template <class U> struct $partspecdecl[[Tmpl]]<bool, U> {};
406 extern template struct Tmpl<float, bool>;
407 template struct Tmpl<double, bool>;
408 )");
409 runSymbolCollector(Header.code(), /*Main=*/"");
410 EXPECT_THAT(Symbols,
411 UnorderedElementsAre(
412 AllOf(QName("Tmpl"), DeclRange(Header.range()),
413 ForCodeCompletion(true)),
414 AllOf(QName("Tmpl"), DeclRange(Header.range("specdecl")),
415 ForCodeCompletion(false)),
416 AllOf(QName("Tmpl"), DeclRange(Header.range("partspecdecl")),
417 ForCodeCompletion(false)),
418 AllOf(QName("Tmpl::x"), DeclRange(Header.range("xdecl")),
419 ForCodeCompletion(false))));
420 }
421
TEST_F(SymbolCollectorTest,TemplateArgs)422 TEST_F(SymbolCollectorTest, TemplateArgs) {
423 Annotations Header(R"(
424 template <class X> class $barclasstemp[[Bar]] {};
425 template <class T, class U, template<typename> class Z, int Q>
426 struct [[Tmpl]] { T $xdecl[[x]] = 0; };
427
428 // template-template, non-type and type full spec
429 template <> struct $specdecl[[Tmpl]]<int, bool, Bar, 3> {};
430
431 // template-template, non-type and type partial spec
432 template <class U, int T> struct $partspecdecl[[Tmpl]]<bool, U, Bar, T> {};
433 // instantiation
434 extern template struct Tmpl<float, bool, Bar, 8>;
435 // instantiation
436 template struct Tmpl<double, bool, Bar, 2>;
437
438 template <typename ...> class $fooclasstemp[[Foo]] {};
439 // parameter-packs full spec
440 template<> class $parampack[[Foo]]<Bar<int>, int, double> {};
441 // parameter-packs partial spec
442 template<class T> class $parampackpartial[[Foo]]<T, T> {};
443
444 template <int ...> class $bazclasstemp[[Baz]] {};
445 // non-type parameter-packs full spec
446 template<> class $parampacknontype[[Baz]]<3, 5, 8> {};
447 // non-type parameter-packs partial spec
448 template<int T> class $parampacknontypepartial[[Baz]]<T, T> {};
449
450 template <template <class> class ...> class $fozclasstemp[[Foz]] {};
451 // template-template parameter-packs full spec
452 template<> class $parampacktempltempl[[Foz]]<Bar, Bar> {};
453 // template-template parameter-packs partial spec
454 template<template <class> class T>
455 class $parampacktempltemplpartial[[Foz]]<T, T> {};
456 )");
457 runSymbolCollector(Header.code(), /*Main=*/"");
458 EXPECT_THAT(
459 Symbols,
460 AllOf(
461 Contains(AllOf(QName("Tmpl"), TemplateArgs("<int, bool, Bar, 3>"),
462 DeclRange(Header.range("specdecl")),
463 ForCodeCompletion(false))),
464 Contains(AllOf(QName("Tmpl"), TemplateArgs("<bool, U, Bar, T>"),
465 DeclRange(Header.range("partspecdecl")),
466 ForCodeCompletion(false))),
467 Contains(AllOf(QName("Foo"), TemplateArgs("<Bar<int>, int, double>"),
468 DeclRange(Header.range("parampack")),
469 ForCodeCompletion(false))),
470 Contains(AllOf(QName("Foo"), TemplateArgs("<T, T>"),
471 DeclRange(Header.range("parampackpartial")),
472 ForCodeCompletion(false))),
473 Contains(AllOf(QName("Baz"), TemplateArgs("<3, 5, 8>"),
474 DeclRange(Header.range("parampacknontype")),
475 ForCodeCompletion(false))),
476 Contains(AllOf(QName("Baz"), TemplateArgs("<T, T>"),
477 DeclRange(Header.range("parampacknontypepartial")),
478 ForCodeCompletion(false))),
479 Contains(AllOf(QName("Foz"), TemplateArgs("<Bar, Bar>"),
480 DeclRange(Header.range("parampacktempltempl")),
481 ForCodeCompletion(false))),
482 Contains(AllOf(QName("Foz"), TemplateArgs("<T, T>"),
483 DeclRange(Header.range("parampacktempltemplpartial")),
484 ForCodeCompletion(false)))));
485 }
486
TEST_F(SymbolCollectorTest,ObjCSymbols)487 TEST_F(SymbolCollectorTest, ObjCSymbols) {
488 const std::string Header = R"(
489 @interface Person
490 - (void)someMethodName:(void*)name1 lastName:(void*)lName;
491 @end
492
493 @implementation Person
494 - (void)someMethodName:(void*)name1 lastName:(void*)lName{
495 int foo;
496 ^(int param){ int bar; };
497 }
498 @end
499
500 @interface Person (MyCategory)
501 - (void)someMethodName2:(void*)name2;
502 @end
503
504 @implementation Person (MyCategory)
505 - (void)someMethodName2:(void*)name2 {
506 int foo2;
507 }
508 @end
509
510 @protocol MyProtocol
511 - (void)someMethodName3:(void*)name3;
512 @end
513 )";
514 TestFileName = testPath("test.m");
515 runSymbolCollector(Header, /*Main=*/"", {"-fblocks", "-xobjective-c++"});
516 EXPECT_THAT(Symbols,
517 UnorderedElementsAre(
518 QName("Person"), QName("Person::someMethodName:lastName:"),
519 QName("MyCategory"), QName("Person::someMethodName2:"),
520 QName("MyProtocol"), QName("MyProtocol::someMethodName3:")));
521 }
522
TEST_F(SymbolCollectorTest,ObjCPropertyImpl)523 TEST_F(SymbolCollectorTest, ObjCPropertyImpl) {
524 const std::string Header = R"(
525 @interface Container
526 @property(nonatomic) int magic;
527 @end
528
529 @implementation Container
530 @end
531 )";
532 TestFileName = testPath("test.m");
533 runSymbolCollector(Header, /*Main=*/"", {"-xobjective-c++"});
534 EXPECT_THAT(Symbols, Contains(QName("Container")));
535 EXPECT_THAT(Symbols, Contains(QName("Container::magic")));
536 // FIXME: Results also contain Container::_magic on some platforms.
537 // Figure out why it's platform-dependent.
538 }
539
TEST_F(SymbolCollectorTest,ObjCLocations)540 TEST_F(SymbolCollectorTest, ObjCLocations) {
541 Annotations Header(R"(
542 // Declared in header, defined in main.
543 @interface $dogdecl[[Dog]]
544 @end
545 @interface $fluffydecl[[Dog]] (Fluffy)
546 @end
547 )");
548 Annotations Main(R"(
549 @interface Dog ()
550 @end
551 @implementation $dogdef[[Dog]]
552 @end
553 @implementation $fluffydef[[Dog]] (Fluffy)
554 @end
555 // Category with no declaration (only implementation).
556 @implementation $ruff[[Dog]] (Ruff)
557 @end
558 // Implicitly defined interface.
559 @implementation $catdog[[CatDog]]
560 @end
561 )");
562 runSymbolCollector(Header.code(), Main.code(),
563 {"-xobjective-c++", "-Wno-objc-root-class"});
564 EXPECT_THAT(Symbols,
565 UnorderedElementsAre(
566 AllOf(QName("Dog"), DeclRange(Header.range("dogdecl")),
567 DefRange(Main.range("dogdef"))),
568 AllOf(QName("Fluffy"), DeclRange(Header.range("fluffydecl")),
569 DefRange(Main.range("fluffydef"))),
570 AllOf(QName("CatDog"), DeclRange(Main.range("catdog")),
571 DefRange(Main.range("catdog"))),
572 AllOf(QName("Ruff"), DeclRange(Main.range("ruff")),
573 DefRange(Main.range("ruff")))));
574 }
575
TEST_F(SymbolCollectorTest,ObjCForwardDecls)576 TEST_F(SymbolCollectorTest, ObjCForwardDecls) {
577 Annotations Header(R"(
578 // Forward declared in header, declared and defined in main.
579 @protocol Barker;
580 @class Dog;
581 // Never fully declared so Clang latches onto this decl.
582 @class $catdogdecl[[CatDog]];
583 )");
584 Annotations Main(R"(
585 @protocol $barkerdecl[[Barker]]
586 - (void)woof;
587 @end
588 @interface $dogdecl[[Dog]]<Barker>
589 - (void)woof;
590 @end
591 @implementation $dogdef[[Dog]]
592 - (void)woof {}
593 @end
594 @implementation $catdogdef[[CatDog]]
595 @end
596 )");
597 runSymbolCollector(Header.code(), Main.code(),
598 {"-xobjective-c++", "-Wno-objc-root-class"});
599 EXPECT_THAT(Symbols,
600 UnorderedElementsAre(
601 AllOf(QName("CatDog"), DeclRange(Header.range("catdogdecl")),
602 DefRange(Main.range("catdogdef"))),
603 AllOf(QName("Dog"), DeclRange(Main.range("dogdecl")),
604 DefRange(Main.range("dogdef"))),
605 AllOf(QName("Barker"), DeclRange(Main.range("barkerdecl"))),
606 QName("Barker::woof"), QName("Dog::woof")));
607 }
608
TEST_F(SymbolCollectorTest,ObjCClassExtensions)609 TEST_F(SymbolCollectorTest, ObjCClassExtensions) {
610 Annotations Header(R"(
611 @interface $catdecl[[Cat]]
612 @end
613 )");
614 Annotations Main(R"(
615 @interface Cat ()
616 - (void)meow;
617 @end
618 @interface Cat ()
619 - (void)pur;
620 @end
621 )");
622 runSymbolCollector(Header.code(), Main.code(),
623 {"-xobjective-c++", "-Wno-objc-root-class"});
624 EXPECT_THAT(Symbols,
625 UnorderedElementsAre(
626 AllOf(QName("Cat"), DeclRange(Header.range("catdecl"))),
627 QName("Cat::meow"), QName("Cat::pur")));
628 }
629
TEST_F(SymbolCollectorTest,Locations)630 TEST_F(SymbolCollectorTest, Locations) {
631 Annotations Header(R"cpp(
632 // Declared in header, defined in main.
633 extern int $xdecl[[X]];
634 class $clsdecl[[Cls]];
635 void $printdecl[[print]]();
636
637 // Declared in header, defined nowhere.
638 extern int $zdecl[[Z]];
639
640 void $foodecl[[fo\
641 o]]();
642 )cpp");
643 Annotations Main(R"cpp(
644 int $xdef[[X]] = 42;
645 class $clsdef[[Cls]] {};
646 void $printdef[[print]]() {}
647
648 // Declared/defined in main only.
649 int $ydecl[[Y]];
650 )cpp");
651 runSymbolCollector(Header.code(), Main.code());
652 EXPECT_THAT(Symbols,
653 UnorderedElementsAre(
654 AllOf(QName("X"), DeclRange(Header.range("xdecl")),
655 DefRange(Main.range("xdef"))),
656 AllOf(QName("Cls"), DeclRange(Header.range("clsdecl")),
657 DefRange(Main.range("clsdef"))),
658 AllOf(QName("print"), DeclRange(Header.range("printdecl")),
659 DefRange(Main.range("printdef"))),
660 AllOf(QName("Z"), DeclRange(Header.range("zdecl"))),
661 AllOf(QName("foo"), DeclRange(Header.range("foodecl"))),
662 AllOf(QName("Y"), DeclRange(Main.range("ydecl")))));
663 }
664
TEST_F(SymbolCollectorTest,Refs)665 TEST_F(SymbolCollectorTest, Refs) {
666 Annotations Header(R"(
667 #define MACRO(X) (X + 1)
668 class Foo {
669 public:
670 Foo() {}
671 Foo(int);
672 };
673 class Bar;
674 void func();
675
676 namespace NS {} // namespace ref is ignored
677 )");
678 Annotations Main(R"(
679 class $bar[[Bar]] {};
680
681 void $func[[func]]();
682
683 void fff() {
684 $foo[[Foo]] foo;
685 $bar[[Bar]] bar;
686 $func[[func]]();
687 int abc = 0;
688 $foo[[Foo]] foo2 = abc;
689 abc = $macro[[MACRO]](1);
690 }
691 )");
692 Annotations SymbolsOnlyInMainCode(R"(
693 #define FUNC(X) (X+1)
694 int a;
695 void b() {}
696 static const int c = FUNC(1);
697 class d {};
698 )");
699 CollectorOpts.RefFilter = RefKind::All;
700 CollectorOpts.CollectMacro = true;
701 runSymbolCollector(Header.code(),
702 (Main.code() + SymbolsOnlyInMainCode.code()).str());
703 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Foo").ID,
704 HaveRanges(Main.ranges("foo")))));
705 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Bar").ID,
706 HaveRanges(Main.ranges("bar")))));
707 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "func").ID,
708 HaveRanges(Main.ranges("func")))));
709 EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(Symbols, "NS").ID, _))));
710 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "MACRO").ID,
711 HaveRanges(Main.ranges("macro")))));
712 // - (a, b) externally visible and should have refs.
713 // - (c, FUNC) externally invisible and had no refs collected.
714 auto MainSymbols =
715 TestTU::withHeaderCode(SymbolsOnlyInMainCode.code()).headerSymbols();
716 EXPECT_THAT(Refs, Contains(Pair(findSymbol(MainSymbols, "a").ID, _)));
717 EXPECT_THAT(Refs, Contains(Pair(findSymbol(MainSymbols, "b").ID, _)));
718 EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(MainSymbols, "c").ID, _))));
719 EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(MainSymbols, "FUNC").ID, _))));
720
721 // Run the collector again with CollectMainFileRefs = true.
722 // We need to recreate InMemoryFileSystem because runSymbolCollector()
723 // calls MemoryBuffer::getMemBuffer(), which makes the buffers unusable
724 // after runSymbolCollector() exits.
725 InMemoryFileSystem = new llvm::vfs::InMemoryFileSystem();
726 CollectorOpts.CollectMainFileRefs = true;
727 runSymbolCollector(Header.code(),
728 (Main.code() + SymbolsOnlyInMainCode.code()).str());
729 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "a").ID, _)));
730 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "b").ID, _)));
731 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "c").ID, _)));
732 // However, references to main-file macros are not collected.
733 EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(Symbols, "FUNC").ID, _))));
734 }
735
TEST_F(SymbolCollectorTest,RefContainers)736 TEST_F(SymbolCollectorTest, RefContainers) {
737 Annotations Code(R"cpp(
738 int $toplevel1[[f1]](int);
739 void f2() {
740 (void) $ref1a[[f1]](1);
741 auto fptr = &$ref1b[[f1]];
742 }
743 int $toplevel2[[v1]] = $ref2[[f1]](2);
744 void f3(int arg = $ref3[[f1]](3));
745 struct S1 {
746 int $classscope1[[member1]] = $ref4[[f1]](4);
747 int $classscope2[[member2]] = 42;
748 };
749 constexpr int f4(int x) { return x + 1; }
750 template <int I = $ref5[[f4]](0)> struct S2 {};
751 S2<$ref6[[f4]](0)> v2;
752 S2<$ref7a[[f4]](0)> f5(S2<$ref7b[[f4]](0)>);
753 namespace N {
754 void $namespacescope1[[f6]]();
755 int $namespacescope2[[v3]];
756 }
757 )cpp");
758 CollectorOpts.RefFilter = RefKind::All;
759 CollectorOpts.CollectMainFileRefs = true;
760 runSymbolCollector("", Code.code());
761 auto FindRefWithRange = [&](Range R) -> Optional<Ref> {
762 for (auto &Entry : Refs) {
763 for (auto &Ref : Entry.second) {
764 if (rangesMatch(Ref.Location, R))
765 return Ref;
766 }
767 }
768 return llvm::None;
769 };
770 auto Container = [&](llvm::StringRef RangeName) {
771 auto Ref = FindRefWithRange(Code.range(RangeName));
772 EXPECT_TRUE(bool(Ref));
773 return Ref->Container;
774 };
775 EXPECT_EQ(Container("ref1a"),
776 findSymbol(Symbols, "f2").ID); // function body (call)
777 EXPECT_EQ(Container("ref1b"),
778 findSymbol(Symbols, "f2").ID); // function body (address-of)
779 EXPECT_EQ(Container("ref2"),
780 findSymbol(Symbols, "v1").ID); // variable initializer
781 EXPECT_EQ(Container("ref3"),
782 findSymbol(Symbols, "f3").ID); // function parameter default value
783 EXPECT_EQ(Container("ref4"),
784 findSymbol(Symbols, "S1::member1").ID); // member initializer
785 EXPECT_EQ(Container("ref5"),
786 findSymbol(Symbols, "S2").ID); // template parameter default value
787 EXPECT_EQ(Container("ref6"),
788 findSymbol(Symbols, "v2").ID); // type of variable
789 EXPECT_EQ(Container("ref7a"),
790 findSymbol(Symbols, "f5").ID); // return type of function
791 EXPECT_EQ(Container("ref7b"),
792 findSymbol(Symbols, "f5").ID); // parameter type of function
793
794 EXPECT_FALSE(Container("classscope1").isNull());
795 EXPECT_FALSE(Container("namespacescope1").isNull());
796
797 EXPECT_EQ(Container("toplevel1"), Container("toplevel2"));
798 EXPECT_EQ(Container("classscope1"), Container("classscope2"));
799 EXPECT_EQ(Container("namespacescope1"), Container("namespacescope2"));
800
801 EXPECT_NE(Container("toplevel1"), Container("namespacescope1"));
802 EXPECT_NE(Container("toplevel1"), Container("classscope1"));
803 EXPECT_NE(Container("classscope1"), Container("namespacescope1"));
804 }
805
TEST_F(SymbolCollectorTest,MacroRefInHeader)806 TEST_F(SymbolCollectorTest, MacroRefInHeader) {
807 Annotations Header(R"(
808 #define $foo[[FOO]](X) (X + 1)
809 #define $bar[[BAR]](X) (X + 2)
810
811 // Macro defined multiple times.
812 #define $ud1[[UD]] 1
813 int ud_1 = $ud1[[UD]];
814 #undef UD
815
816 #define $ud2[[UD]] 2
817 int ud_2 = $ud2[[UD]];
818 #undef UD
819
820 // Macros from token concatenations not included.
821 #define $concat[[CONCAT]](X) X##A()
822 #define $prepend[[PREPEND]](X) MACRO##X()
823 #define $macroa[[MACROA]]() 123
824 int B = $concat[[CONCAT]](MACRO);
825 int D = $prepend[[PREPEND]](A);
826
827 void fff() {
828 int abc = $foo[[FOO]](1) + $bar[[BAR]]($foo[[FOO]](1));
829 }
830 )");
831 CollectorOpts.RefFilter = RefKind::All;
832 CollectorOpts.RefsInHeaders = true;
833 // Need this to get the SymbolID for macros for tests.
834 CollectorOpts.CollectMacro = true;
835
836 runSymbolCollector(Header.code(), "");
837
838 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "FOO").ID,
839 HaveRanges(Header.ranges("foo")))));
840 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "BAR").ID,
841 HaveRanges(Header.ranges("bar")))));
842 // No unique ID for multiple symbols named UD. Check for ranges only.
843 EXPECT_THAT(Refs, Contains(Pair(_, HaveRanges(Header.ranges("ud1")))));
844 EXPECT_THAT(Refs, Contains(Pair(_, HaveRanges(Header.ranges("ud2")))));
845 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "CONCAT").ID,
846 HaveRanges(Header.ranges("concat")))));
847 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "PREPEND").ID,
848 HaveRanges(Header.ranges("prepend")))));
849 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "MACROA").ID,
850 HaveRanges(Header.ranges("macroa")))));
851 }
852
TEST_F(SymbolCollectorTest,MacroRefWithoutCollectingSymbol)853 TEST_F(SymbolCollectorTest, MacroRefWithoutCollectingSymbol) {
854 Annotations Header(R"(
855 #define $foo[[FOO]](X) (X + 1)
856 int abc = $foo[[FOO]](1);
857 )");
858 CollectorOpts.RefFilter = RefKind::All;
859 CollectorOpts.RefsInHeaders = true;
860 CollectorOpts.CollectMacro = false;
861 runSymbolCollector(Header.code(), "");
862 EXPECT_THAT(Refs, Contains(Pair(_, HaveRanges(Header.ranges("foo")))));
863 }
864
TEST_F(SymbolCollectorTest,MacrosWithRefFilter)865 TEST_F(SymbolCollectorTest, MacrosWithRefFilter) {
866 Annotations Header("#define $macro[[MACRO]](X) (X + 1)");
867 Annotations Main("void foo() { int x = $macro[[MACRO]](1); }");
868 CollectorOpts.RefFilter = RefKind::Unknown;
869 runSymbolCollector(Header.code(), Main.code());
870 EXPECT_THAT(Refs, IsEmpty());
871 }
872
TEST_F(SymbolCollectorTest,SpelledReferences)873 TEST_F(SymbolCollectorTest, SpelledReferences) {
874 struct {
875 llvm::StringRef Header;
876 llvm::StringRef Main;
877 llvm::StringRef TargetSymbolName;
878 } TestCases[] = {
879 {
880 R"cpp(
881 struct Foo;
882 #define MACRO Foo
883 )cpp",
884 R"cpp(
885 struct $spelled[[Foo]] {
886 $spelled[[Foo]]();
887 ~$spelled[[Foo]]();
888 };
889 $spelled[[Foo]] Variable1;
890 $implicit[[MACRO]] Variable2;
891 )cpp",
892 "Foo",
893 },
894 {
895 R"cpp(
896 class Foo {
897 public:
898 Foo() = default;
899 };
900 )cpp",
901 R"cpp(
902 void f() { Foo $implicit[[f]]; f = $spelled[[Foo]]();}
903 )cpp",
904 "Foo::Foo" /// constructor.
905 },
906 };
907 CollectorOpts.RefFilter = RefKind::All;
908 CollectorOpts.RefsInHeaders = false;
909 for (const auto& T : TestCases) {
910 Annotations Header(T.Header);
911 Annotations Main(T.Main);
912 // Reset the file system.
913 InMemoryFileSystem = new llvm::vfs::InMemoryFileSystem;
914 runSymbolCollector(Header.code(), Main.code());
915
916 const auto SpelledRanges = Main.ranges("spelled");
917 const auto ImplicitRanges = Main.ranges("implicit");
918 RefSlab::Builder SpelledSlabBuilder, ImplicitSlabBuilder;
919 const auto TargetID = findSymbol(Symbols, T.TargetSymbolName).ID;
920 for (const auto &SymbolAndRefs : Refs) {
921 const auto ID = SymbolAndRefs.first;
922 if (ID != TargetID)
923 continue;
924 for (const auto &Ref : SymbolAndRefs.second)
925 if ((Ref.Kind & RefKind::Spelled) != RefKind::Unknown)
926 SpelledSlabBuilder.insert(ID, Ref);
927 else
928 ImplicitSlabBuilder.insert(ID, Ref);
929 }
930 const auto SpelledRefs = std::move(SpelledSlabBuilder).build(),
931 ImplicitRefs = std::move(ImplicitSlabBuilder).build();
932 EXPECT_THAT(SpelledRefs,
933 Contains(Pair(TargetID, HaveRanges(SpelledRanges))));
934 EXPECT_THAT(ImplicitRefs,
935 Contains(Pair(TargetID, HaveRanges(ImplicitRanges))));
936 }
937 }
938
TEST_F(SymbolCollectorTest,NameReferences)939 TEST_F(SymbolCollectorTest, NameReferences) {
940 CollectorOpts.RefFilter = RefKind::All;
941 CollectorOpts.RefsInHeaders = true;
942 Annotations Header(R"(
943 class [[Foo]] {
944 public:
945 [[Foo]]() {}
946 ~[[Foo]]() {}
947 };
948 )");
949 CollectorOpts.RefFilter = RefKind::All;
950 runSymbolCollector(Header.code(), "");
951 // When we find references for class Foo, we expect to see all
952 // constructor/destructor references.
953 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Foo").ID,
954 HaveRanges(Header.ranges()))));
955 }
956
TEST_F(SymbolCollectorTest,RefsOnMacros)957 TEST_F(SymbolCollectorTest, RefsOnMacros) {
958 // Refs collected from SymbolCollector behave in the same way as
959 // AST-based xrefs.
960 CollectorOpts.RefFilter = RefKind::All;
961 CollectorOpts.RefsInHeaders = true;
962 Annotations Header(R"(
963 #define TYPE(X) X
964 #define FOO Foo
965 #define CAT(X, Y) X##Y
966 class [[Foo]] {};
967 void test() {
968 TYPE([[Foo]]) foo;
969 [[FOO]] foo2;
970 TYPE(TYPE([[Foo]])) foo3;
971 [[CAT]](Fo, o) foo4;
972 }
973 )");
974 CollectorOpts.RefFilter = RefKind::All;
975 runSymbolCollector(Header.code(), "");
976 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Foo").ID,
977 HaveRanges(Header.ranges()))));
978 }
979
TEST_F(SymbolCollectorTest,HeaderAsMainFile)980 TEST_F(SymbolCollectorTest, HeaderAsMainFile) {
981 CollectorOpts.RefFilter = RefKind::All;
982 Annotations Header(R"(
983 class $Foo[[Foo]] {};
984
985 void $Func[[Func]]() {
986 $Foo[[Foo]] fo;
987 }
988 )");
989 // We should collect refs to main-file symbols in all cases:
990
991 // 1. The main file is normal .cpp file.
992 TestFileName = testPath("foo.cpp");
993 runSymbolCollector("", Header.code());
994 EXPECT_THAT(Refs,
995 UnorderedElementsAre(Pair(findSymbol(Symbols, "Foo").ID,
996 HaveRanges(Header.ranges("Foo"))),
997 Pair(findSymbol(Symbols, "Func").ID,
998 HaveRanges(Header.ranges("Func")))));
999
1000 // 2. Run the .h file as main file.
1001 TestFileName = testPath("foo.h");
1002 runSymbolCollector("", Header.code(),
1003 /*ExtraArgs=*/{"-xobjective-c++-header"});
1004 EXPECT_THAT(Symbols, UnorderedElementsAre(QName("Foo"), QName("Func")));
1005 EXPECT_THAT(Refs,
1006 UnorderedElementsAre(Pair(findSymbol(Symbols, "Foo").ID,
1007 HaveRanges(Header.ranges("Foo"))),
1008 Pair(findSymbol(Symbols, "Func").ID,
1009 HaveRanges(Header.ranges("Func")))));
1010
1011 // 3. Run the .hh file as main file (without "-x c++-header").
1012 TestFileName = testPath("foo.hh");
1013 runSymbolCollector("", Header.code());
1014 EXPECT_THAT(Symbols, UnorderedElementsAre(QName("Foo"), QName("Func")));
1015 EXPECT_THAT(Refs,
1016 UnorderedElementsAre(Pair(findSymbol(Symbols, "Foo").ID,
1017 HaveRanges(Header.ranges("Foo"))),
1018 Pair(findSymbol(Symbols, "Func").ID,
1019 HaveRanges(Header.ranges("Func")))));
1020 }
1021
TEST_F(SymbolCollectorTest,RefsInHeaders)1022 TEST_F(SymbolCollectorTest, RefsInHeaders) {
1023 CollectorOpts.RefFilter = RefKind::All;
1024 CollectorOpts.RefsInHeaders = true;
1025 CollectorOpts.CollectMacro = true;
1026 Annotations Header(R"(
1027 #define $macro[[MACRO]](x) (x+1)
1028 class $foo[[Foo]] {};
1029 )");
1030 runSymbolCollector(Header.code(), "");
1031 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Foo").ID,
1032 HaveRanges(Header.ranges("foo")))));
1033 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "MACRO").ID,
1034 HaveRanges(Header.ranges("macro")))));
1035 }
1036
TEST_F(SymbolCollectorTest,BaseOfRelations)1037 TEST_F(SymbolCollectorTest, BaseOfRelations) {
1038 std::string Header = R"(
1039 class Base {};
1040 class Derived : public Base {};
1041 )";
1042 runSymbolCollector(Header, /*Main=*/"");
1043 const Symbol &Base = findSymbol(Symbols, "Base");
1044 const Symbol &Derived = findSymbol(Symbols, "Derived");
1045 EXPECT_THAT(Relations,
1046 Contains(Relation{Base.ID, RelationKind::BaseOf, Derived.ID}));
1047 }
1048
TEST_F(SymbolCollectorTest,OverrideRelationsSimpleInheritance)1049 TEST_F(SymbolCollectorTest, OverrideRelationsSimpleInheritance) {
1050 std::string Header = R"cpp(
1051 class A {
1052 virtual void foo();
1053 };
1054 class B : public A {
1055 void foo() override; // A::foo
1056 virtual void bar();
1057 };
1058 class C : public B {
1059 void bar() override; // B::bar
1060 };
1061 class D: public C {
1062 void foo() override; // B::foo
1063 void bar() override; // C::bar
1064 };
1065 )cpp";
1066 runSymbolCollector(Header, /*Main=*/"");
1067 const Symbol &AFoo = findSymbol(Symbols, "A::foo");
1068 const Symbol &BFoo = findSymbol(Symbols, "B::foo");
1069 const Symbol &DFoo = findSymbol(Symbols, "D::foo");
1070
1071 const Symbol &BBar = findSymbol(Symbols, "B::bar");
1072 const Symbol &CBar = findSymbol(Symbols, "C::bar");
1073 const Symbol &DBar = findSymbol(Symbols, "D::bar");
1074
1075 std::vector<Relation> Result;
1076 for (const Relation &R : Relations)
1077 if (R.Predicate == RelationKind::OverriddenBy)
1078 Result.push_back(R);
1079 EXPECT_THAT(Result, UnorderedElementsAre(
1080 OverriddenBy(AFoo, BFoo), OverriddenBy(BBar, CBar),
1081 OverriddenBy(BFoo, DFoo), OverriddenBy(CBar, DBar)));
1082 }
1083
TEST_F(SymbolCollectorTest,OverrideRelationsMultipleInheritance)1084 TEST_F(SymbolCollectorTest, OverrideRelationsMultipleInheritance) {
1085 std::string Header = R"cpp(
1086 class A {
1087 virtual void foo();
1088 };
1089 class B {
1090 virtual void bar();
1091 };
1092 class C : public B {
1093 void bar() override; // B::bar
1094 virtual void baz();
1095 }
1096 class D : public A, C {
1097 void foo() override; // A::foo
1098 void bar() override; // C::bar
1099 void baz() override; // C::baz
1100 };
1101 )cpp";
1102 runSymbolCollector(Header, /*Main=*/"");
1103 const Symbol &AFoo = findSymbol(Symbols, "A::foo");
1104 const Symbol &BBar = findSymbol(Symbols, "B::bar");
1105 const Symbol &CBar = findSymbol(Symbols, "C::bar");
1106 const Symbol &CBaz = findSymbol(Symbols, "C::baz");
1107 const Symbol &DFoo = findSymbol(Symbols, "D::foo");
1108 const Symbol &DBar = findSymbol(Symbols, "D::bar");
1109 const Symbol &DBaz = findSymbol(Symbols, "D::baz");
1110
1111 std::vector<Relation> Result;
1112 for (const Relation &R : Relations)
1113 if (R.Predicate == RelationKind::OverriddenBy)
1114 Result.push_back(R);
1115 EXPECT_THAT(Result, UnorderedElementsAre(
1116 OverriddenBy(BBar, CBar), OverriddenBy(AFoo, DFoo),
1117 OverriddenBy(CBar, DBar), OverriddenBy(CBaz, DBaz)));
1118 }
1119
TEST_F(SymbolCollectorTest,CountReferences)1120 TEST_F(SymbolCollectorTest, CountReferences) {
1121 const std::string Header = R"(
1122 class W;
1123 class X {};
1124 class Y;
1125 class Z {}; // not used anywhere
1126 Y* y = nullptr; // used in header doesn't count
1127 #define GLOBAL_Z(name) Z name;
1128 )";
1129 const std::string Main = R"(
1130 W* w = nullptr;
1131 W* w2 = nullptr; // only one usage counts
1132 X x();
1133 class V;
1134 class Y{}; // definition doesn't count as a reference
1135 V* v = nullptr;
1136 GLOBAL_Z(z); // Not a reference to Z, we don't spell the type.
1137 )";
1138 CollectorOpts.CountReferences = true;
1139 runSymbolCollector(Header, Main);
1140 EXPECT_THAT(
1141 Symbols,
1142 UnorderedElementsAreArray(
1143 {AllOf(QName("W"), RefCount(1)), AllOf(QName("X"), RefCount(1)),
1144 AllOf(QName("Y"), RefCount(0)), AllOf(QName("Z"), RefCount(0)),
1145 AllOf(QName("y"), RefCount(0)), AllOf(QName("z"), RefCount(0)),
1146 AllOf(QName("x"), RefCount(0)), AllOf(QName("w"), RefCount(0)),
1147 AllOf(QName("w2"), RefCount(0)), AllOf(QName("V"), RefCount(1)),
1148 AllOf(QName("v"), RefCount(0))}));
1149 }
1150
TEST_F(SymbolCollectorTest,SymbolRelativeNoFallback)1151 TEST_F(SymbolCollectorTest, SymbolRelativeNoFallback) {
1152 runSymbolCollector("class Foo {};", /*Main=*/"");
1153 EXPECT_THAT(Symbols, UnorderedElementsAre(
1154 AllOf(QName("Foo"), DeclURI(TestHeaderURI))));
1155 }
1156
TEST_F(SymbolCollectorTest,SymbolRelativeWithFallback)1157 TEST_F(SymbolCollectorTest, SymbolRelativeWithFallback) {
1158 TestHeaderName = "x.h";
1159 TestFileName = "x.cpp";
1160 TestHeaderURI = URI::create(testPath(TestHeaderName)).toString();
1161 CollectorOpts.FallbackDir = testRoot();
1162 runSymbolCollector("class Foo {};", /*Main=*/"");
1163 EXPECT_THAT(Symbols, UnorderedElementsAre(
1164 AllOf(QName("Foo"), DeclURI(TestHeaderURI))));
1165 }
1166
TEST_F(SymbolCollectorTest,UnittestURIScheme)1167 TEST_F(SymbolCollectorTest, UnittestURIScheme) {
1168 // Use test URI scheme from URITests.cpp
1169 TestHeaderName = testPath("x.h");
1170 TestFileName = testPath("x.cpp");
1171 runSymbolCollector("class Foo {};", /*Main=*/"");
1172 EXPECT_THAT(Symbols, UnorderedElementsAre(
1173 AllOf(QName("Foo"), DeclURI("unittest:///x.h"))));
1174 }
1175
TEST_F(SymbolCollectorTest,IncludeEnums)1176 TEST_F(SymbolCollectorTest, IncludeEnums) {
1177 const std::string Header = R"(
1178 enum {
1179 Red
1180 };
1181 enum Color {
1182 Green
1183 };
1184 enum class Color2 {
1185 Yellow
1186 };
1187 namespace ns {
1188 enum {
1189 Black
1190 };
1191 }
1192 )";
1193 runSymbolCollector(Header, /*Main=*/"");
1194 EXPECT_THAT(Symbols,
1195 UnorderedElementsAre(
1196 AllOf(QName("Red"), ForCodeCompletion(true)),
1197 AllOf(QName("Color"), ForCodeCompletion(true)),
1198 AllOf(QName("Green"), ForCodeCompletion(true)),
1199 AllOf(QName("Color2"), ForCodeCompletion(true)),
1200 AllOf(QName("Color2::Yellow"), ForCodeCompletion(false)),
1201 AllOf(QName("ns"), ForCodeCompletion(true)),
1202 AllOf(QName("ns::Black"), ForCodeCompletion(true))));
1203 }
1204
TEST_F(SymbolCollectorTest,NamelessSymbols)1205 TEST_F(SymbolCollectorTest, NamelessSymbols) {
1206 const std::string Header = R"(
1207 struct {
1208 int a;
1209 } Foo;
1210 )";
1211 runSymbolCollector(Header, /*Main=*/"");
1212 EXPECT_THAT(Symbols, UnorderedElementsAre(QName("Foo"),
1213 QName("(anonymous struct)::a")));
1214 }
1215
TEST_F(SymbolCollectorTest,SymbolFormedFromRegisteredSchemeFromMacro)1216 TEST_F(SymbolCollectorTest, SymbolFormedFromRegisteredSchemeFromMacro) {
1217
1218 Annotations Header(R"(
1219 #define FF(name) \
1220 class name##_Test {};
1221
1222 $expansion[[FF]](abc);
1223
1224 #define FF2() \
1225 class $spelling[[Test]] {};
1226
1227 FF2();
1228 )");
1229
1230 runSymbolCollector(Header.code(), /*Main=*/"");
1231 EXPECT_THAT(Symbols,
1232 UnorderedElementsAre(
1233 AllOf(QName("abc_Test"), DeclRange(Header.range("expansion")),
1234 DeclURI(TestHeaderURI)),
1235 AllOf(QName("Test"), DeclRange(Header.range("spelling")),
1236 DeclURI(TestHeaderURI))));
1237 }
1238
TEST_F(SymbolCollectorTest,SymbolFormedByCLI)1239 TEST_F(SymbolCollectorTest, SymbolFormedByCLI) {
1240 Annotations Header(R"(
1241 #ifdef NAME
1242 class $expansion[[NAME]] {};
1243 #endif
1244 )");
1245 runSymbolCollector(Header.code(), /*Main=*/"", /*ExtraArgs=*/{"-DNAME=name"});
1246 EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(
1247 QName("name"), DeclRange(Header.range("expansion")),
1248 DeclURI(TestHeaderURI))));
1249 }
1250
TEST_F(SymbolCollectorTest,SymbolsInMainFile)1251 TEST_F(SymbolCollectorTest, SymbolsInMainFile) {
1252 const std::string Main = R"(
1253 class Foo {};
1254 void f1();
1255 inline void f2() {}
1256
1257 namespace {
1258 void ff() {}
1259 }
1260 namespace foo {
1261 namespace {
1262 class Bar {};
1263 }
1264 }
1265 void main_f() {}
1266 void f1() {}
1267 )";
1268 runSymbolCollector(/*Header=*/"", Main);
1269 EXPECT_THAT(Symbols, UnorderedElementsAre(
1270 QName("Foo"), QName("f1"), QName("f2"), QName("ff"),
1271 QName("foo"), QName("foo::Bar"), QName("main_f")));
1272 }
1273
TEST_F(SymbolCollectorTest,Documentation)1274 TEST_F(SymbolCollectorTest, Documentation) {
1275 const std::string Header = R"(
1276 // Doc Foo
1277 class Foo {
1278 // Doc f
1279 int f();
1280 };
1281 )";
1282 CollectorOpts.StoreAllDocumentation = false;
1283 runSymbolCollector(Header, /* Main */ "");
1284 EXPECT_THAT(Symbols,
1285 UnorderedElementsAre(
1286 AllOf(QName("Foo"), Doc("Doc Foo"), ForCodeCompletion(true)),
1287 AllOf(QName("Foo::f"), Doc(""), ReturnType(""),
1288 ForCodeCompletion(false))));
1289
1290 CollectorOpts.StoreAllDocumentation = true;
1291 runSymbolCollector(Header, /* Main */ "");
1292 EXPECT_THAT(Symbols,
1293 UnorderedElementsAre(
1294 AllOf(QName("Foo"), Doc("Doc Foo"), ForCodeCompletion(true)),
1295 AllOf(QName("Foo::f"), Doc("Doc f"), ReturnType(""),
1296 ForCodeCompletion(false))));
1297 }
1298
TEST_F(SymbolCollectorTest,ClassMembers)1299 TEST_F(SymbolCollectorTest, ClassMembers) {
1300 const std::string Header = R"(
1301 class Foo {
1302 void f() {}
1303 void g();
1304 static void sf() {}
1305 static void ssf();
1306 static int x;
1307 };
1308 )";
1309 const std::string Main = R"(
1310 void Foo::g() {}
1311 void Foo::ssf() {}
1312 )";
1313 runSymbolCollector(Header, Main);
1314 EXPECT_THAT(
1315 Symbols,
1316 UnorderedElementsAre(
1317 QName("Foo"),
1318 AllOf(QName("Foo::f"), ReturnType(""), ForCodeCompletion(false)),
1319 AllOf(QName("Foo::g"), ReturnType(""), ForCodeCompletion(false)),
1320 AllOf(QName("Foo::sf"), ReturnType(""), ForCodeCompletion(false)),
1321 AllOf(QName("Foo::ssf"), ReturnType(""), ForCodeCompletion(false)),
1322 AllOf(QName("Foo::x"), ReturnType(""), ForCodeCompletion(false))));
1323 }
1324
TEST_F(SymbolCollectorTest,Scopes)1325 TEST_F(SymbolCollectorTest, Scopes) {
1326 const std::string Header = R"(
1327 namespace na {
1328 class Foo {};
1329 namespace nb {
1330 class Bar {};
1331 }
1332 }
1333 )";
1334 runSymbolCollector(Header, /*Main=*/"");
1335 EXPECT_THAT(Symbols,
1336 UnorderedElementsAre(QName("na"), QName("na::nb"),
1337 QName("na::Foo"), QName("na::nb::Bar")));
1338 }
1339
TEST_F(SymbolCollectorTest,ExternC)1340 TEST_F(SymbolCollectorTest, ExternC) {
1341 const std::string Header = R"(
1342 extern "C" { class Foo {}; }
1343 namespace na {
1344 extern "C" { class Bar {}; }
1345 }
1346 )";
1347 runSymbolCollector(Header, /*Main=*/"");
1348 EXPECT_THAT(Symbols, UnorderedElementsAre(QName("na"), QName("Foo"),
1349 QName("na::Bar")));
1350 }
1351
TEST_F(SymbolCollectorTest,SkipInlineNamespace)1352 TEST_F(SymbolCollectorTest, SkipInlineNamespace) {
1353 const std::string Header = R"(
1354 namespace na {
1355 inline namespace nb {
1356 class Foo {};
1357 }
1358 }
1359 namespace na {
1360 // This is still inlined.
1361 namespace nb {
1362 class Bar {};
1363 }
1364 }
1365 )";
1366 runSymbolCollector(Header, /*Main=*/"");
1367 EXPECT_THAT(Symbols,
1368 UnorderedElementsAre(QName("na"), QName("na::nb"),
1369 QName("na::Foo"), QName("na::Bar")));
1370 }
1371
TEST_F(SymbolCollectorTest,SymbolWithDocumentation)1372 TEST_F(SymbolCollectorTest, SymbolWithDocumentation) {
1373 const std::string Header = R"(
1374 namespace nx {
1375 /// Foo comment.
1376 int ff(int x, double y) { return 0; }
1377 }
1378 )";
1379 runSymbolCollector(Header, /*Main=*/"");
1380 EXPECT_THAT(
1381 Symbols,
1382 UnorderedElementsAre(
1383 QName("nx"), AllOf(QName("nx::ff"), Labeled("ff(int x, double y)"),
1384 ReturnType("int"), Doc("Foo comment."))));
1385 }
1386
TEST_F(SymbolCollectorTest,Snippet)1387 TEST_F(SymbolCollectorTest, Snippet) {
1388 const std::string Header = R"(
1389 namespace nx {
1390 void f() {}
1391 int ff(int x, double y) { return 0; }
1392 }
1393 )";
1394 runSymbolCollector(Header, /*Main=*/"");
1395 EXPECT_THAT(Symbols,
1396 UnorderedElementsAre(
1397 QName("nx"),
1398 AllOf(QName("nx::f"), Labeled("f()"), Snippet("f()")),
1399 AllOf(QName("nx::ff"), Labeled("ff(int x, double y)"),
1400 Snippet("ff(${1:int x}, ${2:double y})"))));
1401 }
1402
TEST_F(SymbolCollectorTest,IncludeHeaderSameAsFileURI)1403 TEST_F(SymbolCollectorTest, IncludeHeaderSameAsFileURI) {
1404 CollectorOpts.CollectIncludePath = true;
1405 runSymbolCollector("#pragma once\nclass Foo {};", /*Main=*/"");
1406 EXPECT_THAT(Symbols, UnorderedElementsAre(
1407 AllOf(QName("Foo"), DeclURI(TestHeaderURI))));
1408 EXPECT_THAT(Symbols.begin()->IncludeHeaders,
1409 UnorderedElementsAre(IncludeHeaderWithRef(TestHeaderURI, 1u)));
1410 }
1411
TEST_F(SymbolCollectorTest,CanonicalSTLHeader)1412 TEST_F(SymbolCollectorTest, CanonicalSTLHeader) {
1413 CollectorOpts.CollectIncludePath = true;
1414 CanonicalIncludes Includes;
1415 auto Language = LangOptions();
1416 Language.CPlusPlus = true;
1417 Includes.addSystemHeadersMapping(Language);
1418 CollectorOpts.Includes = &Includes;
1419 runSymbolCollector(
1420 R"cpp(
1421 namespace std {
1422 class string {};
1423 // Move overloads have special handling.
1424 template <typename T> T&& move(T&&);
1425 template <typename I, typename O> O move(I, I, O);
1426 }
1427 )cpp",
1428 /*Main=*/"");
1429 for (const auto &S : Symbols)
1430 llvm::errs() << S.Scope << S.Name << " in " << S.IncludeHeaders.size()
1431 << "\n";
1432 EXPECT_THAT(
1433 Symbols,
1434 UnorderedElementsAre(
1435 QName("std"),
1436 AllOf(QName("std::string"), DeclURI(TestHeaderURI),
1437 IncludeHeader("<string>")),
1438 AllOf(Labeled("move(T &&)"), IncludeHeader("<utility>")),
1439 AllOf(Labeled("move(I, I, O)"), IncludeHeader("<algorithm>"))));
1440 }
1441
TEST_F(SymbolCollectorTest,IWYUPragma)1442 TEST_F(SymbolCollectorTest, IWYUPragma) {
1443 CollectorOpts.CollectIncludePath = true;
1444 CanonicalIncludes Includes;
1445 PragmaHandler = collectIWYUHeaderMaps(&Includes);
1446 CollectorOpts.Includes = &Includes;
1447 const std::string Header = R"(
1448 // IWYU pragma: private, include the/good/header.h
1449 class Foo {};
1450 )";
1451 runSymbolCollector(Header, /*Main=*/"");
1452 EXPECT_THAT(Symbols, UnorderedElementsAre(
1453 AllOf(QName("Foo"), DeclURI(TestHeaderURI),
1454 IncludeHeader("\"the/good/header.h\""))));
1455 }
1456
TEST_F(SymbolCollectorTest,IWYUPragmaWithDoubleQuotes)1457 TEST_F(SymbolCollectorTest, IWYUPragmaWithDoubleQuotes) {
1458 CollectorOpts.CollectIncludePath = true;
1459 CanonicalIncludes Includes;
1460 PragmaHandler = collectIWYUHeaderMaps(&Includes);
1461 CollectorOpts.Includes = &Includes;
1462 const std::string Header = R"(
1463 // IWYU pragma: private, include "the/good/header.h"
1464 class Foo {};
1465 )";
1466 runSymbolCollector(Header, /*Main=*/"");
1467 EXPECT_THAT(Symbols, UnorderedElementsAre(
1468 AllOf(QName("Foo"), DeclURI(TestHeaderURI),
1469 IncludeHeader("\"the/good/header.h\""))));
1470 }
1471
TEST_F(SymbolCollectorTest,SkipIncFileWhenCanonicalizeHeaders)1472 TEST_F(SymbolCollectorTest, SkipIncFileWhenCanonicalizeHeaders) {
1473 CollectorOpts.CollectIncludePath = true;
1474 CanonicalIncludes Includes;
1475 Includes.addMapping(TestHeaderName, "<canonical>");
1476 CollectorOpts.Includes = &Includes;
1477 auto IncFile = testPath("test.inc");
1478 auto IncURI = URI::create(IncFile).toString();
1479 InMemoryFileSystem->addFile(IncFile, 0,
1480 llvm::MemoryBuffer::getMemBuffer("class X {};"));
1481 runSymbolCollector("#include \"test.inc\"\nclass Y {};", /*Main=*/"",
1482 /*ExtraArgs=*/{"-I", testRoot()});
1483 EXPECT_THAT(Symbols,
1484 UnorderedElementsAre(AllOf(QName("X"), DeclURI(IncURI),
1485 IncludeHeader("<canonical>")),
1486 AllOf(QName("Y"), DeclURI(TestHeaderURI),
1487 IncludeHeader("<canonical>"))));
1488 }
1489
TEST_F(SymbolCollectorTest,MainFileIsHeaderWhenSkipIncFile)1490 TEST_F(SymbolCollectorTest, MainFileIsHeaderWhenSkipIncFile) {
1491 CollectorOpts.CollectIncludePath = true;
1492 // To make this case as hard as possible, we won't tell clang main is a
1493 // header. No extension, no -x c++-header.
1494 TestFileName = testPath("no_ext_main");
1495 TestFileURI = URI::create(TestFileName).toString();
1496 auto IncFile = testPath("test.inc");
1497 auto IncURI = URI::create(IncFile).toString();
1498 InMemoryFileSystem->addFile(IncFile, 0,
1499 llvm::MemoryBuffer::getMemBuffer("class X {};"));
1500 runSymbolCollector("", R"cpp(
1501 // Can't use #pragma once in a main file clang doesn't think is a header.
1502 #ifndef MAIN_H_
1503 #define MAIN_H_
1504 #include "test.inc"
1505 #endif
1506 )cpp",
1507 /*ExtraArgs=*/{"-I", testRoot()});
1508 EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), DeclURI(IncURI),
1509 IncludeHeader(TestFileURI))));
1510 }
1511
TEST_F(SymbolCollectorTest,IncFileInNonHeader)1512 TEST_F(SymbolCollectorTest, IncFileInNonHeader) {
1513 CollectorOpts.CollectIncludePath = true;
1514 TestFileName = testPath("main.cc");
1515 TestFileURI = URI::create(TestFileName).toString();
1516 auto IncFile = testPath("test.inc");
1517 auto IncURI = URI::create(IncFile).toString();
1518 InMemoryFileSystem->addFile(IncFile, 0,
1519 llvm::MemoryBuffer::getMemBuffer("class X {};"));
1520 runSymbolCollector("", R"cpp(
1521 #include "test.inc"
1522 )cpp",
1523 /*ExtraArgs=*/{"-I", testRoot()});
1524 EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), DeclURI(IncURI),
1525 Not(IncludeHeader()))));
1526 }
1527
1528 // Features that depend on header-guards are fragile. Header guards are only
1529 // recognized when the file ends, so we have to defer checking for them.
TEST_F(SymbolCollectorTest,HeaderGuardDetected)1530 TEST_F(SymbolCollectorTest, HeaderGuardDetected) {
1531 CollectorOpts.CollectIncludePath = true;
1532 CollectorOpts.CollectMacro = true;
1533 runSymbolCollector(R"cpp(
1534 #ifndef HEADER_GUARD_
1535 #define HEADER_GUARD_
1536
1537 // Symbols are seen before the header guard is complete.
1538 #define MACRO
1539 int decl();
1540
1541 #endif // Header guard is recognized here.
1542 )cpp",
1543 "");
1544 EXPECT_THAT(Symbols, Not(Contains(QName("HEADER_GUARD_"))));
1545 EXPECT_THAT(Symbols, Each(IncludeHeader()));
1546 }
1547
TEST_F(SymbolCollectorTest,NonModularHeader)1548 TEST_F(SymbolCollectorTest, NonModularHeader) {
1549 auto TU = TestTU::withHeaderCode("int x();");
1550 EXPECT_THAT(TU.headerSymbols(), ElementsAre(IncludeHeader()));
1551
1552 // Files missing include guards aren't eligible for insertion.
1553 TU.ImplicitHeaderGuard = false;
1554 EXPECT_THAT(TU.headerSymbols(), ElementsAre(Not(IncludeHeader())));
1555
1556 // We recognize some patterns of trying to prevent insertion.
1557 TU = TestTU::withHeaderCode(R"cpp(
1558 #ifndef SECRET
1559 #error "This file isn't safe to include directly"
1560 #endif
1561 int x();
1562 )cpp");
1563 TU.ExtraArgs.push_back("-DSECRET"); // *we're* able to include it.
1564 EXPECT_THAT(TU.headerSymbols(), ElementsAre(Not(IncludeHeader())));
1565 }
1566
TEST_F(SymbolCollectorTest,AvoidUsingFwdDeclsAsCanonicalDecls)1567 TEST_F(SymbolCollectorTest, AvoidUsingFwdDeclsAsCanonicalDecls) {
1568 CollectorOpts.CollectIncludePath = true;
1569 Annotations Header(R"(
1570 #pragma once
1571 // Forward declarations of TagDecls.
1572 class C;
1573 struct S;
1574 union U;
1575
1576 // Canonical declarations.
1577 class $cdecl[[C]] {};
1578 struct $sdecl[[S]] {};
1579 union $udecl[[U]] {int $xdecl[[x]]; bool $ydecl[[y]];};
1580 )");
1581 runSymbolCollector(Header.code(), /*Main=*/"");
1582 EXPECT_THAT(
1583 Symbols,
1584 UnorderedElementsAre(
1585 AllOf(QName("C"), DeclURI(TestHeaderURI),
1586 DeclRange(Header.range("cdecl")), IncludeHeader(TestHeaderURI),
1587 DefURI(TestHeaderURI), DefRange(Header.range("cdecl"))),
1588 AllOf(QName("S"), DeclURI(TestHeaderURI),
1589 DeclRange(Header.range("sdecl")), IncludeHeader(TestHeaderURI),
1590 DefURI(TestHeaderURI), DefRange(Header.range("sdecl"))),
1591 AllOf(QName("U"), DeclURI(TestHeaderURI),
1592 DeclRange(Header.range("udecl")), IncludeHeader(TestHeaderURI),
1593 DefURI(TestHeaderURI), DefRange(Header.range("udecl"))),
1594 AllOf(QName("U::x"), DeclURI(TestHeaderURI),
1595 DeclRange(Header.range("xdecl")), DefURI(TestHeaderURI),
1596 DefRange(Header.range("xdecl"))),
1597 AllOf(QName("U::y"), DeclURI(TestHeaderURI),
1598 DeclRange(Header.range("ydecl")), DefURI(TestHeaderURI),
1599 DefRange(Header.range("ydecl")))));
1600 }
1601
TEST_F(SymbolCollectorTest,ClassForwardDeclarationIsCanonical)1602 TEST_F(SymbolCollectorTest, ClassForwardDeclarationIsCanonical) {
1603 CollectorOpts.CollectIncludePath = true;
1604 runSymbolCollector(/*Header=*/"#pragma once\nclass X;",
1605 /*Main=*/"class X {};");
1606 EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(
1607 QName("X"), DeclURI(TestHeaderURI),
1608 IncludeHeader(TestHeaderURI), DefURI(TestFileURI))));
1609 }
1610
TEST_F(SymbolCollectorTest,UTF16Character)1611 TEST_F(SymbolCollectorTest, UTF16Character) {
1612 // ö is 2-bytes.
1613 Annotations Header(/*Header=*/"class [[pörk]] {};");
1614 runSymbolCollector(Header.code(), /*Main=*/"");
1615 EXPECT_THAT(Symbols, UnorderedElementsAre(
1616 AllOf(QName("pörk"), DeclRange(Header.range()))));
1617 }
1618
TEST_F(SymbolCollectorTest,DoNotIndexSymbolsInFriendDecl)1619 TEST_F(SymbolCollectorTest, DoNotIndexSymbolsInFriendDecl) {
1620 Annotations Header(R"(
1621 namespace nx {
1622 class $z[[Z]] {};
1623 class X {
1624 friend class Y;
1625 friend class Z;
1626 friend void foo();
1627 friend void $bar[[bar]]() {}
1628 };
1629 class $y[[Y]] {};
1630 void $foo[[foo]]();
1631 }
1632 )");
1633 runSymbolCollector(Header.code(), /*Main=*/"");
1634
1635 EXPECT_THAT(Symbols,
1636 UnorderedElementsAre(
1637 QName("nx"), QName("nx::X"),
1638 AllOf(QName("nx::Y"), DeclRange(Header.range("y"))),
1639 AllOf(QName("nx::Z"), DeclRange(Header.range("z"))),
1640 AllOf(QName("nx::foo"), DeclRange(Header.range("foo"))),
1641 AllOf(QName("nx::bar"), DeclRange(Header.range("bar")))));
1642 }
1643
TEST_F(SymbolCollectorTest,ReferencesInFriendDecl)1644 TEST_F(SymbolCollectorTest, ReferencesInFriendDecl) {
1645 const std::string Header = R"(
1646 class X;
1647 class Y;
1648 )";
1649 const std::string Main = R"(
1650 class C {
1651 friend ::X;
1652 friend class Y;
1653 };
1654 )";
1655 CollectorOpts.CountReferences = true;
1656 runSymbolCollector(Header, Main);
1657 EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), RefCount(1)),
1658 AllOf(QName("Y"), RefCount(1)),
1659 AllOf(QName("C"), RefCount(0))));
1660 }
1661
TEST_F(SymbolCollectorTest,Origin)1662 TEST_F(SymbolCollectorTest, Origin) {
1663 CollectorOpts.Origin = SymbolOrigin::Static;
1664 runSymbolCollector("class Foo {};", /*Main=*/"");
1665 EXPECT_THAT(Symbols, UnorderedElementsAre(
1666 Field(&Symbol::Origin, SymbolOrigin::Static)));
1667 runSymbolCollector("#define FOO", /*Main=*/"");
1668 EXPECT_THAT(Symbols, UnorderedElementsAre(
1669 Field(&Symbol::Origin, SymbolOrigin::Static)));
1670 }
1671
TEST_F(SymbolCollectorTest,CollectMacros)1672 TEST_F(SymbolCollectorTest, CollectMacros) {
1673 CollectorOpts.CollectIncludePath = true;
1674 Annotations Header(R"(
1675 #pragma once
1676 #define X 1
1677 #define $mac[[MAC]](x) int x
1678 #define $used[[USED]](y) float y;
1679
1680 MAC(p);
1681 )");
1682
1683 Annotations Main(R"(
1684 #define $main[[MAIN]] 1
1685 USED(t);
1686 )");
1687 CollectorOpts.CountReferences = true;
1688 CollectorOpts.CollectMacro = true;
1689 runSymbolCollector(Header.code(), Main.code());
1690 EXPECT_THAT(
1691 Symbols,
1692 UnorderedElementsAre(
1693 QName("p"), QName("t"),
1694 AllOf(QName("X"), DeclURI(TestHeaderURI),
1695 IncludeHeader(TestHeaderURI)),
1696 AllOf(Labeled("MAC(x)"), RefCount(0),
1697
1698 DeclRange(Header.range("mac")), VisibleOutsideFile()),
1699 AllOf(Labeled("USED(y)"), RefCount(1),
1700 DeclRange(Header.range("used")), VisibleOutsideFile()),
1701 AllOf(Labeled("MAIN"), RefCount(0), DeclRange(Main.range("main")),
1702 Not(VisibleOutsideFile()))));
1703 }
1704
TEST_F(SymbolCollectorTest,DeprecatedSymbols)1705 TEST_F(SymbolCollectorTest, DeprecatedSymbols) {
1706 const std::string Header = R"(
1707 void TestClangc() __attribute__((deprecated("", "")));
1708 void TestClangd();
1709 )";
1710 runSymbolCollector(Header, /**/ "");
1711 EXPECT_THAT(Symbols, UnorderedElementsAre(
1712 AllOf(QName("TestClangc"), Deprecated()),
1713 AllOf(QName("TestClangd"), Not(Deprecated()))));
1714 }
1715
TEST_F(SymbolCollectorTest,ImplementationDetail)1716 TEST_F(SymbolCollectorTest, ImplementationDetail) {
1717 const std::string Header = R"(
1718 #define DECL_NAME(x, y) x##_##y##_Decl
1719 #define DECL(x, y) class DECL_NAME(x, y) {};
1720 DECL(X, Y); // X_Y_Decl
1721
1722 class Public {};
1723 )";
1724 runSymbolCollector(Header, /**/ "");
1725 EXPECT_THAT(Symbols,
1726 UnorderedElementsAre(
1727 AllOf(QName("X_Y_Decl"), ImplementationDetail()),
1728 AllOf(QName("Public"), Not(ImplementationDetail()))));
1729 }
1730
TEST_F(SymbolCollectorTest,UsingDecl)1731 TEST_F(SymbolCollectorTest, UsingDecl) {
1732 const char *Header = R"(
1733 void foo();
1734 namespace std {
1735 using ::foo;
1736 })";
1737 runSymbolCollector(Header, /**/ "");
1738 EXPECT_THAT(Symbols, Contains(QName("std::foo")));
1739 }
1740
TEST_F(SymbolCollectorTest,CBuiltins)1741 TEST_F(SymbolCollectorTest, CBuiltins) {
1742 // In C, printf in stdio.h is a redecl of an implicit builtin.
1743 const char *Header = R"(
1744 extern int printf(const char*, ...);
1745 )";
1746 runSymbolCollector(Header, /**/ "", {"-xc"});
1747 EXPECT_THAT(Symbols, Contains(QName("printf")));
1748 }
1749
TEST_F(SymbolCollectorTest,InvalidSourceLoc)1750 TEST_F(SymbolCollectorTest, InvalidSourceLoc) {
1751 const char *Header = R"(
1752 void operator delete(void*)
1753 __attribute__((__externally_visible__));)";
1754 runSymbolCollector(Header, /**/ "");
1755 EXPECT_THAT(Symbols, Contains(QName("operator delete")));
1756 }
1757
TEST_F(SymbolCollectorTest,BadUTF8)1758 TEST_F(SymbolCollectorTest, BadUTF8) {
1759 // Extracted from boost/spirit/home/support/char_encoding/iso8859_1.hpp
1760 // This looks like UTF-8 and fools clang, but has high-ISO-8859-1 comments.
1761 const char *Header = "int PUNCT = 0;\n"
1762 "/* \xa1 */ int types[] = { /* \xa1 */PUNCT };";
1763 CollectorOpts.RefFilter = RefKind::All;
1764 CollectorOpts.RefsInHeaders = true;
1765 runSymbolCollector(Header, "");
1766 EXPECT_THAT(Symbols, Contains(AllOf(QName("types"), Doc("\xef\xbf\xbd "))));
1767 EXPECT_THAT(Symbols, Contains(QName("PUNCT")));
1768 // Reference is stored, although offset within line is not reliable.
1769 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "PUNCT").ID, _)));
1770 }
1771
TEST_F(SymbolCollectorTest,MacrosInHeaders)1772 TEST_F(SymbolCollectorTest, MacrosInHeaders) {
1773 CollectorOpts.CollectMacro = true;
1774 TestFileName = testPath("test.h");
1775 runSymbolCollector("", "#define X");
1776 EXPECT_THAT(Symbols,
1777 UnorderedElementsAre(AllOf(QName("X"), ForCodeCompletion(true))));
1778 }
1779
1780 // Regression test for a crash-bug we used to have.
TEST_F(SymbolCollectorTest,UndefOfModuleMacro)1781 TEST_F(SymbolCollectorTest, UndefOfModuleMacro) {
1782 auto TU = TestTU::withCode(R"cpp(#include "bar.h")cpp");
1783 TU.AdditionalFiles["bar.h"] = R"cpp(
1784 #include "foo.h"
1785 #undef X
1786 )cpp";
1787 TU.AdditionalFiles["foo.h"] = "#define X 1";
1788 TU.AdditionalFiles["module.map"] = R"cpp(
1789 module foo {
1790 header "foo.h"
1791 export *
1792 }
1793 )cpp";
1794 TU.ExtraArgs.push_back("-fmodules");
1795 TU.ExtraArgs.push_back("-fmodule-map-file=" + testPath("module.map"));
1796 TU.OverlayRealFileSystemForModules = true;
1797
1798 TU.build();
1799 // We mostly care about not crashing, but verify that we didn't insert garbage
1800 // about X too.
1801 EXPECT_THAT(TU.headerSymbols(), Not(Contains(QName("X"))));
1802 }
1803
1804 } // namespace
1805 } // namespace clangd
1806 } // namespace clang
1807