23#include "clang/Basic/SourceManager.h"
24#include "clang/Format/Format.h"
25#include "clang/Frontend/FrontendActions.h"
26#include "clang/Frontend/PrecompiledPreamble.h"
27#include "llvm/ADT/StringMap.h"
28#include "llvm/ADT/StringRef.h"
29#include "llvm/Support/Error.h"
30#include "llvm/Support/MemoryBuffer.h"
31#include "llvm/Support/ScopedPrinter.h"
32#include "llvm/Support/VirtualFileSystem.h"
33#include "llvm/Testing/Annotations/Annotations.h"
34#include "gmock/gmock.h"
35#include "gtest/gtest-matchers.h"
36#include "gtest/gtest.h"
44using testing::Contains;
45using testing::ElementsAre;
47using testing::IsEmpty;
48using testing::Matcher;
49using testing::MatchesRegex;
50using testing::UnorderedElementsAre;
51using testing::UnorderedElementsAreArray;
58 return arg.first() ==
File && arg.second == D;
64collectPatchedIncludes(llvm::StringRef ModifiedContents,
65 llvm::StringRef BaselineContents,
66 llvm::StringRef MainFileName =
"main.cpp") {
69 TU.Filename = MainFileName.str();
71 TU.ExtraArgs = {
"-fno-ms-compatibility"};
72 auto BaselinePreamble = TU.preamble();
74 TU.Code = ModifiedContents.str();
75 auto PI = TU.inputs(FS);
79 IgnoreDiagnostics Diags;
87 auto Bounds = Lexer::ComputePreamble(ModifiedContents,
CI->getLangOpts());
90 llvm::MemoryBuffer::getMemBufferCopy(
91 ModifiedContents.slice(0,
Bounds.Size).str()),
92 PI.TFS->view(PI.CompileCommand.Directory), Diags);
93 PreprocessOnlyAction
Action;
95 ADD_FAILURE() <<
"failed begin source file";
98 IncludeStructure Includes;
100 if (llvm::Error Err =
Action.Execute()) {
101 ADD_FAILURE() <<
"failed to execute action: " << std::move(Err);
110TEST(PreamblePatchTest, IncludeParsing) {
112 llvm::StringRef Cases[] = {
114 R
"cpp(^#include "a.h")cpp",
118 garbage, finishes preamble
134 ^#include <b.h>)cpp",
139 #/**/include <b.h>)cpp",
142 for (
const auto &Case : Cases) {
143 Annotations Test(Case);
144 const auto Code = Test.code();
149 auto Points = Test.points();
150 ASSERT_EQ(Includes.size(), Points.size());
151 for (
size_t I = 0,
E = Includes.size(); I !=
E; ++I)
152 EXPECT_EQ(Includes[I].HashLine, Points[I].line);
156TEST(PreamblePatchTest, ContainsNewIncludes) {
157 constexpr llvm::StringLiteral BaselineContents = R
"cpp(
159 #include <b.h> // This will be removed
162 constexpr llvm::StringLiteral ModifiedContents = R
"cpp(
164 #include <c.h> // This has changed a line.
165 #include <c.h> // This is a duplicate.
166 #include <d.h> // This is newly introduced.
168 auto Includes = collectPatchedIncludes(ModifiedContents, BaselineContents)
174TEST(PreamblePatchTest, MainFileIsEscaped) {
175 auto Includes = collectPatchedIncludes(
"#include <a.h>",
"",
"file\"name.cpp")
181TEST(PreamblePatchTest, PatchesPreambleIncludes) {
183 IgnoreDiagnostics Diags;
185 #include "a.h" // IWYU pragma: keep
191 TU.AdditionalFiles["a.h"] =
"#include \"b.h\"";
192 TU.AdditionalFiles[
"b.h"] =
"";
193 TU.AdditionalFiles[
"c.h"] =
"";
194 auto PI = TU.inputs(FS);
212 PP.preambleIncludes(),
220std::optional<ParsedAST>
221createPatchedAST(llvm::StringRef Baseline, llvm::StringRef Modified,
222 llvm::StringMap<std::string> AdditionalFiles = {}) {
224 TU.AdditionalFiles = std::move(AdditionalFiles);
225 auto BaselinePreamble = TU.preamble();
226 if (!BaselinePreamble) {
227 ADD_FAILURE() <<
"Failed to build baseline preamble";
231 IgnoreDiagnostics Diags;
233 TU.Code = Modified.str();
236 ADD_FAILURE() <<
"Failed to build compiler invocation";
240 {}, BaselinePreamble);
243std::string getPreamblePatch(llvm::StringRef Baseline,
244 llvm::StringRef Modified) {
246 if (!BaselinePreamble) {
247 ADD_FAILURE() <<
"Failed to build baseline preamble";
258TEST(PreamblePatchTest, IncludesArePreserved) {
259 llvm::StringLiteral Baseline = R
"(//error-ok
263 llvm::StringLiteral Modified = R"(//error-ok
268 auto Includes = createPatchedAST(Baseline, Modified.str())
269 ->getIncludeStructure()
271 EXPECT_TRUE(!Includes.empty());
274 .getIncludeStructure()
278TEST(PreamblePatchTest, Define) {
281 const char *
const Contents;
282 const char *
const ExpectedPatch;
288 R"cpp(#line 0 ".*main.cpp"
300 R"cpp(#line 0 ".*main.cpp"
312 R"cpp(#line 0 ".*main.cpp"
320 for (
const auto &Case : Cases) {
321 SCOPED_TRACE(Case.Contents);
322 llvm::Annotations Modified(Case.Contents);
323 EXPECT_THAT(getPreamblePatch(
"", Modified.code()),
324 MatchesRegex(Case.ExpectedPatch));
326 auto AST = createPatchedAST(
"", Modified.code());
328 std::vector<llvm::Annotations::Range> MacroRefRanges;
329 for (
auto &
M :
AST->getMacros().MacroRefs) {
330 for (
auto &O :
M.getSecond())
331 MacroRefRanges.push_back({O.StartOffset, O.EndOffset});
333 EXPECT_THAT(MacroRefRanges, Contains(Modified.range()));
337TEST(PreamblePatchTest, OrderingPreserved) {
338 llvm::StringLiteral Baseline =
"#define BAR(X) X";
339 Annotations Modified(R
"cpp(
340 #define BAR(X, Y) X Y
345 llvm::StringLiteral ExpectedPatch(R"cpp(#line 0 ".*main.cpp"
348#define BAR\(X, Y\) X Y
353 EXPECT_THAT(getPreamblePatch(Baseline, Modified.code()),
354 MatchesRegex(ExpectedPatch.str()));
356 auto AST = createPatchedAST(Baseline, Modified.code());
360TEST(PreamblePatchTest, LocateMacroAtWorks) {
362 const char *
const Baseline;
363 const char *
const Modified;
377 #undef $use^FOO)cpp",
413 for (
const auto &Case : Cases) {
414 SCOPED_TRACE(Case.Modified);
415 llvm::Annotations Modified(Case.Modified);
416 auto AST = createPatchedAST(Case.Baseline, Modified.code());
419 const auto &SM =
AST->getSourceManager();
420 auto *MacroTok =
AST->getTokens().spelledTokenContaining(
421 SM.getComposedLoc(SM.getMainFileID(), Modified.point(
"use")));
422 ASSERT_TRUE(MacroTok);
425 ASSERT_TRUE(FoundMacro);
426 EXPECT_THAT(FoundMacro->Name,
"FOO");
428 auto MacroLoc = FoundMacro->NameLoc;
429 EXPECT_EQ(SM.getFileID(MacroLoc), SM.getMainFileID());
430 EXPECT_EQ(SM.getFileOffset(MacroLoc), Modified.point(
"def"));
434TEST(PreamblePatchTest, LocateMacroAtDeletion) {
437 llvm::StringLiteral Baseline =
"#define FOO";
438 llvm::Annotations Modified(
"^FOO");
440 auto AST = createPatchedAST(Baseline, Modified.code());
443 const auto &SM =
AST->getSourceManager();
444 auto *MacroTok =
AST->getTokens().spelledTokenContaining(
445 SM.getComposedLoc(SM.getMainFileID(), Modified.point()));
446 ASSERT_TRUE(MacroTok);
449 ASSERT_TRUE(FoundMacro);
450 EXPECT_THAT(FoundMacro->Name,
"FOO");
453 format::getLLVMStyle(),
nullptr);
455 EXPECT_THAT(HI->Definition, testing::IsEmpty());
460 llvm::StringLiteral Baseline =
"#define FOO";
461 Annotations Modified(R
"cpp(#define BAR
464 auto AST = createPatchedAST(Baseline, Modified.code());
467 auto HI =
getHover(*
AST, Modified.point(), format::getLLVMStyle(),
nullptr);
469 EXPECT_THAT(HI->Definition,
"#define BAR");
473MATCHER_P(referenceRangeIs, R,
"") {
return arg.Loc.range == R; }
475TEST(PreamblePatchTest, RefsToMacros) {
477 const char *
const Baseline;
478 const char *
const Modified;
504 for (
const auto &Case : Cases) {
505 Annotations Modified(Case.Modified);
506 auto AST = createPatchedAST(
"", Modified.code());
509 const auto &SM =
AST->getSourceManager();
510 std::vector<Matcher<ReferencesResult::Reference>> ExpectedLocations;
511 for (
const auto &R : Modified.ranges())
512 ExpectedLocations.push_back(referenceRangeIs(R));
514 for (
const auto &P : Modified.points()) {
516 AST->getTokens().spelledTokenContaining(SM.getComposedLoc(
519 ASSERT_TRUE(MacroTok);
521 testing::ElementsAreArray(ExpectedLocations));
526TEST(TranslatePreamblePatchLocation, Simple) {
531 TU.Code = R
"cpp(// line 1
535 TU.Filename = "main.cpp";
536 TU.HeaderFilename =
"__preamble_patch__.h";
537 TU.ImplicitHeaderGuard =
false;
539 auto AST = TU.build();
540 auto &SM =
AST.getSourceManager();
542 EXPECT_NE(SM.getFileID(ND.getLocation()), SM.getMainFileID());
545 auto DecompLoc = SM.getDecomposedLoc(TranslatedLoc);
546 EXPECT_EQ(DecompLoc.first, SM.getMainFileID());
547 EXPECT_EQ(SM.getLineNumber(DecompLoc.first, DecompLoc.second), 3U);
550TEST(PreamblePatch, ModifiedBounds) {
552 const char *
const Baseline;
553 const char *
const Modified;
563 {
"#define FOO",
"#define BAR"},
572 for (
const auto &Case : Cases) {
574 auto BaselinePreamble = TU.preamble();
575 ASSERT_TRUE(BaselinePreamble);
577 Annotations Modified(Case.Modified);
578 TU.Code = Modified.code().str();
581 TU.inputs(FS), *BaselinePreamble);
583 IgnoreDiagnostics Diags;
587 const auto ExpectedBounds =
588 Lexer::ComputePreamble(Case.Modified,
CI->getLangOpts());
589 EXPECT_EQ(PP.modifiedBounds().Size, ExpectedBounds.Size);
590 EXPECT_EQ(PP.modifiedBounds().PreambleEndsAtStartOfLine,
591 ExpectedBounds.PreambleEndsAtStartOfLine);
595TEST(PreamblePatch, MacroLoc) {
596 llvm::StringLiteral Baseline =
"\n#define MACRO 12\nint num = MACRO;";
597 llvm::StringLiteral Modified =
" \n#define MACRO 12\nint num = MACRO;";
598 auto AST = createPatchedAST(Baseline, Modified);
602TEST(PreamblePatch, NoopWhenNotRequested) {
603 llvm::StringLiteral Baseline =
"#define M\nint num = M;";
604 llvm::StringLiteral Modified =
"#define M\n#include <foo.h>\nint num = M;";
606 auto BaselinePreamble = TU.preamble();
607 ASSERT_TRUE(BaselinePreamble);
609 TU.Code = Modified.str();
612 TU.inputs(FS), *BaselinePreamble);
613 EXPECT_TRUE(PP.text().empty());
616::testing::Matcher<const Diag &>
617withNote(::testing::Matcher<Note> NoteMatcher) {
620MATCHER_P(Diag, Range,
"Diag at " + llvm::to_string(Range)) {
621 return arg.Range ==
Range;
624 "Diag at " + llvm::to_string(Range) +
" = [" +
Name +
"]") {
625 return arg.Range ==
Range && arg.Name ==
Name;
628TEST(PreamblePatch, DiagnosticsFromMainASTAreInRightPlace) {
630 Annotations
Code(
"#define FOO");
632 Annotations NewCode(
"[[x]];/* error-ok */");
633 auto AST = createPatchedAST(
Code.code(), NewCode.code());
634 EXPECT_THAT(
AST->getDiagnostics(),
635 ElementsAre(Diag(NewCode.range(),
"missing_type_specifier")));
639 Annotations
Code(
"#define FOO");
640 Annotations NewCode(R
"(
643[[x]];/* error-ok */)");
644 auto AST = createPatchedAST(
Code.code(), NewCode.code());
645 EXPECT_THAT(
AST->getDiagnostics(),
646 ElementsAre(Diag(NewCode.range(),
"missing_type_specifier")));
650TEST(PreamblePatch, DiagnosticsToPreamble) {
654 WithContextValue WithCfg(
Config::Key, std::move(Cfg));
656 llvm::StringMap<std::string> AdditionalFiles;
657 AdditionalFiles[
"foo.h"] =
"#pragma once";
658 AdditionalFiles[
"bar.h"] =
"#pragma once";
662[[#include "foo.h"]])");
664 Annotations NewCode(R
"([[# include "foo.h"]])");
665 auto AST = createPatchedAST(
Code.code(), NewCode.code(), AdditionalFiles);
666 EXPECT_THAT(
AST->getDiagnostics(),
667 ElementsAre(Diag(NewCode.range(),
"unused-includes")));
673[[#include "foo.h"]])");
674 Annotations NewCode(R"(
675$bar[[#include "bar.h"]]
677$foo[[#include "foo.h"]])");
678 auto AST = createPatchedAST(
Code.code(), NewCode.code(), AdditionalFiles);
680 AST->getDiagnostics(),
681 UnorderedElementsAre(Diag(NewCode.range(
"bar"),
"unused-includes"),
682 Diag(NewCode.range(
"foo"),
"unused-includes")));
685 Annotations
Code(
"#define [[FOO]] 1\n");
689 Annotations NewCode(R
"(#define BARXYZ 1
690#define $foo1[[FOO]] 1
692#define $foo2[[FOO]] 2)");
693 auto AST = createPatchedAST(
Code.code(), NewCode.code(), AdditionalFiles);
695 AST->getDiagnostics(),
696 ElementsAre(AllOf(Diag(NewCode.range(
"foo2"),
"-Wmacro-redefined"),
697 withNote(Diag(NewCode.range(
"foo1"))))));
701TEST(PreamblePatch, TranslatesDiagnosticsInPreamble) {
704 Annotations
Code(
"#include [[<foo>]]");
705 Annotations NewCode(R
"(
707#include [[<foo>]])");
708 auto AST = createPatchedAST(
Code.code(), NewCode.code());
709 EXPECT_THAT(
AST->getDiagnostics(),
710 ElementsAre(Diag(NewCode.range(),
"pp_file_not_found")));
716#include [[<foo>]])");
717 Annotations NewCode("#include [[<foo>]]");
718 auto AST = createPatchedAST(
Code.code(), NewCode.code());
719 EXPECT_THAT(
AST->getDiagnostics(),
720 ElementsAre(Diag(NewCode.range(),
"pp_file_not_found")));
724 Annotations
Code(
"#include [[<foo>]]");
725 Annotations NewCode(
"#define BAR\n#define BAZ\n");
726 auto AST = createPatchedAST(
Code.code(), NewCode.code());
727 EXPECT_THAT(
AST->getDiagnostics(), IsEmpty());
731 Annotations
Code(
"#include [[<foo>]]");
732 Annotations NewCode(R
"(
737 auto AST = createPatchedAST(
Code.code(), NewCode.code());
738 EXPECT_THAT(
AST->getDiagnostics(),
739 ElementsAre(Diag(NewCode.range(),
"pp_file_not_found")));
743 Annotations
Code(
"#include [[<foo>]]");
744 Annotations NewCode(
" # include <foo>");
745 auto AST = createPatchedAST(
Code.code(), NewCode.code());
746 EXPECT_THAT(
AST->getDiagnostics(), IsEmpty());
754 Annotations NewCode(R"(#include [[<fo\
756 auto AST = createPatchedAST(
Code.code(), NewCode.code());
757 EXPECT_THAT(
AST->getDiagnostics(),
758 ElementsAre(Diag(NewCode.range(),
"pp_file_not_found")));
767 Annotations NewCode(R"(#include <fo\
769 auto AST = createPatchedAST(
Code.code(), NewCode.code());
770 EXPECT_THAT(
AST->getDiagnostics(), IsEmpty());
775#define $note[[BAR]] 1
776#define $main[[BAR]] 2)");
777 Annotations NewCode(R"(
779#define $note[[BAR]] 1
781#define $main[[BAR]] 2)");
782 auto AST = createPatchedAST(
Code.code(), NewCode.code());
784 AST->getDiagnostics(),
785 ElementsAre(AllOf(Diag(NewCode.range(
"main"),
"-Wmacro-redefined"),
786 withNote(Diag(NewCode.range(
"note"))))));
791#define $note[[BAR]] 1
792#define $main[[BAR]] 2)");
793 Annotations NewCode(R"(
794#define $main[[BAR]] 2)");
795 auto AST = createPatchedAST(
Code.code(), NewCode.code());
797 AST->getDiagnostics(),
798 ElementsAre(AllOf(Diag(NewCode.range(
"main"),
"-Wmacro-redefined"),
804#define $note[[BAR]] 1
805#define $main[[BAR]] 2)");
806 Annotations NewCode(R"(
809 auto AST = createPatchedAST(
Code.code(), NewCode.code());
810 EXPECT_THAT(
AST->getDiagnostics(), IsEmpty());
821 Annotations NewCode(
"");
822 auto AST = createPatchedAST(
Code.code(), NewCode.code());
823 EXPECT_THAT(
AST->getDiagnostics(), IsEmpty());
835 Annotations NewCode(R
"(
838 auto AST = createPatchedAST(
Code.code(), NewCode.code());
840 AST->getDiagnostics(),
841 ElementsAre(Diag(NewCode.range(),
"pp_unterminated_conditional")));
846 return std::tie(arg.Rng, arg.Trivia) == std::tie(Range,
Text);
849TEST(PreamblePatch, MacroAndMarkHandling) {
851 Annotations
Code(R
"cpp(
859 Annotations NewCode(R"cpp(
870 auto AST = createPatchedAST(
Code.code(), NewCode.code());
871 EXPECT_THAT(
AST->getMacros().Names.keys(),
872 UnorderedElementsAreArray({
"FOO",
"BAR",
"BAZ"}));
873 EXPECT_THAT(
AST->getMarks(),
874 UnorderedElementsAre(Mark(NewCode.range(
"x"),
" XX"),
875 Mark(NewCode.range(
"y"),
" YY")));
879TEST(PreamblePatch, PatchFileEntry) {
880 Annotations
Code(R
"cpp(#define FOO)cpp");
881 Annotations NewCode(R"cpp(
885 auto AST = createPatchedAST(
Code.code(),
Code.code());
891 auto AST = createPatchedAST(
Code.code(), NewCode.code());
894 ASSERT_NE(FE, std::nullopt);
895 EXPECT_THAT(FE->getName().str(),
llvm::SmallString< 256U > Name
CharSourceRange Range
SourceRange for the file name.
const google::protobuf::Message & M
std::unique_ptr< CompilerInvocation > CI
std::vector< Inclusion > MainFileIncludes
void collect(const CompilerInstance &CI)
static std::optional< ParsedAST > build(llvm::StringRef Filename, const ParseInputs &Inputs, std::unique_ptr< clang::CompilerInvocation > CI, llvm::ArrayRef< Diag > CompilerInvocationDiags, std::shared_ptr< const PreambleData > Preamble)
Attempts to run Clang and store the parsed AST.
static OptionalFileEntryRef getPatchEntry(llvm::StringRef MainFilePath, const SourceManager &SM)
Returns the FileEntry for the preamble patch of MainFilePath in SM, if any.
llvm::StringRef text() const
Returns textual patch contents.
static PreamblePatch createMacroPatch(llvm::StringRef FileName, const ParseInputs &Modified, const PreambleData &Baseline)
static PreamblePatch createFullPatch(llvm::StringRef FileName, const ParseInputs &Modified, const PreambleData &Baseline)
Builds a patch that contains new PP directives introduced to the preamble section of Modified compare...
static constexpr llvm::StringLiteral HeaderName
const NamedDecl & findDecl(ParsedAST &AST, llvm::StringRef QName)
Position offsetToPosition(llvm::StringRef Code, size_t Offset)
Turn an offset in Code into a [line, column] pair.
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, "")
SourceLocation translatePreamblePatchLocation(SourceLocation Loc, const SourceManager &SM)
Translates locations inside preamble patch to their main-file equivalent using presumed locations.
ReferencesResult findReferences(ParsedAST &AST, Position Pos, uint32_t Limit, const SymbolIndex *Index, bool AddContext)
Returns references of the symbol at a specified Pos.
std::string testPath(PathRef File, llvm::sys::path::Style Style)
std::optional< DefinedMacro > locateMacroAt(const syntax::Token &SpelledTok, Preprocessor &PP)
Gets the macro referenced by SpelledTok.
std::optional< HoverInfo > getHover(ParsedAST &AST, Position Pos, const format::FormatStyle &Style, const SymbolIndex *Index)
Get the hover information when hovering at Pos.
std::shared_ptr< const PreambleData > buildPreamble(PathRef FileName, CompilerInvocation CI, const ParseInputs &Inputs, bool StoreInMemory, PreambleParsedCallback PreambleCallback, PreambleBuildStats *Stats)
Build a preamble for the new inputs unless an old one can be reused.
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::Expected< size_t > positionToOffset(llvm::StringRef Code, Position P, bool AllowColumnsBeyondLineLength)
Turn a [line, column] pair into an offset in Code.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static clangd::Key< Config > Key
Context key which can be used to set the current Config.
@ Strict
Diagnose missing and unused includes.
IncludesPolicy UnusedIncludes
IncludesPolicy MissingIncludes
struct clang::clangd::Config::@4 Diagnostics
Controls warnings and errors when parsing code.
std::vector< Note > Notes
Elaborate on the problem, usually pointing to a related piece of code.
SrcMgr::CharacteristicKind FileKind
std::optional< unsigned > HeaderID
static TestTU withHeaderCode(llvm::StringRef HeaderCode)
static TestTU withCode(llvm::StringRef Code)
std::shared_ptr< const PreambleData > preamble(PreambleParsedCallback PreambleCallback=nullptr) const