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