blob: e69a408c085bde49d9495ed75770506d5fd68dba [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 Kramer3a45fab2016-04-28 11:21:29 +000030 std::vector<std::string> Args = {"include_fixer", "-fsyntax-only", FileName};
31 Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
Benjamin Kramer6b236262016-04-20 12:43:43 +000032 tooling::ToolInvocation Invocation(
Benjamin Kramer3a45fab2016-04-28 11:21:29 +000033 Args, ToolAction, Files.get(),
34 std::make_shared<PCHContainerOperations>());
Benjamin Kramer6b236262016-04-20 12:43:43 +000035
36 InMemoryFileSystem->addFile(FileName, 0,
37 llvm::MemoryBuffer::getMemBuffer(Code));
38
39 InMemoryFileSystem->addFile("foo.h", 0,
40 llvm::MemoryBuffer::getMemBuffer("\n"));
Benjamin Kramer3a45fab2016-04-28 11:21:29 +000041 InMemoryFileSystem->addFile("dir/bar.h", 0,
42 llvm::MemoryBuffer::getMemBuffer("\n"));
43 InMemoryFileSystem->addFile("dir/otherdir/qux.h", 0,
Benjamin Kramer6b236262016-04-20 12:43:43 +000044 llvm::MemoryBuffer::getMemBuffer("\n"));
45 return Invocation.run();
46}
47
Benjamin Kramer3a45fab2016-04-28 11:21:29 +000048static std::string runIncludeFixer(
49 StringRef Code,
50 const std::vector<std::string> &ExtraArgs = std::vector<std::string>()) {
Haojian Wu631e5f22016-05-13 15:17:17 +000051 std::vector<SymbolInfo> Symbols = {
52 SymbolInfo("string", SymbolInfo::SymbolKind::Class, "<string>", 1,
53 {{SymbolInfo::ContextType::Namespace, "std"}}),
54 SymbolInfo("sting", SymbolInfo::SymbolKind::Class, "\"sting\"", 1,
55 {{SymbolInfo::ContextType::Namespace, "std"}}),
56 SymbolInfo("size_type", SymbolInfo::SymbolKind::Variable, "<string>", 1,
57 {{SymbolInfo::ContextType::Namespace, "string"},
58 {SymbolInfo::ContextType::Namespace, "std"}}),
59 SymbolInfo("foo", SymbolInfo::SymbolKind::Class, "\"dir/otherdir/qux.h\"",
60 1, {{SymbolInfo::ContextType::Namespace, "b"},
61 {SymbolInfo::ContextType::Namespace, "a"}}),
Haojian Wu57cdcb02016-05-13 15:44:16 +000062 SymbolInfo("bar", SymbolInfo::SymbolKind::Class, "\"bar.h\"",
63 1, {{SymbolInfo::ContextType::Namespace, "b"},
64 {SymbolInfo::ContextType::Namespace, "a"}}),
Benjamin Kramer3a45fab2016-04-28 11:21:29 +000065 };
Benjamin Kramera3d82332016-05-13 09:27:54 +000066 auto SymbolIndexMgr = llvm::make_unique<include_fixer::SymbolIndexManager>();
67 SymbolIndexMgr->addSymbolIndex(
Haojian Wu631e5f22016-05-13 15:17:17 +000068 llvm::make_unique<include_fixer::InMemorySymbolIndex>(Symbols));
Eric Liu692aca62016-05-04 08:22:35 +000069
Benjamin Kramer6b236262016-04-20 12:43:43 +000070 std::vector<clang::tooling::Replacement> Replacements;
Benjamin Kramera3d82332016-05-13 09:27:54 +000071 IncludeFixerActionFactory Factory(*SymbolIndexMgr, Replacements);
Benjamin Kramer3a45fab2016-04-28 11:21:29 +000072 runOnCode(&Factory, Code, "input.cc", ExtraArgs);
Benjamin Kramer6b236262016-04-20 12:43:43 +000073 clang::RewriterTestContext Context;
74 clang::FileID ID = Context.createInMemoryFile("input.cc", Code);
75 clang::tooling::applyAllReplacements(Replacements, Context.Rewrite);
76 return Context.getRewrittenText(ID);
77}
78
79TEST(IncludeFixer, Typo) {
80 EXPECT_EQ("#include <string>\nstd::string foo;\n",
81 runIncludeFixer("std::string foo;\n"));
82
83 EXPECT_EQ(
84 "// comment\n#include <string>\n#include \"foo.h\"\nstd::string foo;\n"
Benjamin Kramer3a45fab2016-04-28 11:21:29 +000085 "#include \"dir/bar.h\"\n",
Benjamin Kramer6b236262016-04-20 12:43:43 +000086 runIncludeFixer("// comment\n#include \"foo.h\"\nstd::string foo;\n"
Benjamin Kramer3a45fab2016-04-28 11:21:29 +000087 "#include \"dir/bar.h\"\n"));
Benjamin Kramer6b236262016-04-20 12:43:43 +000088
89 EXPECT_EQ("#include <string>\n#include \"foo.h\"\nstd::string foo;\n",
90 runIncludeFixer("#include \"foo.h\"\nstd::string foo;\n"));
91
92 EXPECT_EQ(
93 "#include <string>\n#include \"foo.h\"\nstd::string::size_type foo;\n",
94 runIncludeFixer("#include \"foo.h\"\nstd::string::size_type foo;\n"));
95
Eric Liu692aca62016-05-04 08:22:35 +000096 // string without "std::" can also be fixed since fixed db results go through
Benjamin Kramera3d82332016-05-13 09:27:54 +000097 // SymbolIndexManager, and SymbolIndexManager matches unqualified identifiers
98 // too.
Eric Liu692aca62016-05-04 08:22:35 +000099 EXPECT_EQ("#include <string>\nstring foo;\n",
100 runIncludeFixer("string foo;\n"));
Benjamin Kramer6b236262016-04-20 12:43:43 +0000101}
102
103TEST(IncludeFixer, IncompleteType) {
104 EXPECT_EQ(
105 "#include <string>\n#include \"foo.h\"\n"
106 "namespace std {\nclass string;\n}\nstring foo;\n",
107 runIncludeFixer("#include \"foo.h\"\n"
108 "namespace std {\nclass string;\n}\nstring foo;\n"));
109}
110
Benjamin Kramer3a45fab2016-04-28 11:21:29 +0000111TEST(IncludeFixer, MinimizeInclude) {
112 std::vector<std::string> IncludePath = {"-Idir/"};
113 EXPECT_EQ("#include \"otherdir/qux.h\"\na::b::foo bar;\n",
114 runIncludeFixer("a::b::foo bar;\n", IncludePath));
115
116 IncludePath = {"-isystemdir"};
117 EXPECT_EQ("#include <otherdir/qux.h>\na::b::foo bar;\n",
118 runIncludeFixer("a::b::foo bar;\n", IncludePath));
119
120 IncludePath = {"-iquotedir"};
121 EXPECT_EQ("#include \"otherdir/qux.h\"\na::b::foo bar;\n",
122 runIncludeFixer("a::b::foo bar;\n", IncludePath));
123
124 IncludePath = {"-Idir", "-Idir/otherdir"};
125 EXPECT_EQ("#include \"qux.h\"\na::b::foo bar;\n",
126 runIncludeFixer("a::b::foo bar;\n", IncludePath));
127}
128
Benjamin Krameraf34e062016-05-17 12:35:18 +0000129#ifndef _WIN32
NAKAMURA Takumic92b8e52016-05-10 22:46:32 +0000130// It doesn't pass for targeting win32. Investigating.
Benjamin Kramerad935002016-05-10 08:25:31 +0000131TEST(IncludeFixer, NestedName) {
132 EXPECT_EQ("#include \"dir/otherdir/qux.h\"\n"
Benjamin Krameraf34e062016-05-17 12:35:18 +0000133 "int x = a::b::foo(0);\n",
134 runIncludeFixer("int x = a::b::foo(0);\n"));
135
136 // FIXME: Handle simple macros.
137 EXPECT_EQ("#define FOO a::b::foo\nint x = FOO;\n",
138 runIncludeFixer("#define FOO a::b::foo\nint x = FOO;\n"));
139 EXPECT_EQ("#define FOO(x) a::##x\nint x = FOO(b::foo);\n",
140 runIncludeFixer("#define FOO(x) a::##x\nint x = FOO(b::foo);\n"));
141
142 EXPECT_EQ("#include \"dir/otherdir/qux.h\"\n"
Benjamin Kramerad935002016-05-10 08:25:31 +0000143 "namespace a {}\nint a = a::b::foo(0);\n",
144 runIncludeFixer("namespace a {}\nint a = a::b::foo(0);\n"));
145}
NAKAMURA Takumic92b8e52016-05-10 22:46:32 +0000146#endif
Benjamin Kramerad935002016-05-10 08:25:31 +0000147
Benjamin Kramerc3459a52016-05-10 08:25:28 +0000148TEST(IncludeFixer, MultipleMissingSymbols) {
149 EXPECT_EQ("#include <string>\nstd::string bar;\nstd::sting foo;\n",
150 runIncludeFixer("std::string bar;\nstd::sting foo;\n"));
151}
152
Haojian Wu57cdcb02016-05-13 15:44:16 +0000153TEST(IncludeFixer, ScopedNamespaceSymbols) {
154 EXPECT_EQ("#include \"bar.h\"\nnamespace a { b::bar b; }\n",
155 runIncludeFixer("namespace a { b::bar b; }\n"));
156 EXPECT_EQ("#include \"bar.h\"\nnamespace A { a::b::bar b; }\n",
157 runIncludeFixer("namespace A { a::b::bar b; }\n"));
158 EXPECT_EQ("#include \"bar.h\"\nnamespace a { void func() { b::bar b; } }\n",
159 runIncludeFixer("namespace a { void func() { b::bar b; } }\n"));
160 EXPECT_EQ("namespace A { c::b::bar b; }\n",
161 runIncludeFixer("namespace A { c::b::bar b; }\n"));
162 // FIXME: The header should not be added here. Remove this after we support
163 // full match.
164 EXPECT_EQ("#include \"bar.h\"\nnamespace A { b::bar b; }\n",
165 runIncludeFixer("namespace A { b::bar b; }\n"));
166}
Benjamin Kramer6b236262016-04-20 12:43:43 +0000167} // namespace
168} // namespace include_fixer
169} // namespace clang