blob: 3bdddf114e8b2e25e184161be6bc9a1928832ca7 [file] [log] [blame]
Benjamin Kramer6b236262016-04-20 12:43:43 +00001//===-- IncludeFixerTest.cpp - Include fixer unit tests -------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
Benjamin Kramera3d82332016-05-13 09:27:54 +000010#include "InMemorySymbolIndex.h"
Benjamin Kramer6b236262016-04-20 12:43:43 +000011#include "IncludeFixer.h"
Benjamin Kramera3d82332016-05-13 09:27:54 +000012#include "SymbolIndexManager.h"
Benjamin Kramer3a45fab2016-04-28 11:21:29 +000013#include "unittests/Tooling/RewriterTestContext.h"
Benjamin Kramer6b236262016-04-20 12:43:43 +000014#include "clang/Tooling/Tooling.h"
15#include "gtest/gtest.h"
Benjamin Kramer6b236262016-04-20 12:43:43 +000016
17namespace clang {
18namespace include_fixer {
19namespace {
20
Haojian Wu631e5f22016-05-13 15:17:17 +000021using find_all_symbols::SymbolInfo;
22
Benjamin Kramer6b236262016-04-20 12:43:43 +000023static bool runOnCode(tooling::ToolAction *ToolAction, StringRef Code,
Benjamin Kramer3a45fab2016-04-28 11:21:29 +000024 StringRef FileName,
25 const std::vector<std::string> &ExtraArgs) {
Benjamin Kramer6b236262016-04-20 12:43:43 +000026 llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem(
27 new vfs::InMemoryFileSystem);
28 llvm::IntrusiveRefCntPtr<FileManager> Files(
29 new FileManager(FileSystemOptions(), InMemoryFileSystem));
Benjamin Kramer2ecd0902016-05-18 09:28:45 +000030 // FIXME: Investigate why -fms-compatibility breaks tests.
31 std::vector<std::string> Args = {"include_fixer", "-fsyntax-only",
32 "-fno-ms-compatibility", FileName};
Benjamin Kramer3a45fab2016-04-28 11:21:29 +000033 Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
Benjamin Kramer6b236262016-04-20 12:43:43 +000034 tooling::ToolInvocation Invocation(
Benjamin Kramer3a45fab2016-04-28 11:21:29 +000035 Args, ToolAction, Files.get(),
36 std::make_shared<PCHContainerOperations>());
Benjamin Kramer6b236262016-04-20 12:43:43 +000037
38 InMemoryFileSystem->addFile(FileName, 0,
39 llvm::MemoryBuffer::getMemBuffer(Code));
40
41 InMemoryFileSystem->addFile("foo.h", 0,
42 llvm::MemoryBuffer::getMemBuffer("\n"));
Benjamin Kramer3a45fab2016-04-28 11:21:29 +000043 InMemoryFileSystem->addFile("dir/bar.h", 0,
44 llvm::MemoryBuffer::getMemBuffer("\n"));
45 InMemoryFileSystem->addFile("dir/otherdir/qux.h", 0,
Benjamin Kramer6b236262016-04-20 12:43:43 +000046 llvm::MemoryBuffer::getMemBuffer("\n"));
47 return Invocation.run();
48}
49
Benjamin Kramer3a45fab2016-04-28 11:21:29 +000050static std::string runIncludeFixer(
51 StringRef Code,
52 const std::vector<std::string> &ExtraArgs = std::vector<std::string>()) {
Haojian Wu631e5f22016-05-13 15:17:17 +000053 std::vector<SymbolInfo> Symbols = {
54 SymbolInfo("string", SymbolInfo::SymbolKind::Class, "<string>", 1,
55 {{SymbolInfo::ContextType::Namespace, "std"}}),
56 SymbolInfo("sting", SymbolInfo::SymbolKind::Class, "\"sting\"", 1,
57 {{SymbolInfo::ContextType::Namespace, "std"}}),
Haojian Wu631e5f22016-05-13 15:17:17 +000058 SymbolInfo("foo", SymbolInfo::SymbolKind::Class, "\"dir/otherdir/qux.h\"",
59 1, {{SymbolInfo::ContextType::Namespace, "b"},
60 {SymbolInfo::ContextType::Namespace, "a"}}),
Haojian Wu57cdcb02016-05-13 15:44:16 +000061 SymbolInfo("bar", SymbolInfo::SymbolKind::Class, "\"bar.h\"",
62 1, {{SymbolInfo::ContextType::Namespace, "b"},
63 {SymbolInfo::ContextType::Namespace, "a"}}),
Haojian Wuff6d1952016-05-18 09:04:43 +000064 SymbolInfo("Green", SymbolInfo::SymbolKind::Class, "\"color.h\"",
65 1, {{SymbolInfo::ContextType::EnumDecl, "Color"},
66 {SymbolInfo::ContextType::Namespace, "b"},
67 {SymbolInfo::ContextType::Namespace, "a"}}),
Benjamin Kramer3a45fab2016-04-28 11:21:29 +000068 };
Benjamin Kramera3d82332016-05-13 09:27:54 +000069 auto SymbolIndexMgr = llvm::make_unique<include_fixer::SymbolIndexManager>();
70 SymbolIndexMgr->addSymbolIndex(
Haojian Wu631e5f22016-05-13 15:17:17 +000071 llvm::make_unique<include_fixer::InMemorySymbolIndex>(Symbols));
Eric Liu692aca62016-05-04 08:22:35 +000072
Haojian Wu11e9bd22016-05-31 09:31:51 +000073 IncludeFixerContext FixerContext;
74 IncludeFixerActionFactory Factory(*SymbolIndexMgr, FixerContext, "llvm");
75
Benjamin Kramer3a45fab2016-04-28 11:21:29 +000076 runOnCode(&Factory, Code, "input.cc", ExtraArgs);
Haojian Wu11e9bd22016-05-31 09:31:51 +000077 std::vector<clang::tooling::Replacement> Replacements;
78 if (!FixerContext.Headers.empty()) {
79 Replacements = clang::include_fixer::createInsertHeaderReplacements(
80 Code, "input.cc", FixerContext.Headers.front(),
81 FixerContext.FirstIncludeOffset);
82 }
Benjamin Kramer6b236262016-04-20 12:43:43 +000083 clang::RewriterTestContext Context;
84 clang::FileID ID = Context.createInMemoryFile("input.cc", Code);
85 clang::tooling::applyAllReplacements(Replacements, Context.Rewrite);
86 return Context.getRewrittenText(ID);
87}
88
89TEST(IncludeFixer, Typo) {
Eric Liu702cfd12016-05-19 08:21:09 +000090 EXPECT_EQ("#include <string>\n\nstd::string foo;\n",
Benjamin Kramer6b236262016-04-20 12:43:43 +000091 runIncludeFixer("std::string foo;\n"));
92
93 EXPECT_EQ(
Eric Liu702cfd12016-05-19 08:21:09 +000094 "// comment\n#include \"foo.h\"\n#include <string>\nstd::string foo;\n"
Benjamin Kramer3a45fab2016-04-28 11:21:29 +000095 "#include \"dir/bar.h\"\n",
Benjamin Kramer6b236262016-04-20 12:43:43 +000096 runIncludeFixer("// comment\n#include \"foo.h\"\nstd::string foo;\n"
Benjamin Kramer3a45fab2016-04-28 11:21:29 +000097 "#include \"dir/bar.h\"\n"));
Benjamin Kramer6b236262016-04-20 12:43:43 +000098
Eric Liu702cfd12016-05-19 08:21:09 +000099 EXPECT_EQ("#include \"foo.h\"\n#include <string>\nstd::string foo;\n",
Benjamin Kramer6b236262016-04-20 12:43:43 +0000100 runIncludeFixer("#include \"foo.h\"\nstd::string foo;\n"));
101
102 EXPECT_EQ(
Eric Liu702cfd12016-05-19 08:21:09 +0000103 "#include \"foo.h\"\n#include <string>\nstd::string::size_type foo;\n",
Benjamin Kramer6b236262016-04-20 12:43:43 +0000104 runIncludeFixer("#include \"foo.h\"\nstd::string::size_type foo;\n"));
105
Eric Liu692aca62016-05-04 08:22:35 +0000106 // string without "std::" can also be fixed since fixed db results go through
Benjamin Kramera3d82332016-05-13 09:27:54 +0000107 // SymbolIndexManager, and SymbolIndexManager matches unqualified identifiers
108 // too.
Eric Liu702cfd12016-05-19 08:21:09 +0000109 EXPECT_EQ("#include <string>\n\nstring foo;\n",
Eric Liu692aca62016-05-04 08:22:35 +0000110 runIncludeFixer("string foo;\n"));
Benjamin Kramer9b15b6f2016-05-19 12:41:56 +0000111
112 // Fully qualified name.
113 EXPECT_EQ("#include <string>\n\n::std::string foo;\n",
114 runIncludeFixer("::std::string foo;\n"));
115 // Should not match std::string.
116 EXPECT_EQ("::string foo;\n", runIncludeFixer("::string foo;\n"));
Benjamin Kramer6b236262016-04-20 12:43:43 +0000117}
118
119TEST(IncludeFixer, IncompleteType) {
120 EXPECT_EQ(
Eric Liu702cfd12016-05-19 08:21:09 +0000121 "#include \"foo.h\"\n#include <string>\n"
Benjamin Kramer6b236262016-04-20 12:43:43 +0000122 "namespace std {\nclass string;\n}\nstring foo;\n",
123 runIncludeFixer("#include \"foo.h\"\n"
124 "namespace std {\nclass string;\n}\nstring foo;\n"));
125}
126
Benjamin Kramer3a45fab2016-04-28 11:21:29 +0000127TEST(IncludeFixer, MinimizeInclude) {
128 std::vector<std::string> IncludePath = {"-Idir/"};
Eric Liu702cfd12016-05-19 08:21:09 +0000129 EXPECT_EQ("#include \"otherdir/qux.h\"\n\na::b::foo bar;\n",
Benjamin Kramer3a45fab2016-04-28 11:21:29 +0000130 runIncludeFixer("a::b::foo bar;\n", IncludePath));
131
132 IncludePath = {"-isystemdir"};
Eric Liu702cfd12016-05-19 08:21:09 +0000133 EXPECT_EQ("#include <otherdir/qux.h>\n\na::b::foo bar;\n",
Benjamin Kramer3a45fab2016-04-28 11:21:29 +0000134 runIncludeFixer("a::b::foo bar;\n", IncludePath));
135
136 IncludePath = {"-iquotedir"};
Eric Liu702cfd12016-05-19 08:21:09 +0000137 EXPECT_EQ("#include \"otherdir/qux.h\"\n\na::b::foo bar;\n",
Benjamin Kramer3a45fab2016-04-28 11:21:29 +0000138 runIncludeFixer("a::b::foo bar;\n", IncludePath));
139
140 IncludePath = {"-Idir", "-Idir/otherdir"};
Eric Liu702cfd12016-05-19 08:21:09 +0000141 EXPECT_EQ("#include \"qux.h\"\n\na::b::foo bar;\n",
Benjamin Kramer3a45fab2016-04-28 11:21:29 +0000142 runIncludeFixer("a::b::foo bar;\n", IncludePath));
143}
144
Benjamin Kramerad935002016-05-10 08:25:31 +0000145TEST(IncludeFixer, NestedName) {
Eric Liu702cfd12016-05-19 08:21:09 +0000146 EXPECT_EQ("#include \"dir/otherdir/qux.h\"\n\n"
Benjamin Krameraf34e062016-05-17 12:35:18 +0000147 "int x = a::b::foo(0);\n",
Benjamin Kramer04666a22016-05-19 11:39:56 +0000148 runIncludeFixer("int x = a::b::foo(0);\n"));
Benjamin Krameraf34e062016-05-17 12:35:18 +0000149
150 // FIXME: Handle simple macros.
151 EXPECT_EQ("#define FOO a::b::foo\nint x = FOO;\n",
152 runIncludeFixer("#define FOO a::b::foo\nint x = FOO;\n"));
153 EXPECT_EQ("#define FOO(x) a::##x\nint x = FOO(b::foo);\n",
154 runIncludeFixer("#define FOO(x) a::##x\nint x = FOO(b::foo);\n"));
155
Eric Liu702cfd12016-05-19 08:21:09 +0000156 EXPECT_EQ("#include \"dir/otherdir/qux.h\"\n\n"
Benjamin Kramerad935002016-05-10 08:25:31 +0000157 "namespace a {}\nint a = a::b::foo(0);\n",
Benjamin Kramer04666a22016-05-19 11:39:56 +0000158 runIncludeFixer("namespace a {}\nint a = a::b::foo(0);\n"));
Benjamin Kramerad935002016-05-10 08:25:31 +0000159}
160
Benjamin Kramerc3459a52016-05-10 08:25:28 +0000161TEST(IncludeFixer, MultipleMissingSymbols) {
Eric Liu702cfd12016-05-19 08:21:09 +0000162 EXPECT_EQ("#include <string>\n\nstd::string bar;\nstd::sting foo;\n",
Benjamin Kramerc3459a52016-05-10 08:25:28 +0000163 runIncludeFixer("std::string bar;\nstd::sting foo;\n"));
164}
165
Haojian Wu57cdcb02016-05-13 15:44:16 +0000166TEST(IncludeFixer, ScopedNamespaceSymbols) {
Eric Liu702cfd12016-05-19 08:21:09 +0000167 EXPECT_EQ("#include \"bar.h\"\n\nnamespace a {\nb::bar b;\n}",
168 runIncludeFixer("namespace a {\nb::bar b;\n}"));
169 EXPECT_EQ("#include \"bar.h\"\n\nnamespace A {\na::b::bar b;\n}",
170 runIncludeFixer("namespace A {\na::b::bar b;\n}"));
171 EXPECT_EQ("#include \"bar.h\"\n\nnamespace a {\nvoid func() { b::bar b; }\n}",
172 runIncludeFixer("namespace a {\nvoid func() { b::bar b; }\n}"));
Haojian Wu57cdcb02016-05-13 15:44:16 +0000173 EXPECT_EQ("namespace A { c::b::bar b; }\n",
174 runIncludeFixer("namespace A { c::b::bar b; }\n"));
175 // FIXME: The header should not be added here. Remove this after we support
176 // full match.
Eric Liu702cfd12016-05-19 08:21:09 +0000177 EXPECT_EQ("#include \"bar.h\"\n\nnamespace A {\nb::bar b;\n}",
178 runIncludeFixer("namespace A {\nb::bar b;\n}"));
Haojian Wu57cdcb02016-05-13 15:44:16 +0000179}
Haojian Wuff6d1952016-05-18 09:04:43 +0000180
181TEST(IncludeFixer, EnumConstantSymbols) {
Eric Liu702cfd12016-05-19 08:21:09 +0000182 EXPECT_EQ("#include \"color.h\"\n\nint test = a::b::Green;\n",
Haojian Wuff6d1952016-05-18 09:04:43 +0000183 runIncludeFixer("int test = a::b::Green;\n"));
184}
185
Eric Liu702cfd12016-05-19 08:21:09 +0000186// FIXME: add test cases for inserting and sorting multiple headers when
187// include-fixer supports multiple headers insertion.
188TEST(IncludeFixer, InsertAndSortSingleHeader) {
189 // Insert one header.
190 std::string Code = "#include \"a.h\"\n"
191 "#include \"foo.h\"\n"
192 "\n"
193 "namespace a { b::bar b; }";
194 std::string Expected = "#include \"a.h\"\n"
195 "#include \"bar.h\"\n"
196 "#include \"foo.h\"\n"
197 "\n"
198 "namespace a { b::bar b; }";
199 EXPECT_EQ(Expected, runIncludeFixer(Code));
200}
201
Benjamin Kramer6b236262016-04-20 12:43:43 +0000202} // namespace
203} // namespace include_fixer
204} // namespace clang