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