clang-tools 10.0.0

SymbolCollectorTests.cpp
Go to the documentation of this file.
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 }
73 MATCHER_P(DeclRange, Pos, "") {
74 return std::make_tuple(arg.CanonicalDeclaration.Start.line(),
75 arg.CanonicalDeclaration.Start.column(),
76 arg.CanonicalDeclaration.End.line(),
77 arg.CanonicalDeclaration.End.column()) ==
78 std::make_tuple(Pos.start.line, Pos.start.character, Pos.end.line,
79 Pos.end.character);
80 }
81 MATCHER_P(DefRange, Pos, "") {
82 return std::make_tuple(
83 arg.Definition.Start.line(), arg.Definition.Start.column(),
84 arg.Definition.End.line(), arg.Definition.End.column()) ==
85 std::make_tuple(Pos.start.line, Pos.start.character, Pos.end.line,
86 Pos.end.character);
87 }
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;
92 }
93 MATCHER(Deprecated, "") { return arg.Flags & Symbol::Deprecated; }
94 MATCHER(ImplementationDetail, "") {
95 return arg.Flags & Symbol::ImplementationDetail;
96 }
97 MATCHER(VisibleOutsideFile, "") {
98 return static_cast<bool>(arg.Flags & Symbol::VisibleOutsideFile);
99 }
100 MATCHER(RefRange, "") {
101 const Ref &Pos = ::testing::get<0>(arg);
102 const Range &Range = ::testing::get<1>(arg);
103 return std::make_tuple(Pos.Location.Start.line(), Pos.Location.Start.column(),
104 Pos.Location.End.line(), Pos.Location.End.column()) ==
105 std::make_tuple(Range.start.line, Range.start.character,
106 Range.end.line, Range.end.character);
107 }
108 ::testing::Matcher<const std::vector<Ref> &>
109 HaveRanges(const std::vector<Range> Ranges) {
110 return ::testing::UnorderedPointwise(RefRange(), Ranges);
111 }
112
113 class ShouldCollectSymbolTest : public ::testing::Test {
114 public:
115 void build(llvm::StringRef HeaderCode, llvm::StringRef Code = "") {
116 File.HeaderFilename = HeaderName;
117 File.Filename = FileName;
118 File.HeaderCode = HeaderCode;
119 File.Code = Code;
120 AST = File.build();
121 }
122
123 // build() must have been called.
124 bool shouldCollect(llvm::StringRef Name, bool Qualified = true) {
125 assert(AST.hasValue());
126 const NamedDecl &ND =
127 Qualified ? findDecl(*AST, Name) : findUnqualifiedDecl(*AST, Name);
128 const SourceManager &SM = AST->getSourceManager();
129 bool MainFile = isInsideMainFile(ND.getBeginLoc(), SM);
130 return SymbolCollector::shouldCollectSymbol(
131 ND, AST->getASTContext(), SymbolCollector::Options(), MainFile);
132 }
133
134 protected:
135 std::string HeaderName = "f.h";
136 std::string FileName = "f.cpp";
137 TestTU File;
138 llvm::Optional<ParsedAST> AST; // Initialized after build.
139 };
140
141 TEST_F(ShouldCollectSymbolTest, ShouldCollectSymbol) {
142 build(R"(
143 namespace nx {
144 class X{};
145 auto f() { int Local; } // auto ensures function body is parsed.
146 struct { int x; } var;
147 }
148 )",
149 R"(
150 class InMain {};
151 namespace { class InAnonymous {}; }
152 static void g();
153 )");
154 auto AST = File.build();
155 EXPECT_TRUE(shouldCollect("nx"));
156 EXPECT_TRUE(shouldCollect("nx::X"));
157 EXPECT_TRUE(shouldCollect("nx::f"));
158 EXPECT_TRUE(shouldCollect("InMain"));
159 EXPECT_TRUE(shouldCollect("InAnonymous", /*Qualified=*/false));
160 EXPECT_TRUE(shouldCollect("g"));
161
162 EXPECT_FALSE(shouldCollect("Local", /*Qualified=*/false));
163 }
164
165 TEST_F(ShouldCollectSymbolTest, NoPrivateProtoSymbol) {
166 HeaderName = "f.proto.h";
167 build(
168 R"(// Generated by the protocol buffer compiler. DO NOT EDIT!
169 namespace nx {
170 class Top_Level {};
171 class TopLevel {};
172 enum Kind {
173 KIND_OK,
174 Kind_Not_Ok,
175 };
176 })");
177 EXPECT_TRUE(shouldCollect("nx::TopLevel"));
178 EXPECT_TRUE(shouldCollect("nx::Kind::KIND_OK"));
179 EXPECT_TRUE(shouldCollect("nx::Kind"));
180
181 EXPECT_FALSE(shouldCollect("nx::Top_Level"));
182 EXPECT_FALSE(shouldCollect("nx::Kind::Kind_Not_Ok"));
183 }
184
185 TEST_F(ShouldCollectSymbolTest, DoubleCheckProtoHeaderComment) {
186 HeaderName = "f.proto.h";
187 build(R"(
188 namespace nx {
189 class Top_Level {};
190 enum Kind {
191 Kind_Fine
192 };
193 }
194 )");
195 EXPECT_TRUE(shouldCollect("nx::Top_Level"));
196 EXPECT_TRUE(shouldCollect("nx::Kind_Fine"));
197 }
198
199 class SymbolIndexActionFactory : public tooling::FrontendActionFactory {
200 public:
201 SymbolIndexActionFactory(SymbolCollector::Options COpts,
202 CommentHandler *PragmaHandler)
203 : COpts(std::move(COpts)), PragmaHandler(PragmaHandler) {}
204
205 std::unique_ptr<FrontendAction> create() override {
206 class IndexAction : public ASTFrontendAction {
207 public:
208 IndexAction(std::shared_ptr<index::IndexDataConsumer> DataConsumer,
209 const index::IndexingOptions &Opts,
210 CommentHandler *PragmaHandler)
211 : DataConsumer(std::move(DataConsumer)), Opts(Opts),
212 PragmaHandler(PragmaHandler) {}
213
214 std::unique_ptr<ASTConsumer>
215 CreateASTConsumer(CompilerInstance &CI, llvm::StringRef InFile) override {
216 if (PragmaHandler)
217 CI.getPreprocessor().addCommentHandler(PragmaHandler);
218 return createIndexingASTConsumer(DataConsumer, Opts,
219 CI.getPreprocessorPtr());
220 }
221
222 bool BeginInvocation(CompilerInstance &CI) override {
223 // Make the compiler parse all comments.
224 CI.getLangOpts().CommentOpts.ParseAllComments = true;
225 return true;
226 }
227
228 private:
229 std::shared_ptr<index::IndexDataConsumer> DataConsumer;
230 index::IndexingOptions Opts;
231 CommentHandler *PragmaHandler;
232 };
233 index::IndexingOptions IndexOpts;
234 IndexOpts.SystemSymbolFilter =
235 index::IndexingOptions::SystemSymbolFilterKind::All;
236 IndexOpts.IndexFunctionLocals = false;
237 Collector = std::make_shared<SymbolCollector>(COpts);
238 return std::make_unique<IndexAction>(Collector, std::move(IndexOpts),
239 PragmaHandler);
240 }
241
242 std::shared_ptr<SymbolCollector> Collector;
243 SymbolCollector::Options COpts;
244 CommentHandler *PragmaHandler;
245 };
246
247 class SymbolCollectorTest : public ::testing::Test {
248 public:
249 SymbolCollectorTest()
250 : InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem),
251 TestHeaderName(testPath("symbol.h")),
252 TestFileName(testPath("symbol.cc")) {
253 TestHeaderURI = URI::create(TestHeaderName).toString();
254 TestFileURI = URI::create(TestFileName).toString();
255 }
256
257 // Note that unlike TestTU, no automatic header guard is added.
258 // HeaderCode should start with #pragma once to be treated as modular.
259 bool runSymbolCollector(llvm::StringRef HeaderCode, llvm::StringRef MainCode,
260 const std::vector<std::string> &ExtraArgs = {}) {
261 llvm::IntrusiveRefCntPtr<FileManager> Files(
262 new FileManager(FileSystemOptions(), InMemoryFileSystem));
263
264 auto Factory = std::make_unique<SymbolIndexActionFactory>(
265 CollectorOpts, PragmaHandler.get());
266
267 std::vector<std::string> Args = {"symbol_collector", "-fsyntax-only",
268 "-xc++", "-include", TestHeaderName};
269 Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
270 // This allows to override the "-xc++" with something else, i.e.
271 // -xobjective-c++.
272 Args.push_back(TestFileName);
273
274 tooling::ToolInvocation Invocation(
275 Args, Factory->create(), Files.get(),
276 std::make_shared<PCHContainerOperations>());
277
278 InMemoryFileSystem->addFile(TestHeaderName, 0,
279 llvm::MemoryBuffer::getMemBuffer(HeaderCode));
280 InMemoryFileSystem->addFile(TestFileName, 0,
281 llvm::MemoryBuffer::getMemBuffer(MainCode));
282 Invocation.run();
283 Symbols = Factory->Collector->takeSymbols();
284 Refs = Factory->Collector->takeRefs();
285 Relations = Factory->Collector->takeRelations();
286 return true;
287 }
288
289 protected:
290 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem;
291 std::string TestHeaderName;
292 std::string TestHeaderURI;
293 std::string TestFileName;
294 std::string TestFileURI;
295 SymbolSlab Symbols;
296 RefSlab Refs;
297 RelationSlab Relations;
298 SymbolCollector::Options CollectorOpts;
299 std::unique_ptr<CommentHandler> PragmaHandler;
300 };
301
302 TEST_F(SymbolCollectorTest, CollectSymbols) {
303 const std::string Header = R"(
304 class Foo {
305 Foo() {}
306 Foo(int a) {}
307 void f();
308 friend void f1();
309 friend class Friend;
310 Foo& operator=(const Foo&);
311 ~Foo();
312 class Nested {
313 void f();
314 };
315 };
316 class Friend {
317 };
318
319 void f1();
320 inline void f2() {}
321 static const int KInt = 2;
322 const char* kStr = "123";
323
324 namespace {
325 void ff() {} // ignore
326 }
327
328 void f1() {}
329
330 namespace foo {
331 // Type alias
332 typedef int int32;
333 using int32_t = int32;
334
335 // Variable
336 int v1;
337
338 // Namespace
339 namespace bar {
340 int v2;
341 }
342 // Namespace alias
343 namespace baz = bar;
344
345 using bar::v2;
346 } // namespace foo
347 )";
348 runSymbolCollector(Header, /*Main=*/"");
349 EXPECT_THAT(Symbols,
350 UnorderedElementsAreArray(
351 {AllOf(QName("Foo"), ForCodeCompletion(true)),
352 AllOf(QName("Foo::Foo"), ForCodeCompletion(false)),
353 AllOf(QName("Foo::Foo"), ForCodeCompletion(false)),
354 AllOf(QName("Foo::f"), ForCodeCompletion(false)),
355 AllOf(QName("Foo::~Foo"), ForCodeCompletion(false)),
356 AllOf(QName("Foo::operator="), ForCodeCompletion(false)),
357 AllOf(QName("Foo::Nested"), ForCodeCompletion(false)),
358 AllOf(QName("Foo::Nested::f"), ForCodeCompletion(false)),
359
360 AllOf(QName("Friend"), ForCodeCompletion(true)),
361 AllOf(QName("f1"), ForCodeCompletion(true)),
362 AllOf(QName("f2"), ForCodeCompletion(true)),
363 AllOf(QName("KInt"), ForCodeCompletion(true)),
364 AllOf(QName("kStr"), ForCodeCompletion(true)),
365 AllOf(QName("foo"), ForCodeCompletion(true)),
366 AllOf(QName("foo::bar"), ForCodeCompletion(true)),
367 AllOf(QName("foo::int32"), ForCodeCompletion(true)),
368 AllOf(QName("foo::int32_t"), ForCodeCompletion(true)),
369 AllOf(QName("foo::v1"), ForCodeCompletion(true)),
370 AllOf(QName("foo::bar::v2"), ForCodeCompletion(true)),
371 AllOf(QName("foo::v2"), ForCodeCompletion(true)),
372 AllOf(QName("foo::baz"), ForCodeCompletion(true))}));
373 }
374
375 TEST_F(SymbolCollectorTest, FileLocal) {
376 const std::string Header = R"(
377 class Foo {};
378 namespace {
379 class Ignored {};
380 }
381 void bar();
382 )";
383 const std::string Main = R"(
384 class ForwardDecl;
385 void bar() {}
386 static void a();
387 class B {};
388 namespace {
389 void c();
390 }
391 )";
392 runSymbolCollector(Header, Main);
393 EXPECT_THAT(Symbols,
394 UnorderedElementsAre(
395 AllOf(QName("Foo"), VisibleOutsideFile()),
396 AllOf(QName("bar"), VisibleOutsideFile()),
397 AllOf(QName("a"), Not(VisibleOutsideFile())),
398 AllOf(QName("B"), Not(VisibleOutsideFile())),
399 AllOf(QName("c"), Not(VisibleOutsideFile())),
400 // FIXME: ForwardDecl likely *is* visible outside.
401 AllOf(QName("ForwardDecl"), Not(VisibleOutsideFile()))));
402 }
403
404 TEST_F(SymbolCollectorTest, Template) {
405 Annotations Header(R"(
406 // Primary template and explicit specialization are indexed, instantiation
407 // is not.
408 template <class T, class U> struct [[Tmpl]] {T $xdecl[[x]] = 0;};
409 template <> struct $specdecl[[Tmpl]]<int, bool> {};
410 template <class U> struct $partspecdecl[[Tmpl]]<bool, U> {};
411 extern template struct Tmpl<float, bool>;
412 template struct Tmpl<double, bool>;
413 )");
414 runSymbolCollector(Header.code(), /*Main=*/"");
415 EXPECT_THAT(Symbols,
416 UnorderedElementsAre(
417 AllOf(QName("Tmpl"), DeclRange(Header.range()),
418 ForCodeCompletion(true)),
419 AllOf(QName("Tmpl"), DeclRange(Header.range("specdecl")),
420 ForCodeCompletion(false)),
421 AllOf(QName("Tmpl"), DeclRange(Header.range("partspecdecl")),
422 ForCodeCompletion(false)),
423 AllOf(QName("Tmpl::x"), DeclRange(Header.range("xdecl")),
424 ForCodeCompletion(false))));
425 }
426
427 TEST_F(SymbolCollectorTest, TemplateArgs) {
428 Annotations Header(R"(
429 template <class X> class $barclasstemp[[Bar]] {};
430 template <class T, class U, template<typename> class Z, int Q>
431 struct [[Tmpl]] { T $xdecl[[x]] = 0; };
432
433 // template-template, non-type and type full spec
434 template <> struct $specdecl[[Tmpl]]<int, bool, Bar, 3> {};
435
436 // template-template, non-type and type partial spec
437 template <class U, int T> struct $partspecdecl[[Tmpl]]<bool, U, Bar, T> {};
438 // instantiation
439 extern template struct Tmpl<float, bool, Bar, 8>;
440 // instantiation
441 template struct Tmpl<double, bool, Bar, 2>;
442
443 template <typename ...> class $fooclasstemp[[Foo]] {};
444 // parameter-packs full spec
445 template<> class $parampack[[Foo]]<Bar<int>, int, double> {};
446 // parameter-packs partial spec
447 template<class T> class $parampackpartial[[Foo]]<T, T> {};
448
449 template <int ...> class $bazclasstemp[[Baz]] {};
450 // non-type parameter-packs full spec
451 template<> class $parampacknontype[[Baz]]<3, 5, 8> {};
452 // non-type parameter-packs partial spec
453 template<int T> class $parampacknontypepartial[[Baz]]<T, T> {};
454
455 template <template <class> class ...> class $fozclasstemp[[Foz]] {};
456 // template-template parameter-packs full spec
457 template<> class $parampacktempltempl[[Foz]]<Bar, Bar> {};
458 // template-template parameter-packs partial spec
459 template<template <class> class T>
460 class $parampacktempltemplpartial[[Foz]]<T, T> {};
461 )");
462 runSymbolCollector(Header.code(), /*Main=*/"");
463 EXPECT_THAT(
464 Symbols,
465 AllOf(
466 Contains(AllOf(QName("Tmpl"), TemplateArgs("<int, bool, Bar, 3>"),
467 DeclRange(Header.range("specdecl")),
468 ForCodeCompletion(false))),
469 Contains(AllOf(QName("Tmpl"), TemplateArgs("<bool, U, Bar, T>"),
470 DeclRange(Header.range("partspecdecl")),
471 ForCodeCompletion(false))),
472 Contains(AllOf(QName("Foo"), TemplateArgs("<Bar<int>, int, double>"),
473 DeclRange(Header.range("parampack")),
474 ForCodeCompletion(false))),
475 Contains(AllOf(QName("Foo"), TemplateArgs("<T, T>"),
476 DeclRange(Header.range("parampackpartial")),
477 ForCodeCompletion(false))),
478 Contains(AllOf(QName("Baz"), TemplateArgs("<3, 5, 8>"),
479 DeclRange(Header.range("parampacknontype")),
480 ForCodeCompletion(false))),
481 Contains(AllOf(QName("Baz"), TemplateArgs("<T, T>"),
482 DeclRange(Header.range("parampacknontypepartial")),
483 ForCodeCompletion(false))),
484 Contains(AllOf(QName("Foz"), TemplateArgs("<Bar, Bar>"),
485 DeclRange(Header.range("parampacktempltempl")),
486 ForCodeCompletion(false))),
487 Contains(AllOf(QName("Foz"), TemplateArgs("<T, T>"),
488 DeclRange(Header.range("parampacktempltemplpartial")),
489 ForCodeCompletion(false)))));
490 }
491
492 TEST_F(SymbolCollectorTest, ObjCSymbols) {
493 const std::string Header = R"(
494 @interface Person
495 - (void)someMethodName:(void*)name1 lastName:(void*)lName;
496 @end
497
498 @implementation Person
499 - (void)someMethodName:(void*)name1 lastName:(void*)lName{
500 int foo;
501 ^(int param){ int bar; };
502 }
503 @end
504
505 @interface Person (MyCategory)
506 - (void)someMethodName2:(void*)name2;
507 @end
508
509 @implementation Person (MyCategory)
510 - (void)someMethodName2:(void*)name2 {
511 int foo2;
512 }
513 @end
514
515 @protocol MyProtocol
516 - (void)someMethodName3:(void*)name3;
517 @end
518 )";
519 TestFileName = testPath("test.m");
520 runSymbolCollector(Header, /*Main=*/"", {"-fblocks", "-xobjective-c++"});
521 EXPECT_THAT(Symbols,
522 UnorderedElementsAre(
523 QName("Person"), QName("Person::someMethodName:lastName:"),
524 QName("MyCategory"), QName("Person::someMethodName2:"),
525 QName("MyProtocol"), QName("MyProtocol::someMethodName3:")));
526 }
527
528 TEST_F(SymbolCollectorTest, ObjCPropertyImpl) {
529 const std::string Header = R"(
530 @interface Container
531 @property(nonatomic) int magic;
532 @end
533
534 @implementation Container
535 @end
536 )";
537 TestFileName = testPath("test.m");
538 runSymbolCollector(Header, /*Main=*/"", {"-xobjective-c++"});
539 EXPECT_THAT(Symbols, Contains(QName("Container")));
540 EXPECT_THAT(Symbols, Contains(QName("Container::magic")));
541 // FIXME: Results also contain Container::_magic on some platforms.
542 // Figure out why it's platform-dependent.
543 }
544
545 TEST_F(SymbolCollectorTest, Locations) {
546 Annotations Header(R"cpp(
547 // Declared in header, defined in main.
548 extern int $xdecl[[X]];
549 class $clsdecl[[Cls]];
550 void $printdecl[[print]]();
551
552 // Declared in header, defined nowhere.
553 extern int $zdecl[[Z]];
554
555 void $foodecl[[fo\
556 o]]();
557 )cpp");
558 Annotations Main(R"cpp(
559 int $xdef[[X]] = 42;
560 class $clsdef[[Cls]] {};
561 void $printdef[[print]]() {}
562
563 // Declared/defined in main only.
564 int $ydecl[[Y]];
565 )cpp");
566 runSymbolCollector(Header.code(), Main.code());
567 EXPECT_THAT(Symbols,
568 UnorderedElementsAre(
569 AllOf(QName("X"), DeclRange(Header.range("xdecl")),
570 DefRange(Main.range("xdef"))),
571 AllOf(QName("Cls"), DeclRange(Header.range("clsdecl")),
572 DefRange(Main.range("clsdef"))),
573 AllOf(QName("print"), DeclRange(Header.range("printdecl")),
574 DefRange(Main.range("printdef"))),
575 AllOf(QName("Z"), DeclRange(Header.range("zdecl"))),
576 AllOf(QName("foo"), DeclRange(Header.range("foodecl"))),
577 AllOf(QName("Y"), DeclRange(Main.range("ydecl")))));
578 }
579
580 TEST_F(SymbolCollectorTest, Refs) {
581 Annotations Header(R"(
582 #define MACRO(X) (X + 1)
583 class Foo {
584 public:
585 Foo() {}
586 Foo(int);
587 };
588 class Bar;
589 void func();
590
591 namespace NS {} // namespace ref is ignored
592 )");
593 Annotations Main(R"(
594 class $bar[[Bar]] {};
595
596 void $func[[func]]();
597
598 void fff() {
599 $foo[[Foo]] foo;
600 $bar[[Bar]] bar;
601 $func[[func]]();
602 int abc = 0;
603 $foo[[Foo]] foo2 = abc;
604 abc = $macro[[MACRO]](1);
605 }
606 )");
607 Annotations SymbolsOnlyInMainCode(R"(
608 #define FUNC(X) (X+1)
609 int a;
610 void b() {}
611 static const int c = FUNC(1);
612 class d {};
613 )");
614 CollectorOpts.RefFilter = RefKind::All;
615 CollectorOpts.CollectMacro = true;
616 runSymbolCollector(Header.code(),
617 (Main.code() + SymbolsOnlyInMainCode.code()).str());
618 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Foo").ID,
619 HaveRanges(Main.ranges("foo")))));
620 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Bar").ID,
621 HaveRanges(Main.ranges("bar")))));
622 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "func").ID,
623 HaveRanges(Main.ranges("func")))));
624 EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(Symbols, "NS").ID, _))));
625 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "MACRO").ID,
626 HaveRanges(Main.ranges("macro")))));
627 // Symbols *only* in the main file (a, b, c, FUNC) had no refs collected.
628 auto MainSymbols =
629 TestTU::withHeaderCode(SymbolsOnlyInMainCode.code()).headerSymbols();
630 EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(MainSymbols, "a").ID, _))));
631 EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(MainSymbols, "b").ID, _))));
632 EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(MainSymbols, "c").ID, _))));
633 EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(MainSymbols, "FUNC").ID, _))));
634 }
635
636 TEST_F(SymbolCollectorTest, MacroRefInHeader) {
637 Annotations Header(R"(
638 #define $foo[[FOO]](X) (X + 1)
639 #define $bar[[BAR]](X) (X + 2)
640
641 // Macro defined multiple times.
642 #define $ud1[[UD]] 1
643 int ud_1 = $ud1[[UD]];
644 #undef UD
645
646 #define $ud2[[UD]] 2
647 int ud_2 = $ud2[[UD]];
648 #undef UD
649
650 // Macros from token concatenations not included.
651 #define $concat[[CONCAT]](X) X##A()
652 #define $prepend[[PREPEND]](X) MACRO##X()
653 #define $macroa[[MACROA]]() 123
654 int B = $concat[[CONCAT]](MACRO);
655 int D = $prepend[[PREPEND]](A);
656
657 void fff() {
658 int abc = $foo[[FOO]](1) + $bar[[BAR]]($foo[[FOO]](1));
659 }
660 )");
661 CollectorOpts.RefFilter = RefKind::All;
662 CollectorOpts.RefsInHeaders = true;
663 // Need this to get the SymbolID for macros for tests.
664 CollectorOpts.CollectMacro = true;
665
666 runSymbolCollector(Header.code(), "");
667
668 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "FOO").ID,
669 HaveRanges(Header.ranges("foo")))));
670 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "BAR").ID,
671 HaveRanges(Header.ranges("bar")))));
672 // No unique ID for multiple symbols named UD. Check for ranges only.
673 EXPECT_THAT(Refs, Contains(Pair(_, HaveRanges(Header.ranges("ud1")))));
674 EXPECT_THAT(Refs, Contains(Pair(_, HaveRanges(Header.ranges("ud2")))));
675 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "CONCAT").ID,
676 HaveRanges(Header.ranges("concat")))));
677 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "PREPEND").ID,
678 HaveRanges(Header.ranges("prepend")))));
679 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "MACROA").ID,
680 HaveRanges(Header.ranges("macroa")))));
681 }
682
683 TEST_F(SymbolCollectorTest, MacroRefWithoutCollectingSymbol) {
684 Annotations Header(R"(
685 #define $foo[[FOO]](X) (X + 1)
686 int abc = $foo[[FOO]](1);
687 )");
688 CollectorOpts.RefFilter = RefKind::All;
689 CollectorOpts.RefsInHeaders = true;
690 CollectorOpts.CollectMacro = false;
691 runSymbolCollector(Header.code(), "");
692 EXPECT_THAT(Refs, Contains(Pair(_, HaveRanges(Header.ranges("foo")))));
693 }
694
695 TEST_F(SymbolCollectorTest, MacrosWithRefFilter) {
696 Annotations Header("#define $macro[[MACRO]](X) (X + 1)");
697 Annotations Main("void foo() { int x = $macro[[MACRO]](1); }");
698 CollectorOpts.RefFilter = RefKind::Unknown;
699 runSymbolCollector(Header.code(), Main.code());
700 EXPECT_THAT(Refs, IsEmpty());
701 }
702
703 TEST_F(SymbolCollectorTest, NameReferences) {
704 CollectorOpts.RefFilter = RefKind::All;
705 CollectorOpts.RefsInHeaders = true;
706 Annotations Header(R"(
707 class [[Foo]] {
708 public:
709 [[Foo]]() {}
710 ~[[Foo]]() {}
711 };
712 )");
713 CollectorOpts.RefFilter = RefKind::All;
714 runSymbolCollector(Header.code(), "");
715 // When we find references for class Foo, we expect to see all
716 // constructor/destructor references.
717 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Foo").ID,
718 HaveRanges(Header.ranges()))));
719 }
720
721 TEST_F(SymbolCollectorTest, RefsOnMacros) {
722 // Refs collected from SymbolCollector behave in the same way as
723 // AST-based xrefs.
724 CollectorOpts.RefFilter = RefKind::All;
725 CollectorOpts.RefsInHeaders = true;
726 Annotations Header(R"(
727 #define TYPE(X) X
728 #define FOO Foo
729 #define CAT(X, Y) X##Y
730 class [[Foo]] {};
731 void test() {
732 TYPE([[Foo]]) foo;
733 [[FOO]] foo2;
734 TYPE(TYPE([[Foo]])) foo3;
735 [[CAT]](Fo, o) foo4;
736 }
737 )");
738 CollectorOpts.RefFilter = RefKind::All;
739 runSymbolCollector(Header.code(), "");
740 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Foo").ID,
741 HaveRanges(Header.ranges()))));
742 }
743
744 TEST_F(SymbolCollectorTest, HeaderAsMainFile) {
745 CollectorOpts.RefFilter = RefKind::All;
746 Annotations Header(R"(
747 class $Foo[[Foo]] {};
748
749 void $Func[[Func]]() {
750 $Foo[[Foo]] fo;
751 }
752 )");
753 // The main file is normal .cpp file, we shouldn't collect any refs of symbols
754 // which are not declared in the preamble.
755 TestFileName = testPath("foo.cpp");
756 runSymbolCollector("", Header.code());
757 EXPECT_THAT(Refs, UnorderedElementsAre());
758
759 // Run the .h file as main file, we should collect the refs.
760 TestFileName = testPath("foo.h");
761 runSymbolCollector("", Header.code(),
762 /*ExtraArgs=*/{"-xobjective-c++-header"});
763 EXPECT_THAT(Symbols, UnorderedElementsAre(QName("Foo"), QName("Func")));
764 EXPECT_THAT(Refs,
765 UnorderedElementsAre(Pair(findSymbol(Symbols, "Foo").ID,
766 HaveRanges(Header.ranges("Foo"))),
767 Pair(findSymbol(Symbols, "Func").ID,
768 HaveRanges(Header.ranges("Func")))));
769
770 // Run the .hh file as main file (without "-x c++-header"), we should collect
771 // the refs as well.
772 TestFileName = testPath("foo.hh");
773 runSymbolCollector("", Header.code());
774 EXPECT_THAT(Symbols, UnorderedElementsAre(QName("Foo"), QName("Func")));
775 EXPECT_THAT(Refs,
776 UnorderedElementsAre(Pair(findSymbol(Symbols, "Foo").ID,
777 HaveRanges(Header.ranges("Foo"))),
778 Pair(findSymbol(Symbols, "Func").ID,
779 HaveRanges(Header.ranges("Func")))));
780 }
781
782 TEST_F(SymbolCollectorTest, RefsInHeaders) {
783 CollectorOpts.RefFilter = RefKind::All;
784 CollectorOpts.RefsInHeaders = true;
785 CollectorOpts.CollectMacro = true;
786 Annotations Header(R"(
787 #define $macro[[MACRO]](x) (x+1)
788 class $foo[[Foo]] {};
789 )");
790 runSymbolCollector(Header.code(), "");
791 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Foo").ID,
792 HaveRanges(Header.ranges("foo")))));
793 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "MACRO").ID,
794 HaveRanges(Header.ranges("macro")))));
795 }
796
797 TEST_F(SymbolCollectorTest, Relations) {
798 std::string Header = R"(
799 class Base {};
800 class Derived : public Base {};
801 )";
802 runSymbolCollector(Header, /*Main=*/"");
803 const Symbol &Base = findSymbol(Symbols, "Base");
804 const Symbol &Derived = findSymbol(Symbols, "Derived");
805 EXPECT_THAT(Relations,
806 Contains(Relation{Base.ID, RelationKind::BaseOf, Derived.ID}));
807 }
808
809 TEST_F(SymbolCollectorTest, CountReferences) {
810 const std::string Header = R"(
811 class W;
812 class X {};
813 class Y;
814 class Z {}; // not used anywhere
815 Y* y = nullptr; // used in header doesn't count
816 #define GLOBAL_Z(name) Z name;
817 )";
818 const std::string Main = R"(
819 W* w = nullptr;
820 W* w2 = nullptr; // only one usage counts
821 X x();
822 class V;
823 class Y{}; // definition doesn't count as a reference
824 V* v = nullptr;
825 GLOBAL_Z(z); // Not a reference to Z, we don't spell the type.
826 )";
827 CollectorOpts.CountReferences = true;
828 runSymbolCollector(Header, Main);
829 EXPECT_THAT(
830 Symbols,
831 UnorderedElementsAreArray(
832 {AllOf(QName("W"), RefCount(1)), AllOf(QName("X"), RefCount(1)),
833 AllOf(QName("Y"), RefCount(0)), AllOf(QName("Z"), RefCount(0)),
834 AllOf(QName("y"), RefCount(0)), AllOf(QName("z"), RefCount(0)),
835 AllOf(QName("x"), RefCount(0)), AllOf(QName("w"), RefCount(0)),
836 AllOf(QName("w2"), RefCount(0)), AllOf(QName("V"), RefCount(1)),
837 AllOf(QName("v"), RefCount(0))}));
838 }
839
840 TEST_F(SymbolCollectorTest, SymbolRelativeNoFallback) {
841 runSymbolCollector("class Foo {};", /*Main=*/"");
842 EXPECT_THAT(Symbols, UnorderedElementsAre(
843 AllOf(QName("Foo"), DeclURI(TestHeaderURI))));
844 }
845
846 TEST_F(SymbolCollectorTest, SymbolRelativeWithFallback) {
847 TestHeaderName = "x.h";
848 TestFileName = "x.cpp";
849 TestHeaderURI = URI::create(testPath(TestHeaderName)).toString();
850 CollectorOpts.FallbackDir = testRoot();
851 runSymbolCollector("class Foo {};", /*Main=*/"");
852 EXPECT_THAT(Symbols, UnorderedElementsAre(
853 AllOf(QName("Foo"), DeclURI(TestHeaderURI))));
854 }
855
856 TEST_F(SymbolCollectorTest, UnittestURIScheme) {
857 // Use test URI scheme from URITests.cpp
858 TestHeaderName = testPath("x.h");
859 TestFileName = testPath("x.cpp");
860 runSymbolCollector("class Foo {};", /*Main=*/"");
861 EXPECT_THAT(Symbols, UnorderedElementsAre(
862 AllOf(QName("Foo"), DeclURI("unittest:///x.h"))));
863 }
864
865 TEST_F(SymbolCollectorTest, IncludeEnums) {
866 const std::string Header = R"(
867 enum {
868 Red
869 };
870 enum Color {
871 Green
872 };
873 enum class Color2 {
874 Yellow
875 };
876 namespace ns {
877 enum {
878 Black
879 };
880 }
881 )";
882 runSymbolCollector(Header, /*Main=*/"");
883 EXPECT_THAT(Symbols,
884 UnorderedElementsAre(
885 AllOf(QName("Red"), ForCodeCompletion(true)),
886 AllOf(QName("Color"), ForCodeCompletion(true)),
887 AllOf(QName("Green"), ForCodeCompletion(true)),
888 AllOf(QName("Color2"), ForCodeCompletion(true)),
889 AllOf(QName("Color2::Yellow"), ForCodeCompletion(false)),
890 AllOf(QName("ns"), ForCodeCompletion(true)),
891 AllOf(QName("ns::Black"), ForCodeCompletion(true))));
892 }
893
894 TEST_F(SymbolCollectorTest, NamelessSymbols) {
895 const std::string Header = R"(
896 struct {
897 int a;
898 } Foo;
899 )";
900 runSymbolCollector(Header, /*Main=*/"");
901 EXPECT_THAT(Symbols, UnorderedElementsAre(QName("Foo"),
902 QName("(anonymous struct)::a")));
903 }
904
905 TEST_F(SymbolCollectorTest, SymbolFormedFromRegisteredSchemeFromMacro) {
906
907 Annotations Header(R"(
908 #define FF(name) \
909 class name##_Test {};
910
911 $expansion[[FF]](abc);
912
913 #define FF2() \
914 class $spelling[[Test]] {};
915
916 FF2();
917 )");
918
919 runSymbolCollector(Header.code(), /*Main=*/"");
920 EXPECT_THAT(Symbols,
921 UnorderedElementsAre(
922 AllOf(QName("abc_Test"), DeclRange(Header.range("expansion")),
923 DeclURI(TestHeaderURI)),
924 AllOf(QName("Test"), DeclRange(Header.range("spelling")),
925 DeclURI(TestHeaderURI))));
926 }
927
928 TEST_F(SymbolCollectorTest, SymbolFormedByCLI) {
929 Annotations Header(R"(
930 #ifdef NAME
931 class $expansion[[NAME]] {};
932 #endif
933 )");
934 runSymbolCollector(Header.code(), /*Main=*/"", /*ExtraArgs=*/{"-DNAME=name"});
935 EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(
936 QName("name"), DeclRange(Header.range("expansion")),
937 DeclURI(TestHeaderURI))));
938 }
939
940 TEST_F(SymbolCollectorTest, SymbolsInMainFile) {
941 const std::string Main = R"(
942 class Foo {};
943 void f1();
944 inline void f2() {}
945
946 namespace {
947 void ff() {}
948 }
949 namespace foo {
950 namespace {
951 class Bar {};
952 }
953 }
954 void main_f() {}
955 void f1() {}
956 )";
957 runSymbolCollector(/*Header=*/"", Main);
958 EXPECT_THAT(Symbols, UnorderedElementsAre(
959 QName("Foo"), QName("f1"), QName("f2"), QName("ff"),
960 QName("foo"), QName("foo::Bar"), QName("main_f")));
961 }
962
963 TEST_F(SymbolCollectorTest, Documentation) {
964 const std::string Header = R"(
965 // Doc Foo
966 class Foo {
967 // Doc f
968 int f();
969 };
970 )";
971 CollectorOpts.StoreAllDocumentation = false;
972 runSymbolCollector(Header, /* Main */ "");
973 EXPECT_THAT(Symbols,
974 UnorderedElementsAre(
975 AllOf(QName("Foo"), Doc("Doc Foo"), ForCodeCompletion(true)),
976 AllOf(QName("Foo::f"), Doc(""), ReturnType(""),
977 ForCodeCompletion(false))));
978
979 CollectorOpts.StoreAllDocumentation = true;
980 runSymbolCollector(Header, /* Main */ "");
981 EXPECT_THAT(Symbols,
982 UnorderedElementsAre(
983 AllOf(QName("Foo"), Doc("Doc Foo"), ForCodeCompletion(true)),
984 AllOf(QName("Foo::f"), Doc("Doc f"), ReturnType(""),
985 ForCodeCompletion(false))));
986 }
987
988 TEST_F(SymbolCollectorTest, ClassMembers) {
989 const std::string Header = R"(
990 class Foo {
991 void f() {}
992 void g();
993 static void sf() {}
994 static void ssf();
995 static int x;
996 };
997 )";
998 const std::string Main = R"(
999 void Foo::g() {}
1000 void Foo::ssf() {}
1001 )";
1002 runSymbolCollector(Header, Main);
1003 EXPECT_THAT(
1004 Symbols,
1005 UnorderedElementsAre(
1006 QName("Foo"),
1007 AllOf(QName("Foo::f"), ReturnType(""), ForCodeCompletion(false)),
1008 AllOf(QName("Foo::g"), ReturnType(""), ForCodeCompletion(false)),
1009 AllOf(QName("Foo::sf"), ReturnType(""), ForCodeCompletion(false)),
1010 AllOf(QName("Foo::ssf"), ReturnType(""), ForCodeCompletion(false)),
1011 AllOf(QName("Foo::x"), ReturnType(""), ForCodeCompletion(false))));
1012 }
1013
1014 TEST_F(SymbolCollectorTest, Scopes) {
1015 const std::string Header = R"(
1016 namespace na {
1017 class Foo {};
1018 namespace nb {
1019 class Bar {};
1020 }
1021 }
1022 )";
1023 runSymbolCollector(Header, /*Main=*/"");
1024 EXPECT_THAT(Symbols,
1025 UnorderedElementsAre(QName("na"), QName("na::nb"),
1026 QName("na::Foo"), QName("na::nb::Bar")));
1027 }
1028
1029 TEST_F(SymbolCollectorTest, ExternC) {
1030 const std::string Header = R"(
1031 extern "C" { class Foo {}; }
1032 namespace na {
1033 extern "C" { class Bar {}; }
1034 }
1035 )";
1036 runSymbolCollector(Header, /*Main=*/"");
1037 EXPECT_THAT(Symbols, UnorderedElementsAre(QName("na"), QName("Foo"),
1038 QName("na::Bar")));
1039 }
1040
1041 TEST_F(SymbolCollectorTest, SkipInlineNamespace) {
1042 const std::string Header = R"(
1043 namespace na {
1044 inline namespace nb {
1045 class Foo {};
1046 }
1047 }
1048 namespace na {
1049 // This is still inlined.
1050 namespace nb {
1051 class Bar {};
1052 }
1053 }
1054 )";
1055 runSymbolCollector(Header, /*Main=*/"");
1056 EXPECT_THAT(Symbols,
1057 UnorderedElementsAre(QName("na"), QName("na::nb"),
1058 QName("na::Foo"), QName("na::Bar")));
1059 }
1060
1061 TEST_F(SymbolCollectorTest, SymbolWithDocumentation) {
1062 const std::string Header = R"(
1063 namespace nx {
1064 /// Foo comment.
1065 int ff(int x, double y) { return 0; }
1066 }
1067 )";
1068 runSymbolCollector(Header, /*Main=*/"");
1069 EXPECT_THAT(
1070 Symbols,
1071 UnorderedElementsAre(
1072 QName("nx"), AllOf(QName("nx::ff"), Labeled("ff(int x, double y)"),
1073 ReturnType("int"), Doc("Foo comment."))));
1074 }
1075
1076 TEST_F(SymbolCollectorTest, Snippet) {
1077 const std::string Header = R"(
1078 namespace nx {
1079 void f() {}
1080 int ff(int x, double y) { return 0; }
1081 }
1082 )";
1083 runSymbolCollector(Header, /*Main=*/"");
1084 EXPECT_THAT(Symbols,
1085 UnorderedElementsAre(
1086 QName("nx"),
1087 AllOf(QName("nx::f"), Labeled("f()"), Snippet("f()")),
1088 AllOf(QName("nx::ff"), Labeled("ff(int x, double y)"),
1089 Snippet("ff(${1:int x}, ${2:double y})"))));
1090 }
1091
1092 TEST_F(SymbolCollectorTest, IncludeHeaderSameAsFileURI) {
1093 CollectorOpts.CollectIncludePath = true;
1094 runSymbolCollector("#pragma once\nclass Foo {};", /*Main=*/"");
1095 EXPECT_THAT(Symbols, UnorderedElementsAre(
1096 AllOf(QName("Foo"), DeclURI(TestHeaderURI))));
1097 EXPECT_THAT(Symbols.begin()->IncludeHeaders,
1098 UnorderedElementsAre(IncludeHeaderWithRef(TestHeaderURI, 1u)));
1099 }
1100
1101 TEST_F(SymbolCollectorTest, CanonicalSTLHeader) {
1102 CollectorOpts.CollectIncludePath = true;
1103 CanonicalIncludes Includes;
1104 auto Language = LangOptions();
1105 Language.CPlusPlus = true;
1106 Includes.addSystemHeadersMapping(Language);
1107 CollectorOpts.Includes = &Includes;
1108 runSymbolCollector("namespace std { class string {}; }", /*Main=*/"");
1109 EXPECT_THAT(Symbols,
1110 Contains(AllOf(QName("std::string"), DeclURI(TestHeaderURI),
1111 IncludeHeader("<string>"))));
1112 }
1113
1114 TEST_F(SymbolCollectorTest, IWYUPragma) {
1115 CollectorOpts.CollectIncludePath = true;
1116 CanonicalIncludes Includes;
1117 PragmaHandler = collectIWYUHeaderMaps(&Includes);
1118 CollectorOpts.Includes = &Includes;
1119 const std::string Header = R"(
1120 // IWYU pragma: private, include the/good/header.h
1121 class Foo {};
1122 )";
1123 runSymbolCollector(Header, /*Main=*/"");
1124 EXPECT_THAT(Symbols, UnorderedElementsAre(
1125 AllOf(QName("Foo"), DeclURI(TestHeaderURI),
1126 IncludeHeader("\"the/good/header.h\""))));
1127 }
1128
1129 TEST_F(SymbolCollectorTest, IWYUPragmaWithDoubleQuotes) {
1130 CollectorOpts.CollectIncludePath = true;
1131 CanonicalIncludes Includes;
1132 PragmaHandler = collectIWYUHeaderMaps(&Includes);
1133 CollectorOpts.Includes = &Includes;
1134 const std::string Header = R"(
1135 // IWYU pragma: private, include "the/good/header.h"
1136 class Foo {};
1137 )";
1138 runSymbolCollector(Header, /*Main=*/"");
1139 EXPECT_THAT(Symbols, UnorderedElementsAre(
1140 AllOf(QName("Foo"), DeclURI(TestHeaderURI),
1141 IncludeHeader("\"the/good/header.h\""))));
1142 }
1143
1144 TEST_F(SymbolCollectorTest, SkipIncFileWhenCanonicalizeHeaders) {
1145 CollectorOpts.CollectIncludePath = true;
1146 CanonicalIncludes Includes;
1147 Includes.addMapping(TestHeaderName, "<canonical>");
1148 CollectorOpts.Includes = &Includes;
1149 auto IncFile = testPath("test.inc");
1150 auto IncURI = URI::create(IncFile).toString();
1151 InMemoryFileSystem->addFile(IncFile, 0,
1152 llvm::MemoryBuffer::getMemBuffer("class X {};"));
1153 runSymbolCollector("#include \"test.inc\"\nclass Y {};", /*Main=*/"",
1154 /*ExtraArgs=*/{"-I", testRoot()});
1155 EXPECT_THAT(Symbols,
1156 UnorderedElementsAre(AllOf(QName("X"), DeclURI(IncURI),
1157 IncludeHeader("<canonical>")),
1158 AllOf(QName("Y"), DeclURI(TestHeaderURI),
1159 IncludeHeader("<canonical>"))));
1160 }
1161
1162 TEST_F(SymbolCollectorTest, MainFileIsHeaderWhenSkipIncFile) {
1163 CollectorOpts.CollectIncludePath = true;
1164 // To make this case as hard as possible, we won't tell clang main is a
1165 // header. No extension, no -x c++-header.
1166 TestFileName = testPath("no_ext_main");
1167 TestFileURI = URI::create(TestFileName).toString();
1168 auto IncFile = testPath("test.inc");
1169 auto IncURI = URI::create(IncFile).toString();
1170 InMemoryFileSystem->addFile(IncFile, 0,
1171 llvm::MemoryBuffer::getMemBuffer("class X {};"));
1172 runSymbolCollector("", R"cpp(
1173 // Can't use #pragma once in a main file clang doesn't think is a header.
1174 #ifndef MAIN_H_
1175 #define MAIN_H_
1176 #include "test.inc"
1177 #endif
1178 )cpp",
1179 /*ExtraArgs=*/{"-I", testRoot()});
1180 EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), DeclURI(IncURI),
1181 IncludeHeader(TestFileURI))));
1182 }
1183
1184 TEST_F(SymbolCollectorTest, IncFileInNonHeader) {
1185 CollectorOpts.CollectIncludePath = true;
1186 TestFileName = testPath("main.cc");
1187 TestFileURI = URI::create(TestFileName).toString();
1188 auto IncFile = testPath("test.inc");
1189 auto IncURI = URI::create(IncFile).toString();
1190 InMemoryFileSystem->addFile(IncFile, 0,
1191 llvm::MemoryBuffer::getMemBuffer("class X {};"));
1192 runSymbolCollector("", R"cpp(
1193 #include "test.inc"
1194 )cpp",
1195 /*ExtraArgs=*/{"-I", testRoot()});
1196 EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), DeclURI(IncURI),
1197 Not(IncludeHeader()))));
1198 }
1199
1200 // Features that depend on header-guards are fragile. Header guards are only
1201 // recognized when the file ends, so we have to defer checking for them.
1202 TEST_F(SymbolCollectorTest, HeaderGuardDetected) {
1203 CollectorOpts.CollectIncludePath = true;
1204 CollectorOpts.CollectMacro = true;
1205 runSymbolCollector(R"cpp(
1206 #ifndef HEADER_GUARD_
1207 #define HEADER_GUARD_
1208
1209 // Symbols are seen before the header guard is complete.
1210 #define MACRO
1211 int decl();
1212
1213 #endif // Header guard is recognized here.
1214 )cpp",
1215 "");
1216 EXPECT_THAT(Symbols, Not(Contains(QName("HEADER_GUARD_"))));
1217 EXPECT_THAT(Symbols, Each(IncludeHeader()));
1218 }
1219
1220 TEST_F(SymbolCollectorTest, NonModularHeader) {
1221 auto TU = TestTU::withHeaderCode("int x();");
1222 EXPECT_THAT(TU.headerSymbols(), ElementsAre(IncludeHeader()));
1223
1224 // Files missing include guards aren't eligible for insertion.
1225 TU.ImplicitHeaderGuard = false;
1226 EXPECT_THAT(TU.headerSymbols(), ElementsAre(Not(IncludeHeader())));
1227
1228 // We recognize some patterns of trying to prevent insertion.
1229 TU = TestTU::withHeaderCode(R"cpp(
1230 #ifndef SECRET
1231 #error "This file isn't safe to include directly"
1232 #endif
1233 int x();
1234 )cpp");
1235 TU.ExtraArgs.push_back("-DSECRET"); // *we're* able to include it.
1236 EXPECT_THAT(TU.headerSymbols(), ElementsAre(Not(IncludeHeader())));
1237 }
1238
1239 TEST_F(SymbolCollectorTest, AvoidUsingFwdDeclsAsCanonicalDecls) {
1240 CollectorOpts.CollectIncludePath = true;
1241 Annotations Header(R"(
1242 #pragma once
1243 // Forward declarations of TagDecls.
1244 class C;
1245 struct S;
1246 union U;
1247
1248 // Canonical declarations.
1249 class $cdecl[[C]] {};
1250 struct $sdecl[[S]] {};
1251 union $udecl[[U]] {int $xdecl[[x]]; bool $ydecl[[y]];};
1252 )");
1253 runSymbolCollector(Header.code(), /*Main=*/"");
1254 EXPECT_THAT(
1255 Symbols,
1256 UnorderedElementsAre(
1257 AllOf(QName("C"), DeclURI(TestHeaderURI),
1258 DeclRange(Header.range("cdecl")), IncludeHeader(TestHeaderURI),
1259 DefURI(TestHeaderURI), DefRange(Header.range("cdecl"))),
1260 AllOf(QName("S"), DeclURI(TestHeaderURI),
1261 DeclRange(Header.range("sdecl")), IncludeHeader(TestHeaderURI),
1262 DefURI(TestHeaderURI), DefRange(Header.range("sdecl"))),
1263 AllOf(QName("U"), DeclURI(TestHeaderURI),
1264 DeclRange(Header.range("udecl")), IncludeHeader(TestHeaderURI),
1265 DefURI(TestHeaderURI), DefRange(Header.range("udecl"))),
1266 AllOf(QName("U::x"), DeclURI(TestHeaderURI),
1267 DeclRange(Header.range("xdecl")), DefURI(TestHeaderURI),
1268 DefRange(Header.range("xdecl"))),
1269 AllOf(QName("U::y"), DeclURI(TestHeaderURI),
1270 DeclRange(Header.range("ydecl")), DefURI(TestHeaderURI),
1271 DefRange(Header.range("ydecl")))));
1272 }
1273
1274 TEST_F(SymbolCollectorTest, ClassForwardDeclarationIsCanonical) {
1275 CollectorOpts.CollectIncludePath = true;
1276 runSymbolCollector(/*Header=*/"#pragma once\nclass X;",
1277 /*Main=*/"class X {};");
1278 EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(
1279 QName("X"), DeclURI(TestHeaderURI),
1280 IncludeHeader(TestHeaderURI), DefURI(TestFileURI))));
1281 }
1282
1283 TEST_F(SymbolCollectorTest, UTF16Character) {
1284 // ö is 2-bytes.
1285 Annotations Header(/*Header=*/"class [[pörk]] {};");
1286 runSymbolCollector(Header.code(), /*Main=*/"");
1287 EXPECT_THAT(Symbols, UnorderedElementsAre(
1288 AllOf(QName("pörk"), DeclRange(Header.range()))));
1289 }
1290
1291 TEST_F(SymbolCollectorTest, DoNotIndexSymbolsInFriendDecl) {
1292 Annotations Header(R"(
1293 namespace nx {
1294 class $z[[Z]] {};
1295 class X {
1296 friend class Y;
1297 friend class Z;
1298 friend void foo();
1299 friend void $bar[[bar]]() {}
1300 };
1301 class $y[[Y]] {};
1302 void $foo[[foo]]();
1303 }
1304 )");
1305 runSymbolCollector(Header.code(), /*Main=*/"");
1306
1307 EXPECT_THAT(Symbols,
1308 UnorderedElementsAre(
1309 QName("nx"), QName("nx::X"),
1310 AllOf(QName("nx::Y"), DeclRange(Header.range("y"))),
1311 AllOf(QName("nx::Z"), DeclRange(Header.range("z"))),
1312 AllOf(QName("nx::foo"), DeclRange(Header.range("foo"))),
1313 AllOf(QName("nx::bar"), DeclRange(Header.range("bar")))));
1314 }
1315
1316 TEST_F(SymbolCollectorTest, ReferencesInFriendDecl) {
1317 const std::string Header = R"(
1318 class X;
1319 class Y;
1320 )";
1321 const std::string Main = R"(
1322 class C {
1323 friend ::X;
1324 friend class Y;
1325 };
1326 )";
1327 CollectorOpts.CountReferences = true;
1328 runSymbolCollector(Header, Main);
1329 EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), RefCount(1)),
1330 AllOf(QName("Y"), RefCount(1)),
1331 AllOf(QName("C"), RefCount(0))));
1332 }
1333
1334 TEST_F(SymbolCollectorTest, Origin) {
1335 CollectorOpts.Origin = SymbolOrigin::Static;
1336 runSymbolCollector("class Foo {};", /*Main=*/"");
1337 EXPECT_THAT(Symbols, UnorderedElementsAre(
1338 Field(&Symbol::Origin, SymbolOrigin::Static)));
1339 }
1340
1341 TEST_F(SymbolCollectorTest, CollectMacros) {
1342 CollectorOpts.CollectIncludePath = true;
1343 Annotations Header(R"(
1344 #pragma once
1345 #define X 1
1346 #define $mac[[MAC]](x) int x
1347 #define $used[[USED]](y) float y;
1348
1349 MAC(p);
1350 )");
1351
1352 Annotations Main(R"(
1353 #define $main[[MAIN]] 1
1354 USED(t);
1355 )");
1356 CollectorOpts.CountReferences = true;
1357 CollectorOpts.CollectMacro = true;
1358 runSymbolCollector(Header.code(), Main.code());
1359 EXPECT_THAT(
1360 Symbols,
1361 UnorderedElementsAre(
1362 QName("p"), QName("t"),
1363 AllOf(QName("X"), DeclURI(TestHeaderURI),
1364 IncludeHeader(TestHeaderURI)),
1365 AllOf(Labeled("MAC(x)"), RefCount(0),
1366
1367 DeclRange(Header.range("mac")), VisibleOutsideFile()),
1368 AllOf(Labeled("USED(y)"), RefCount(1),
1369 DeclRange(Header.range("used")), VisibleOutsideFile()),
1370 AllOf(Labeled("MAIN"), RefCount(0), DeclRange(Main.range("main")),
1371 Not(VisibleOutsideFile()))));
1372 }
1373
1374 TEST_F(SymbolCollectorTest, DeprecatedSymbols) {
1375 const std::string Header = R"(
1376 void TestClangc() __attribute__((deprecated("", "")));
1377 void TestClangd();
1378 )";
1379 runSymbolCollector(Header, /**/ "");
1380 EXPECT_THAT(Symbols, UnorderedElementsAre(
1381 AllOf(QName("TestClangc"), Deprecated()),
1382 AllOf(QName("TestClangd"), Not(Deprecated()))));
1383 }
1384
1385 TEST_F(SymbolCollectorTest, ImplementationDetail) {
1386 const std::string Header = R"(
1387 #define DECL_NAME(x, y) x##_##y##_Decl
1388 #define DECL(x, y) class DECL_NAME(x, y) {};
1389 DECL(X, Y); // X_Y_Decl
1390
1391 class Public {};
1392 )";
1393 runSymbolCollector(Header, /**/ "");
1394 EXPECT_THAT(Symbols,
1395 UnorderedElementsAre(
1396 AllOf(QName("X_Y_Decl"), ImplementationDetail()),
1397 AllOf(QName("Public"), Not(ImplementationDetail()))));
1398 }
1399
1400 TEST_F(SymbolCollectorTest, UsingDecl) {
1401 const char *Header = R"(
1402 void foo();
1403 namespace std {
1404 using ::foo;
1405 })";
1406 runSymbolCollector(Header, /**/ "");
1407 EXPECT_THAT(Symbols, Contains(QName("std::foo")));
1408 }
1409
1410 TEST_F(SymbolCollectorTest, CBuiltins) {
1411 // In C, printf in stdio.h is a redecl of an implicit builtin.
1412 const char *Header = R"(
1413 extern int printf(const char*, ...);
1414 )";
1415 runSymbolCollector(Header, /**/ "", {"-xc"});
1416 EXPECT_THAT(Symbols, Contains(QName("printf")));
1417 }
1418
1419 TEST_F(SymbolCollectorTest, InvalidSourceLoc) {
1420 const char *Header = R"(
1421 void operator delete(void*)
1422 __attribute__((__externally_visible__));)";
1423 runSymbolCollector(Header, /**/ "");
1424 EXPECT_THAT(Symbols, Contains(QName("operator delete")));
1425 }
1426
1427 } // namespace
1428 } // namespace clangd
1429 } // namespace clang
clang::clangd::collectIWYUHeaderMaps
std::unique_ptr< CommentHandler > collectIWYUHeaderMaps(CanonicalIncludes *Includes)
Returns a CommentHandler that parses pragma comment on include files to determine when we should incl...
Definition: CanonicalIncludes.cpp:63
clang::clangd::RefKind::Unknown
Code
std::string Code
Definition: FindTargetTests.cpp:67
clang::clangd::MATCHER_P
MATCHER_P(Named, N, "")
Definition: BackgroundIndexTests.cpp:26
HeaderName
std::string HeaderName
Definition: SymbolCollectorTests.cpp:135
CollectorOpts
SymbolCollector::Options CollectorOpts
Definition: SymbolCollectorTests.cpp:298
FileName
std::string FileName
Definition: SymbolCollectorTests.cpp:136
InMemoryFileSystem
llvm::IntrusiveRefCntPtr< llvm::vfs::InMemoryFileSystem > InMemoryFileSystem
Definition: SymbolCollectorTests.cpp:290
TestHeaderName
std::string TestHeaderName
Definition: SymbolCollectorTests.cpp:291
clang::clangd::isInsideMainFile
bool isInsideMainFile(SourceLocation Loc, const SourceManager &SM)
Returns true iff Loc is inside the main file.
Definition: SourceCode.cpp:534
clang::clangd::Symbol::VisibleOutsideFile
Symbol is visible to other files (not e.g. a static helper function).
Definition: Symbol.h:125
clang::clangd::RefKind::All
clang::clangd::RelationKind::BaseOf
TestFS.h
clang::clangd::findUnqualifiedDecl
const NamedDecl & findUnqualifiedDecl(ParsedAST &AST, llvm::StringRef Name)
Definition: TestTU.cpp:159
TestTU.h
TestHeaderURI
std::string TestHeaderURI
Definition: SymbolCollectorTests.cpp:292
MainFile
std::string MainFile
Definition: HeadersTests.cpp:121
clang::clangd::TEST_F
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
Definition: BackgroundIndexTests.cpp:88
clang::clangd::Symbol::ImplementationDetail
Symbol is an implementation detail.
Definition: Symbol.h:123
COpts
SymbolCollector::Options COpts
Definition: SymbolCollectorTests.cpp:243
QName
std::string QName
Definition: DiagnosticsTests.cpp:523
clang::clangd::SymbolOrigin::AST
clang::clangd::TestTU::withHeaderCode
static TestTU withHeaderCode(llvm::StringRef HeaderCode)
Definition: TestTU.h:39
Annotations.h
clang::clangd::Symbol::IndexedForCodeCompletion
Whether or not this symbol is meant to be used for the code completion.
Definition: Symbol.h:119
clang::clangd::testPath
std::string testPath(PathRef File)
Definition: TestFS.cpp:82
clang::clangd::CompletionItemKind::Field
clang::clangd::SymbolOrigin::Static
clang::clangd::MATCHER
MATCHER(Declared, "")
Definition: BackgroundIndexTests.cpp:27
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:27
clang::clangd::testRoot
const char * testRoot()
Definition: TestFS.cpp:74
clang::clangd::CompletionItemKind::Snippet
Relations
RelationSlab Relations
Definition: SymbolCollectorTests.cpp:297
Collector
std::shared_ptr< SymbolCollector > Collector
Definition: SymbolCollectorTests.cpp:242
Symbols
SymbolSlab Symbols
Definition: SymbolCollectorTests.cpp:295
ReturnType
std::string ReturnType
Definition: CodeComplete.cpp:395
Pos
Position Pos
Definition: SourceCode.cpp:772
clang::clangd::Position::line
int line
Line position in a document (zero-based).
Definition: Protocol.h:129
clang::clangd::Position::character
int character
Character offset on a line in a document (zero-based).
Definition: Protocol.h:134
clang::clangd::SymbolCollector::shouldCollectSymbol
static bool shouldCollectSymbol(const NamedDecl &ND, const ASTContext &ASTCtx, const Options &Opts, bool IsMainFileSymbol)
Returns true is ND should be collected.
Definition: SymbolCollector.cpp:202
clang::clangd::URI::create
static llvm::Expected< URI > create(llvm::StringRef AbsolutePath, llvm::StringRef Scheme)
Creates a URI for a file in the given scheme.
Definition: URI.cpp:197
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
SymbolCollector.h
References
unsigned References
Definition: CodeComplete.cpp:157
clang::clangd::findSymbol
const Symbol & findSymbol(const SymbolSlab &Slab, llvm::StringRef QName)
Definition: TestTU.cpp:97
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:38
IncludeHeader
std::string IncludeHeader
Definition: DiagnosticsTests.cpp:525
TestFileName
std::string TestFileName
Definition: SymbolCollectorTests.cpp:293
TestFileURI
std::string TestFileURI
Definition: SymbolCollectorTests.cpp:294
clang::clangd::Symbol::Deprecated
Indicates if the symbol is deprecated.
Definition: Symbol.h:121
Refs
RefSlab Refs
Definition: SymbolCollectorTests.cpp:296
Base
std::unique_ptr< GlobalCompilationDatabase > Base
Definition: GlobalCompilationDatabaseTests.cpp:83
clang::clangd::Symbol::Origin
SymbolOrigin Origin
Where this symbol came from. Usually an index provides a constant value.
Definition: Symbol.h:61
clang::clangd::CompletionItemKind::File
Files
llvm::StringMap< std::string > Files
Definition: TUSchedulerTests.cpp:119
clang::clangd::findDecl
const NamedDecl & findDecl(ParsedAST &AST, llvm::StringRef QName)
Definition: TestTU.cpp:118
PragmaHandler
CommentHandler * PragmaHandler
Definition: SymbolCollectorTests.cpp:244