brintos

brintos / llvm-project-archived public Read only

0
0
Text · 22.9 KiB · 74321c3 Raw
685 lines · cpp
1//===--- AnalysisTest.cpp -------------------------------------------------===//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 "clang-include-cleaner/Analysis.h"10#include "AnalysisInternal.h"11#include "TypesInternal.h"12#include "clang-include-cleaner/Record.h"13#include "clang-include-cleaner/Types.h"14#include "clang/AST/ASTContext.h"15#include "clang/AST/DeclBase.h"16#include "clang/Basic/FileManager.h"17#include "clang/Basic/IdentifierTable.h"18#include "clang/Basic/SourceLocation.h"19#include "clang/Basic/SourceManager.h"20#include "clang/Format/Format.h"21#include "clang/Frontend/FrontendActions.h"22#include "clang/Testing/TestAST.h"23#include "clang/Tooling/Inclusions/StandardLibrary.h"24#include "llvm/ADT/ArrayRef.h"25#include "llvm/ADT/IntrusiveRefCntPtr.h"26#include "llvm/ADT/SmallVector.h"27#include "llvm/ADT/StringRef.h"28#include "llvm/Support/Error.h"29#include "llvm/Support/MemoryBuffer.h"30#include "llvm/Support/ScopedPrinter.h"31#include "llvm/Support/VirtualFileSystem.h"32#include "llvm/Testing/Annotations/Annotations.h"33#include "gmock/gmock.h"34#include "gtest/gtest.h"35#include <cstddef>36#include <map>37#include <memory>38#include <string>39#include <vector>40 41namespace clang::include_cleaner {42namespace {43using testing::_;44using testing::AllOf;45using testing::Contains;46using testing::ElementsAre;47using testing::Pair;48using testing::UnorderedElementsAre;49 50std::string guard(llvm::StringRef Code) {51  return "#pragma once\n" + Code.str();52}53 54class WalkUsedTest : public testing::Test {55protected:56  TestInputs Inputs;57  PragmaIncludes PI;58  WalkUsedTest() {59    Inputs.MakeAction = [this] {60      struct Hook : public SyntaxOnlyAction {61      public:62        Hook(PragmaIncludes *Out) : Out(Out) {}63        bool BeginSourceFileAction(clang::CompilerInstance &CI) override {64          Out->record(CI);65          return true;66        }67 68        PragmaIncludes *Out;69      };70      return std::make_unique<Hook>(&PI);71    };72  }73 74  std::multimap<size_t, std::vector<Header>>75  offsetToProviders(TestAST &AST,76                    llvm::ArrayRef<SymbolReference> MacroRefs = {}) {77    const auto &SM = AST.sourceManager();78    llvm::SmallVector<Decl *> TopLevelDecls;79    for (Decl *D : AST.context().getTranslationUnitDecl()->decls()) {80      if (!SM.isWrittenInMainFile(SM.getExpansionLoc(D->getLocation())))81        continue;82      TopLevelDecls.emplace_back(D);83    }84    std::multimap<size_t, std::vector<Header>> OffsetToProviders;85    walkUsed(TopLevelDecls, MacroRefs, &PI, AST.preprocessor(),86             [&](const SymbolReference &Ref, llvm::ArrayRef<Header> Providers) {87               auto [FID, Offset] = SM.getDecomposedLoc(Ref.RefLocation);88               if (FID != SM.getMainFileID())89                 ADD_FAILURE() << "Reference outside of the main file!";90               OffsetToProviders.emplace(Offset, Providers.vec());91             });92    return OffsetToProviders;93  }94};95 96TEST_F(WalkUsedTest, Basic) {97  llvm::Annotations Code(R"cpp(98  #include "header.h"99  #include "private.h"100 101  // No reference reported for the Parameter "p".102  void $bar^bar($private^Private p) {103    $foo^foo();104    std::$vector^vector $vconstructor^$v^v;105    $builtin^__builtin_popcount(1);106    std::$move^move(3);107  }108  )cpp");109  Inputs.Code = Code.code();110  Inputs.ExtraFiles["header.h"] = guard(R"cpp(111  void foo();112  namespace std { class vector {}; int&& move(int&&); }113  )cpp");114  Inputs.ExtraFiles["private.h"] = guard(R"cpp(115    // IWYU pragma: private, include "path/public.h"116    class Private {};117  )cpp");118 119  TestAST AST(Inputs);120  auto &SM = AST.sourceManager();121  auto HeaderFile = Header(*AST.fileManager().getOptionalFileRef("header.h"));122  auto PrivateFile = Header(*AST.fileManager().getOptionalFileRef("private.h"));123  auto PublicFile = Header("\"path/public.h\"");124  auto MainFile = Header(*SM.getFileEntryRefForID(SM.getMainFileID()));125  auto VectorSTL = Header(*tooling::stdlib::Header::named("<vector>"));126  auto UtilitySTL = Header(*tooling::stdlib::Header::named("<utility>"));127  EXPECT_THAT(128      offsetToProviders(AST),129      UnorderedElementsAre(130          Pair(Code.point("bar"), UnorderedElementsAre(MainFile)),131          Pair(Code.point("private"),132               UnorderedElementsAre(PublicFile, PrivateFile)),133          Pair(Code.point("foo"), UnorderedElementsAre(HeaderFile)),134          Pair(Code.point("vector"), UnorderedElementsAre(VectorSTL)),135          Pair(Code.point("vconstructor"), UnorderedElementsAre(VectorSTL)),136          Pair(Code.point("v"), UnorderedElementsAre(MainFile)),137          Pair(Code.point("builtin"), testing::IsEmpty()),138          Pair(Code.point("move"), UnorderedElementsAre(UtilitySTL))));139}140 141TEST_F(WalkUsedTest, MultipleProviders) {142  llvm::Annotations Code(R"cpp(143  #include "header1.h"144  #include "header2.h"145  void foo();146 147  void bar() {148    $foo^foo();149  }150  )cpp");151  Inputs.Code = Code.code();152  Inputs.ExtraFiles["header1.h"] = guard(R"cpp(153  void foo();154  )cpp");155  Inputs.ExtraFiles["header2.h"] = guard(R"cpp(156  void foo();157  )cpp");158 159  TestAST AST(Inputs);160  auto &SM = AST.sourceManager();161  auto HeaderFile1 = Header(*AST.fileManager().getOptionalFileRef("header1.h"));162  auto HeaderFile2 = Header(*AST.fileManager().getOptionalFileRef("header2.h"));163  auto MainFile = Header(*SM.getFileEntryRefForID(SM.getMainFileID()));164  EXPECT_THAT(165      offsetToProviders(AST),166      Contains(Pair(Code.point("foo"),167                    UnorderedElementsAre(HeaderFile1, HeaderFile2, MainFile))));168}169 170TEST_F(WalkUsedTest, MacroRefs) {171  llvm::Annotations Code(R"cpp(172    #include "hdr.h"173    int $3^x = $1^ANSWER;174    int $4^y = $2^ANSWER;175  )cpp");176  llvm::Annotations Hdr(guard("#define ^ANSWER 42"));177  Inputs.Code = Code.code();178  Inputs.ExtraFiles["hdr.h"] = Hdr.code();179  TestAST AST(Inputs);180  auto &SM = AST.sourceManager();181  auto &PP = AST.preprocessor();182  auto HdrFile = *SM.getFileManager().getOptionalFileRef("hdr.h");183  auto MainFile = Header(*SM.getFileEntryRefForID(SM.getMainFileID()));184 185  auto HdrID = SM.translateFile(HdrFile);186 187  Symbol Answer1 = Macro{PP.getIdentifierInfo("ANSWER"),188                         SM.getComposedLoc(HdrID, Hdr.point())};189  Symbol Answer2 = Macro{PP.getIdentifierInfo("ANSWER"),190                         SM.getComposedLoc(HdrID, Hdr.point())};191  EXPECT_THAT(192      offsetToProviders(193          AST,194          {SymbolReference{195               Answer1, SM.getComposedLoc(SM.getMainFileID(), Code.point("1")),196               RefType::Explicit},197           SymbolReference{198               Answer2, SM.getComposedLoc(SM.getMainFileID(), Code.point("2")),199               RefType::Explicit}}),200      UnorderedElementsAre(201          Pair(Code.point("1"), UnorderedElementsAre(HdrFile)),202          Pair(Code.point("2"), UnorderedElementsAre(HdrFile)),203          Pair(Code.point("3"), UnorderedElementsAre(MainFile)),204          Pair(Code.point("4"), UnorderedElementsAre(MainFile))));205}206 207class AnalyzeTest : public testing::Test {208protected:209  TestInputs Inputs;210  PragmaIncludes PI;211  RecordedPP PP;212  llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> ExtraFS = nullptr;213 214  AnalyzeTest() {215    Inputs.MakeAction = [this] {216      struct Hook : public SyntaxOnlyAction {217      public:218        Hook(RecordedPP &PP, PragmaIncludes &PI,219             llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> ExtraFS)220            : PP(PP), PI(PI), ExtraFS(std::move(ExtraFS)) {}221        bool BeginSourceFileAction(clang::CompilerInstance &CI) override {222          CI.getPreprocessor().addPPCallbacks(PP.record(CI.getPreprocessor()));223          PI.record(CI);224          return true;225        }226 227        bool BeginInvocation(CompilerInstance &CI) override {228          if (!ExtraFS)229            return true;230          auto OverlayFS =231              llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(232                  CI.getFileManager().getVirtualFileSystemPtr());233          OverlayFS->pushOverlay(ExtraFS);234          CI.getFileManager().setVirtualFileSystem(std::move(OverlayFS));235          return true;236        }237 238        RecordedPP &PP;239        PragmaIncludes &PI;240        llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> ExtraFS;241      };242      return std::make_unique<Hook>(PP, PI, ExtraFS);243    };244  }245};246 247TEST_F(AnalyzeTest, Basic) {248  Inputs.Code = R"cpp(249#include "a.h"250#include "b.h"251#include "keep.h" // IWYU pragma: keep252 253int x = a + c;254)cpp";255  Inputs.ExtraFiles["a.h"] = guard("int a;");256  Inputs.ExtraFiles["b.h"] = guard(R"cpp(257    #include "c.h"258    int b;259  )cpp");260  Inputs.ExtraFiles["c.h"] = guard("int c;");261  Inputs.ExtraFiles["keep.h"] = guard("");262  TestAST AST(Inputs);263  auto Decls = AST.context().getTranslationUnitDecl()->decls();264  auto Results =265      analyze(std::vector<Decl *>{Decls.begin(), Decls.end()},266              PP.MacroReferences, PP.Includes, &PI, AST.preprocessor());267  auto CHeader = llvm::cantFail(268      AST.context().getSourceManager().getFileManager().getFileRef("c.h"));269 270  const Include *B = PP.Includes.atLine(3);271  ASSERT_EQ(B->Spelled, "b.h");272  EXPECT_THAT(Results.Missing, ElementsAre(Pair("\"c.h\"", Header(CHeader))));273  EXPECT_THAT(Results.Unused, ElementsAre(B));274}275 276TEST_F(AnalyzeTest, PrivateUsedInPublic) {277  // Check that umbrella header uses private include.278  Inputs.Code = R"cpp(#include "private.h")cpp";279  Inputs.ExtraFiles["private.h"] =280      guard("// IWYU pragma: private, include \"public.h\"");281  Inputs.FileName = "public.h";282  TestAST AST(Inputs);283  EXPECT_FALSE(PP.Includes.all().empty());284  auto Results = analyze({}, {}, PP.Includes, &PI, AST.preprocessor());285  EXPECT_THAT(Results.Unused, testing::IsEmpty());286}287 288TEST_F(AnalyzeTest, NoCrashWhenUnresolved) {289  // Check that umbrella header uses private include.290  Inputs.Code = R"cpp(#include "not_found.h")cpp";291  Inputs.ErrorOK = true;292  TestAST AST(Inputs);293  EXPECT_FALSE(PP.Includes.all().empty());294  auto Results = analyze({}, {}, PP.Includes, &PI, AST.preprocessor());295  EXPECT_THAT(Results.Unused, testing::IsEmpty());296}297 298TEST_F(AnalyzeTest, ResourceDirIsIgnored) {299  Inputs.ExtraArgs.push_back("-resource-dir");300  Inputs.ExtraArgs.push_back("resources");301  Inputs.ExtraArgs.push_back("-internal-isystem");302  Inputs.ExtraArgs.push_back("resources/include");303  Inputs.Code = R"cpp(304    #include <amintrin.h>305    #include <imintrin.h>306    void baz() {307      bar();308    }309  )cpp";310  Inputs.ExtraFiles["resources/include/amintrin.h"] = guard("");311  Inputs.ExtraFiles["resources/include/emintrin.h"] = guard(R"cpp(312    void bar();313  )cpp");314  Inputs.ExtraFiles["resources/include/imintrin.h"] = guard(R"cpp(315    #include <emintrin.h>316  )cpp");317  TestAST AST(Inputs);318  auto Results = analyze({}, {}, PP.Includes, &PI, AST.preprocessor());319  EXPECT_THAT(Results.Unused, testing::IsEmpty());320  EXPECT_THAT(Results.Missing, testing::IsEmpty());321}322 323TEST_F(AnalyzeTest, DifferentHeaderSameSpelling) {324  Inputs.ExtraArgs.push_back("-Ifoo");325  Inputs.ExtraArgs.push_back("-Ifoo_inner");326  // `foo` is declared in foo_inner/foo.h, but there's no way to spell it327  // directly. Make sure we don't generate unusued/missing include findings in328  // such cases.329  Inputs.Code = R"cpp(330    #include <foo.h>331    void baz() {332      foo();333    }334  )cpp";335  Inputs.ExtraFiles["foo/foo.h"] = guard("#include_next <foo.h>");336  Inputs.ExtraFiles["foo_inner/foo.h"] = guard(R"cpp(337    void foo();338  )cpp");339  TestAST AST(Inputs);340  std::vector<Decl *> DeclsInTU;341  for (auto *D : AST.context().getTranslationUnitDecl()->decls())342    DeclsInTU.push_back(D);343  auto Results = analyze(DeclsInTU, {}, PP.Includes, &PI, AST.preprocessor());344  EXPECT_THAT(Results.Unused, testing::IsEmpty());345  EXPECT_THAT(Results.Missing, testing::IsEmpty());346}347 348TEST_F(AnalyzeTest, SpellingIncludesWithSymlinks) {349  llvm::Annotations Code(R"cpp(350  #include "header.h"351  void $bar^bar() {352    $foo^foo();353  }354  )cpp");355  Inputs.Code = Code.code();356  ExtraFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();357  ExtraFS->addFile("content_for/0", /*ModificationTime=*/{},358                   llvm::MemoryBuffer::getMemBufferCopy(guard(R"cpp(359  #include "inner.h"360  )cpp")));361  ExtraFS->addSymbolicLink("header.h", "content_for/0",362                           /*ModificationTime=*/{});363  ExtraFS->addFile("content_for/1", /*ModificationTime=*/{},364                   llvm::MemoryBuffer::getMemBufferCopy(guard(R"cpp(365  void foo();366  )cpp")));367  ExtraFS->addSymbolicLink("inner.h", "content_for/1",368                           /*ModificationTime=*/{});369 370  TestAST AST(Inputs);371  std::vector<Decl *> DeclsInTU;372  for (auto *D : AST.context().getTranslationUnitDecl()->decls())373    DeclsInTU.push_back(D);374  auto Results = analyze(DeclsInTU, {}, PP.Includes, &PI, AST.preprocessor());375  // Check that we're spelling header using the symlink, and not underlying376  // path.377  EXPECT_THAT(Results.Missing, testing::ElementsAre(Pair("\"inner.h\"", _)));378  // header.h should be unused.379  EXPECT_THAT(Results.Unused, Not(testing::IsEmpty()));380 381  {382    // Make sure filtering is also applied to symlink, not underlying file.383    auto HeaderFilter = [](llvm::StringRef Path) { return Path == "inner.h"; };384    Results = analyze(DeclsInTU, {}, PP.Includes, &PI, AST.preprocessor(),385                      HeaderFilter);386    EXPECT_THAT(Results.Missing, testing::ElementsAre(Pair("\"inner.h\"", _)));387    // header.h should be unused.388    EXPECT_THAT(Results.Unused, Not(testing::IsEmpty()));389  }390  {391    auto HeaderFilter = [](llvm::StringRef Path) { return Path == "header.h"; };392    Results = analyze(DeclsInTU, {}, PP.Includes, &PI, AST.preprocessor(),393                      HeaderFilter);394    // header.h should be ignored now.395    EXPECT_THAT(Results.Unused, Not(testing::IsEmpty()));396    EXPECT_THAT(Results.Missing, testing::ElementsAre(Pair("\"inner.h\"", _)));397  }398}399 400// Make sure that the references to implicit operator new/delete are reported as401// ambigious.402TEST_F(AnalyzeTest, ImplicitOperatorNewDeleteNotMissing) {403  ExtraFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();404  ExtraFS->addFile("header.h",405                   /*ModificationTime=*/{},406                   llvm::MemoryBuffer::getMemBufferCopy(guard(R"cpp(407  void* operator new(decltype(sizeof(int)));408  )cpp")));409  ExtraFS->addFile("wrapper.h",410                   /*ModificationTime=*/{},411                   llvm::MemoryBuffer::getMemBufferCopy(guard(R"cpp(412  #include "header.h"413  )cpp")));414 415  Inputs.Code = R"cpp(416      #include "wrapper.h"417      void bar() {418        operator new(3);419      })cpp";420  TestAST AST(Inputs);421  std::vector<Decl *> DeclsInTU;422  for (auto *D : AST.context().getTranslationUnitDecl()->decls())423    DeclsInTU.push_back(D);424  auto Results = analyze(DeclsInTU, {}, PP.Includes, &PI, AST.preprocessor());425  EXPECT_THAT(Results.Missing, testing::IsEmpty());426}427 428TEST_F(AnalyzeTest, ImplicitOperatorNewDeleteNotUnused) {429  ExtraFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();430  ExtraFS->addFile("header.h",431                   /*ModificationTime=*/{},432                   llvm::MemoryBuffer::getMemBufferCopy(guard(R"cpp(433  void* operator new(decltype(sizeof(int)));434  )cpp")));435 436  Inputs.Code = R"cpp(437      #include "header.h"438      void bar() {439        operator new(3);440      })cpp";441  TestAST AST(Inputs);442  std::vector<Decl *> DeclsInTU;443  for (auto *D : AST.context().getTranslationUnitDecl()->decls())444    DeclsInTU.push_back(D);445  auto Results = analyze(DeclsInTU, {}, PP.Includes, &PI, AST.preprocessor());446  EXPECT_THAT(Results.Unused, testing::IsEmpty());447}448 449TEST(FixIncludes, Basic) {450  llvm::StringRef Code = R"cpp(#include "d.h"451#include "a.h"452#include "b.h"453#include <c.h>454)cpp";455 456  Includes Inc;457  Include I;458  I.Spelled = "a.h";459  I.Line = 2;460  Inc.add(I);461  I.Spelled = "b.h";462  I.Line = 3;463  Inc.add(I);464  I.Spelled = "c.h";465  I.Line = 4;466  I.Angled = true;467  Inc.add(I);468 469  AnalysisResults Results;470  Results.Missing.emplace_back("\"aa.h\"", Header(""));471  Results.Missing.emplace_back("\"ab.h\"", Header(""));472  Results.Missing.emplace_back("<e.h>", Header(""));473  Results.Unused.push_back(Inc.atLine(3));474  Results.Unused.push_back(Inc.atLine(4));475 476  EXPECT_EQ(fixIncludes(Results, "d.cc", Code, format::getLLVMStyle()),477R"cpp(#include "d.h"478#include "a.h"479#include "aa.h"480#include "ab.h"481#include <e.h>482)cpp");483 484  Results = {};485  Results.Missing.emplace_back("\"d.h\"", Header(""));486  Code = R"cpp(#include "a.h")cpp";487  EXPECT_EQ(fixIncludes(Results, "d.cc", Code, format::getLLVMStyle()),488R"cpp(#include "d.h"489#include "a.h")cpp");490}491 492MATCHER_P3(expandedAt, FileID, Offset, SM, "") {493  auto [ExpanedFileID, ExpandedOffset] = SM->getDecomposedExpansionLoc(arg);494  return ExpanedFileID == FileID && ExpandedOffset == Offset;495}496MATCHER_P3(spelledAt, FileID, Offset, SM, "") {497  auto [SpelledFileID, SpelledOffset] = SM->getDecomposedSpellingLoc(arg);498  return SpelledFileID == FileID && SpelledOffset == Offset;499}500TEST(WalkUsed, FilterRefsNotSpelledInMainFile) {501  // Each test is expected to have a single expected ref of `target` symbol502  // (or have none).503  // The location in the reported ref is a macro location. $expand points to504  // the macro location, and $spell points to the spelled location.505  struct {506    llvm::StringRef Header;507    llvm::StringRef Main;508  } TestCases[] = {509      // Tests for decl references.510      {511          /*Header=*/"int target();",512          R"cpp(513            #define CALL_FUNC $spell^target()514 515            int b = $expand^CALL_FUNC;516          )cpp",517      },518      {/*Header=*/R"cpp(519           int target();520           #define CALL_FUNC target()521           )cpp",522       // No ref of `target` being reported, as it is not spelled in main file.523       "int a = CALL_FUNC;"},524      {525          /*Header=*/R"cpp(526            int target();527            #define PLUS_ONE(X) X() + 1528          )cpp",529          R"cpp(530            int a = $expand^PLUS_ONE($spell^target);531          )cpp",532      },533      {534          /*Header=*/R"cpp(535            int target();536            #define PLUS_ONE(X) X() + 1537          )cpp",538          R"cpp(539            int a = $expand^PLUS_ONE($spell^target);540          )cpp",541      },542      // Tests for macro references543      {/*Header=*/"#define target 1",544       R"cpp(545          #define USE_target $spell^target546          int b = $expand^USE_target;547        )cpp"},548      {/*Header=*/R"cpp(549          #define target 1550          #define USE_target target551        )cpp",552       // No ref of `target` being reported, it is not spelled in main file.553       R"cpp(554          int a = USE_target;555        )cpp"},556  };557 558  for (const auto &T : TestCases) {559    llvm::Annotations Main(T.Main);560    TestInputs Inputs(Main.code());561    Inputs.ExtraFiles["header.h"] = guard(T.Header);562    RecordedPP Recorded;563    Inputs.MakeAction = [&]() {564      struct RecordAction : public SyntaxOnlyAction {565        RecordedPP &Out;566        RecordAction(RecordedPP &Out) : Out(Out) {}567        bool BeginSourceFileAction(clang::CompilerInstance &CI) override {568          auto &PP = CI.getPreprocessor();569          PP.addPPCallbacks(Out.record(PP));570          return true;571        }572      };573      return std::make_unique<RecordAction>(Recorded);574    };575    Inputs.ExtraArgs.push_back("-include");576    Inputs.ExtraArgs.push_back("header.h");577    TestAST AST(Inputs);578    llvm::SmallVector<Decl *> TopLevelDecls;579    for (Decl *D : AST.context().getTranslationUnitDecl()->decls())580      TopLevelDecls.emplace_back(D);581    auto &SM = AST.sourceManager();582 583    SourceLocation RefLoc;584    walkUsed(TopLevelDecls, Recorded.MacroReferences,585             /*PragmaIncludes=*/nullptr, AST.preprocessor(),586             [&](const SymbolReference &Ref, llvm::ArrayRef<Header>) {587               if (!Ref.RefLocation.isMacroID())588                 return;589               if (llvm::to_string(Ref.Target) == "target") {590                 ASSERT_TRUE(RefLoc.isInvalid())591                     << "Expected only one 'target' ref loc per testcase";592                 RefLoc = Ref.RefLocation;593               }594             });595    FileID MainFID = SM.getMainFileID();596    if (RefLoc.isValid()) {597      EXPECT_THAT(RefLoc, AllOf(expandedAt(MainFID, Main.point("expand"), &SM),598                                spelledAt(MainFID, Main.point("spell"), &SM)))599          << T.Main.str();600    } else {601      EXPECT_THAT(Main.points(), testing::IsEmpty());602    }603  }604}605 606struct Tag {607  friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Tag &T) {608    return OS << "Anon Tag";609  }610};611TEST(Hints, Ordering) {612  auto Hinted = [](Hints Hints) {613    return clang::include_cleaner::Hinted<Tag>({}, Hints);614  };615  EXPECT_LT(Hinted(Hints::None), Hinted(Hints::CompleteSymbol));616  EXPECT_LT(Hinted(Hints::CompleteSymbol), Hinted(Hints::PublicHeader));617  EXPECT_LT(Hinted(Hints::PreferredHeader), Hinted(Hints::PublicHeader));618  EXPECT_LT(Hinted(Hints::CompleteSymbol | Hints::PreferredHeader),619            Hinted(Hints::PublicHeader));620}621 622// Test ast traversal & redecl selection end-to-end for templates, as explicit623// instantiations/specializations are not redecls of the primary template. We624// need to make sure we're selecting the right ones.625TEST_F(WalkUsedTest, TemplateDecls) {626  llvm::Annotations Code(R"cpp(627    #include "fwd.h"628    #include "def.h"629    #include "partial.h"630    template <> struct $exp_spec^Foo<char> {};631    template struct $exp^Foo<int>;632    $full^Foo<int> x;633    $implicit^Foo<bool> y;634    $partial^Foo<int*> z;635  )cpp");636  Inputs.Code = Code.code();637  Inputs.ExtraFiles["fwd.h"] = guard("template<typename> struct Foo;");638  Inputs.ExtraFiles["def.h"] = guard("template<typename> struct Foo {};");639  Inputs.ExtraFiles["partial.h"] =640      guard("template<typename T> struct Foo<T*> {};");641  TestAST AST(Inputs);642  auto &SM = AST.sourceManager();643  auto Fwd = *SM.getFileManager().getOptionalFileRef("fwd.h");644  auto Def = *SM.getFileManager().getOptionalFileRef("def.h");645  auto Partial = *SM.getFileManager().getOptionalFileRef("partial.h");646 647  EXPECT_THAT(648      offsetToProviders(AST),649      AllOf(Contains(650                Pair(Code.point("exp_spec"), UnorderedElementsAre(Fwd, Def))),651            Contains(Pair(Code.point("exp"), UnorderedElementsAre(Fwd, Def))),652            Contains(Pair(Code.point("full"), UnorderedElementsAre(Fwd, Def))),653            Contains(654                Pair(Code.point("implicit"), UnorderedElementsAre(Fwd, Def))),655            Contains(656                Pair(Code.point("partial"), UnorderedElementsAre(Partial)))));657}658 659TEST_F(WalkUsedTest, IgnoresIdentityMacros) {660  llvm::Annotations Code(R"cpp(661  #include "header.h"662  void $bar^bar() {663    $stdin^stdin();664  }665  )cpp");666  Inputs.Code = Code.code();667  Inputs.ExtraFiles["header.h"] = guard(R"cpp(668  #include "inner.h"669  void stdin();670  )cpp");671  Inputs.ExtraFiles["inner.h"] = guard(R"cpp(672  #define stdin stdin673  )cpp");674 675  TestAST AST(Inputs);676  auto &SM = AST.sourceManager();677  auto MainFile = Header(*SM.getFileEntryRefForID(SM.getMainFileID()));678  EXPECT_THAT(offsetToProviders(AST),679              UnorderedElementsAre(680                  // FIXME: we should have a reference from stdin to header.h681                  Pair(Code.point("bar"), UnorderedElementsAre(MainFile))));682}683} // namespace684} // namespace clang::include_cleaner685