brintos

brintos / llvm-project-archived public Read only

0
0
Text · 14.4 KiB · 778ab89 Raw
373 lines · cpp
1//===-- IncludeFixerTest.cpp - Include fixer unit tests -------------------===//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 "InMemorySymbolIndex.h"10#include "IncludeFixer.h"11#include "SymbolIndexManager.h"12#include "unittests/Tooling/RewriterTestContext.h"13#include "clang/Tooling/Tooling.h"14#include "gtest/gtest.h"15 16namespace clang {17namespace include_fixer {18namespace {19 20using find_all_symbols::SymbolInfo;21using find_all_symbols::SymbolAndSignals;22 23static bool runOnCode(tooling::ToolAction *ToolAction, StringRef Code,24                      StringRef FileName,25                      const std::vector<std::string> &ExtraArgs) {26  llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(27      new llvm::vfs::InMemoryFileSystem);28  llvm::IntrusiveRefCntPtr<FileManager> Files(29      new FileManager(FileSystemOptions(), InMemoryFileSystem));30  // FIXME: Investigate why -fms-compatibility breaks tests.31  std::vector<std::string> Args = {"include_fixer", "-fsyntax-only",32                                   "-fno-ms-compatibility",33                                   std::string(FileName)};34  Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());35  tooling::ToolInvocation Invocation(36      Args, ToolAction, Files.get(),37      std::make_shared<PCHContainerOperations>());38 39  InMemoryFileSystem->addFile(FileName, 0,40                              llvm::MemoryBuffer::getMemBuffer(Code));41 42  InMemoryFileSystem->addFile("foo.h", 0,43                              llvm::MemoryBuffer::getMemBuffer("\n"));44  InMemoryFileSystem->addFile("dir/bar.h", 0,45                              llvm::MemoryBuffer::getMemBuffer("\n"));46  InMemoryFileSystem->addFile("dir/otherdir/qux.h", 0,47                              llvm::MemoryBuffer::getMemBuffer("\n"));48  InMemoryFileSystem->addFile("header.h", 0,49                              llvm::MemoryBuffer::getMemBuffer("bar b;"));50  return Invocation.run();51}52 53static std::string runIncludeFixer(54    StringRef Code,55    const std::vector<std::string> &ExtraArgs = std::vector<std::string>()) {56  std::vector<SymbolAndSignals> Symbols = {57      {SymbolInfo("string", SymbolInfo::SymbolKind::Class, "<string>",58                  {{SymbolInfo::ContextType::Namespace, "std"}}),59       SymbolInfo::Signals{}},60      {SymbolInfo("sting", SymbolInfo::SymbolKind::Class, "\"sting\"",61                  {{SymbolInfo::ContextType::Namespace, "std"}}),62       SymbolInfo::Signals{}},63      {SymbolInfo("foo", SymbolInfo::SymbolKind::Class,64                  "\"dir/otherdir/qux.h\"",65                  {{SymbolInfo::ContextType::Namespace, "b"},66                   {SymbolInfo::ContextType::Namespace, "a"}}),67       SymbolInfo::Signals{}},68      {SymbolInfo("bar", SymbolInfo::SymbolKind::Class, "\"bar.h\"",69                  {{SymbolInfo::ContextType::Namespace, "b"},70                   {SymbolInfo::ContextType::Namespace, "a"}}),71       SymbolInfo::Signals{}},72      {SymbolInfo("bar", SymbolInfo::SymbolKind::Class, "\"bar2.h\"",73                  {{SymbolInfo::ContextType::Namespace, "c"},74                   {SymbolInfo::ContextType::Namespace, "a"}}),75       SymbolInfo::Signals{}},76      {SymbolInfo("Green", SymbolInfo::SymbolKind::Class, "\"color.h\"",77                  {{SymbolInfo::ContextType::EnumDecl, "Color"},78                   {SymbolInfo::ContextType::Namespace, "b"},79                   {SymbolInfo::ContextType::Namespace, "a"}}),80       SymbolInfo::Signals{}},81      {SymbolInfo("Vector", SymbolInfo::SymbolKind::Class, "\"Vector.h\"",82                  {{SymbolInfo::ContextType::Namespace, "__a"},83                   {SymbolInfo::ContextType::Namespace, "a"}}),84       SymbolInfo::Signals{/*Seen=*/2, 0}},85      {SymbolInfo("Vector", SymbolInfo::SymbolKind::Class, "\"Vector.h\"",86                  {{SymbolInfo::ContextType::Namespace, "a"}}),87       SymbolInfo::Signals{/*Seen=*/2, 0}},88      {SymbolInfo("StrCat", SymbolInfo::SymbolKind::Class, "\"strcat.h\"",89                  {{SymbolInfo::ContextType::Namespace, "str"}}),90       SymbolInfo::Signals{}},91      {SymbolInfo("str", SymbolInfo::SymbolKind::Class, "\"str.h\"", {}),92       SymbolInfo::Signals{}},93      {SymbolInfo("foo2", SymbolInfo::SymbolKind::Class, "\"foo2.h\"", {}),94       SymbolInfo::Signals{}},95  };96  auto SymbolIndexMgr = std::make_unique<SymbolIndexManager>();97  SymbolIndexMgr->addSymbolIndex(98      [=]() { return std::make_unique<InMemorySymbolIndex>(Symbols); });99 100  std::vector<IncludeFixerContext> FixerContexts;101  IncludeFixerActionFactory Factory(*SymbolIndexMgr, FixerContexts, "llvm");102  std::string FakeFileName = "input.cc";103  runOnCode(&Factory, Code, FakeFileName, ExtraArgs);104  assert(FixerContexts.size() == 1);105  if (FixerContexts.front().getHeaderInfos().empty())106    return std::string(Code);107  auto Replaces = createIncludeFixerReplacements(Code, FixerContexts.front());108  EXPECT_TRUE(static_cast<bool>(Replaces))109      << llvm::toString(Replaces.takeError()) << "\n";110  if (!Replaces)111    return "";112  RewriterTestContext Context;113  FileID ID = Context.createInMemoryFile(FakeFileName, Code);114  tooling::applyAllReplacements(*Replaces, Context.Rewrite);115  return Context.getRewrittenText(ID);116}117 118TEST(IncludeFixer, Typo) {119  EXPECT_EQ("#include <string>\nstd::string foo;\n",120            runIncludeFixer("std::string foo;\n"));121 122  EXPECT_EQ("// comment\n#include \"foo.h\"\n#include <string>\n"123            "std::string foo;\n#include \"dir/bar.h\"\n",124            runIncludeFixer("// comment\n#include \"foo.h\"\nstd::string foo;\n"125                            "#include \"dir/bar.h\"\n"));126 127  EXPECT_EQ("#include \"foo.h\"\n#include <string>\nstd::string foo;\n",128            runIncludeFixer("#include \"foo.h\"\nstd::string foo;\n"));129 130  EXPECT_EQ(131      "#include \"foo.h\"\n#include <string>\nstd::string::size_type foo;\n",132      runIncludeFixer("#include \"foo.h\"\nstd::string::size_type foo;\n"));133 134  EXPECT_EQ("#include <string>\nstd::string foo;\n",135            runIncludeFixer("string foo;\n"));136 137  // Should not match std::string.138  EXPECT_EQ("::string foo;\n", runIncludeFixer("::string foo;\n"));139}140 141TEST(IncludeFixer, IncompleteType) {142  EXPECT_EQ(143      "#include \"foo.h\"\n#include <string>\n"144      "namespace std {\nclass string;\n}\nstd::string foo;\n",145      runIncludeFixer("#include \"foo.h\"\n"146                      "namespace std {\nclass string;\n}\nstring foo;\n"));147 148  EXPECT_EQ("#include <string>\n"149            "class string;\ntypedef string foo;\nfoo f;\n",150            runIncludeFixer("class string;\ntypedef string foo;\nfoo f;\n"));151}152 153TEST(IncludeFixer, MinimizeInclude) {154  std::vector<std::string> IncludePath = {"-Idir/"};155  EXPECT_EQ("#include \"otherdir/qux.h\"\na::b::foo bar;\n",156            runIncludeFixer("a::b::foo bar;\n", IncludePath));157 158  IncludePath = {"-isystemdir"};159  EXPECT_EQ("#include <otherdir/qux.h>\na::b::foo bar;\n",160            runIncludeFixer("a::b::foo bar;\n", IncludePath));161 162  IncludePath = {"-iquotedir"};163  EXPECT_EQ("#include \"otherdir/qux.h\"\na::b::foo bar;\n",164            runIncludeFixer("a::b::foo bar;\n", IncludePath));165 166  IncludePath = {"-Idir", "-Idir/otherdir"};167  EXPECT_EQ("#include \"qux.h\"\na::b::foo bar;\n",168            runIncludeFixer("a::b::foo bar;\n", IncludePath));169}170 171TEST(IncludeFixer, NestedName) {172  EXPECT_EQ("#include \"dir/otherdir/qux.h\"\n"173            "int x = a::b::foo(0);\n",174            runIncludeFixer("int x = a::b::foo(0);\n"));175 176  // FIXME: Handle simple macros.177  EXPECT_EQ("#define FOO a::b::foo\nint x = FOO;\n",178            runIncludeFixer("#define FOO a::b::foo\nint x = FOO;\n"));179  EXPECT_EQ("#define FOO(x) a::##x\nint x = FOO(b::foo);\n",180            runIncludeFixer("#define FOO(x) a::##x\nint x = FOO(b::foo);\n"));181 182  // The empty namespace is cleaned up by clang-format after clang-include-fixer183  // finishes.184  EXPECT_EQ("#include \"dir/otherdir/qux.h\"\n"185            "\nint a = a::b::foo(0);\n",186            runIncludeFixer("namespace a {}\nint a = a::b::foo(0);\n"));187}188 189TEST(IncludeFixer, MultipleMissingSymbols) {190  EXPECT_EQ("#include <string>\nstd::string bar;\nstd::sting foo;\n",191            runIncludeFixer("std::string bar;\nstd::sting foo;\n"));192}193 194TEST(IncludeFixer, ScopedNamespaceSymbols) {195  EXPECT_EQ("#include \"bar.h\"\nnamespace a {\nb::bar b;\n}",196            runIncludeFixer("namespace a {\nb::bar b;\n}"));197  EXPECT_EQ("#include \"bar.h\"\nnamespace A {\na::b::bar b;\n}",198            runIncludeFixer("namespace A {\na::b::bar b;\n}"));199  EXPECT_EQ("#include \"bar.h\"\nnamespace a {\nvoid func() { b::bar b; }\n} "200            "// namespace a",201            runIncludeFixer("namespace a {\nvoid func() { b::bar b; }\n}"));202  EXPECT_EQ("namespace A { c::b::bar b; }\n",203            runIncludeFixer("namespace A { c::b::bar b; }\n"));204  // FIXME: The header should not be added here. Remove this after we support205  // full match.206  EXPECT_EQ("#include \"bar.h\"\nnamespace A {\na::b::bar b;\n}",207            runIncludeFixer("namespace A {\nb::bar b;\n}"));208 209  // Finds candidates for "str::StrCat".210  EXPECT_EQ("#include \"strcat.h\"\nnamespace foo2 {\nstr::StrCat b;\n}",211            runIncludeFixer("namespace foo2 {\nstr::StrCat b;\n}"));212  // str::StrCat2 doesn't exist.213  // In these two cases, StrCat2 is a nested class of class str.214  EXPECT_EQ("#include \"str.h\"\nnamespace foo2 {\nstr::StrCat2 b;\n}",215            runIncludeFixer("namespace foo2 {\nstr::StrCat2 b;\n}"));216  EXPECT_EQ("#include \"str.h\"\nnamespace ns {\nstr::StrCat2 b;\n}",217            runIncludeFixer("namespace ns {\nstr::StrCat2 b;\n}"));218}219 220TEST(IncludeFixer, EnumConstantSymbols) {221  EXPECT_EQ("#include \"color.h\"\nint test = a::b::Green;\n",222            runIncludeFixer("int test = a::b::Green;\n"));223}224 225TEST(IncludeFixer, IgnoreSymbolFromHeader) {226  std::string Code = "#include \"header.h\"";227  EXPECT_EQ(Code, runIncludeFixer(Code));228}229 230// FIXME: add test cases for inserting and sorting multiple headers when231// clang-include-fixer supports multiple headers insertion.232TEST(IncludeFixer, InsertAndSortSingleHeader) {233  // Insert one header.234  std::string Code = "#include \"a.h\"\n"235                     "#include \"foo.h\"\n"236                     "\n"237                     "namespace a {\nb::bar b;\n}\n";238  std::string Expected = "#include \"a.h\"\n"239                         "#include \"bar.h\"\n"240                         "#include \"foo.h\"\n"241                         "\n"242                         "namespace a {\nb::bar b;\n}\n";243  EXPECT_EQ(Expected, runIncludeFixer(Code));244}245 246TEST(IncludeFixer, DoNotDeleteMatchedSymbol) {247  EXPECT_EQ("#include \"Vector.h\"\na::Vector v;",248            runIncludeFixer("a::Vector v;"));249}250 251TEST(IncludeFixer, FixNamespaceQualifiers) {252  EXPECT_EQ("#include \"bar.h\"\na::b::bar b;\n",253            runIncludeFixer("b::bar b;\n"));254  EXPECT_EQ("#include \"bar.h\"\na::b::bar b;\n",255            runIncludeFixer("a::b::bar b;\n"));256  EXPECT_EQ("#include \"bar.h\"\na::b::bar b;\n",257            runIncludeFixer("bar b;\n"));258  EXPECT_EQ("#include \"bar.h\"\nnamespace a {\nb::bar b;\n}\n",259            runIncludeFixer("namespace a {\nb::bar b;\n}\n"));260  EXPECT_EQ("#include \"bar.h\"\nnamespace a {\nb::bar b;\n}\n",261            runIncludeFixer("namespace a {\nbar b;\n}\n"));262  EXPECT_EQ("#include \"bar.h\"\nnamespace a {\nnamespace b{\nbar b;\n}\n} "263            "// namespace a\n",264            runIncludeFixer("namespace a {\nnamespace b{\nbar b;\n}\n}\n"));265  EXPECT_EQ("c::b::bar b;\n",266            runIncludeFixer("c::b::bar b;\n"));267  EXPECT_EQ("#include \"bar.h\"\nnamespace d {\na::b::bar b;\n}\n",268            runIncludeFixer("namespace d {\nbar b;\n}\n"));269  EXPECT_EQ("#include \"bar2.h\"\nnamespace c {\na::c::bar b;\n}\n",270            runIncludeFixer("namespace c {\nbar b;\n}\n"));271 272  // Test common qualifiers reduction.273  EXPECT_EQ("#include \"bar.h\"\nnamespace a {\nnamespace d {\nb::bar b;\n}\n} "274            "// namespace a\n",275            runIncludeFixer("namespace a {\nnamespace d {\nbar b;\n}\n}\n"));276  EXPECT_EQ("#include \"bar.h\"\nnamespace d {\nnamespace a {\na::b::bar "277            "b;\n}\n} // namespace d\n",278            runIncludeFixer("namespace d {\nnamespace a {\nbar b;\n}\n}\n"));279 280  // Test nested classes.281  EXPECT_EQ("#include \"bar.h\"\nnamespace d {\na::b::bar::t b;\n}\n",282            runIncludeFixer("namespace d {\nbar::t b;\n}\n"));283  EXPECT_EQ("#include \"bar.h\"\nnamespace c {\na::b::bar::t b;\n}\n",284            runIncludeFixer("namespace c {\nbar::t b;\n}\n"));285  EXPECT_EQ("#include \"bar.h\"\nnamespace a {\nb::bar::t b;\n}\n",286            runIncludeFixer("namespace a {\nbar::t b;\n}\n"));287 288  EXPECT_EQ("#include \"color.h\"\nint test = a::b::Green;\n",289            runIncludeFixer("int test = Green;\n"));290  EXPECT_EQ("#include \"color.h\"\nnamespace d {\nint test = a::b::Green;\n}\n",291            runIncludeFixer("namespace d {\nint test = Green;\n}\n"));292  EXPECT_EQ("#include \"color.h\"\nnamespace a {\nint test = b::Green;\n}\n",293            runIncludeFixer("namespace a {\nint test = Green;\n}\n"));294 295  // Test global scope operator.296  EXPECT_EQ("#include \"bar.h\"\n::a::b::bar b;\n",297            runIncludeFixer("::a::b::bar b;\n"));298  EXPECT_EQ("#include \"bar.h\"\nnamespace a {\n::a::b::bar b;\n}\n",299            runIncludeFixer("namespace a {\n::a::b::bar b;\n}\n"));300}301 302TEST(IncludeFixer, FixNamespaceQualifiersForAllInstances) {303  const char TestCode[] = R"(304namespace a {305bar b;306int func1() {307  bar a;308                                                             bar *p = new bar();309  return 0;310}311} // namespace a312 313namespace a {314bar func2() {315  bar f;316  return f;317}318} // namespace a319 320// Non-fixed cases:321void f() {322  bar b;323}324 325namespace a {326namespace c {327  bar b;328} // namespace c329} // namespace a330)";331 332  const char ExpectedCode[] = R"(333#include "bar.h"334namespace a {335b::bar b;336int func1() {337  b::bar a;338  b::bar *p = new b::bar();339  return 0;340}341} // namespace a342 343namespace a {344b::bar func2() {345  b::bar f;346  return f;347}348} // namespace a349 350// Non-fixed cases:351void f() {352  bar b;353}354 355namespace a {356namespace c {357  bar b;358} // namespace c359} // namespace a360)";361 362  EXPECT_EQ(ExpectedCode, runIncludeFixer(TestCode));363}364 365TEST(IncludeFixer, DontAddQualifiersForMissingCompleteType) {366  EXPECT_EQ("#include \"bar.h\"\nclass bar;\nvoid f() {\nbar* b;\nb->f();\n}",367            runIncludeFixer("class bar;\nvoid f() {\nbar* b;\nb->f();\n}"));368}369 370} // namespace371} // namespace include_fixer372} // namespace clang373