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;
37using ::testing::IsEmpty;
39using ::testing::UnorderedElementsAre;
41class HeadersTest :
public ::testing::Test {
47 FS.Files[
testPath(
"sub/EMPTY")] =
"";
51 std::unique_ptr<CompilerInstance> setupClang() {
52 auto Cmd = CDB.getCompileCommand(MainFile);
53 assert(
static_cast<bool>(Cmd));
56 PI.CompileCommand = *Cmd;
59 EXPECT_TRUE(
static_cast<bool>(
CI));
61 CI->getDiagnosticOpts().IgnoreWarnings =
true;
62 auto VFS = PI.TFS->view(Cmd->Directory);
64 std::move(
CI),
nullptr,
65 llvm::MemoryBuffer::getMemBuffer(FS.Files[MainFile], MainFile),
66 std::move(VFS), IgnoreDiags);
68 EXPECT_FALSE(
Clang->getFrontendOpts().Inputs.empty());
74 IncludeStructure &Includes) {
75 auto &SM =
Clang->getSourceManager();
77 EXPECT_THAT_EXPECTED(
Entry, llvm::Succeeded());
78 return Includes.getOrCreateID(*
Entry);
81 IncludeStructure collectIncludes() {
83 PreprocessOnlyAction
Action;
85 Action.BeginSourceFile(*Clang,
Clang->getFrontendOpts().Inputs[0]));
86 IncludeStructure Includes;
87 Includes.collect(*Clang);
88 EXPECT_FALSE(
Action.Execute());
96 const std::vector<Inclusion> &Inclusions = {}) {
98 PreprocessOnlyAction
Action;
100 Action.BeginSourceFile(*Clang,
Clang->getFrontendOpts().Inputs[0]));
102 if (Preferred.empty())
103 Preferred = Original;
104 auto ToHeaderFile = [](llvm::StringRef Header) {
105 return HeaderFile{std::string(Header),
106 !llvm::sys::path::is_absolute(Header)};
109 IncludeInserter Inserter(MainFile,
"", format::getLLVMStyle(),
110 CDB.getCompileCommand(MainFile)->Directory,
111 &
Clang->getPreprocessor().getHeaderSearchInfo());
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);
119 return Path.value_or(
"");
122 std::optional<TextEdit> insert(llvm::StringRef VerbatimHeader,
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 auto Edit = Inserter.insert(VerbatimHeader,
Directive);
138 MockCompilationDatabase CDB;
143 std::unique_ptr<CompilerInstance>
Clang;
148MATCHER_P(includeLine, N,
"") {
return arg.HashLine == N; }
149MATCHER_P(directive, D,
"") {
return arg.Directive ==
D; }
152 if (arg.getFirst() !=
File)
153 *result_listener <<
"file =" <<
static_cast<unsigned>(arg.getFirst());
154 if (arg.getSecond() !=
D)
155 *result_listener <<
"distance =" << arg.getSecond();
156 return arg.getFirst() ==
File && arg.getSecond() ==
D;
159TEST_F(HeadersTest, CollectRewrittenAndResolved) {
161#include "sub/bar.h" // not shortest
163 std::string BarHeader = testPath("sub/bar.h");
164 FS.Files[BarHeader] =
"";
166 auto Includes = collectIncludes();
168 UnorderedElementsAre(
169 AllOf(written(
"\"sub/bar.h\""), resolved(BarHeader))));
171 UnorderedElementsAre(Distance(getID(
MainFile, Includes), 0u),
172 Distance(getID(BarHeader, Includes), 1u)));
175TEST_F(HeadersTest, OnlyCollectInclusionsInMain) {
176 std::string BazHeader =
testPath(
"sub/baz.h");
177 FS.Files[BazHeader] =
"";
178 std::string BarHeader =
testPath(
"sub/bar.h");
179 FS.Files[BarHeader] = R
"cpp(
185 auto Includes = collectIncludes();
188 UnorderedElementsAre(AllOf(written(
"\"bar.h\""), resolved(BarHeader))));
190 UnorderedElementsAre(Distance(getID(
MainFile, Includes), 0u),
191 Distance(getID(BarHeader, Includes), 1u),
192 Distance(getID(BazHeader, Includes), 2u)));
194 EXPECT_THAT(Includes.
includeDepth(getID(BarHeader, Includes)),
195 UnorderedElementsAre(Distance(getID(BarHeader, Includes), 0u),
196 Distance(getID(BazHeader, Includes), 1u)));
199TEST_F(HeadersTest, CacheBySpellingIsBuiltForMainInclusions) {
200 std::string FooHeader =
testPath(
"foo.h");
201 FS.Files[FooHeader] = R
"cpp(
204 std::string BarHeader = testPath("bar.h");
205 FS.Files[BarHeader] = R
"cpp(
208 std::string BazHeader = testPath("baz.h");
209 FS.Files[BazHeader] = R
"cpp(
217 auto Includes = collectIncludes();
219 UnorderedElementsAre(written(
"\"foo.h\""), written(
"\"bar.h\""),
220 written(
"\"baz.h\"")));
229TEST_F(HeadersTest, PreambleIncludesPresentOnce) {
240 TU.HeaderFilename = "a.h";
241 EXPECT_THAT(TU.build().getIncludeStructure().MainFileIncludes,
242 ElementsAre(includeLine(1), includeLine(3), includeLine(5)));
245TEST_F(HeadersTest, UnResolvedInclusion) {
250 EXPECT_THAT(collectIncludes().MainFileIncludes,
251 UnorderedElementsAre(AllOf(written("\"foo.h\""), resolved(
""))));
252 EXPECT_THAT(collectIncludes().IncludeChildren, IsEmpty());
255TEST_F(HeadersTest, IncludedFilesGraph) {
260 std::string BarHeader = testPath("bar.h");
261 FS.Files[BarHeader] =
"";
262 std::string FooHeader =
testPath(
"foo.h");
263 FS.Files[FooHeader] = R
"cpp(
267 std::string BazHeader = testPath("baz.h");
268 FS.Files[BazHeader] =
"";
270 auto Includes = collectIncludes();
272 SmallVector<IncludeStructure::HeaderID>>
274 {getID(BarHeader, Includes), getID(FooHeader, Includes)}},
275 {getID(FooHeader, Includes),
276 {getID(BarHeader, Includes), getID(BazHeader, Includes)}}};
280TEST_F(HeadersTest, IncludeDirective) {
288 CDB.ExtraClangFlags.push_back(
"-fno-ms-compatibility");
289 EXPECT_THAT(collectIncludes().MainFileIncludes,
290 UnorderedElementsAre(directive(tok::pp_include),
291 directive(tok::pp_import),
292 directive(tok::pp_include_next)));
295TEST_F(HeadersTest, InsertInclude) {
298 EXPECT_EQ(calculate(
Path),
"\"bar.h\"");
301TEST_F(HeadersTest, DoNotInsertIfInSameFile) {
306TEST_F(HeadersTest, DoNotInsertOffIncludePath) {
308 EXPECT_EQ(calculate(
testPath(
"sub2/main.cpp")),
"");
311TEST_F(HeadersTest, ShortenIncludesInSearchPath) {
312 std::string BarHeader =
testPath(
"sub/bar.h");
313 EXPECT_EQ(calculate(BarHeader),
"\"bar.h\"");
318 EXPECT_EQ(calculate(BarHeader),
"\"sub/bar.h\"");
321TEST_F(HeadersTest, ShortenedIncludeNotInSearchPath) {
322 std::string BarHeader =
323 llvm::sys::path::convert_to_slash(
testPath(
"sub-2/bar.h"));
324 EXPECT_EQ(calculate(BarHeader,
""),
"\"sub-2/bar.h\"");
327TEST_F(HeadersTest, PreferredHeader) {
328 std::string BarHeader =
testPath(
"sub/bar.h");
329 EXPECT_EQ(calculate(BarHeader,
"<bar>"),
"<bar>");
331 std::string BazHeader =
testPath(
"sub/baz.h");
332 EXPECT_EQ(calculate(BarHeader, BazHeader),
"\"baz.h\"");
335TEST_F(HeadersTest, DontInsertDuplicatePreferred) {
337 Inc.Written =
"\"bar.h\"";
339 EXPECT_EQ(calculate(
testPath(
"sub/bar.h"),
"\"bar.h\"", {Inc}),
"");
340 EXPECT_EQ(calculate(
"\"x.h\"",
"\"bar.h\"", {Inc}),
"");
343TEST_F(HeadersTest, DontInsertDuplicateResolved) {
345 Inc.Written =
"fake-bar.h";
346 Inc.Resolved =
testPath(
"sub/bar.h");
347 EXPECT_EQ(calculate(Inc.Resolved,
"", {Inc}),
"");
349 EXPECT_EQ(calculate(Inc.Resolved,
"\"BAR.h\"", {Inc}),
"");
352TEST_F(HeadersTest, PreferInserted) {
353 auto Edit = insert(
"<y>", tooling::IncludeDirective::Include);
355 EXPECT_EQ(Edit->newText,
"#include <y>\n");
357 Edit = insert(
"\"header.h\"", tooling::IncludeDirective::Import);
359 EXPECT_EQ(Edit->newText,
"#import \"header.h\"\n");
362TEST(Headers, NoHeaderSearchInfo) {
364 IncludeInserter Inserter(
MainFile,
"", format::getLLVMStyle(),
367 auto HeaderPath =
testPath(
"sub/bar.h");
368 auto Inserting = HeaderFile{HeaderPath,
false};
369 auto Verbatim = HeaderFile{
"<x>",
true};
371 EXPECT_EQ(Inserter.calculateIncludePath(Inserting,
MainFile),
372 std::string(
"\"sub/bar.h\""));
373 EXPECT_EQ(Inserter.shouldInsertInclude(HeaderPath, Inserting),
false);
375 EXPECT_EQ(Inserter.calculateIncludePath(Verbatim,
MainFile),
377 EXPECT_EQ(Inserter.shouldInsertInclude(HeaderPath, Verbatim),
true);
379 EXPECT_EQ(Inserter.calculateIncludePath(Inserting,
"sub2/main2.cpp"),
383TEST_F(HeadersTest, PresumedLocations) {
384 std::string HeaderFile =
"__preamble_patch__.h";
388 llvm::formatv(
"#line 0 \"{0}\"", llvm::sys::path::filename(
MainFile));
395 FS.Files[
MainFile] =
"#include \"__preamble_patch__.h\"\n\n";
396 EXPECT_THAT(collectIncludes().MainFileIncludes,
397 Not(Contains(written(
"<a.h>"))));
400 CDB.ExtraClangFlags = {
"-include",
testPath(HeaderFile)};
401 EXPECT_THAT(collectIncludes().MainFileIncludes,
402 Contains(AllOf(includeLine(2), written(
"<a.h>"))));
std::string Filename
Filename as a string.
const MacroDirective * Directive
std::vector< HeaderEntry > HeaderContents
std::vector< const char * > Expected
std::unique_ptr< CompilerInvocation > CI
llvm::SmallVector< const Inclusion * > mainFileIncludesWithSpelling(llvm::StringRef Spelling) const
llvm::DenseMap< HeaderID, unsigned > includeDepth(HeaderID Root=MainFileID) const
std::vector< Inclusion > MainFileIncludes
llvm::DenseMap< HeaderID, SmallVector< HeaderID > > IncludeChildren
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
std::string Path
A typedef to represent a file path.
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.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static TestTU withCode(llvm::StringRef Code)