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().spelledTokenAt(
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().spelledTokenAt(
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()) {
515 auto *MacroTok =
AST->getTokens().spelledTokenAt(SM.getComposedLoc(
518 ASSERT_TRUE(MacroTok);
520 testing::ElementsAreArray(ExpectedLocations));
525TEST(TranslatePreamblePatchLocation, Simple) {
530 TU.Code = R
"cpp(// line 1
534 TU.Filename = "main.cpp";
535 TU.HeaderFilename =
"__preamble_patch__.h";
536 TU.ImplicitHeaderGuard =
false;
538 auto AST = TU.build();
539 auto &SM =
AST.getSourceManager();
541 EXPECT_NE(SM.getFileID(ND.getLocation()), SM.getMainFileID());
544 auto DecompLoc = SM.getDecomposedLoc(TranslatedLoc);
545 EXPECT_EQ(DecompLoc.first, SM.getMainFileID());
546 EXPECT_EQ(SM.getLineNumber(DecompLoc.first, DecompLoc.second), 3U);
549TEST(PreamblePatch, ModifiedBounds) {
551 const char *
const Baseline;
552 const char *
const Modified;
562 {
"#define FOO",
"#define BAR"},
571 for (
const auto &Case : Cases) {
573 auto BaselinePreamble = TU.preamble();
574 ASSERT_TRUE(BaselinePreamble);
576 Annotations Modified(Case.Modified);
577 TU.Code = Modified.code().str();
580 TU.inputs(FS), *BaselinePreamble);
582 IgnoreDiagnostics Diags;
586 const auto ExpectedBounds =
587 Lexer::ComputePreamble(Case.Modified,
CI->getLangOpts());
588 EXPECT_EQ(PP.modifiedBounds().Size, ExpectedBounds.Size);
589 EXPECT_EQ(PP.modifiedBounds().PreambleEndsAtStartOfLine,
590 ExpectedBounds.PreambleEndsAtStartOfLine);
594TEST(PreamblePatch, MacroLoc) {
595 llvm::StringLiteral Baseline =
"\n#define MACRO 12\nint num = MACRO;";
596 llvm::StringLiteral Modified =
" \n#define MACRO 12\nint num = MACRO;";
597 auto AST = createPatchedAST(Baseline, Modified);
601TEST(PreamblePatch, NoopWhenNotRequested) {
602 llvm::StringLiteral Baseline =
"#define M\nint num = M;";
603 llvm::StringLiteral Modified =
"#define M\n#include <foo.h>\nint num = M;";
605 auto BaselinePreamble = TU.preamble();
606 ASSERT_TRUE(BaselinePreamble);
608 TU.Code = Modified.str();
611 TU.inputs(FS), *BaselinePreamble);
612 EXPECT_TRUE(PP.text().empty());
615::testing::Matcher<const Diag &>
616withNote(::testing::Matcher<Note> NoteMatcher) {
619MATCHER_P(Diag, Range,
"Diag at " + llvm::to_string(Range)) {
620 return arg.Range ==
Range;
623 "Diag at " + llvm::to_string(Range) +
" = [" +
Name +
"]") {
624 return arg.Range ==
Range && arg.Name ==
Name;
627TEST(PreamblePatch, DiagnosticsFromMainASTAreInRightPlace) {
629 Annotations
Code(
"#define FOO");
631 Annotations NewCode(
"[[x]];/* error-ok */");
632 auto AST = createPatchedAST(
Code.code(), NewCode.code());
633 EXPECT_THAT(
AST->getDiagnostics(),
634 ElementsAre(Diag(NewCode.range(),
"missing_type_specifier")));
638 Annotations
Code(
"#define FOO");
639 Annotations NewCode(R
"(
642[[x]];/* error-ok */)");
643 auto AST = createPatchedAST(
Code.code(), NewCode.code());
644 EXPECT_THAT(
AST->getDiagnostics(),
645 ElementsAre(Diag(NewCode.range(),
"missing_type_specifier")));
649TEST(PreamblePatch, DiagnosticsToPreamble) {
653 WithContextValue WithCfg(
Config::Key, std::move(Cfg));
655 llvm::StringMap<std::string> AdditionalFiles;
656 AdditionalFiles[
"foo.h"] =
"#pragma once";
657 AdditionalFiles[
"bar.h"] =
"#pragma once";
661[[#include "foo.h"]])");
663 Annotations NewCode(R
"([[# include "foo.h"]])");
664 auto AST = createPatchedAST(
Code.code(), NewCode.code(), AdditionalFiles);
665 EXPECT_THAT(
AST->getDiagnostics(),
666 ElementsAre(Diag(NewCode.range(),
"unused-includes")));
672[[#include "foo.h"]])");
673 Annotations NewCode(R"(
674$bar[[#include "bar.h"]]
676$foo[[#include "foo.h"]])");
677 auto AST = createPatchedAST(
Code.code(), NewCode.code(), AdditionalFiles);
679 AST->getDiagnostics(),
680 UnorderedElementsAre(Diag(NewCode.range(
"bar"),
"unused-includes"),
681 Diag(NewCode.range(
"foo"),
"unused-includes")));
684 Annotations
Code(
"#define [[FOO]] 1\n");
688 Annotations NewCode(R
"(#define BARXYZ 1
689#define $foo1[[FOO]] 1
691#define $foo2[[FOO]] 2)");
692 auto AST = createPatchedAST(
Code.code(), NewCode.code(), AdditionalFiles);
694 AST->getDiagnostics(),
695 ElementsAre(AllOf(Diag(NewCode.range(
"foo2"),
"-Wmacro-redefined"),
696 withNote(Diag(NewCode.range(
"foo1"))))));
700TEST(PreamblePatch, TranslatesDiagnosticsInPreamble) {
703 Annotations
Code(
"#include [[<foo>]]");
704 Annotations NewCode(R
"(
706#include [[<foo>]])");
707 auto AST = createPatchedAST(
Code.code(), NewCode.code());
708 EXPECT_THAT(
AST->getDiagnostics(),
709 ElementsAre(Diag(NewCode.range(),
"pp_file_not_found")));
715#include [[<foo>]])");
716 Annotations NewCode("#include [[<foo>]]");
717 auto AST = createPatchedAST(
Code.code(), NewCode.code());
718 EXPECT_THAT(
AST->getDiagnostics(),
719 ElementsAre(Diag(NewCode.range(),
"pp_file_not_found")));
723 Annotations
Code(
"#include [[<foo>]]");
724 Annotations NewCode(
"#define BAR\n#define BAZ\n");
725 auto AST = createPatchedAST(
Code.code(), NewCode.code());
726 EXPECT_THAT(
AST->getDiagnostics(), IsEmpty());
730 Annotations
Code(
"#include [[<foo>]]");
731 Annotations NewCode(R
"(
736 auto AST = createPatchedAST(
Code.code(), NewCode.code());
737 EXPECT_THAT(
AST->getDiagnostics(),
738 ElementsAre(Diag(NewCode.range(),
"pp_file_not_found")));
742 Annotations
Code(
"#include [[<foo>]]");
743 Annotations NewCode(
" # include <foo>");
744 auto AST = createPatchedAST(
Code.code(), NewCode.code());
745 EXPECT_THAT(
AST->getDiagnostics(), IsEmpty());
753 Annotations NewCode(R"(#include [[<fo\
755 auto AST = createPatchedAST(
Code.code(), NewCode.code());
756 EXPECT_THAT(
AST->getDiagnostics(),
757 ElementsAre(Diag(NewCode.range(),
"pp_file_not_found")));
766 Annotations NewCode(R"(#include <fo\
768 auto AST = createPatchedAST(
Code.code(), NewCode.code());
769 EXPECT_THAT(
AST->getDiagnostics(), IsEmpty());
774#define $note[[BAR]] 1
775#define $main[[BAR]] 2)");
776 Annotations NewCode(R"(
778#define $note[[BAR]] 1
780#define $main[[BAR]] 2)");
781 auto AST = createPatchedAST(
Code.code(), NewCode.code());
783 AST->getDiagnostics(),
784 ElementsAre(AllOf(Diag(NewCode.range(
"main"),
"-Wmacro-redefined"),
785 withNote(Diag(NewCode.range(
"note"))))));
790#define $note[[BAR]] 1
791#define $main[[BAR]] 2)");
792 Annotations NewCode(R"(
793#define $main[[BAR]] 2)");
794 auto AST = createPatchedAST(
Code.code(), NewCode.code());
796 AST->getDiagnostics(),
797 ElementsAre(AllOf(Diag(NewCode.range(
"main"),
"-Wmacro-redefined"),
803#define $note[[BAR]] 1
804#define $main[[BAR]] 2)");
805 Annotations NewCode(R"(
808 auto AST = createPatchedAST(
Code.code(), NewCode.code());
809 EXPECT_THAT(
AST->getDiagnostics(), IsEmpty());
820 Annotations NewCode(
"");
821 auto AST = createPatchedAST(
Code.code(), NewCode.code());
822 EXPECT_THAT(
AST->getDiagnostics(), IsEmpty());
834 Annotations NewCode(R
"(
837 auto AST = createPatchedAST(
Code.code(), NewCode.code());
839 AST->getDiagnostics(),
840 ElementsAre(Diag(NewCode.range(),
"pp_unterminated_conditional")));
845 return std::tie(arg.Rng, arg.Trivia) == std::tie(Range,
Text);
848TEST(PreamblePatch, MacroAndMarkHandling) {
850 Annotations
Code(R
"cpp(
858 Annotations NewCode(R"cpp(
869 auto AST = createPatchedAST(
Code.code(), NewCode.code());
870 EXPECT_THAT(
AST->getMacros().Names.keys(),
871 UnorderedElementsAreArray({
"FOO",
"BAR",
"BAZ"}));
872 EXPECT_THAT(
AST->getMarks(),
873 UnorderedElementsAre(Mark(NewCode.range(
"x"),
" XX"),
874 Mark(NewCode.range(
"y"),
" YY")));
878TEST(PreamblePatch, PatchFileEntry) {
879 Annotations
Code(R
"cpp(#define FOO)cpp");
880 Annotations NewCode(R"cpp(
884 auto AST = createPatchedAST(
Code.code(),
Code.code());
890 auto AST = createPatchedAST(
Code.code(), NewCode.code());
893 ASSERT_NE(FE, std::nullopt);
894 EXPECT_THAT(FE->getName().str(),
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