clang-tools  10.0.0svn
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/Index/IndexingOptions.h"
18 #include "clang/Tooling/Tooling.h"
19 #include "llvm/ADT/IntrusiveRefCntPtr.h"
20 #include "llvm/ADT/STLExtras.h"
21 #include "llvm/ADT/StringRef.h"
22 #include "llvm/Support/MemoryBuffer.h"
23 #include "llvm/Support/VirtualFileSystem.h"
24 #include "gmock/gmock-matchers.h"
25 #include "gmock/gmock-more-matchers.h"
26 #include "gmock/gmock.h"
27 #include "gtest/gtest.h"
28 
29 #include <memory>
30 #include <string>
31 
32 namespace clang {
33 namespace clangd {
34 namespace {
35 
36 using ::testing::_;
37 using ::testing::AllOf;
38 using ::testing::Contains;
39 using ::testing::Each;
40 using ::testing::ElementsAre;
41 using ::testing::Field;
42 using ::testing::Not;
43 using ::testing::Pair;
44 using ::testing::UnorderedElementsAre;
45 using ::testing::UnorderedElementsAreArray;
46 
47 // GMock helpers for matching Symbol.
48 MATCHER_P(Labeled, Label, "") {
49  return (arg.Name + arg.Signature).str() == Label;
50 }
51 MATCHER_P(ReturnType, D, "") { return arg.ReturnType == D; }
52 MATCHER_P(Doc, D, "") { return arg.Documentation == D; }
53 MATCHER_P(Snippet, S, "") {
54  return (arg.Name + arg.CompletionSnippetSuffix).str() == S;
55 }
56 MATCHER_P(QName, Name, "") { return (arg.Scope + arg.Name).str() == Name; }
57 MATCHER_P(TemplateArgs, TemplArgs, "") {
58  return arg.TemplateSpecializationArgs == TemplArgs;
59 }
60 MATCHER_P(DeclURI, P, "") {
61  return StringRef(arg.CanonicalDeclaration.FileURI) == P;
62 }
63 MATCHER_P(DefURI, P, "") { return StringRef(arg.Definition.FileURI) == P; }
64 MATCHER(IncludeHeader, "") { return !arg.IncludeHeaders.empty(); }
65 MATCHER_P(IncludeHeader, P, "") {
66  return (arg.IncludeHeaders.size() == 1) &&
67  (arg.IncludeHeaders.begin()->IncludeHeader == P);
68 }
69 MATCHER_P2(IncludeHeaderWithRef, IncludeHeader, References, "") {
70  return (arg.IncludeHeader == IncludeHeader) && (arg.References == References);
71 }
72 MATCHER_P(DeclRange, Pos, "") {
73  return std::make_tuple(arg.CanonicalDeclaration.Start.line(),
74  arg.CanonicalDeclaration.Start.column(),
75  arg.CanonicalDeclaration.End.line(),
76  arg.CanonicalDeclaration.End.column()) ==
77  std::make_tuple(Pos.start.line, Pos.start.character, Pos.end.line,
78  Pos.end.character);
79 }
80 MATCHER_P(DefRange, Pos, "") {
81  return std::make_tuple(
82  arg.Definition.Start.line(), arg.Definition.Start.column(),
83  arg.Definition.End.line(), arg.Definition.End.column()) ==
84  std::make_tuple(Pos.start.line, Pos.start.character, Pos.end.line,
85  Pos.end.character);
86 }
87 MATCHER_P(RefCount, R, "") { return int(arg.References) == R; }
88 MATCHER_P(ForCodeCompletion, IsIndexedForCodeCompletion, "") {
89  return static_cast<bool>(arg.Flags & Symbol::IndexedForCodeCompletion) ==
90  IsIndexedForCodeCompletion;
91 }
92 MATCHER(Deprecated, "") { return arg.Flags & Symbol::Deprecated; }
93 MATCHER(ImplementationDetail, "") {
94  return arg.Flags & Symbol::ImplementationDetail;
95 }
96 MATCHER(VisibleOutsideFile, "") {
97  return static_cast<bool>(arg.Flags & Symbol::VisibleOutsideFile);
98 }
99 MATCHER(RefRange, "") {
100  const Ref &Pos = ::testing::get<0>(arg);
101  const Range &Range = ::testing::get<1>(arg);
102  return std::make_tuple(Pos.Location.Start.line(), Pos.Location.Start.column(),
103  Pos.Location.End.line(), Pos.Location.End.column()) ==
104  std::make_tuple(Range.start.line, Range.start.character,
105  Range.end.line, Range.end.character);
106 }
107 ::testing::Matcher<const std::vector<Ref> &>
108 HaveRanges(const std::vector<Range> Ranges) {
109  return ::testing::UnorderedPointwise(RefRange(), Ranges);
110 }
111 
112 class ShouldCollectSymbolTest : public ::testing::Test {
113 public:
114  void build(llvm::StringRef HeaderCode, llvm::StringRef Code = "") {
115  File.HeaderFilename = HeaderName;
116  File.Filename = FileName;
117  File.HeaderCode = HeaderCode;
118  File.Code = Code;
119  AST = File.build();
120  }
121 
122  // build() must have been called.
123  bool shouldCollect(llvm::StringRef Name, bool Qualified = true) {
124  assert(AST.hasValue());
125  const NamedDecl &ND =
126  Qualified ? findDecl(*AST, Name) : findUnqualifiedDecl(*AST, Name);
127  const SourceManager &SM = AST->getSourceManager();
128  bool MainFile = isInsideMainFile(ND.getBeginLoc(), SM);
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  std::unique_ptr<FrontendAction> create() override {
205  class IndexAction : public ASTFrontendAction {
206  public:
207  IndexAction(std::shared_ptr<index::IndexDataConsumer> DataConsumer,
208  const index::IndexingOptions &Opts,
209  CommentHandler *PragmaHandler)
210  : DataConsumer(std::move(DataConsumer)), Opts(Opts),
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 createIndexingASTConsumer(DataConsumer, Opts, CI.getPreprocessorPtr());
218  }
219 
220  bool BeginInvocation(CompilerInstance &CI) override {
221  // Make the compiler parse all comments.
222  CI.getLangOpts().CommentOpts.ParseAllComments = true;
223  return true;
224  }
225 
226  private:
227  std::shared_ptr<index::IndexDataConsumer> DataConsumer;
228  index::IndexingOptions Opts;
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 std::make_unique<IndexAction>(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 = std::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, RelationKind::BaseOf, 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  Includes.addSystemHeadersMapping(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...
std::string Code
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:537
Symbol is visible to other files (not e.g. a static helper function).
Definition: Symbol.h:125
const NamedDecl & findUnqualifiedDecl(ParsedAST &AST, llvm::StringRef Name)
Definition: TestTU.cpp:159
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:849
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:197
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
unsigned References
const Symbol & findSymbol(const SymbolSlab &Slab, llvm::StringRef QName)
Definition: TestTU.cpp:97
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:118
CommentHandler * PragmaHandler