brintos

brintos / llvm-project-archived public Read only

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