1 //===-- IncludeFixerTest.cpp - Include fixer unit tests -------------------===//
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-exception
7 //===----------------------------------------------------------------------===//
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"
17 namespace include_fixer
{
20 using find_all_symbols::SymbolInfo
;
21 using find_all_symbols::SymbolAndSignals
;
23 static bool runOnCode(tooling::ToolAction
*ToolAction
, StringRef Code
,
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
>());
39 InMemoryFileSystem
->addFile(FileName
, 0,
40 llvm::MemoryBuffer::getMemBuffer(Code
));
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();
53 static std::string
runIncludeFixer(
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
{}},
96 auto SymbolIndexMgr
= std::make_unique
<SymbolIndexManager
>();
97 SymbolIndexMgr
->addSymbolIndex(
98 [=]() { return std::make_unique
<InMemorySymbolIndex
>(Symbols
); });
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";
112 RewriterTestContext Context
;
113 FileID ID
= Context
.createInMemoryFile(FakeFileName
, Code
);
114 tooling::applyAllReplacements(*Replaces
, Context
.Rewrite
);
115 return Context
.getRewrittenText(ID
);
118 TEST(IncludeFixer
, Typo
) {
119 EXPECT_EQ("#include <string>\nstd::string foo;\n",
120 runIncludeFixer("std::string foo;\n"));
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"));
127 EXPECT_EQ("#include \"foo.h\"\n#include <string>\nstd::string foo;\n",
128 runIncludeFixer("#include \"foo.h\"\nstd::string foo;\n"));
131 "#include \"foo.h\"\n#include <string>\nstd::string::size_type foo;\n",
132 runIncludeFixer("#include \"foo.h\"\nstd::string::size_type foo;\n"));
134 EXPECT_EQ("#include <string>\nstd::string foo;\n",
135 runIncludeFixer("string foo;\n"));
137 // Should not match std::string.
138 EXPECT_EQ("::string foo;\n", runIncludeFixer("::string foo;\n"));
141 TEST(IncludeFixer
, IncompleteType
) {
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"));
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"));
153 TEST(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
));
158 IncludePath
= {"-isystemdir"};
159 EXPECT_EQ("#include <otherdir/qux.h>\na::b::foo bar;\n",
160 runIncludeFixer("a::b::foo bar;\n", IncludePath
));
162 IncludePath
= {"-iquotedir"};
163 EXPECT_EQ("#include \"otherdir/qux.h\"\na::b::foo bar;\n",
164 runIncludeFixer("a::b::foo bar;\n", IncludePath
));
166 IncludePath
= {"-Idir", "-Idir/otherdir"};
167 EXPECT_EQ("#include \"qux.h\"\na::b::foo bar;\n",
168 runIncludeFixer("a::b::foo bar;\n", IncludePath
));
171 TEST(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"));
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"));
182 // The empty namespace is cleaned up by clang-format after clang-include-fixer
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"));
189 TEST(IncludeFixer
, MultipleMissingSymbols
) {
190 EXPECT_EQ("#include <string>\nstd::string bar;\nstd::sting foo;\n",
191 runIncludeFixer("std::string bar;\nstd::sting foo;\n"));
194 TEST(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} "
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 support
206 EXPECT_EQ("#include \"bar.h\"\nnamespace A {\na::b::bar b;\n}",
207 runIncludeFixer("namespace A {\nb::bar b;\n}"));
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}"));
220 TEST(IncludeFixer
, EnumConstantSymbols
) {
221 EXPECT_EQ("#include \"color.h\"\nint test = a::b::Green;\n",
222 runIncludeFixer("int test = a::b::Green;\n"));
225 TEST(IncludeFixer
, IgnoreSymbolFromHeader
) {
226 std::string Code
= "#include \"header.h\"";
227 EXPECT_EQ(Code
, runIncludeFixer(Code
));
230 // FIXME: add test cases for inserting and sorting multiple headers when
231 // clang-include-fixer supports multiple headers insertion.
232 TEST(IncludeFixer
, InsertAndSortSingleHeader
) {
233 // Insert one header.
234 std::string Code
= "#include \"a.h\"\n"
235 "#include \"foo.h\"\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"
242 "namespace a {\nb::bar b;\n}\n";
243 EXPECT_EQ(Expected
, runIncludeFixer(Code
));
246 TEST(IncludeFixer
, DoNotDeleteMatchedSymbol
) {
247 EXPECT_EQ("#include \"Vector.h\"\na::Vector v;",
248 runIncludeFixer("a::Vector v;"));
251 TEST(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} "
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"));
272 // Test common qualifiers reduction.
273 EXPECT_EQ("#include \"bar.h\"\nnamespace a {\nnamespace d {\nb::bar b;\n}\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"));
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"));
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"));
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"));
302 TEST(IncludeFixer
, FixNamespaceQualifiersForAllInstances
) {
303 const char TestCode
[] = R
"(
332 const char ExpectedCode
[] = R
"(
338 b::bar *p = new b::bar();
362 EXPECT_EQ(ExpectedCode
, runIncludeFixer(TestCode
));
365 TEST(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}"));
371 } // namespace include_fixer