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