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