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
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(position: Args.end(), first: ExtraArgs.begin(), last: ExtraArgs.end());
35 tooling::ToolInvocation Invocation(
36 Args, ToolAction, Files.get(),
37 std::make_shared<PCHContainerOperations>());
38
39 InMemoryFileSystem->addFile(Path: FileName, ModificationTime: 0,
40 Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: Code));
41
42 InMemoryFileSystem->addFile(Path: "foo.h", ModificationTime: 0,
43 Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: "\n"));
44 InMemoryFileSystem->addFile(Path: "dir/bar.h", ModificationTime: 0,
45 Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: "\n"));
46 InMemoryFileSystem->addFile(Path: "dir/otherdir/qux.h", ModificationTime: 0,
47 Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: "\n"));
48 InMemoryFileSystem->addFile(Path: "header.h", ModificationTime: 0,
49 Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: "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 {.Symbol: SymbolInfo("string", SymbolInfo::SymbolKind::Class, "<string>",
58 {{SymbolInfo::ContextType::Namespace, "std"}}),
59 .Signals: SymbolInfo::Signals{}},
60 {.Symbol: SymbolInfo("sting", SymbolInfo::SymbolKind::Class, "\"sting\"",
61 {{SymbolInfo::ContextType::Namespace, "std"}}),
62 .Signals: SymbolInfo::Signals{}},
63 {.Symbol: SymbolInfo("foo", SymbolInfo::SymbolKind::Class,
64 "\"dir/otherdir/qux.h\"",
65 {{SymbolInfo::ContextType::Namespace, "b"},
66 {SymbolInfo::ContextType::Namespace, "a"}}),
67 .Signals: SymbolInfo::Signals{}},
68 {.Symbol: SymbolInfo("bar", SymbolInfo::SymbolKind::Class, "\"bar.h\"",
69 {{SymbolInfo::ContextType::Namespace, "b"},
70 {SymbolInfo::ContextType::Namespace, "a"}}),
71 .Signals: SymbolInfo::Signals{}},
72 {.Symbol: SymbolInfo("bar", SymbolInfo::SymbolKind::Class, "\"bar2.h\"",
73 {{SymbolInfo::ContextType::Namespace, "c"},
74 {SymbolInfo::ContextType::Namespace, "a"}}),
75 .Signals: SymbolInfo::Signals{}},
76 {.Symbol: SymbolInfo("Green", SymbolInfo::SymbolKind::Class, "\"color.h\"",
77 {{SymbolInfo::ContextType::EnumDecl, "Color"},
78 {SymbolInfo::ContextType::Namespace, "b"},
79 {SymbolInfo::ContextType::Namespace, "a"}}),
80 .Signals: SymbolInfo::Signals{}},
81 {.Symbol: SymbolInfo("Vector", SymbolInfo::SymbolKind::Class, "\"Vector.h\"",
82 {{SymbolInfo::ContextType::Namespace, "__a"},
83 {SymbolInfo::ContextType::Namespace, "a"}}),
84 .Signals: SymbolInfo::Signals{/*Seen=*/2, 0}},
85 {.Symbol: SymbolInfo("Vector", SymbolInfo::SymbolKind::Class, "\"Vector.h\"",
86 {{SymbolInfo::ContextType::Namespace, "a"}}),
87 .Signals: SymbolInfo::Signals{/*Seen=*/2, 0}},
88 {.Symbol: SymbolInfo("StrCat", SymbolInfo::SymbolKind::Class, "\"strcat.h\"",
89 {{SymbolInfo::ContextType::Namespace, "str"}}),
90 .Signals: SymbolInfo::Signals{}},
91 {.Symbol: SymbolInfo("str", SymbolInfo::SymbolKind::Class, "\"str.h\"", {}),
92 .Signals: SymbolInfo::Signals{}},
93 {.Symbol: SymbolInfo("foo2", SymbolInfo::SymbolKind::Class, "\"foo2.h\"", {}),
94 .Signals: SymbolInfo::Signals{}},
95 };
96 auto SymbolIndexMgr = std::make_unique<SymbolIndexManager>();
97 SymbolIndexMgr->addSymbolIndex(
98 F: [=]() { return std::make_unique<InMemorySymbolIndex>(args: Symbols); });
99
100 std::vector<IncludeFixerContext> FixerContexts;
101 IncludeFixerActionFactory Factory(*SymbolIndexMgr, FixerContexts, "llvm");
102 std::string FakeFileName = "input.cc";
103 runOnCode(ToolAction: &Factory, Code, FileName: FakeFileName, ExtraArgs);
104 assert(FixerContexts.size() == 1);
105 if (FixerContexts.front().getHeaderInfos().empty())
106 return std::string(Code);
107 auto Replaces = createIncludeFixerReplacements(Code, Context: FixerContexts.front());
108 EXPECT_TRUE(static_cast<bool>(Replaces))
109 << llvm::toString(E: Replaces.takeError()) << "\n";
110 if (!Replaces)
111 return "";
112 RewriterTestContext Context;
113 FileID ID = Context.createInMemoryFile(Name: FakeFileName, Content: Code);
114 tooling::applyAllReplacements(Replaces: *Replaces, Rewrite&: 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-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
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 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
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 when
231// 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 a
312
313namespace a {
314bar func2() {
315 bar f;
316 return f;
317}
318} // namespace a
319
320// Non-fixed cases:
321void f() {
322 bar b;
323}
324
325namespace a {
326namespace c {
327 bar b;
328} // namespace c
329} // namespace a
330)";
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 a
342
343namespace a {
344b::bar func2() {
345 b::bar f;
346 return f;
347}
348} // namespace a
349
350// Non-fixed cases:
351void f() {
352 bar b;
353}
354
355namespace a {
356namespace c {
357 bar b;
358} // namespace c
359} // namespace a
360)";
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} // namespace
371} // namespace include_fixer
372} // namespace clang
373

source code of clang-tools-extra/unittests/clang-include-fixer/IncludeFixerTest.cpp