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

source code of clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp