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