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-exception
6 //
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
16 namespace clang {
17 namespace include_fixer {
18 namespace {
19
20 using find_all_symbols::SymbolInfo;
21 using find_all_symbols::SymbolAndSignals;
22
runOnCode(tooling::ToolAction * ToolAction,StringRef Code,StringRef FileName,const std::vector<std::string> & ExtraArgs)23 static 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
runIncludeFixer(StringRef Code,const std::vector<std::string> & ExtraArgs=std::vector<std::string> ())53 static 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
TEST(IncludeFixer,Typo)118 TEST(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
TEST(IncludeFixer,IncompleteType)141 TEST(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
TEST(IncludeFixer,MinimizeInclude)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));
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
TEST(IncludeFixer,NestedName)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"));
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-fixer
183 // 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
TEST(IncludeFixer,MultipleMissingSymbols)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"));
192 }
193
TEST(IncludeFixer,ScopedNamespaceSymbols)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} "
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 support
205 // 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
TEST(IncludeFixer,EnumConstantSymbols)220 TEST(IncludeFixer, EnumConstantSymbols) {
221 EXPECT_EQ("#include \"color.h\"\nint test = a::b::Green;\n",
222 runIncludeFixer("int test = a::b::Green;\n"));
223 }
224
TEST(IncludeFixer,IgnoreSymbolFromHeader)225 TEST(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 when
231 // clang-include-fixer supports multiple headers insertion.
TEST(IncludeFixer,InsertAndSortSingleHeader)232 TEST(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
TEST(IncludeFixer,DoNotDeleteMatchedSymbol)246 TEST(IncludeFixer, DoNotDeleteMatchedSymbol) {
247 EXPECT_EQ("#include \"Vector.h\"\na::Vector v;",
248 runIncludeFixer("a::Vector v;"));
249 }
250
TEST(IncludeFixer,FixNamespaceQualifiers)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} "
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
TEST(IncludeFixer,FixNamespaceQualifiersForAllInstances)302 TEST(IncludeFixer, FixNamespaceQualifiersForAllInstances) {
303 const char TestCode[] = R"(
304 namespace a {
305 bar b;
306 int func1() {
307 bar a;
308 bar *p = new bar();
309 return 0;
310 }
311 } // namespace a
312
313 namespace a {
314 bar func2() {
315 bar f;
316 return f;
317 }
318 } // namespace a
319
320 // Non-fixed cases:
321 void f() {
322 bar b;
323 }
324
325 namespace a {
326 namespace c {
327 bar b;
328 } // namespace c
329 } // namespace a
330 )";
331
332 const char ExpectedCode[] = R"(
333 #include "bar.h"
334 namespace a {
335 b::bar b;
336 int func1() {
337 b::bar a;
338 b::bar *p = new b::bar();
339 return 0;
340 }
341 } // namespace a
342
343 namespace a {
344 b::bar func2() {
345 b::bar f;
346 return f;
347 }
348 } // namespace a
349
350 // Non-fixed cases:
351 void f() {
352 bar b;
353 }
354
355 namespace a {
356 namespace c {
357 bar b;
358 } // namespace c
359 } // namespace a
360 )";
361
362 EXPECT_EQ(ExpectedCode, runIncludeFixer(TestCode));
363 }
364
TEST(IncludeFixer,DontAddQualifiersForMissingCompleteType)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}"));
368 }
369
370 } // namespace
371 } // namespace include_fixer
372 } // namespace clang
373