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