1 //===--- PreambleTests.cpp --------------------------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "Annotations.h"
12 #include "Diagnostics.h"
15 #include "ParsedAST.h"
18 #include "SourceCode.h"
22 #include "support/Context.h"
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"
44 using testing::Contains
;
45 using testing::ElementsAre
;
47 using testing::IsEmpty
;
48 using testing::Matcher
;
49 using testing::MatchesRegex
;
50 using testing::UnorderedElementsAre
;
51 using testing::UnorderedElementsAreArray
;
57 MATCHER_P2(Distance
, File
, D
, "") {
58 return arg
.first() == File
&& arg
.second
== D
;
61 // Builds a preamble for BaselineContents, patches it for ModifiedContents and
62 // returns the includes in the patch.
64 collectPatchedIncludes(llvm::StringRef ModifiedContents
,
65 llvm::StringRef BaselineContents
,
66 llvm::StringRef MainFileName
= "main.cpp") {
68 auto TU
= TestTU::withCode(BaselineContents
);
69 TU
.Filename
= MainFileName
.str();
70 // ms-compatibility changes meaning of #import, make sure it is turned off.
71 TU
.ExtraArgs
= {"-fno-ms-compatibility"};
72 auto BaselinePreamble
= TU
.preamble();
74 TU
.Code
= ModifiedContents
.str();
75 auto PI
= TU
.inputs(FS
);
76 auto PP
= PreamblePatch::createFullPatch(testPath(TU
.Filename
), PI
,
78 // Collect patch contents.
79 IgnoreDiagnostics Diags
;
80 auto CI
= buildCompilerInvocation(PI
, Diags
);
82 // Run preprocessor over the modified contents with patched Invocation. We
83 // provide a preamble and trim contents to ensure only the implicit header
84 // introduced by the patch is parsed and nothing else.
85 // We don't run PP directly over the patch cotents to test production
87 auto Bounds
= Lexer::ComputePreamble(ModifiedContents
, CI
->getLangOpts());
89 prepareCompilerInstance(std::move(CI
), &BaselinePreamble
->Preamble
,
90 llvm::MemoryBuffer::getMemBufferCopy(
91 ModifiedContents
.slice(0, Bounds
.Size
).str()),
92 PI
.TFS
->view(PI
.CompileCommand
.Directory
), Diags
);
93 PreprocessOnlyAction Action
;
94 if (!Action
.BeginSourceFile(*Clang
, Clang
->getFrontendOpts().Inputs
[0])) {
95 ADD_FAILURE() << "failed begin source file";
98 IncludeStructure Includes
;
99 Includes
.collect(*Clang
);
100 if (llvm::Error Err
= Action
.Execute()) {
101 ADD_FAILURE() << "failed to execute action: " << std::move(Err
);
104 Action
.EndSourceFile();
108 // Check preamble lexing logic by building an empty preamble and patching it
109 // with all the contents.
110 TEST(PreamblePatchTest
, IncludeParsing
) {
111 // We expect any line with a point to show up in the patch.
112 llvm::StringRef Cases
[] = {
114 R
"cpp(^#include "a
.h
")cpp",
115 // Both preamble and mainfile
118 garbage, finishes preamble
134 ^#include <b.h>)cpp",
135 // Directive is not part of preamble if it is not the token immediately
136 // followed by the hash (#).
139 #/**/include <b.h>)cpp",
142 for (const auto &Case : Cases) {
143 Annotations Test(Case);
144 const auto Code = Test.code();
148 collectPatchedIncludes(Code, /*BaselineContents=*/"").MainFileIncludes;
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);
156 TEST(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)
170 EXPECT_THAT(Includes, ElementsAre(AllOf(Field(&Inclusion::Written, "<d
.h
>"),
171 Field(&Inclusion::HashLine, 4))));
174 TEST(PreamblePatchTest, MainFileIsEscaped) {
175 auto Includes = collectPatchedIncludes("#include <a.h>", "", "file\"name.cpp")
177 EXPECT_THAT(Includes, ElementsAre(AllOf(Field(&Inclusion::Written, "<a.h>"),
178 Field(&Inclusion::HashLine, 0))));
181 TEST(PreamblePatchTest, PatchesPreambleIncludes) {
183 IgnoreDiagnostics Diags;
184 auto TU = TestTU::withCode(R"cpp(
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);
195 auto BaselinePreamble = buildPreamble(
196 TU.Filename, *buildCompilerInvocation(PI, Diags), PI, true, nullptr);
197 // We drop c.h from modified and add a new header. Since the latter is patched
198 // we should only get a.h in preamble includes. d.h shouldn't be part of the
199 // preamble, as it's coming from a disabled region.
207 auto PP = PreamblePatch::createFullPatch(testPath(TU.Filename), TU.inputs(FS),
209 // Only a.h should exists in the preamble, as c.h has been dropped and b.h was
212 PP.preambleIncludes(),
214 Field(&Inclusion::Written, "\"a
.h
\""),
215 Field(&Inclusion::Resolved, testPath("a
.h
")),
216 Field(&Inclusion::HeaderID, testing::Not(testing::Eq(std::nullopt))),
217 Field(&Inclusion::FileKind, SrcMgr::CharacteristicKind::C_User))));
220 std::optional<ParsedAST>
221 createPatchedAST(llvm::StringRef Baseline, llvm::StringRef Modified,
222 llvm::StringMap<std::string> AdditionalFiles = {}) {
223 auto TU = TestTU::withCode(Baseline);
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();
234 auto CI = buildCompilerInvocation(TU.inputs(FS), Diags);
236 ADD_FAILURE() << "Failed to build compiler invocation
";
239 return ParsedAST::build(testPath(TU.Filename), TU.inputs(FS), std::move(CI),
240 {}, BaselinePreamble);
243 std::string getPreamblePatch(llvm::StringRef Baseline,
244 llvm::StringRef Modified) {
245 auto BaselinePreamble = TestTU::withCode(Baseline).preamble();
246 if (!BaselinePreamble) {
247 ADD_FAILURE() << "Failed to build baseline preamble
";
251 auto TU = TestTU::withCode(Modified);
252 return PreamblePatch::createFullPatch(testPath("main
.cpp
"), TU.inputs(FS),
258 TEST(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());
272 EXPECT_EQ(Includes, TestTU::withCode(Baseline)
274 .getIncludeStructure()
278 TEST(PreamblePatchTest, Define) {
279 // BAR should be defined while parsing the AST.
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()));
337 TEST(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());
360 TEST(PreamblePatchTest, LocateMacroAtWorks) {
362 const char *const Baseline;
363 const char *const Modified;
365 // Addition of new directive
372 // Available inside preamble section
377 #undef $use^FOO)cpp",
379 // Available after undef, as we don't patch those
387 // Identifier on a different line
395 // In presence of comment tokens
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);
424 auto FoundMacro = locateMacroAt(*MacroTok, AST->getPreprocessor());
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
"));
434 TEST(PreamblePatchTest, LocateMacroAtDeletion) {
436 // We don't patch deleted define directives, make sure we don't crash.
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);
448 auto FoundMacro = locateMacroAt(*MacroTok, AST->getPreprocessor());
449 ASSERT_TRUE(FoundMacro);
450 EXPECT_THAT(FoundMacro->Name, "FOO");
452 getHover(*AST, offsetToPosition(Modified.code(), Modified.point()),
453 format::getLLVMStyle(), nullptr);
455 EXPECT_THAT(HI->Definition, testing::IsEmpty());
459 // Offset is valid, but underlying text is different.
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");
473 MATCHER_P(referenceRangeIs
, R
, "") { return arg
.Loc
.range
== R
; }
475 TEST(PreamblePatchTest
, RefsToMacros
) {
477 const char *const Baseline
;
478 const char *const Modified
;
495 // Ref in preamble section
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(
518 llvm::cantFail(positionToOffset(Modified
.code(), P
))));
519 ASSERT_TRUE(MacroTok
);
520 EXPECT_THAT(findReferences(*AST
, P
, 0).References
,
521 testing::ElementsAreArray(ExpectedLocations
));
526 TEST(TranslatePreamblePatchLocation
, Simple
) {
527 auto TU
= TestTU::withHeaderCode(R
"cpp(
530 // Presumed line/col needs to be valid in the main file.
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();
541 auto &ND
= findDecl(AST
, "foo");
542 EXPECT_NE(SM
.getFileID(ND
.getLocation()), SM
.getMainFileID());
544 auto TranslatedLoc
= translatePreamblePatchLocation(ND
.getLocation(), SM
);
545 auto DecompLoc
= SM
.getDecomposedLoc(TranslatedLoc
);
546 EXPECT_EQ(DecompLoc
.first
, SM
.getMainFileID());
547 EXPECT_EQ(SM
.getLineNumber(DecompLoc
.first
, DecompLoc
.second
), 3U);
550 TEST(PreamblePatch
, ModifiedBounds
) {
552 const char *const Baseline
;
553 const char *const Modified
;
563 {"#define FOO", "#define BAR"},
572 for (const auto &Case
: Cases
) {
573 auto TU
= TestTU::withCode(Case
.Baseline
);
574 auto BaselinePreamble
= TU
.preamble();
575 ASSERT_TRUE(BaselinePreamble
);
577 Annotations
Modified(Case
.Modified
);
578 TU
.Code
= Modified
.code().str();
580 auto PP
= PreamblePatch::createFullPatch(testPath(TU
.Filename
),
581 TU
.inputs(FS
), *BaselinePreamble
);
583 IgnoreDiagnostics Diags
;
584 auto CI
= buildCompilerInvocation(TU
.inputs(FS
), 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
);
595 TEST(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
);
602 TEST(PreamblePatch
, NoopWhenNotRequested
) {
603 llvm::StringLiteral Baseline
= "#define M\nint num = M;";
604 llvm::StringLiteral Modified
= "#define M\n#include <foo.h>\nint num = M;";
605 auto TU
= TestTU::withCode(Baseline
);
606 auto BaselinePreamble
= TU
.preamble();
607 ASSERT_TRUE(BaselinePreamble
);
609 TU
.Code
= Modified
.str();
611 auto PP
= PreamblePatch::createMacroPatch(testPath(TU
.Filename
),
612 TU
.inputs(FS
), *BaselinePreamble
);
613 EXPECT_TRUE(PP
.text().empty());
616 ::testing::Matcher
<const Diag
&>
617 withNote(::testing::Matcher
<Note
> NoteMatcher
) {
618 return Field(&Diag::Notes
, ElementsAre(NoteMatcher
));
620 MATCHER_P(Diag
, Range
, "Diag at " + llvm::to_string(Range
)) {
621 return arg
.Range
== Range
;
623 MATCHER_P2(Diag
, Range
, Name
,
624 "Diag at " + llvm::to_string(Range
) + " = [" + Name
+ "]") {
625 return arg
.Range
== Range
&& arg
.Name
== Name
;
628 TEST(PreamblePatch
, DiagnosticsFromMainASTAreInRightPlace
) {
630 Annotations
Code("#define FOO");
631 // Check with removals from preamble.
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")));
638 // Check with additions to preamble.
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")));
650 TEST(PreamblePatch
, DiagnosticsToPreamble
) {
652 Cfg
.Diagnostics
.UnusedIncludes
= Config::IncludesPolicy::Strict
;
653 Cfg
.Diagnostics
.MissingIncludes
= Config::IncludesPolicy::Strict
;
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
"]])");
663 // Check with removals from preamble.
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")));
670 // Check with additions to preamble.
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");
686 // Check ranges for notes.
687 // This also makes sure we don't generate missing-include diagnostics
688 // because macros are redefined in preamble-patch.
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"))))));
701 TEST(PreamblePatch
, TranslatesDiagnosticsInPreamble
) {
703 // Check with additions to preamble.
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")));
713 // Check with removals from preamble.
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")));
723 // Drop line with diags.
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());
730 // Picks closest line in case of multiple alternatives.
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")));
742 // Drop diag if line spelling has changed.
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")));
761 // Multiple lines with change.
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"))))));
789 // Preserves diag without 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"),
799 Field(&Diag::Notes
, IsEmpty()))));
802 // Make sure orphaned notes are not promoted to diags.
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());
818 // This code will emit a diagnostic for unterminated #ifndef (as stale
819 // preamble has the conditional but main file doesn't terminate it).
820 // We shouldn't emit any diagnotiscs (and shouldn't crash).
821 Annotations
NewCode("");
822 auto AST
= createPatchedAST(Code
.code(), NewCode
.code());
823 EXPECT_THAT(AST
->getDiagnostics(), IsEmpty());
831 // This code will emit a diagnostic for unterminated #ifndef (as stale
832 // preamble has the conditional but main file doesn't terminate it).
833 // We shouldn't emit any diagnotiscs (and shouldn't crash).
834 // FIXME: Patch/ignore diagnostics in such cases.
835 Annotations
NewCode(R
"(
838 auto AST
= createPatchedAST(Code
.code(), NewCode
.code());
840 AST
->getDiagnostics(),
841 ElementsAre(Diag(NewCode
.range(), "pp_unterminated_conditional")));
845 MATCHER_P2(Mark
, Range
, Text
, "") {
846 return std::tie(arg
.Rng
, arg
.Trivia
) == std::tie(Range
, Text
);
849 TEST(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")));
879 TEST(PreamblePatch
, PatchFileEntry
) {
880 Annotations
Code(R
"cpp(#define FOO)cpp");
881 Annotations
NewCode(R
"cpp(
885 auto AST
= createPatchedAST(Code
.code(), Code
.code());
887 PreamblePatch::getPatchEntry(AST
->tuPath(), AST
->getSourceManager()),
891 auto AST
= createPatchedAST(Code
.code(), NewCode
.code());
893 PreamblePatch::getPatchEntry(AST
->tuPath(), AST
->getSourceManager());
894 ASSERT_NE(FE
, std::nullopt
);
895 EXPECT_THAT(FE
->getName().str(),
896 testing::EndsWith(PreamblePatch::HeaderName
.str()));
901 } // namespace clangd