clang-tools  9.0.0
SymbolCollectorTests.cpp
Go to the documentation of this file.
1 //===-- SymbolCollectorTests.cpp -------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "Annotations.h"
10 #include "TestFS.h"
11 #include "TestTU.h"
12 #include "index/SymbolCollector.h"
13 #include "clang/Basic/FileManager.h"
14 #include "clang/Basic/FileSystemOptions.h"
15 #include "clang/Frontend/CompilerInstance.h"
16 #include "clang/Index/IndexingAction.h"
17 #include "clang/Tooling/Tooling.h"
18 #include "llvm/ADT/IntrusiveRefCntPtr.h"
19 #include "llvm/ADT/STLExtras.h"
20 #include "llvm/ADT/StringRef.h"
21 #include "llvm/Support/MemoryBuffer.h"
22 #include "llvm/Support/VirtualFileSystem.h"
23 #include "gmock/gmock-matchers.h"
24 #include "gmock/gmock-more-matchers.h"
25 #include "gmock/gmock.h"
26 #include "gtest/gtest.h"
27 
28 #include <memory>
29 #include <string>
30 
31 namespace clang {
32 namespace clangd {
33 namespace {
34 
35 using ::testing::_;
36 using ::testing::AllOf;
37 using ::testing::Contains;
38 using ::testing::Each;
39 using ::testing::ElementsAre;
40 using ::testing::Field;
41 using ::testing::Not;
42 using ::testing::Pair;
43 using ::testing::UnorderedElementsAre;
44 using ::testing::UnorderedElementsAreArray;
45 
46 // GMock helpers for matching Symbol.
47 MATCHER_P(Labeled, Label, "") {
48  return (arg.Name + arg.Signature).str() == Label;
49 }
50 MATCHER_P(ReturnType, D, "") { return arg.ReturnType == D; }
51 MATCHER_P(Doc, D, "") { return arg.Documentation == D; }
52 MATCHER_P(Snippet, S, "") {
53  return (arg.Name + arg.CompletionSnippetSuffix).str() == S;
54 }
55 MATCHER_P(QName, Name, "") { return (arg.Scope + arg.Name).str() == Name; }
56 MATCHER_P(TemplateArgs, TemplArgs, "") {
57  return arg.TemplateSpecializationArgs == TemplArgs;
58 }
59 MATCHER_P(DeclURI, P, "") {
60  return StringRef(arg.CanonicalDeclaration.FileURI) == P;
61 }
62 MATCHER_P(DefURI, P, "") { return StringRef(arg.Definition.FileURI) == P; }
63 MATCHER(IncludeHeader, "") { return !arg.IncludeHeaders.empty(); }
64 MATCHER_P(IncludeHeader, P, "") {
65  return (arg.IncludeHeaders.size() == 1) &&
66  (arg.IncludeHeaders.begin()->IncludeHeader == P);
67 }
68 MATCHER_P2(IncludeHeaderWithRef, IncludeHeader, References, "") {
69  return (arg.IncludeHeader == IncludeHeader) && (arg.References == References);
70 }
71 MATCHER_P(DeclRange, Pos, "") {
72  return std::make_tuple(arg.CanonicalDeclaration.Start.line(),
73  arg.CanonicalDeclaration.Start.column(),
74  arg.CanonicalDeclaration.End.line(),
75  arg.CanonicalDeclaration.End.column()) ==
76  std::make_tuple(Pos.start.line, Pos.start.character, Pos.end.line,
77  Pos.end.character);
78 }
79 MATCHER_P(DefRange, Pos, "") {
80  return std::make_tuple(
81  arg.Definition.Start.line(), arg.Definition.Start.column(),
82  arg.Definition.End.line(), arg.Definition.End.column()) ==
83  std::make_tuple(Pos.start.line, Pos.start.character, Pos.end.line,
84  Pos.end.character);
85 }
86 MATCHER_P(RefCount, R, "") { return int(arg.References) == R; }
87 MATCHER_P(ForCodeCompletion, IsIndexedForCodeCompletion, "") {
88  return static_cast<bool>(arg.Flags & Symbol::IndexedForCodeCompletion) ==
89  IsIndexedForCodeCompletion;
90 }
91 MATCHER(Deprecated, "") { return arg.Flags & Symbol::Deprecated; }
92 MATCHER(ImplementationDetail, "") {
93  return arg.Flags & Symbol::ImplementationDetail;
94 }
95 MATCHER(VisibleOutsideFile, "") {
96  return static_cast<bool>(arg.Flags & Symbol::VisibleOutsideFile);
97 }
98 MATCHER(RefRange, "") {
99  const Ref &Pos = ::testing::get<0>(arg);
100  const Range &Range = ::testing::get<1>(arg);
101  return std::make_tuple(Pos.Location.Start.line(), Pos.Location.Start.column(),
102  Pos.Location.End.line(), Pos.Location.End.column()) ==
103  std::make_tuple(Range.start.line, Range.start.character,
104  Range.end.line, Range.end.character);
105 }
106 ::testing::Matcher<const std::vector<Ref> &>
107 HaveRanges(const std::vector<Range> Ranges) {
108  return ::testing::UnorderedPointwise(RefRange(), Ranges);
109 }
110 
111 class ShouldCollectSymbolTest : public ::testing::Test {
112 public:
113  void build(llvm::StringRef HeaderCode, llvm::StringRef Code = "") {
114  File.HeaderFilename = HeaderName;
115  File.Filename = FileName;
116  File.HeaderCode = HeaderCode;
117  File.Code = Code;
118  AST = File.build();
119  }
120 
121  // build() must have been called.
122  bool shouldCollect(llvm::StringRef Name, bool Qualified = true) {
123  assert(AST.hasValue());
124  const NamedDecl &ND =
125  Qualified ? findDecl(*AST, Name) : findUnqualifiedDecl(*AST, Name);
126  const SourceManager &SM = AST->getSourceManager();
127  bool MainFile = isInsideMainFile(ND.getBeginLoc(), SM);
129  ND, AST->getASTContext(), SymbolCollector::Options(), MainFile);
130  }
131 
132 protected:
133  std::string HeaderName = "f.h";
134  std::string FileName = "f.cpp";
135  TestTU File;
136  llvm::Optional<ParsedAST> AST; // Initialized after build.
137 };
138 
139 TEST_F(ShouldCollectSymbolTest, ShouldCollectSymbol) {
140  build(R"(
141  namespace nx {
142  class X{};
143  auto f() { int Local; } // auto ensures function body is parsed.
144  struct { int x; } var;
145  }
146  )",
147  R"(
148  class InMain {};
149  namespace { class InAnonymous {}; }
150  static void g();
151  )");
152  auto AST = File.build();
153  EXPECT_TRUE(shouldCollect("nx"));
154  EXPECT_TRUE(shouldCollect("nx::X"));
155  EXPECT_TRUE(shouldCollect("nx::f"));
156  EXPECT_TRUE(shouldCollect("InMain"));
157  EXPECT_TRUE(shouldCollect("InAnonymous", /*Qualified=*/false));
158  EXPECT_TRUE(shouldCollect("g"));
159 
160  EXPECT_FALSE(shouldCollect("Local", /*Qualified=*/false));
161 }
162 
163 TEST_F(ShouldCollectSymbolTest, NoPrivateProtoSymbol) {
164  HeaderName = "f.proto.h";
165  build(
166  R"(// Generated by the protocol buffer compiler. DO NOT EDIT!
167  namespace nx {
168  class Top_Level {};
169  class TopLevel {};
170  enum Kind {
171  KIND_OK,
172  Kind_Not_Ok,
173  };
174  })");
175  EXPECT_TRUE(shouldCollect("nx::TopLevel"));
176  EXPECT_TRUE(shouldCollect("nx::Kind::KIND_OK"));
177  EXPECT_TRUE(shouldCollect("nx::Kind"));
178 
179  EXPECT_FALSE(shouldCollect("nx::Top_Level"));
180  EXPECT_FALSE(shouldCollect("nx::Kind::Kind_Not_Ok"));
181 }
182 
183 TEST_F(ShouldCollectSymbolTest, DoubleCheckProtoHeaderComment) {
184  HeaderName = "f.proto.h";
185  build(R"(
186  namespace nx {
187  class Top_Level {};
188  enum Kind {
189  Kind_Fine
190  };
191  }
192  )");
193  EXPECT_TRUE(shouldCollect("nx::Top_Level"));
194  EXPECT_TRUE(shouldCollect("nx::Kind_Fine"));
195 }
196 
197 class SymbolIndexActionFactory : public tooling::FrontendActionFactory {
198 public:
199  SymbolIndexActionFactory(SymbolCollector::Options COpts,
200  CommentHandler *PragmaHandler)
201  : COpts(std::move(COpts)), PragmaHandler(PragmaHandler) {}
202 
203  clang::FrontendAction *create() override {
204  class WrappedIndexAction : public WrapperFrontendAction {
205  public:
206  WrappedIndexAction(std::shared_ptr<SymbolCollector> C,
207  const index::IndexingOptions &Opts,
208  CommentHandler *PragmaHandler)
209  : WrapperFrontendAction(
210  index::createIndexingAction(C, Opts, nullptr)),
211  PragmaHandler(PragmaHandler) {}
212 
213  std::unique_ptr<ASTConsumer>
214  CreateASTConsumer(CompilerInstance &CI, llvm::StringRef InFile) override {
215  if (PragmaHandler)
216  CI.getPreprocessor().addCommentHandler(PragmaHandler);
217  return WrapperFrontendAction::CreateASTConsumer(CI, InFile);
218  }
219 
220  bool BeginInvocation(CompilerInstance &CI) override {
221  // Make the compiler parse all comments.
222  CI.getLangOpts().CommentOpts.ParseAllComments = true;
223  return WrapperFrontendAction::BeginInvocation(CI);
224  }
225 
226  private:
227  index::IndexingOptions IndexOpts;
228  CommentHandler *PragmaHandler;
229  };
230  index::IndexingOptions IndexOpts;
231  IndexOpts.SystemSymbolFilter =
232  index::IndexingOptions::SystemSymbolFilterKind::All;
233  IndexOpts.IndexFunctionLocals = false;
234  Collector = std::make_shared<SymbolCollector>(COpts);
235  return new WrappedIndexAction(Collector, std::move(IndexOpts),
236  PragmaHandler);
237  }
238 
239  std::shared_ptr<SymbolCollector> Collector;
240  SymbolCollector::Options COpts;
241  CommentHandler *PragmaHandler;
242 };
243 
244 class SymbolCollectorTest : public ::testing::Test {
245 public:
246  SymbolCollectorTest()
248  TestHeaderName(testPath("symbol.h")),
249  TestFileName(testPath("symbol.cc")) {
251  TestFileURI = URI::create(TestFileName).toString();
252  }
253 
254  // Note that unlike TestTU, no automatic header guard is added.
255  // HeaderCode should start with #pragma once to be treated as modular.
256  bool runSymbolCollector(llvm::StringRef HeaderCode, llvm::StringRef MainCode,
257  const std::vector<std::string> &ExtraArgs = {}) {
258  llvm::IntrusiveRefCntPtr<FileManager> Files(
259  new FileManager(FileSystemOptions(), InMemoryFileSystem));
260 
261  auto Factory = llvm::make_unique<SymbolIndexActionFactory>(
263 
264  std::vector<std::string> Args = {"symbol_collector", "-fsyntax-only",
265  "-xc++", "-include", TestHeaderName};
266  Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
267  // This allows to override the "-xc++" with something else, i.e.
268  // -xobjective-c++.
269  Args.push_back(TestFileName);
270 
271  tooling::ToolInvocation Invocation(
272  Args, Factory->create(), Files.get(),
273  std::make_shared<PCHContainerOperations>());
274 
276  llvm::MemoryBuffer::getMemBuffer(HeaderCode));
277  InMemoryFileSystem->addFile(TestFileName, 0,
278  llvm::MemoryBuffer::getMemBuffer(MainCode));
279  Invocation.run();
280  Symbols = Factory->Collector->takeSymbols();
281  Refs = Factory->Collector->takeRefs();
282  Relations = Factory->Collector->takeRelations();
283  return true;
284  }
285 
286 protected:
287  llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem;
288  std::string TestHeaderName;
289  std::string TestHeaderURI;
290  std::string TestFileName;
291  std::string TestFileURI;
292  SymbolSlab Symbols;
293  RefSlab Refs;
294  RelationSlab Relations;
295  SymbolCollector::Options CollectorOpts;
296  std::unique_ptr<CommentHandler> PragmaHandler;
297 };
298 
299 TEST_F(SymbolCollectorTest, CollectSymbols) {
300  const std::string Header = R"(
301  class Foo {
302  Foo() {}
303  Foo(int a) {}
304  void f();
305  friend void f1();
306  friend class Friend;
307  Foo& operator=(const Foo&);
308  ~Foo();
309  class Nested {
310  void f();
311  };
312  };
313  class Friend {
314  };
315 
316  void f1();
317  inline void f2() {}
318  static const int KInt = 2;
319  const char* kStr = "123";
320 
321  namespace {
322  void ff() {} // ignore
323  }
324 
325  void f1() {}
326 
327  namespace foo {
328  // Type alias
329  typedef int int32;
330  using int32_t = int32;
331 
332  // Variable
333  int v1;
334 
335  // Namespace
336  namespace bar {
337  int v2;
338  }
339  // Namespace alias
340  namespace baz = bar;
341 
342  using bar::v2;
343  } // namespace foo
344  )";
345  runSymbolCollector(Header, /*Main=*/"");
346  EXPECT_THAT(Symbols,
347  UnorderedElementsAreArray(
348  {AllOf(QName("Foo"), ForCodeCompletion(true)),
349  AllOf(QName("Foo::Foo"), ForCodeCompletion(false)),
350  AllOf(QName("Foo::Foo"), ForCodeCompletion(false)),
351  AllOf(QName("Foo::f"), ForCodeCompletion(false)),
352  AllOf(QName("Foo::~Foo"), ForCodeCompletion(false)),
353  AllOf(QName("Foo::operator="), ForCodeCompletion(false)),
354  AllOf(QName("Foo::Nested"), ForCodeCompletion(false)),
355  AllOf(QName("Foo::Nested::f"), ForCodeCompletion(false)),
356 
357  AllOf(QName("Friend"), ForCodeCompletion(true)),
358  AllOf(QName("f1"), ForCodeCompletion(true)),
359  AllOf(QName("f2"), ForCodeCompletion(true)),
360  AllOf(QName("KInt"), ForCodeCompletion(true)),
361  AllOf(QName("kStr"), ForCodeCompletion(true)),
362  AllOf(QName("foo"), ForCodeCompletion(true)),
363  AllOf(QName("foo::bar"), ForCodeCompletion(true)),
364  AllOf(QName("foo::int32"), ForCodeCompletion(true)),
365  AllOf(QName("foo::int32_t"), ForCodeCompletion(true)),
366  AllOf(QName("foo::v1"), ForCodeCompletion(true)),
367  AllOf(QName("foo::bar::v2"), ForCodeCompletion(true)),
368  AllOf(QName("foo::v2"), ForCodeCompletion(true)),
369  AllOf(QName("foo::baz"), ForCodeCompletion(true))}));
370 }
371 
372 TEST_F(SymbolCollectorTest, FileLocal) {
373  const std::string Header = R"(
374  class Foo {};
375  namespace {
376  class Ignored {};
377  }
378  void bar();
379  )";
380  const std::string Main = R"(
381  class ForwardDecl;
382  void bar() {}
383  static void a();
384  class B {};
385  namespace {
386  void c();
387  }
388  )";
389  runSymbolCollector(Header, Main);
390  EXPECT_THAT(Symbols,
391  UnorderedElementsAre(
392  AllOf(QName("Foo"), VisibleOutsideFile()),
393  AllOf(QName("bar"), VisibleOutsideFile()),
394  AllOf(QName("a"), Not(VisibleOutsideFile())),
395  AllOf(QName("B"), Not(VisibleOutsideFile())),
396  AllOf(QName("c"), Not(VisibleOutsideFile())),
397  // FIXME: ForwardDecl likely *is* visible outside.
398  AllOf(QName("ForwardDecl"), Not(VisibleOutsideFile()))));
399 }
400 
401 TEST_F(SymbolCollectorTest, Template) {
402  Annotations Header(R"(
403  // Primary template and explicit specialization are indexed, instantiation
404  // is not.
405  template <class T, class U> struct [[Tmpl]] {T $xdecl[[x]] = 0;};
406  template <> struct $specdecl[[Tmpl]]<int, bool> {};
407  template <class U> struct $partspecdecl[[Tmpl]]<bool, U> {};
408  extern template struct Tmpl<float, bool>;
409  template struct Tmpl<double, bool>;
410  )");
411  runSymbolCollector(Header.code(), /*Main=*/"");
412  EXPECT_THAT(Symbols,
413  UnorderedElementsAre(
414  AllOf(QName("Tmpl"), DeclRange(Header.range()),
415  ForCodeCompletion(true)),
416  AllOf(QName("Tmpl"), DeclRange(Header.range("specdecl")),
417  ForCodeCompletion(false)),
418  AllOf(QName("Tmpl"), DeclRange(Header.range("partspecdecl")),
419  ForCodeCompletion(false)),
420  AllOf(QName("Tmpl::x"), DeclRange(Header.range("xdecl")),
421  ForCodeCompletion(false))));
422 }
423 
424 TEST_F(SymbolCollectorTest, TemplateArgs) {
425  Annotations Header(R"(
426  template <class X> class $barclasstemp[[Bar]] {};
427  template <class T, class U, template<typename> class Z, int Q>
428  struct [[Tmpl]] { T $xdecl[[x]] = 0; };
429 
430  // template-template, non-type and type full spec
431  template <> struct $specdecl[[Tmpl]]<int, bool, Bar, 3> {};
432 
433  // template-template, non-type and type partial spec
434  template <class U, int T> struct $partspecdecl[[Tmpl]]<bool, U, Bar, T> {};
435  // instantiation
436  extern template struct Tmpl<float, bool, Bar, 8>;
437  // instantiation
438  template struct Tmpl<double, bool, Bar, 2>;
439 
440  template <typename ...> class $fooclasstemp[[Foo]] {};
441  // parameter-packs full spec
442  template<> class $parampack[[Foo]]<Bar<int>, int, double> {};
443  // parameter-packs partial spec
444  template<class T> class $parampackpartial[[Foo]]<T, T> {};
445 
446  template <int ...> class $bazclasstemp[[Baz]] {};
447  // non-type parameter-packs full spec
448  template<> class $parampacknontype[[Baz]]<3, 5, 8> {};
449  // non-type parameter-packs partial spec
450  template<int T> class $parampacknontypepartial[[Baz]]<T, T> {};
451 
452  template <template <class> class ...> class $fozclasstemp[[Foz]] {};
453  // template-template parameter-packs full spec
454  template<> class $parampacktempltempl[[Foz]]<Bar, Bar> {};
455  // template-template parameter-packs partial spec
456  template<template <class> class T>
457  class $parampacktempltemplpartial[[Foz]]<T, T> {};
458  )");
459  runSymbolCollector(Header.code(), /*Main=*/"");
460  EXPECT_THAT(
461  Symbols,
462  AllOf(
463  Contains(AllOf(QName("Tmpl"), TemplateArgs("<int, bool, Bar, 3>"),
464  DeclRange(Header.range("specdecl")),
465  ForCodeCompletion(false))),
466  Contains(AllOf(QName("Tmpl"), TemplateArgs("<bool, U, Bar, T>"),
467  DeclRange(Header.range("partspecdecl")),
468  ForCodeCompletion(false))),
469  Contains(AllOf(QName("Foo"), TemplateArgs("<Bar<int>, int, double>"),
470  DeclRange(Header.range("parampack")),
471  ForCodeCompletion(false))),
472  Contains(AllOf(QName("Foo"), TemplateArgs("<T, T>"),
473  DeclRange(Header.range("parampackpartial")),
474  ForCodeCompletion(false))),
475  Contains(AllOf(QName("Baz"), TemplateArgs("<3, 5, 8>"),
476  DeclRange(Header.range("parampacknontype")),
477  ForCodeCompletion(false))),
478  Contains(AllOf(QName("Baz"), TemplateArgs("<T, T>"),
479  DeclRange(Header.range("parampacknontypepartial")),
480  ForCodeCompletion(false))),
481  Contains(AllOf(QName("Foz"), TemplateArgs("<Bar, Bar>"),
482  DeclRange(Header.range("parampacktempltempl")),
483  ForCodeCompletion(false))),
484  Contains(AllOf(QName("Foz"), TemplateArgs("<T, T>"),
485  DeclRange(Header.range("parampacktempltemplpartial")),
486  ForCodeCompletion(false)))));
487 }
488 
489 TEST_F(SymbolCollectorTest, ObjCSymbols) {
490  const std::string Header = R"(
491  @interface Person
492  - (void)someMethodName:(void*)name1 lastName:(void*)lName;
493  @end
494 
495  @implementation Person
496  - (void)someMethodName:(void*)name1 lastName:(void*)lName{
497  int foo;
498  ^(int param){ int bar; };
499  }
500  @end
501 
502  @interface Person (MyCategory)
503  - (void)someMethodName2:(void*)name2;
504  @end
505 
506  @implementation Person (MyCategory)
507  - (void)someMethodName2:(void*)name2 {
508  int foo2;
509  }
510  @end
511 
512  @protocol MyProtocol
513  - (void)someMethodName3:(void*)name3;
514  @end
515  )";
516  TestFileName = testPath("test.m");
517  runSymbolCollector(Header, /*Main=*/"", {"-fblocks", "-xobjective-c++"});
518  EXPECT_THAT(Symbols,
519  UnorderedElementsAre(
520  QName("Person"), QName("Person::someMethodName:lastName:"),
521  QName("MyCategory"), QName("Person::someMethodName2:"),
522  QName("MyProtocol"), QName("MyProtocol::someMethodName3:")));
523 }
524 
525 TEST_F(SymbolCollectorTest, ObjCPropertyImpl) {
526  const std::string Header = R"(
527  @interface Container
528  @property(nonatomic) int magic;
529  @end
530 
531  @implementation Container
532  @end
533  )";
534  TestFileName = testPath("test.m");
535  runSymbolCollector(Header, /*Main=*/"", {"-xobjective-c++"});
536  EXPECT_THAT(Symbols, Contains(QName("Container")));
537  EXPECT_THAT(Symbols, Contains(QName("Container::magic")));
538  // FIXME: Results also contain Container::_magic on some platforms.
539  // Figure out why it's platform-dependent.
540 }
541 
542 TEST_F(SymbolCollectorTest, Locations) {
543  Annotations Header(R"cpp(
544  // Declared in header, defined in main.
545  extern int $xdecl[[X]];
546  class $clsdecl[[Cls]];
547  void $printdecl[[print]]();
548 
549  // Declared in header, defined nowhere.
550  extern int $zdecl[[Z]];
551 
552  void $foodecl[[fo\
553 o]]();
554  )cpp");
555  Annotations Main(R"cpp(
556  int $xdef[[X]] = 42;
557  class $clsdef[[Cls]] {};
558  void $printdef[[print]]() {}
559 
560  // Declared/defined in main only.
561  int $ydecl[[Y]];
562  )cpp");
563  runSymbolCollector(Header.code(), Main.code());
564  EXPECT_THAT(Symbols,
565  UnorderedElementsAre(
566  AllOf(QName("X"), DeclRange(Header.range("xdecl")),
567  DefRange(Main.range("xdef"))),
568  AllOf(QName("Cls"), DeclRange(Header.range("clsdecl")),
569  DefRange(Main.range("clsdef"))),
570  AllOf(QName("print"), DeclRange(Header.range("printdecl")),
571  DefRange(Main.range("printdef"))),
572  AllOf(QName("Z"), DeclRange(Header.range("zdecl"))),
573  AllOf(QName("foo"), DeclRange(Header.range("foodecl"))),
574  AllOf(QName("Y"), DeclRange(Main.range("ydecl")))));
575 }
576 
577 TEST_F(SymbolCollectorTest, Refs) {
578  Annotations Header(R"(
579  class $foo[[Foo]] {
580  public:
581  $foo[[Foo]]() {}
582  $foo[[Foo]](int);
583  };
584  class $bar[[Bar]];
585  void $func[[func]]();
586 
587  namespace $ns[[NS]] {} // namespace ref is ignored
588  )");
589  Annotations Main(R"(
590  class $bar[[Bar]] {};
591 
592  void $func[[func]]();
593 
594  void fff() {
595  $foo[[Foo]] foo;
596  $bar[[Bar]] bar;
597  $func[[func]]();
598  int abc = 0;
599  $foo[[Foo]] foo2 = abc;
600  }
601  )");
602  Annotations SymbolsOnlyInMainCode(R"(
603  int a;
604  void b() {}
605  static const int c = 0;
606  class d {};
607  )");
608  CollectorOpts.RefFilter = RefKind::All;
609  runSymbolCollector(Header.code(),
610  (Main.code() + SymbolsOnlyInMainCode.code()).str());
611  auto HeaderSymbols = TestTU::withHeaderCode(Header.code()).headerSymbols();
612 
613  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Foo").ID,
614  HaveRanges(Main.ranges("foo")))));
615  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Bar").ID,
616  HaveRanges(Main.ranges("bar")))));
617  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "func").ID,
618  HaveRanges(Main.ranges("func")))));
619  EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(Symbols, "NS").ID, _))));
620  // Symbols *only* in the main file (a, b, c) had no refs collected.
621  auto MainSymbols =
622  TestTU::withHeaderCode(SymbolsOnlyInMainCode.code()).headerSymbols();
623  EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(MainSymbols, "a").ID, _))));
624  EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(MainSymbols, "b").ID, _))));
625  EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(MainSymbols, "c").ID, _))));
626 }
627 
628 
629 TEST_F(SymbolCollectorTest, HeaderAsMainFile) {
630  CollectorOpts.RefFilter = RefKind::All;
631  Annotations Header(R"(
632  class $Foo[[Foo]] {};
633 
634  void $Func[[Func]]() {
635  $Foo[[Foo]] fo;
636  }
637  )");
638  // The main file is normal .cpp file, we shouldn't collect any refs of symbols
639  // which are not declared in the preamble.
640  TestFileName = testPath("foo.cpp");
641  runSymbolCollector("", Header.code());
642  EXPECT_THAT(Refs, UnorderedElementsAre());
643 
644  // Run the .h file as main file, we should collect the refs.
645  TestFileName = testPath("foo.h");
646  runSymbolCollector("", Header.code(),
647  /*ExtraArgs=*/{"-xobjective-c++-header"});
648  EXPECT_THAT(Symbols, UnorderedElementsAre(QName("Foo"), QName("Func")));
649  EXPECT_THAT(Refs, UnorderedElementsAre(Pair(findSymbol(Symbols, "Foo").ID,
650  HaveRanges(Header.ranges("Foo"))),
651  Pair(findSymbol(Symbols, "Func").ID,
652  HaveRanges(Header.ranges("Func")))));
653 }
654 
655 TEST_F(SymbolCollectorTest, RefsInHeaders) {
656  CollectorOpts.RefFilter = RefKind::All;
657  CollectorOpts.RefsInHeaders = true;
658  Annotations Header(R"(
659  class [[Foo]] {};
660  )");
661  runSymbolCollector(Header.code(), "");
662  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Foo").ID,
663  HaveRanges(Header.ranges()))));
664 }
665 
666 TEST_F(SymbolCollectorTest, Relations) {
667  std::string Header = R"(
668  class Base {};
669  class Derived : public Base {};
670  )";
671  runSymbolCollector(Header, /*Main=*/"");
672  const Symbol &Base = findSymbol(Symbols, "Base");
673  const Symbol &Derived = findSymbol(Symbols, "Derived");
674  EXPECT_THAT(Relations,
675  Contains(Relation{Base.ID, index::SymbolRole::RelationBaseOf,
676  Derived.ID}));
677 }
678 
679 TEST_F(SymbolCollectorTest, References) {
680  const std::string Header = R"(
681  class W;
682  class X {};
683  class Y;
684  class Z {}; // not used anywhere
685  Y* y = nullptr; // used in header doesn't count
686  #define GLOBAL_Z(name) Z name;
687  )";
688  const std::string Main = R"(
689  W* w = nullptr;
690  W* w2 = nullptr; // only one usage counts
691  X x();
692  class V;
693  class Y{}; // definition doesn't count as a reference
694  V* v = nullptr;
695  GLOBAL_Z(z); // Not a reference to Z, we don't spell the type.
696  )";
697  CollectorOpts.CountReferences = true;
698  runSymbolCollector(Header, Main);
699  EXPECT_THAT(
700  Symbols,
701  UnorderedElementsAreArray(
702  {AllOf(QName("W"), RefCount(1)), AllOf(QName("X"), RefCount(1)),
703  AllOf(QName("Y"), RefCount(0)), AllOf(QName("Z"), RefCount(0)),
704  AllOf(QName("y"), RefCount(0)), AllOf(QName("z"), RefCount(0)),
705  AllOf(QName("x"), RefCount(0)), AllOf(QName("w"), RefCount(0)),
706  AllOf(QName("w2"), RefCount(0)), AllOf(QName("V"), RefCount(1)),
707  AllOf(QName("v"), RefCount(0))}));
708 }
709 
710 TEST_F(SymbolCollectorTest, SymbolRelativeNoFallback) {
711  runSymbolCollector("class Foo {};", /*Main=*/"");
712  EXPECT_THAT(Symbols, UnorderedElementsAre(
713  AllOf(QName("Foo"), DeclURI(TestHeaderURI))));
714 }
715 
716 TEST_F(SymbolCollectorTest, SymbolRelativeWithFallback) {
717  TestHeaderName = "x.h";
718  TestFileName = "x.cpp";
720  CollectorOpts.FallbackDir = testRoot();
721  runSymbolCollector("class Foo {};", /*Main=*/"");
722  EXPECT_THAT(Symbols, UnorderedElementsAre(
723  AllOf(QName("Foo"), DeclURI(TestHeaderURI))));
724 }
725 
726 TEST_F(SymbolCollectorTest, UnittestURIScheme) {
727  // Use test URI scheme from URITests.cpp
728  TestHeaderName = testPath("x.h");
729  TestFileName = testPath("x.cpp");
730  runSymbolCollector("class Foo {};", /*Main=*/"");
731  EXPECT_THAT(Symbols, UnorderedElementsAre(
732  AllOf(QName("Foo"), DeclURI("unittest:///x.h"))));
733 }
734 
735 TEST_F(SymbolCollectorTest, IncludeEnums) {
736  const std::string Header = R"(
737  enum {
738  Red
739  };
740  enum Color {
741  Green
742  };
743  enum class Color2 {
744  Yellow
745  };
746  namespace ns {
747  enum {
748  Black
749  };
750  }
751  )";
752  runSymbolCollector(Header, /*Main=*/"");
753  EXPECT_THAT(Symbols,
754  UnorderedElementsAre(
755  AllOf(QName("Red"), ForCodeCompletion(true)),
756  AllOf(QName("Color"), ForCodeCompletion(true)),
757  AllOf(QName("Green"), ForCodeCompletion(true)),
758  AllOf(QName("Color2"), ForCodeCompletion(true)),
759  AllOf(QName("Color2::Yellow"), ForCodeCompletion(false)),
760  AllOf(QName("ns"), ForCodeCompletion(true)),
761  AllOf(QName("ns::Black"), ForCodeCompletion(true))));
762 }
763 
764 TEST_F(SymbolCollectorTest, NamelessSymbols) {
765  const std::string Header = R"(
766  struct {
767  int a;
768  } Foo;
769  )";
770  runSymbolCollector(Header, /*Main=*/"");
771  EXPECT_THAT(Symbols, UnorderedElementsAre(QName("Foo"),
772  QName("(anonymous struct)::a")));
773 }
774 
775 TEST_F(SymbolCollectorTest, SymbolFormedFromRegisteredSchemeFromMacro) {
776 
777  Annotations Header(R"(
778  #define FF(name) \
779  class name##_Test {};
780 
781  $expansion[[FF]](abc);
782 
783  #define FF2() \
784  class $spelling[[Test]] {};
785 
786  FF2();
787  )");
788 
789  runSymbolCollector(Header.code(), /*Main=*/"");
790  EXPECT_THAT(Symbols,
791  UnorderedElementsAre(
792  AllOf(QName("abc_Test"), DeclRange(Header.range("expansion")),
793  DeclURI(TestHeaderURI)),
794  AllOf(QName("Test"), DeclRange(Header.range("spelling")),
795  DeclURI(TestHeaderURI))));
796 }
797 
798 TEST_F(SymbolCollectorTest, SymbolFormedByCLI) {
799  Annotations Header(R"(
800  #ifdef NAME
801  class $expansion[[NAME]] {};
802  #endif
803  )");
804  runSymbolCollector(Header.code(), /*Main=*/"", /*ExtraArgs=*/{"-DNAME=name"});
805  EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(
806  QName("name"), DeclRange(Header.range("expansion")),
807  DeclURI(TestHeaderURI))));
808 }
809 
810 TEST_F(SymbolCollectorTest, SymbolsInMainFile) {
811  const std::string Main = R"(
812  class Foo {};
813  void f1();
814  inline void f2() {}
815 
816  namespace {
817  void ff() {}
818  }
819  namespace foo {
820  namespace {
821  class Bar {};
822  }
823  }
824  void main_f() {}
825  void f1() {}
826  )";
827  runSymbolCollector(/*Header=*/"", Main);
828  EXPECT_THAT(Symbols, UnorderedElementsAre(
829  QName("Foo"), QName("f1"), QName("f2"), QName("ff"),
830  QName("foo"), QName("foo::Bar"), QName("main_f")));
831 }
832 
833 TEST_F(SymbolCollectorTest, Documentation) {
834  const std::string Header = R"(
835  // Doc Foo
836  class Foo {
837  // Doc f
838  int f();
839  };
840  )";
841  CollectorOpts.StoreAllDocumentation = false;
842  runSymbolCollector(Header, /* Main */ "");
843  EXPECT_THAT(Symbols,
844  UnorderedElementsAre(
845  AllOf(QName("Foo"), Doc("Doc Foo"), ForCodeCompletion(true)),
846  AllOf(QName("Foo::f"), Doc(""), ReturnType(""),
847  ForCodeCompletion(false))));
848 
849  CollectorOpts.StoreAllDocumentation = true;
850  runSymbolCollector(Header, /* Main */ "");
851  EXPECT_THAT(Symbols,
852  UnorderedElementsAre(
853  AllOf(QName("Foo"), Doc("Doc Foo"), ForCodeCompletion(true)),
854  AllOf(QName("Foo::f"), Doc("Doc f"), ReturnType(""),
855  ForCodeCompletion(false))));
856 }
857 
858 TEST_F(SymbolCollectorTest, ClassMembers) {
859  const std::string Header = R"(
860  class Foo {
861  void f() {}
862  void g();
863  static void sf() {}
864  static void ssf();
865  static int x;
866  };
867  )";
868  const std::string Main = R"(
869  void Foo::g() {}
870  void Foo::ssf() {}
871  )";
872  runSymbolCollector(Header, Main);
873  EXPECT_THAT(
874  Symbols,
875  UnorderedElementsAre(
876  QName("Foo"),
877  AllOf(QName("Foo::f"), ReturnType(""), ForCodeCompletion(false)),
878  AllOf(QName("Foo::g"), ReturnType(""), ForCodeCompletion(false)),
879  AllOf(QName("Foo::sf"), ReturnType(""), ForCodeCompletion(false)),
880  AllOf(QName("Foo::ssf"), ReturnType(""), ForCodeCompletion(false)),
881  AllOf(QName("Foo::x"), ReturnType(""), ForCodeCompletion(false))));
882 }
883 
884 TEST_F(SymbolCollectorTest, Scopes) {
885  const std::string Header = R"(
886  namespace na {
887  class Foo {};
888  namespace nb {
889  class Bar {};
890  }
891  }
892  )";
893  runSymbolCollector(Header, /*Main=*/"");
894  EXPECT_THAT(Symbols,
895  UnorderedElementsAre(QName("na"), QName("na::nb"),
896  QName("na::Foo"), QName("na::nb::Bar")));
897 }
898 
899 TEST_F(SymbolCollectorTest, ExternC) {
900  const std::string Header = R"(
901  extern "C" { class Foo {}; }
902  namespace na {
903  extern "C" { class Bar {}; }
904  }
905  )";
906  runSymbolCollector(Header, /*Main=*/"");
907  EXPECT_THAT(Symbols, UnorderedElementsAre(QName("na"), QName("Foo"),
908  QName("na::Bar")));
909 }
910 
911 TEST_F(SymbolCollectorTest, SkipInlineNamespace) {
912  const std::string Header = R"(
913  namespace na {
914  inline namespace nb {
915  class Foo {};
916  }
917  }
918  namespace na {
919  // This is still inlined.
920  namespace nb {
921  class Bar {};
922  }
923  }
924  )";
925  runSymbolCollector(Header, /*Main=*/"");
926  EXPECT_THAT(Symbols,
927  UnorderedElementsAre(QName("na"), QName("na::nb"),
928  QName("na::Foo"), QName("na::Bar")));
929 }
930 
931 TEST_F(SymbolCollectorTest, SymbolWithDocumentation) {
932  const std::string Header = R"(
933  namespace nx {
934  /// Foo comment.
935  int ff(int x, double y) { return 0; }
936  }
937  )";
938  runSymbolCollector(Header, /*Main=*/"");
939  EXPECT_THAT(
940  Symbols,
941  UnorderedElementsAre(
942  QName("nx"), AllOf(QName("nx::ff"), Labeled("ff(int x, double y)"),
943  ReturnType("int"), Doc("Foo comment."))));
944 }
945 
946 TEST_F(SymbolCollectorTest, Snippet) {
947  const std::string Header = R"(
948  namespace nx {
949  void f() {}
950  int ff(int x, double y) { return 0; }
951  }
952  )";
953  runSymbolCollector(Header, /*Main=*/"");
954  EXPECT_THAT(Symbols,
955  UnorderedElementsAre(
956  QName("nx"),
957  AllOf(QName("nx::f"), Labeled("f()"), Snippet("f()")),
958  AllOf(QName("nx::ff"), Labeled("ff(int x, double y)"),
959  Snippet("ff(${1:int x}, ${2:double y})"))));
960 }
961 
962 TEST_F(SymbolCollectorTest, IncludeHeaderSameAsFileURI) {
963  CollectorOpts.CollectIncludePath = true;
964  runSymbolCollector("#pragma once\nclass Foo {};", /*Main=*/"");
965  EXPECT_THAT(Symbols, UnorderedElementsAre(
966  AllOf(QName("Foo"), DeclURI(TestHeaderURI))));
967  EXPECT_THAT(Symbols.begin()->IncludeHeaders,
968  UnorderedElementsAre(IncludeHeaderWithRef(TestHeaderURI, 1u)));
969 }
970 
971 TEST_F(SymbolCollectorTest, CanonicalSTLHeader) {
972  CollectorOpts.CollectIncludePath = true;
973  CanonicalIncludes Includes;
974  auto Language = LangOptions();
975  Language.CPlusPlus = true;
976  addSystemHeadersMapping(&Includes, Language);
977  CollectorOpts.Includes = &Includes;
978  runSymbolCollector("namespace std { class string {}; }", /*Main=*/"");
979  EXPECT_THAT(Symbols,
980  Contains(AllOf(QName("std::string"), DeclURI(TestHeaderURI),
981  IncludeHeader("<string>"))));
982 }
983 
984 TEST_F(SymbolCollectorTest, IWYUPragma) {
985  CollectorOpts.CollectIncludePath = true;
986  CanonicalIncludes Includes;
988  CollectorOpts.Includes = &Includes;
989  const std::string Header = R"(
990  // IWYU pragma: private, include the/good/header.h
991  class Foo {};
992  )";
993  runSymbolCollector(Header, /*Main=*/"");
994  EXPECT_THAT(Symbols, UnorderedElementsAre(
995  AllOf(QName("Foo"), DeclURI(TestHeaderURI),
996  IncludeHeader("\"the/good/header.h\""))));
997 }
998 
999 TEST_F(SymbolCollectorTest, IWYUPragmaWithDoubleQuotes) {
1000  CollectorOpts.CollectIncludePath = true;
1001  CanonicalIncludes Includes;
1002  PragmaHandler = collectIWYUHeaderMaps(&Includes);
1003  CollectorOpts.Includes = &Includes;
1004  const std::string Header = R"(
1005  // IWYU pragma: private, include "the/good/header.h"
1006  class Foo {};
1007  )";
1008  runSymbolCollector(Header, /*Main=*/"");
1009  EXPECT_THAT(Symbols, UnorderedElementsAre(
1010  AllOf(QName("Foo"), DeclURI(TestHeaderURI),
1011  IncludeHeader("\"the/good/header.h\""))));
1012 }
1013 
1014 TEST_F(SymbolCollectorTest, SkipIncFileWhenCanonicalizeHeaders) {
1015  CollectorOpts.CollectIncludePath = true;
1016  CanonicalIncludes Includes;
1017  Includes.addMapping(TestHeaderName, "<canonical>");
1018  CollectorOpts.Includes = &Includes;
1019  auto IncFile = testPath("test.inc");
1020  auto IncURI = URI::create(IncFile).toString();
1021  InMemoryFileSystem->addFile(IncFile, 0,
1022  llvm::MemoryBuffer::getMemBuffer("class X {};"));
1023  runSymbolCollector("#include \"test.inc\"\nclass Y {};", /*Main=*/"",
1024  /*ExtraArgs=*/{"-I", testRoot()});
1025  EXPECT_THAT(Symbols,
1026  UnorderedElementsAre(AllOf(QName("X"), DeclURI(IncURI),
1027  IncludeHeader("<canonical>")),
1028  AllOf(QName("Y"), DeclURI(TestHeaderURI),
1029  IncludeHeader("<canonical>"))));
1030 }
1031 
1032 TEST_F(SymbolCollectorTest, MainFileIsHeaderWhenSkipIncFile) {
1033  CollectorOpts.CollectIncludePath = true;
1034  // To make this case as hard as possible, we won't tell clang main is a
1035  // header. No extension, no -x c++-header.
1036  TestFileName = testPath("no_ext_main");
1037  TestFileURI = URI::create(TestFileName).toString();
1038  auto IncFile = testPath("test.inc");
1039  auto IncURI = URI::create(IncFile).toString();
1040  InMemoryFileSystem->addFile(IncFile, 0,
1041  llvm::MemoryBuffer::getMemBuffer("class X {};"));
1042  runSymbolCollector("", R"cpp(
1043  // Can't use #pragma once in a main file clang doesn't think is a header.
1044  #ifndef MAIN_H_
1045  #define MAIN_H_
1046  #include "test.inc"
1047  #endif
1048  )cpp",
1049  /*ExtraArgs=*/{"-I", testRoot()});
1050  EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), DeclURI(IncURI),
1052 }
1053 
1054 TEST_F(SymbolCollectorTest, IncFileInNonHeader) {
1055  CollectorOpts.CollectIncludePath = true;
1056  TestFileName = testPath("main.cc");
1057  TestFileURI = URI::create(TestFileName).toString();
1058  auto IncFile = testPath("test.inc");
1059  auto IncURI = URI::create(IncFile).toString();
1060  InMemoryFileSystem->addFile(IncFile, 0,
1061  llvm::MemoryBuffer::getMemBuffer("class X {};"));
1062  runSymbolCollector("", R"cpp(
1063  #include "test.inc"
1064  )cpp",
1065  /*ExtraArgs=*/{"-I", testRoot()});
1066  EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), DeclURI(IncURI),
1067  Not(IncludeHeader()))));
1068 }
1069 
1070 // Features that depend on header-guards are fragile. Header guards are only
1071 // recognized when the file ends, so we have to defer checking for them.
1072 TEST_F(SymbolCollectorTest, HeaderGuardDetected) {
1073  CollectorOpts.CollectIncludePath = true;
1074  CollectorOpts.CollectMacro = true;
1075  runSymbolCollector(R"cpp(
1076  #ifndef HEADER_GUARD_
1077  #define HEADER_GUARD_
1078 
1079  // Symbols are seen before the header guard is complete.
1080  #define MACRO
1081  int decl();
1082 
1083  #endif // Header guard is recognized here.
1084  )cpp",
1085  "");
1086  EXPECT_THAT(Symbols, Not(Contains(QName("HEADER_GUARD_"))));
1087  EXPECT_THAT(Symbols, Each(IncludeHeader()));
1088 }
1089 
1090 TEST_F(SymbolCollectorTest, NonModularHeader) {
1091  auto TU = TestTU::withHeaderCode("int x();");
1092  EXPECT_THAT(TU.headerSymbols(), ElementsAre(IncludeHeader()));
1093 
1094  // Files missing include guards aren't eligible for insertion.
1095  TU.ImplicitHeaderGuard = false;
1096  EXPECT_THAT(TU.headerSymbols(), ElementsAre(Not(IncludeHeader())));
1097 
1098  // We recognize some patterns of trying to prevent insertion.
1099  TU = TestTU::withHeaderCode(R"cpp(
1100 #ifndef SECRET
1101 #error "This file isn't safe to include directly"
1102 #endif
1103  int x();
1104  )cpp");
1105  TU.ExtraArgs.push_back("-DSECRET"); // *we're* able to include it.
1106  EXPECT_THAT(TU.headerSymbols(), ElementsAre(Not(IncludeHeader())));
1107 }
1108 
1109 TEST_F(SymbolCollectorTest, AvoidUsingFwdDeclsAsCanonicalDecls) {
1110  CollectorOpts.CollectIncludePath = true;
1111  Annotations Header(R"(
1112  #pragma once
1113  // Forward declarations of TagDecls.
1114  class C;
1115  struct S;
1116  union U;
1117 
1118  // Canonical declarations.
1119  class $cdecl[[C]] {};
1120  struct $sdecl[[S]] {};
1121  union $udecl[[U]] {int $xdecl[[x]]; bool $ydecl[[y]];};
1122  )");
1123  runSymbolCollector(Header.code(), /*Main=*/"");
1124  EXPECT_THAT(
1125  Symbols,
1126  UnorderedElementsAre(
1127  AllOf(QName("C"), DeclURI(TestHeaderURI),
1128  DeclRange(Header.range("cdecl")), IncludeHeader(TestHeaderURI),
1129  DefURI(TestHeaderURI), DefRange(Header.range("cdecl"))),
1130  AllOf(QName("S"), DeclURI(TestHeaderURI),
1131  DeclRange(Header.range("sdecl")), IncludeHeader(TestHeaderURI),
1132  DefURI(TestHeaderURI), DefRange(Header.range("sdecl"))),
1133  AllOf(QName("U"), DeclURI(TestHeaderURI),
1134  DeclRange(Header.range("udecl")), IncludeHeader(TestHeaderURI),
1135  DefURI(TestHeaderURI), DefRange(Header.range("udecl"))),
1136  AllOf(QName("U::x"), DeclURI(TestHeaderURI),
1137  DeclRange(Header.range("xdecl")), DefURI(TestHeaderURI),
1138  DefRange(Header.range("xdecl"))),
1139  AllOf(QName("U::y"), DeclURI(TestHeaderURI),
1140  DeclRange(Header.range("ydecl")), DefURI(TestHeaderURI),
1141  DefRange(Header.range("ydecl")))));
1142 }
1143 
1144 TEST_F(SymbolCollectorTest, ClassForwardDeclarationIsCanonical) {
1145  CollectorOpts.CollectIncludePath = true;
1146  runSymbolCollector(/*Header=*/"#pragma once\nclass X;",
1147  /*Main=*/"class X {};");
1148  EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(
1149  QName("X"), DeclURI(TestHeaderURI),
1151 }
1152 
1153 TEST_F(SymbolCollectorTest, UTF16Character) {
1154  // ö is 2-bytes.
1155  Annotations Header(/*Header=*/"class [[pörk]] {};");
1156  runSymbolCollector(Header.code(), /*Main=*/"");
1157  EXPECT_THAT(Symbols, UnorderedElementsAre(
1158  AllOf(QName("pörk"), DeclRange(Header.range()))));
1159 }
1160 
1161 TEST_F(SymbolCollectorTest, DoNotIndexSymbolsInFriendDecl) {
1162  Annotations Header(R"(
1163  namespace nx {
1164  class $z[[Z]] {};
1165  class X {
1166  friend class Y;
1167  friend class Z;
1168  friend void foo();
1169  friend void $bar[[bar]]() {}
1170  };
1171  class $y[[Y]] {};
1172  void $foo[[foo]]();
1173  }
1174  )");
1175  runSymbolCollector(Header.code(), /*Main=*/"");
1176 
1177  EXPECT_THAT(Symbols,
1178  UnorderedElementsAre(
1179  QName("nx"), QName("nx::X"),
1180  AllOf(QName("nx::Y"), DeclRange(Header.range("y"))),
1181  AllOf(QName("nx::Z"), DeclRange(Header.range("z"))),
1182  AllOf(QName("nx::foo"), DeclRange(Header.range("foo"))),
1183  AllOf(QName("nx::bar"), DeclRange(Header.range("bar")))));
1184 }
1185 
1186 TEST_F(SymbolCollectorTest, ReferencesInFriendDecl) {
1187  const std::string Header = R"(
1188  class X;
1189  class Y;
1190  )";
1191  const std::string Main = R"(
1192  class C {
1193  friend ::X;
1194  friend class Y;
1195  };
1196  )";
1197  CollectorOpts.CountReferences = true;
1198  runSymbolCollector(Header, Main);
1199  EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), RefCount(1)),
1200  AllOf(QName("Y"), RefCount(1)),
1201  AllOf(QName("C"), RefCount(0))));
1202 }
1203 
1204 TEST_F(SymbolCollectorTest, Origin) {
1206  runSymbolCollector("class Foo {};", /*Main=*/"");
1207  EXPECT_THAT(Symbols, UnorderedElementsAre(
1209 }
1210 
1211 TEST_F(SymbolCollectorTest, CollectMacros) {
1212  CollectorOpts.CollectIncludePath = true;
1213  Annotations Header(R"(
1214  #pragma once
1215  #define X 1
1216  #define $mac[[MAC]](x) int x
1217  #define $used[[USED]](y) float y;
1218 
1219  MAC(p);
1220  )");
1221 
1222  Annotations Main(R"(
1223  #define $main[[MAIN]] 1
1224  USED(t);
1225  )");
1226  CollectorOpts.CountReferences = true;
1227  CollectorOpts.CollectMacro = true;
1228  runSymbolCollector(Header.code(), Main.code());
1229  EXPECT_THAT(
1230  Symbols,
1231  UnorderedElementsAre(
1232  QName("p"), QName("t"),
1233  AllOf(QName("X"), DeclURI(TestHeaderURI),
1235  AllOf(Labeled("MAC(x)"), RefCount(0),
1236 
1237  DeclRange(Header.range("mac")), VisibleOutsideFile()),
1238  AllOf(Labeled("USED(y)"), RefCount(1),
1239  DeclRange(Header.range("used")), VisibleOutsideFile()),
1240  AllOf(Labeled("MAIN"), RefCount(0), DeclRange(Main.range("main")),
1241  Not(VisibleOutsideFile()))));
1242 }
1243 
1244 TEST_F(SymbolCollectorTest, DeprecatedSymbols) {
1245  const std::string Header = R"(
1246  void TestClangc() __attribute__((deprecated("", "")));
1247  void TestClangd();
1248  )";
1249  runSymbolCollector(Header, /**/ "");
1250  EXPECT_THAT(Symbols, UnorderedElementsAre(
1251  AllOf(QName("TestClangc"), Deprecated()),
1252  AllOf(QName("TestClangd"), Not(Deprecated()))));
1253 }
1254 
1255 TEST_F(SymbolCollectorTest, ImplementationDetail) {
1256  const std::string Header = R"(
1257  #define DECL_NAME(x, y) x##_##y##_Decl
1258  #define DECL(x, y) class DECL_NAME(x, y) {};
1259  DECL(X, Y); // X_Y_Decl
1260 
1261  class Public {};
1262  )";
1263  runSymbolCollector(Header, /**/ "");
1264  EXPECT_THAT(Symbols,
1265  UnorderedElementsAre(
1266  AllOf(QName("X_Y_Decl"), ImplementationDetail()),
1267  AllOf(QName("Public"), Not(ImplementationDetail()))));
1268 }
1269 
1270 TEST_F(SymbolCollectorTest, UsingDecl) {
1271  const char *Header = R"(
1272  void foo();
1273  namespace std {
1274  using ::foo;
1275  })";
1276  runSymbolCollector(Header, /**/ "");
1277  EXPECT_THAT(Symbols, Contains(QName("std::foo")));
1278 }
1279 
1280 TEST_F(SymbolCollectorTest, CBuiltins) {
1281  // In C, printf in stdio.h is a redecl of an implicit builtin.
1282  const char *Header = R"(
1283  extern int printf(const char*, ...);
1284  )";
1285  runSymbolCollector(Header, /**/ "", {"-xc"});
1286  EXPECT_THAT(Symbols, Contains(QName("printf")));
1287 }
1288 
1289 TEST_F(SymbolCollectorTest, InvalidSourceLoc) {
1290  const char *Header = R"(
1291  void operator delete(void*)
1292  __attribute__((__externally_visible__));)";
1293  runSymbolCollector(Header, /**/ "");
1294  EXPECT_THAT(Symbols, Contains(QName("operator delete")));
1295 }
1296 
1297 } // namespace
1298 } // namespace clangd
1299 } // namespace clang
std::unique_ptr< CommentHandler > collectIWYUHeaderMaps(CanonicalIncludes *Includes)
Returns a CommentHandler that parses pragma comment on include files to determine when we should incl...
MATCHER_P(Named, N, "")
std::string HeaderName
SymbolCollector::Options CollectorOpts
std::string FileName
llvm::IntrusiveRefCntPtr< llvm::vfs::InMemoryFileSystem > InMemoryFileSystem
std::string TestHeaderName
bool isInsideMainFile(SourceLocation Loc, const SourceManager &SM)
Returns true iff Loc is inside the main file.
Definition: SourceCode.cpp:372
Symbol is visible to other files (not e.g. a static helper function).
Definition: Symbol.h:125
void addSystemHeadersMapping(CanonicalIncludes *Includes, const LangOptions &Language)
Adds mapping for system headers and some special symbols (e.g.
const NamedDecl & findUnqualifiedDecl(ParsedAST &AST, llvm::StringRef Name)
Definition: TestTU.cpp:155
std::string TestHeaderURI
std::string MainFile
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
Symbol is an implementation detail.
Definition: Symbol.h:123
SymbolCollector::Options COpts
std::string QName
static TestTU withHeaderCode(llvm::StringRef HeaderCode)
Definition: TestTU.h:39
Whether or not this symbol is meant to be used for the code completion.
Definition: Symbol.h:119
std::string testPath(PathRef File)
Definition: TestFS.cpp:82
MATCHER(Declared, "")
static constexpr llvm::StringLiteral Name
const char * testRoot()
Definition: TestFS.cpp:74
RelationSlab Relations
std::shared_ptr< SymbolCollector > Collector
const Decl * D
Definition: XRefs.cpp:868
SymbolSlab Symbols
std::string ReturnType
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:186
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
unsigned References
const Symbol & findSymbol(const SymbolSlab &Slab, llvm::StringRef QName)
Definition: TestTU.cpp:93
CharSourceRange Range
SourceRange for the file name.
std::string IncludeHeader
std::string TestFileName
std::string TestFileURI
Indicates if the symbol is deprecated.
Definition: Symbol.h:121
RefSlab Refs
std::unique_ptr< GlobalCompilationDatabase > Base
SymbolOrigin Origin
Where this symbol came from. Usually an index provides a constant value.
Definition: Symbol.h:61
llvm::StringMap< std::string > Files
const NamedDecl & findDecl(ParsedAST &AST, llvm::StringRef QName)
Definition: TestTU.cpp:114
CommentHandler * PragmaHandler