14 #include "clang/Basic/TokenKinds.h"
15 #include "clang/Frontend/CompilerInstance.h"
16 #include "clang/Frontend/CompilerInvocation.h"
17 #include "clang/Frontend/FrontendActions.h"
18 #include "clang/Lex/PreprocessorOptions.h"
19 #include "llvm/ADT/StringRef.h"
20 #include "llvm/Support/FormatVariadic.h"
21 #include "llvm/Support/Path.h"
22 #include "gmock/gmock.h"
23 #include "gtest/gtest.h"
29 using ::testing::AllOf;
30 using ::testing::Contains;
31 using ::testing::ElementsAre;
33 using ::testing::UnorderedElementsAre;
35 class HeadersTest :
public ::testing::Test {
45 std::unique_ptr<CompilerInstance> setupClang() {
47 assert(static_cast<bool>(Cmd));
50 PI.CompileCommand = *Cmd;
53 EXPECT_TRUE(static_cast<bool>(
CI));
55 CI->getDiagnosticOpts().IgnoreWarnings =
true;
56 auto VFS = PI.TFS->view(Cmd->Directory);
58 std::move(
CI),
nullptr,
62 EXPECT_FALSE(Clang->getFrontendOpts().Inputs.empty());
67 IncludeStructure collectIncludes() {
68 auto Clang = setupClang();
69 PreprocessOnlyAction
Action;
71 Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0]));
72 IncludeStructure Includes;
73 Clang->getPreprocessor().addPPCallbacks(
75 EXPECT_FALSE(
Action.Execute());
83 const std::vector<Inclusion> &Inclusions = {}) {
84 auto Clang = setupClang();
85 PreprocessOnlyAction
Action;
87 Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0]));
89 if (Preferred.empty())
91 auto ToHeaderFile = [](llvm::StringRef Header) {
92 return HeaderFile{std::string(Header),
93 !llvm::sys::path::is_absolute(Header)};
96 IncludeInserter Inserter(
MainFile,
"", format::getLLVMStyle(),
98 &Clang->getPreprocessor().getHeaderSearchInfo());
99 for (
const auto &Inc : Inclusions)
100 Inserter.addExisting(Inc);
101 auto Inserted = ToHeaderFile(Preferred);
102 if (!Inserter.shouldInsertInclude(Original, Inserted))
104 auto Path = Inserter.calculateIncludePath(Inserted,
MainFile);
106 return Path.getValueOr(
"");
109 llvm::Optional<TextEdit> insert(llvm::StringRef VerbatimHeader) {
110 auto Clang = setupClang();
111 PreprocessOnlyAction
Action;
113 Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0]));
115 IncludeInserter Inserter(
MainFile,
"", format::getLLVMStyle(),
117 &Clang->getPreprocessor().getHeaderSearchInfo());
118 auto Edit = Inserter.insert(VerbatimHeader);
124 MockCompilationDatabase
CDB;
133 MATCHER_P(IncludeLine, N,
"") {
return arg.HashLine == N; }
134 MATCHER_P(Directive, D,
"") {
return arg.Directive == D; }
136 MATCHER_P2(Distance, File, D,
"") {
137 if (arg.getKey() != File)
138 *result_listener <<
"file =" << arg.getKey().str();
139 if (arg.getValue() != D)
140 *result_listener <<
"distance =" << arg.getValue();
141 return arg.getKey() == File && arg.getValue() == D;
144 TEST_F(HeadersTest, CollectRewrittenAndResolved) {
146 #include "sub/bar.h" // not shortest
148 std::string BarHeader = testPath("sub/bar.h");
151 EXPECT_THAT(collectIncludes().MainFileIncludes,
152 UnorderedElementsAre(
153 AllOf(Written(
"\"sub/bar.h\""), Resolved(BarHeader))));
154 EXPECT_THAT(collectIncludes().includeDepth(
MainFile),
155 UnorderedElementsAre(Distance(
MainFile, 0u),
156 Distance(
testPath(
"sub/bar.h"), 1u)));
159 TEST_F(HeadersTest, OnlyCollectInclusionsInMain) {
160 std::string BazHeader =
testPath(
"sub/baz.h");
162 std::string BarHeader =
testPath(
"sub/bar.h");
170 collectIncludes().MainFileIncludes,
171 UnorderedElementsAre(AllOf(Written("\"bar.h\""), Resolved(BarHeader))));
172 EXPECT_THAT(collectIncludes().includeDepth(
MainFile),
173 UnorderedElementsAre(Distance(
MainFile, 0u),
174 Distance(
testPath(
"sub/bar.h"), 1u),
175 Distance(
testPath(
"sub/baz.h"), 2u)));
177 EXPECT_THAT(collectIncludes().includeDepth(
testPath(
"sub/bar.h")),
178 UnorderedElementsAre(Distance(
testPath(
"sub/bar.h"), 0u),
179 Distance(
testPath(
"sub/baz.h"), 1u)));
182 TEST_F(HeadersTest, PreambleIncludesPresentOnce) {
193 TU.HeaderFilename = "a.h";
194 EXPECT_THAT(TU.build().getIncludeStructure().MainFileIncludes,
195 ElementsAre(IncludeLine(1), IncludeLine(3), IncludeLine(5)));
198 TEST_F(HeadersTest, UnResolvedInclusion) {
203 EXPECT_THAT(collectIncludes().MainFileIncludes,
204 UnorderedElementsAre(AllOf(Written("\"foo.h\""), Resolved(
""))));
205 EXPECT_THAT(collectIncludes().includeDepth(
MainFile),
206 UnorderedElementsAre(Distance(
MainFile, 0u)));
209 TEST_F(HeadersTest, IncludeDirective) {
213 #include_next "foo.h"
218 EXPECT_THAT(collectIncludes().MainFileIncludes,
219 UnorderedElementsAre(Directive(tok::pp_include),
220 Directive(tok::pp_import),
221 Directive(tok::pp_include_next)));
224 TEST_F(HeadersTest, InsertInclude) {
227 EXPECT_EQ(calculate(
Path),
"\"bar.h\"");
230 TEST_F(HeadersTest, DoNotInsertIfInSameFile) {
235 TEST_F(HeadersTest, DoNotInsertOffIncludePath) {
237 EXPECT_EQ(calculate(
testPath(
"sub2/main.cpp")),
"");
240 TEST_F(HeadersTest, ShortenIncludesInSearchPath) {
241 std::string BarHeader =
testPath(
"sub/bar.h");
242 EXPECT_EQ(calculate(BarHeader),
"\"bar.h\"");
247 EXPECT_EQ(calculate(BarHeader),
"\"sub/bar.h\"");
250 TEST_F(HeadersTest, ShortenedIncludeNotInSearchPath) {
251 std::string BarHeader =
252 llvm::sys::path::convert_to_slash(
testPath(
"sub-2/bar.h"));
253 EXPECT_EQ(calculate(BarHeader,
""),
"\"sub-2/bar.h\"");
256 TEST_F(HeadersTest, PreferredHeader) {
257 std::string BarHeader =
testPath(
"sub/bar.h");
258 EXPECT_EQ(calculate(BarHeader,
"<bar>"),
"<bar>");
260 std::string BazHeader =
testPath(
"sub/baz.h");
261 EXPECT_EQ(calculate(BarHeader, BazHeader),
"\"baz.h\"");
264 TEST_F(HeadersTest, DontInsertDuplicatePreferred) {
266 Inc.Written =
"\"bar.h\"";
268 EXPECT_EQ(calculate(
testPath(
"sub/bar.h"),
"\"bar.h\"", {Inc}),
"");
269 EXPECT_EQ(calculate(
"\"x.h\"",
"\"bar.h\"", {Inc}),
"");
272 TEST_F(HeadersTest, DontInsertDuplicateResolved) {
274 Inc.Written =
"fake-bar.h";
275 Inc.Resolved =
testPath(
"sub/bar.h");
276 EXPECT_EQ(calculate(Inc.Resolved,
"", {Inc}),
"");
278 EXPECT_EQ(calculate(Inc.Resolved,
"\"BAR.h\"", {Inc}),
"");
281 TEST_F(HeadersTest, PreferInserted) {
282 auto Edit = insert(
"<y>");
283 EXPECT_TRUE(Edit.hasValue());
284 EXPECT_TRUE(StringRef(Edit->newText).contains(
"<y>"));
287 TEST(Headers, NoHeaderSearchInfo) {
289 IncludeInserter Inserter(
MainFile,
"", format::getLLVMStyle(),
292 auto HeaderPath =
testPath(
"sub/bar.h");
293 auto Inserting = HeaderFile{HeaderPath,
false};
294 auto Verbatim = HeaderFile{
"<x>",
true};
296 EXPECT_EQ(Inserter.calculateIncludePath(Inserting,
MainFile),
297 std::string(
"\"sub/bar.h\""));
298 EXPECT_EQ(Inserter.shouldInsertInclude(HeaderPath, Inserting),
false);
300 EXPECT_EQ(Inserter.calculateIncludePath(Verbatim,
MainFile),
302 EXPECT_EQ(Inserter.shouldInsertInclude(HeaderPath, Verbatim),
true);
304 EXPECT_EQ(Inserter.calculateIncludePath(Inserting,
"sub2/main2.cpp"),
308 TEST_F(HeadersTest, PresumedLocations) {
309 std::string HeaderFile =
"__preamble_patch__.h";
313 llvm::formatv(
"#line 0 \"{0}\"", llvm::sys::path::filename(
MainFile));
321 EXPECT_THAT(collectIncludes().MainFileIncludes,
322 Not(Contains(Written(
"<a.h>"))));
326 EXPECT_THAT(collectIncludes().MainFileIncludes,
327 Contains(AllOf(IncludeLine(2), Written(
"<a.h>"))));