brintos

brintos / llvm-project-archived public Read only

0
0
Text · 12.0 KiB · 2a9b8c9 Raw
377 lines · cpp
1//===------ IndexActionTests.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 "Headers.h"10#include "TestFS.h"11#include "URI.h"12#include "index/IndexAction.h"13#include "index/Serialization.h"14#include "clang/Basic/SourceLocation.h"15#include "clang/Basic/SourceManager.h"16#include "clang/Tooling/Tooling.h"17#include "gmock/gmock.h"18#include "gtest/gtest.h"19#include <string>20 21namespace clang {22namespace clangd {23namespace {24 25using ::testing::AllOf;26using ::testing::ElementsAre;27using ::testing::EndsWith;28using ::testing::Not;29using ::testing::Pair;30using ::testing::UnorderedElementsAre;31using ::testing::UnorderedPointwise;32 33std::string toUri(llvm::StringRef Path) { return URI::create(Path).toString(); }34 35MATCHER(isTU, "") { return arg.Flags & IncludeGraphNode::SourceFlag::IsTU; }36 37MATCHER_P(hasDigest, Digest, "") { return arg.Digest == Digest; }38 39MATCHER_P(hasName, Name, "") { return arg.Name == Name; }40 41MATCHER(hasSameURI, "") {42  llvm::StringRef URI = ::testing::get<0>(arg);43  const std::string &Path = ::testing::get<1>(arg);44  return toUri(Path) == URI;45}46 47MATCHER_P(includeHeader, P, "") {48  return (arg.IncludeHeaders.size() == 1) &&49         (arg.IncludeHeaders.begin()->IncludeHeader == P);50}51 52::testing::Matcher<const IncludeGraphNode &>53includesAre(const std::vector<std::string> &Includes) {54  return ::testing::Field(&IncludeGraphNode::DirectIncludes,55                          UnorderedPointwise(hasSameURI(), Includes));56}57 58void checkNodesAreInitialized(const IndexFileIn &IndexFile,59                              const std::vector<std::string> &Paths) {60  ASSERT_TRUE(IndexFile.Sources);61  EXPECT_THAT(Paths.size(), IndexFile.Sources->size());62  for (llvm::StringRef Path : Paths) {63    auto URI = toUri(Path);64    const auto &Node = IndexFile.Sources->lookup(URI);65    // Uninitialized nodes will have an empty URI.66    EXPECT_EQ(Node.URI.data(), IndexFile.Sources->find(URI)->getKeyData());67  }68}69 70std::map<std::string, const IncludeGraphNode &> toMap(const IncludeGraph &IG) {71  std::map<std::string, const IncludeGraphNode &> Nodes;72  for (auto &I : IG)73    Nodes.emplace(std::string(I.getKey()), I.getValue());74  return Nodes;75}76 77class IndexActionTest : public ::testing::Test {78public:79  IndexActionTest() : InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem) {}80 81  IndexFileIn82  runIndexingAction(llvm::StringRef MainFilePath,83                    const std::vector<std::string> &ExtraArgs = {}) {84    IndexFileIn IndexFile;85    llvm::IntrusiveRefCntPtr<FileManager> Files(86        new FileManager(FileSystemOptions(), InMemoryFileSystem));87 88    auto Action = createStaticIndexingAction(89        Opts, [&](SymbolSlab S) { IndexFile.Symbols = std::move(S); },90        [&](RefSlab R) { IndexFile.Refs = std::move(R); },91        [&](RelationSlab R) { IndexFile.Relations = std::move(R); },92        [&](IncludeGraph IG) { IndexFile.Sources = std::move(IG); });93 94    std::vector<std::string> Args = {"index_action", "-fsyntax-only",95                                     "-xc++",        "-std=c++11",96                                     "-iquote",      testRoot()};97    Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());98    Args.push_back(std::string(MainFilePath));99 100    tooling::ToolInvocation Invocation(101        Args, std::move(Action), Files.get(),102        std::make_shared<PCHContainerOperations>());103 104    Invocation.run();105 106    checkNodesAreInitialized(IndexFile, FilePaths);107    return IndexFile;108  }109 110  void addFile(llvm::StringRef Path, llvm::StringRef Content) {111    InMemoryFileSystem->addFile(Path, 0,112                                llvm::MemoryBuffer::getMemBufferCopy(Content));113    FilePaths.push_back(std::string(Path));114  }115 116protected:117  SymbolCollector::Options Opts;118  std::vector<std::string> FilePaths;119  llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem;120};121 122TEST_F(IndexActionTest, CollectIncludeGraph) {123  std::string MainFilePath = testPath("main.cpp");124  std::string MainCode = "#include \"level1.h\"";125  std::string Level1HeaderPath = testPath("level1.h");126  std::string Level1HeaderCode = "#include \"level2.h\"";127  std::string Level2HeaderPath = testPath("level2.h");128  std::string Level2HeaderCode = "";129 130  addFile(MainFilePath, MainCode);131  addFile(Level1HeaderPath, Level1HeaderCode);132  addFile(Level2HeaderPath, Level2HeaderCode);133 134  IndexFileIn IndexFile = runIndexingAction(MainFilePath);135  auto Nodes = toMap(*IndexFile.Sources);136 137  EXPECT_THAT(Nodes,138              UnorderedElementsAre(139                  Pair(toUri(MainFilePath),140                       AllOf(isTU(), includesAre({Level1HeaderPath}),141                             hasDigest(digest(MainCode)))),142                  Pair(toUri(Level1HeaderPath),143                       AllOf(Not(isTU()), includesAre({Level2HeaderPath}),144                             hasDigest(digest(Level1HeaderCode)))),145                  Pair(toUri(Level2HeaderPath),146                       AllOf(Not(isTU()), includesAre({}),147                             hasDigest(digest(Level2HeaderCode))))));148}149 150TEST_F(IndexActionTest, IncludeGraphSelfInclude) {151  std::string MainFilePath = testPath("main.cpp");152  std::string MainCode = "#include \"header.h\"";153  std::string HeaderPath = testPath("header.h");154  std::string HeaderCode = R"cpp(155      #ifndef _GUARD_156      #define _GUARD_157      #include "header.h"158      #endif)cpp";159 160  addFile(MainFilePath, MainCode);161  addFile(HeaderPath, HeaderCode);162 163  IndexFileIn IndexFile = runIndexingAction(MainFilePath);164  auto Nodes = toMap(*IndexFile.Sources);165 166  EXPECT_THAT(167      Nodes,168      UnorderedElementsAre(169          Pair(toUri(MainFilePath), AllOf(isTU(), includesAre({HeaderPath}),170                                          hasDigest(digest(MainCode)))),171          Pair(toUri(HeaderPath), AllOf(Not(isTU()), includesAre({HeaderPath}),172                                        hasDigest(digest(HeaderCode))))));173}174 175TEST_F(IndexActionTest, IncludeGraphSkippedFile) {176  std::string MainFilePath = testPath("main.cpp");177  std::string MainCode = R"cpp(178      #include "common.h"179      #include "header.h"180      )cpp";181 182  std::string CommonHeaderPath = testPath("common.h");183  std::string CommonHeaderCode = R"cpp(184      #ifndef _GUARD_185      #define _GUARD_186      void f();187      #endif)cpp";188 189  std::string HeaderPath = testPath("header.h");190  std::string HeaderCode = R"cpp(191      #include "common.h"192      void g();)cpp";193 194  addFile(MainFilePath, MainCode);195  addFile(HeaderPath, HeaderCode);196  addFile(CommonHeaderPath, CommonHeaderCode);197 198  IndexFileIn IndexFile = runIndexingAction(MainFilePath);199  auto Nodes = toMap(*IndexFile.Sources);200 201  EXPECT_THAT(202      Nodes, UnorderedElementsAre(203                 Pair(toUri(MainFilePath),204                      AllOf(isTU(), includesAre({HeaderPath, CommonHeaderPath}),205                            hasDigest(digest(MainCode)))),206                 Pair(toUri(HeaderPath),207                      AllOf(Not(isTU()), includesAre({CommonHeaderPath}),208                            hasDigest(digest(HeaderCode)))),209                 Pair(toUri(CommonHeaderPath),210                      AllOf(Not(isTU()), includesAre({}),211                            hasDigest(digest(CommonHeaderCode))))));212}213 214TEST_F(IndexActionTest, IncludeGraphDynamicInclude) {215  std::string MainFilePath = testPath("main.cpp");216  std::string MainCode = R"cpp(217      #ifndef FOO218      #define FOO "main.cpp"219      #else220      #define FOO "header.h"221      #endif222 223      #include FOO)cpp";224  std::string HeaderPath = testPath("header.h");225  std::string HeaderCode = "";226 227  addFile(MainFilePath, MainCode);228  addFile(HeaderPath, HeaderCode);229 230  IndexFileIn IndexFile = runIndexingAction(MainFilePath);231  auto Nodes = toMap(*IndexFile.Sources);232 233  EXPECT_THAT(234      Nodes,235      UnorderedElementsAre(236          Pair(toUri(MainFilePath),237               AllOf(isTU(), includesAre({MainFilePath, HeaderPath}),238                     hasDigest(digest(MainCode)))),239          Pair(toUri(HeaderPath), AllOf(Not(isTU()), includesAre({}),240                                        hasDigest(digest(HeaderCode))))));241}242 243TEST_F(IndexActionTest, NoWarnings) {244  std::string MainFilePath = testPath("main.cpp");245  std::string MainCode = R"cpp(246      void foo(int x) {247        if (x = 1) // -Wparentheses248          return;249        if (x = 1) // -Wparentheses250          return;251      }252      void bar() {}253  )cpp";254  addFile(MainFilePath, MainCode);255  // We set -ferror-limit so the warning-promoted-to-error would be fatal.256  // This would cause indexing to stop (if warnings weren't disabled).257  IndexFileIn IndexFile = runIndexingAction(258      MainFilePath, {"-ferror-limit=1", "-Wparentheses", "-Werror"});259  ASSERT_TRUE(IndexFile.Sources);260  ASSERT_NE(0u, IndexFile.Sources->size());261  EXPECT_THAT(*IndexFile.Symbols, ElementsAre(hasName("foo"), hasName("bar")));262}263 264TEST_F(IndexActionTest, SkipFiles) {265  std::string MainFilePath = testPath("main.cpp");266  addFile(MainFilePath, R"cpp(267    // clang-format off268    #include "good.h"269    #include "bad.h"270    // clang-format on271  )cpp");272  addFile(testPath("good.h"), R"cpp(273    struct S { int s; };274    void f1() { S f; }275    auto unskippable1() { return S(); }276  )cpp");277  addFile(testPath("bad.h"), R"cpp(278    struct T { S t; };279    void f2() { S f; }280    auto unskippable2() { return S(); }281  )cpp");282  Opts.FileFilter = [](const SourceManager &SM, FileID F) {283    return !SM.getFileEntryRefForID(F)->getName().ends_with("bad.h");284  };285  IndexFileIn IndexFile = runIndexingAction(MainFilePath, {"-std=c++14"});286  EXPECT_THAT(*IndexFile.Symbols,287              UnorderedElementsAre(hasName("S"), hasName("s"), hasName("f1"),288                                   hasName("unskippable1")));289  for (const auto &Pair : *IndexFile.Refs)290    for (const auto &Ref : Pair.second)291      EXPECT_THAT(Ref.Location.FileURI, EndsWith("good.h"));292}293 294TEST_F(IndexActionTest, SkipNestedSymbols) {295  std::string MainFilePath = testPath("main.cpp");296  addFile(MainFilePath, R"cpp(297  namespace ns1 {298  namespace ns2 {299  namespace ns3 {300  namespace ns4 {301  namespace ns5 {302  namespace ns6 {303  namespace ns7 {304  namespace ns8 {305  namespace ns9 {306  class Bar {};307  void foo() {308    class Baz {};309  }310  }311  }312  }313  }314  }315  }316  }317  }318  })cpp");319  IndexFileIn IndexFile = runIndexingAction(MainFilePath, {"-std=c++14"});320  EXPECT_THAT(*IndexFile.Symbols, testing::Contains(hasName("foo")));321  EXPECT_THAT(*IndexFile.Symbols, testing::Contains(hasName("Bar")));322  EXPECT_THAT(*IndexFile.Symbols, Not(testing::Contains(hasName("Baz"))));323}324 325TEST_F(IndexActionTest, SymbolFromCC) {326  std::string MainFilePath = testPath("main.cpp");327  addFile(MainFilePath, R"cpp(328 #include "main.h"329 void foo() {}330 )cpp");331  addFile(testPath("main.h"), R"cpp(332 #pragma once333 void foo();334 )cpp");335  Opts.FileFilter = [](const SourceManager &SM, FileID F) {336    return !SM.getFileEntryRefForID(F)->getName().ends_with("main.h");337  };338  IndexFileIn IndexFile = runIndexingAction(MainFilePath, {"-std=c++14"});339  EXPECT_THAT(*IndexFile.Symbols,340              UnorderedElementsAre(AllOf(341                  hasName("foo"),342                  includeHeader(URI::create(testPath("main.h")).toString()))));343}344 345TEST_F(IndexActionTest, IncludeHeaderForwardDecls) {346  std::string MainFilePath = testPath("main.cpp");347  addFile(MainFilePath, R"cpp(348#include "fwd.h"349#include "full.h"350 )cpp");351  addFile(testPath("fwd.h"), R"cpp(352#ifndef _FWD_H_353#define _FWD_H_354struct Foo;355#endif356 )cpp");357  addFile(testPath("full.h"), R"cpp(358#ifndef _FULL_H_359#define _FULL_H_360struct Foo {};361 362// This decl is important, as otherwise we detect control macro for the file,363// before handling definition of Foo.364void other();365#endif366 )cpp");367  IndexFileIn IndexFile = runIndexingAction(MainFilePath);368  EXPECT_THAT(*IndexFile.Symbols,369              testing::Contains(AllOf(370                  hasName("Foo"),371                  includeHeader(URI::create(testPath("full.h")).toString()))))372      << *IndexFile.Symbols->begin();373}374} // namespace375} // namespace clangd376} // namespace clang377