15#include "clang/Basic/TokenKinds.h"
16#include "clang/Frontend/CompilerInstance.h"
17#include "clang/Frontend/CompilerInvocation.h"
18#include "clang/Frontend/FrontendActions.h"
19#include "clang/Tooling/Inclusions/HeaderIncludes.h"
20#include "llvm/ADT/StringRef.h"
21#include "llvm/Support/Error.h"
22#include "llvm/Support/FormatVariadic.h"
23#include "llvm/Support/Path.h"
24#include "llvm/Testing/Support/Error.h"
25#include "gmock/gmock.h"
26#include "gtest/gtest.h"
33using ::testing::AllOf;
34using ::testing::Contains;
35using ::testing::ElementsAre;
36using ::testing::IsEmpty;
38using ::testing::UnorderedElementsAre;
40class HeadersTest :
public ::testing::Test {
43 CDB.ExtraClangFlags = {SearchDirArg.c_str()};
44 FS.Files[MainFile] =
"";
46 FS.Files[
testPath(
"sub/EMPTY")] =
"";
50 std::unique_ptr<CompilerInstance> setupClang() {
51 auto Cmd = CDB.getCompileCommand(MainFile);
52 assert(
static_cast<bool>(Cmd));
55 PI.CompileCommand = *Cmd;
58 EXPECT_TRUE(
static_cast<bool>(CI));
60 CI->getDiagnosticOpts().IgnoreWarnings =
true;
61 auto VFS = PI.TFS->view(Cmd->Directory);
63 std::move(CI),
nullptr,
64 llvm::MemoryBuffer::getMemBuffer(FS.Files[MainFile], MainFile),
65 std::move(VFS), IgnoreDiags);
67 EXPECT_FALSE(Clang->getFrontendOpts().Inputs.empty());
73 IncludeStructure &Includes) {
74 auto &SM = Clang->getSourceManager();
75 auto Entry = SM.getFileManager().getFileRef(Filename);
76 EXPECT_THAT_EXPECTED(Entry, llvm::Succeeded());
77 return Includes.getOrCreateID(*Entry);
80 IncludeStructure collectIncludes() {
82 PreprocessOnlyAction Action;
84 Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0]));
85 IncludeStructure Includes;
86 Includes.collect(*Clang);
87 EXPECT_FALSE(Action.Execute());
88 Action.EndSourceFile();
95 const std::vector<Inclusion> &Inclusions = {}) {
97 PreprocessOnlyAction Action;
99 Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0]));
101 if (Preferred.empty())
102 Preferred = Original;
103 auto ToHeaderFile = [](llvm::StringRef Header) {
104 return HeaderFile{std::string(Header),
105 !llvm::sys::path::is_absolute(Header)};
108 IncludeInserter Inserter(MainFile,
"", format::getLLVMStyle(),
109 CDB.getCompileCommand(MainFile)->Directory,
110 &Clang->getPreprocessor().getHeaderSearchInfo(),
111 QuotedHeaders, AngledHeaders);
112 for (
const auto &Inc : Inclusions)
113 Inserter.addExisting(Inc);
114 auto Inserted = ToHeaderFile(Preferred);
115 if (!Inserter.shouldInsertInclude(Original, Inserted))
117 auto Path = Inserter.calculateIncludePath(Inserted, MainFile);
118 Action.EndSourceFile();
119 return Path.value_or(
"");
122 std::optional<TextEdit> insert(llvm::StringRef VerbatimHeader,
123 tooling::IncludeDirective Directive) {
124 Clang = setupClang();
125 PreprocessOnlyAction Action;
127 Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0]));
129 IncludeInserter Inserter(MainFile,
"", format::getLLVMStyle(),
130 CDB.getCompileCommand(MainFile)->Directory,
131 &Clang->getPreprocessor().getHeaderSearchInfo(),
132 QuotedHeaders, AngledHeaders);
133 auto Edit = Inserter.insert(VerbatimHeader, Directive);
134 Action.EndSourceFile();
139 MockCompilationDatabase CDB;
140 std::string MainFile =
testPath(
"main.cpp");
141 std::string Subdir =
testPath(
"sub");
142 std::string SearchDirArg = (llvm::Twine(
"-I") + Subdir).str();
143 IgnoringDiagConsumer IgnoreDiags;
144 std::vector<std::function<bool(llvm::StringRef)>> QuotedHeaders;
145 std::vector<std::function<bool(llvm::StringRef)>> AngledHeaders;
146 std::unique_ptr<CompilerInstance> Clang;
149MATCHER_P(written, Name,
"") {
return arg.Written == Name; }
150MATCHER_P(resolved, Name,
"") {
return arg.Resolved == Name; }
151MATCHER_P(includeLine, N,
"") {
return arg.HashLine == N; }
152MATCHER_P(directive, D,
"") {
return arg.Directive ==
D; }
155 if (arg.getFirst() !=
File)
156 *result_listener <<
"file =" <<
static_cast<unsigned>(arg.getFirst());
157 if (arg.getSecond() != D)
158 *result_listener <<
"distance =" << arg.getSecond();
159 return arg.getFirst() ==
File && arg.getSecond() ==
D;
162TEST_F(HeadersTest, CollectRewrittenAndResolved) {
163 FS.Files[MainFile] = R
"cpp(
164#include "sub/bar.h" // not shortest
166 std::string BarHeader = testPath("sub/bar.h");
167 FS.Files[BarHeader] =
"";
169 auto Includes = collectIncludes();
170 EXPECT_THAT(Includes.MainFileIncludes,
171 UnorderedElementsAre(
172 AllOf(written(
"\"sub/bar.h\""), resolved(BarHeader))));
173 EXPECT_THAT(Includes.includeDepth(getID(MainFile, Includes)),
174 UnorderedElementsAre(Distance(getID(MainFile, Includes), 0u),
175 Distance(getID(BarHeader, Includes), 1u)));
178TEST_F(HeadersTest, OnlyCollectInclusionsInMain) {
179 std::string BazHeader =
testPath(
"sub/baz.h");
180 FS.Files[BazHeader] =
"";
181 std::string BarHeader =
testPath(
"sub/bar.h");
182 FS.Files[BarHeader] = R
"cpp(
185 FS.Files[MainFile] = R"cpp(
188 auto Includes = collectIncludes();
190 Includes.MainFileIncludes,
191 UnorderedElementsAre(AllOf(written(
"\"bar.h\""), resolved(BarHeader))));
192 EXPECT_THAT(Includes.includeDepth(getID(MainFile, Includes)),
193 UnorderedElementsAre(Distance(getID(MainFile, Includes), 0u),
194 Distance(getID(BarHeader, Includes), 1u),
195 Distance(getID(BazHeader, Includes), 2u)));
197 EXPECT_THAT(Includes.includeDepth(getID(BarHeader, Includes)),
198 UnorderedElementsAre(Distance(getID(BarHeader, Includes), 0u),
199 Distance(getID(BazHeader, Includes), 1u)));
202TEST_F(HeadersTest, CacheBySpellingIsBuiltForMainInclusions) {
203 std::string FooHeader =
testPath(
"foo.h");
204 FS.Files[FooHeader] = R
"cpp(
207 std::string BarHeader = testPath("bar.h");
208 FS.Files[BarHeader] = R
"cpp(
211 std::string BazHeader = testPath("baz.h");
212 FS.Files[BazHeader] = R
"cpp(
215 FS.Files[MainFile] = R"cpp(
220 auto Includes = collectIncludes();
221 EXPECT_THAT(Includes.MainFileIncludes,
222 UnorderedElementsAre(written(
"\"foo.h\""), written(
"\"bar.h\""),
223 written(
"\"baz.h\"")));
224 EXPECT_THAT(Includes.mainFileIncludesWithSpelling(
"\"foo.h\""),
225 UnorderedElementsAre(&Includes.MainFileIncludes[0]));
226 EXPECT_THAT(Includes.mainFileIncludesWithSpelling(
"\"bar.h\""),
227 UnorderedElementsAre(&Includes.MainFileIncludes[1]));
228 EXPECT_THAT(Includes.mainFileIncludesWithSpelling(
"\"baz.h\""),
229 UnorderedElementsAre(&Includes.MainFileIncludes[2]));
232TEST_F(HeadersTest, PreambleIncludesPresentOnce) {
243 TU.HeaderFilename = "a.h";
244 EXPECT_THAT(TU.build().getIncludeStructure().MainFileIncludes,
245 ElementsAre(includeLine(1), includeLine(3), includeLine(5)));
248TEST_F(HeadersTest, UnResolvedInclusion) {
249 FS.Files[MainFile] = R
"cpp(
253 EXPECT_THAT(collectIncludes().MainFileIncludes,
254 UnorderedElementsAre(AllOf(written("\"foo.h\""), resolved(
""))));
255 EXPECT_THAT(collectIncludes().IncludeChildren, IsEmpty());
258TEST_F(HeadersTest, IncludedFilesGraph) {
259 FS.Files[MainFile] = R
"cpp(
263 std::string BarHeader = testPath("bar.h");
264 FS.Files[BarHeader] =
"";
265 std::string FooHeader =
testPath(
"foo.h");
266 FS.Files[FooHeader] = R
"cpp(
270 std::string BazHeader = testPath("baz.h");
271 FS.Files[BazHeader] =
"";
273 auto Includes = collectIncludes();
275 SmallVector<IncludeStructure::HeaderID>>
276 Expected = {{getID(MainFile, Includes),
277 {getID(BarHeader, Includes), getID(FooHeader, Includes)}},
278 {getID(FooHeader, Includes),
279 {getID(BarHeader, Includes), getID(BazHeader, Includes)}}};
280 EXPECT_EQ(Includes.IncludeChildren, Expected);
283TEST_F(HeadersTest, IncludeDirective) {
284 FS.Files[MainFile] = R
"cpp(
291 CDB.ExtraClangFlags.push_back(
"-fno-ms-compatibility");
292 EXPECT_THAT(collectIncludes().MainFileIncludes,
293 UnorderedElementsAre(directive(tok::pp_include),
294 directive(tok::pp_import),
295 directive(tok::pp_include_next)));
298TEST_F(HeadersTest, SearchPath) {
299 FS.Files[
"foo/bar.h"] =
"x";
300 FS.Files[
"foo/bar/baz.h"] =
"y";
301 CDB.ExtraClangFlags.push_back(
"-Ifoo/bar");
302 CDB.ExtraClangFlags.push_back(
"-Ifoo/bar/..");
303 EXPECT_THAT(collectIncludes().SearchPathsCanonical,
307TEST_F(HeadersTest, InsertInclude) {
310 EXPECT_EQ(calculate(
Path),
"\"bar.h\"");
312 AngledHeaders.push_back([](
auto Path) {
return true; });
313 EXPECT_EQ(calculate(
Path),
"<bar.h>");
316TEST_F(HeadersTest, DoNotInsertIfInSameFile) {
318 EXPECT_EQ(calculate(MainFile),
"");
321TEST_F(HeadersTest, DoNotInsertOffIncludePath) {
322 MainFile =
testPath(
"sub/main.cpp");
323 EXPECT_EQ(calculate(
testPath(
"sub2/main.cpp")),
"");
326TEST_F(HeadersTest, ShortenIncludesInSearchPath) {
327 std::string BarHeader =
testPath(
"sub/bar.h");
328 EXPECT_EQ(calculate(BarHeader),
"\"bar.h\"");
330 SearchDirArg = (llvm::Twine(
"-I") + Subdir +
"/..").str();
331 CDB.ExtraClangFlags = {SearchDirArg.c_str()};
333 EXPECT_EQ(calculate(BarHeader),
"\"sub/bar.h\"");
336TEST_F(HeadersTest, ShortenIncludesInSearchPathBracketed) {
337 AngledHeaders.push_back([](
auto Path) {
return true; });
338 std::string BarHeader =
testPath(
"sub/bar.h");
339 EXPECT_EQ(calculate(BarHeader),
"<bar.h>");
341 SearchDirArg = (llvm::Twine(
"-I") + Subdir +
"/..").str();
342 CDB.ExtraClangFlags = {SearchDirArg.c_str()};
344 EXPECT_EQ(calculate(BarHeader),
"<sub/bar.h>");
347TEST_F(HeadersTest, ShortenIncludesInSearchPathBracketedFilterByFullPath) {
350 AngledHeaders.push_back([](
auto Path) {
351 llvm::Regex Pattern(
"sub/.*");
352 return Pattern.match(
Path);
354 std::string BarHeader =
testPath(
"sub/bar.h");
355 EXPECT_EQ(calculate(BarHeader),
"<bar.h>");
358TEST_F(HeadersTest, ShortenedIncludeNotInSearchPath) {
359 std::string BarHeader =
360 llvm::sys::path::convert_to_slash(
testPath(
"sub-2/bar.h"));
361 EXPECT_EQ(calculate(BarHeader,
""),
"\"sub-2/bar.h\"");
364TEST_F(HeadersTest, PreferredHeader) {
365 std::string BarHeader =
testPath(
"sub/bar.h");
366 EXPECT_EQ(calculate(BarHeader,
"<bar>"),
"<bar>");
368 std::string BazHeader =
testPath(
"sub/baz.h");
369 EXPECT_EQ(calculate(BarHeader, BazHeader),
"\"baz.h\"");
371 AngledHeaders.push_back([](
auto Path) {
return true; });
372 std::string BiffHeader =
testPath(
"sub/biff.h");
373 EXPECT_EQ(calculate(BarHeader, BiffHeader),
"<biff.h>");
376TEST_F(HeadersTest, DontInsertDuplicatePreferred) {
380 EXPECT_EQ(calculate(
testPath(
"sub/bar.h"),
"\"bar.h\"", {Inc}),
"");
381 EXPECT_EQ(calculate(
"\"x.h\"",
"\"bar.h\"", {Inc}),
"");
384TEST_F(HeadersTest, DontInsertDuplicateResolved) {
387 Inc.Resolved =
testPath(
"sub/bar.h");
388 EXPECT_EQ(calculate(Inc.Resolved,
"", {Inc}),
"");
390 EXPECT_EQ(calculate(Inc.Resolved,
"\"BAR.h\"", {Inc}),
"");
393TEST_F(HeadersTest, PreferInserted) {
394 auto Edit = insert(
"<y>", tooling::IncludeDirective::Include);
396 EXPECT_EQ(
Edit->newText,
"#include <y>\n");
398 Edit = insert(
"\"header.h\"", tooling::IncludeDirective::Import);
400 EXPECT_EQ(
Edit->newText,
"#import \"header.h\"\n");
403TEST(Headers, NoHeaderSearchInfo) {
404 std::string MainFile =
testPath(
"main.cpp");
409 auto HeaderPath =
testPath(
"sub/bar.h");
410 auto Inserting =
HeaderFile{HeaderPath,
false};
413 EXPECT_EQ(Inserter.calculateIncludePath(Inserting, MainFile),
414 std::string(
"\"sub/bar.h\""));
415 EXPECT_EQ(Inserter.shouldInsertInclude(HeaderPath, Inserting),
false);
417 EXPECT_EQ(Inserter.calculateIncludePath(Verbatim, MainFile),
419 EXPECT_EQ(Inserter.shouldInsertInclude(HeaderPath, Verbatim),
true);
421 EXPECT_EQ(Inserter.calculateIncludePath(Inserting,
"sub2/main2.cpp"),
425TEST_F(HeadersTest, PresumedLocations) {
426 std::string
HeaderFile =
"__preamble_patch__.h";
430 llvm::formatv(
"#line 0 \"{0}\"", llvm::sys::path::filename(MainFile));
437 FS.Files[MainFile] =
"#include \"__preamble_patch__.h\"\n\n";
438 EXPECT_THAT(collectIncludes().MainFileIncludes,
439 Not(Contains(written(
"<a.h>"))));
443 EXPECT_THAT(collectIncludes().MainFileIncludes,
444 Contains(AllOf(includeLine(2), written(
"<a.h>"))));
std::vector< HeaderEntry > HeaderContents
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
std::unique_ptr< CompilerInvocation > buildCompilerInvocation(const ParseInputs &Inputs, clang::DiagnosticConsumer &D, std::vector< std::string > *CC1Args)
Builds compiler invocation that could be used to build AST or preamble.
MATCHER_P2(hasFlag, Flag, Path, "")
std::string testPath(PathRef File, llvm::sys::path::Style Style)
TEST(BackgroundQueueTest, Priority)
std::unique_ptr< CompilerInstance > prepareCompilerInstance(std::unique_ptr< clang::CompilerInvocation > CI, const PrecompiledPreamble *Preamble, std::unique_ptr< llvm::MemoryBuffer > Buffer, llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS, DiagnosticConsumer &DiagsClient)
llvm::StringRef PathRef
A typedef to represent a ref to file path.
std::string Path
A typedef to represent a file path.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
A set of edits generated for a single file.
static TestTU withCode(llvm::StringRef Code)