xref: /llvm-project/clang-tools-extra/clangd/unittests/PreambleTests.cpp (revision 5f1adf0433c6007f8be885b832c852da67e8524c)
1 //===--- PreambleTests.cpp --------------------------------------*- C++ -*-===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "Annotations.h"
10 #include "Compiler.h"
11 #include "Config.h"
12 #include "Diagnostics.h"
13 #include "Headers.h"
14 #include "Hover.h"
15 #include "ParsedAST.h"
16 #include "Preamble.h"
17 #include "Protocol.h"
18 #include "SourceCode.h"
19 #include "TestFS.h"
20 #include "TestTU.h"
21 #include "XRefs.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"
37 #include <memory>
38 #include <optional>
39 #include <string>
40 #include <utility>
41 #include <vector>
42 
43 using testing::AllOf;
44 using testing::Contains;
45 using testing::ElementsAre;
46 using testing::Field;
47 using testing::IsEmpty;
48 using testing::Matcher;
49 using testing::MatchesRegex;
50 using testing::UnorderedElementsAre;
51 using testing::UnorderedElementsAreArray;
52 
53 namespace clang {
54 namespace clangd {
55 namespace {
56 
57 MATCHER_P2(Distance, File, D, "") {
58   return arg.first() == File && arg.second == D;
59 }
60 
61 // Builds a preamble for BaselineContents, patches it for ModifiedContents and
62 // returns the includes in the patch.
63 IncludeStructure
collectPatchedIncludes(llvm::StringRef ModifiedContents,llvm::StringRef BaselineContents,llvm::StringRef MainFileName="main.cpp")64 collectPatchedIncludes(llvm::StringRef ModifiedContents,
65                        llvm::StringRef BaselineContents,
66                        llvm::StringRef MainFileName = "main.cpp") {
67   MockFS FS;
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();
73   // Create the patch.
74   TU.Code = ModifiedContents.str();
75   auto PI = TU.inputs(FS);
76   auto PP = PreamblePatch::createFullPatch(testPath(TU.Filename), PI,
77                                            *BaselinePreamble);
78   // Collect patch contents.
79   IgnoreDiagnostics Diags;
80   auto CI = buildCompilerInvocation(PI, Diags);
81   PP.apply(*CI);
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
86   // behaviour.
87   auto Bounds = Lexer::ComputePreamble(ModifiedContents, CI->getLangOpts());
88   auto Clang =
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";
96     return {};
97   }
98   IncludeStructure Includes;
99   Includes.collect(*Clang);
100   if (llvm::Error Err = Action.Execute()) {
101     ADD_FAILURE() << "failed to execute action: " << std::move(Err);
102     return {};
103   }
104   Action.EndSourceFile();
105   return Includes;
106 }
107 
108 // Check preamble lexing logic by building an empty preamble and patching it
109 // with all the contents.
TEST(PreamblePatchTest,IncludeParsing)110 TEST(PreamblePatchTest, IncludeParsing) {
111   // We expect any line with a point to show up in the patch.
112   llvm::StringRef Cases[] = {
113       // Only preamble
114       R"cpp(^#include "a.h")cpp",
115       // Both preamble and mainfile
116       R"cpp(
117         ^#include "a.h"
118         garbage, finishes preamble
119         #include "a.h")cpp",
120       // Mixed directives
121       R"cpp(
122         ^#include "a.h"
123         #pragma directive
124         // some comments
125         ^#include_next <a.h>
126         #ifdef skipped
127         ^#import "a.h"
128         #endif)cpp",
129       // Broken directives
130       R"cpp(
131         #include "a
132         ^#include "a.h"
133         #include <b
134         ^#include <b.h>)cpp",
135       // Directive is not part of preamble if it is not the token immediately
136       // followed by the hash (#).
137       R"cpp(
138         ^#include "a.h"
139         #/**/include <b.h>)cpp",
140   };
141 
142   for (const auto &Case : Cases) {
143     Annotations Test(Case);
144     const auto Code = Test.code();
145     SCOPED_TRACE(Code);
146 
147     auto Includes =
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);
153   }
154 }
155 
156 TEST(PreamblePatchTest, ContainsNewIncludes) {
157   constexpr llvm::StringLiteral BaselineContents = R"cpp(
158     #include <a.h>
159     #include <b.h> // This will be removed
160     #include <c.h>
161   )cpp";
162   constexpr llvm::StringLiteral ModifiedContents = R"cpp(
163     #include <a.h>
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.
167   )cpp";
168   auto Includes = collectPatchedIncludes(ModifiedContents, BaselineContents)
169                       .MainFileIncludes;
170   EXPECT_THAT(Includes, ElementsAre(AllOf(Field(&Inclusion::Written, "<d.h>"),
171                                           Field(&Inclusion::HashLine, 4))));
172 }
173 
174 TEST(PreamblePatchTest, MainFileIsEscaped) {
175   auto Includes = collectPatchedIncludes("#include <a.h>", "", "file\"name.cpp")
176                       .MainFileIncludes;
177   EXPECT_THAT(Includes, ElementsAre(AllOf(Field(&Inclusion::Written, "<a.h>"),
178                                           Field(&Inclusion::HashLine, 0))));
179 }
180 
181 TEST(PreamblePatchTest, PatchesPreambleIncludes) {
182   MockFS FS;
183   IgnoreDiagnostics Diags;
184   auto TU = TestTU::withCode(R"cpp(
185     #include "a.h" // IWYU pragma: keep
186     #include "c.h"
187     #ifdef FOO
188     #include "d.h"
189     #endif
190   )cpp");
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.
200   TU.Code = R"cpp(
201     #include "a.h"
202     #include "b.h"
203     #ifdef FOO
204     #include "d.h"
205     #endif
206   )cpp";
207   auto PP = PreamblePatch::createFullPatch(testPath(TU.Filename), TU.inputs(FS),
208                                            *BaselinePreamble);
209   // Only a.h should exists in the preamble, as c.h has been dropped and b.h was
210   // newly introduced.
211   EXPECT_THAT(
212       PP.preambleIncludes(),
213       ElementsAre(AllOf(
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))));
218 }
219 
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";
228     return std::nullopt;
229   }
230 
231   IgnoreDiagnostics Diags;
232   MockFS FS;
233   TU.Code = Modified.str();
234   auto CI = buildCompilerInvocation(TU.inputs(FS), Diags);
235   if (!CI) {
236     ADD_FAILURE() << "Failed to build compiler invocation";
237     return std::nullopt;
238   }
239   return ParsedAST::build(testPath(TU.Filename), TU.inputs(FS), std::move(CI),
240                           {}, BaselinePreamble);
241 }
242 
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";
248     return "";
249   }
250   MockFS FS;
251   auto TU = TestTU::withCode(Modified);
252   return PreamblePatch::createFullPatch(testPath("main.cpp"), TU.inputs(FS),
253                                         *BaselinePreamble)
254       .text()
255       .str();
256 }
257 
258 TEST(PreamblePatchTest, IncludesArePreserved) {
259   llvm::StringLiteral Baseline = R"(//error-ok
260 #include <foo>
261 #include <bar>
262 )";
263   llvm::StringLiteral Modified = R"(//error-ok
264 #include <foo>
265 #include <bar>
266 #define FOO)";
267 
268   auto Includes = createPatchedAST(Baseline, Modified.str())
269                       ->getIncludeStructure()
270                       .MainFileIncludes;
271   EXPECT_TRUE(!Includes.empty());
272   EXPECT_EQ(Includes, TestTU::withCode(Baseline)
273                           .build()
274                           .getIncludeStructure()
275                           .MainFileIncludes);
276 }
277 
278 TEST(PreamblePatchTest, Define) {
279   // BAR should be defined while parsing the AST.
280   struct {
281     const char *const Contents;
282     const char *const ExpectedPatch;
283   } Cases[] = {
284       {
285           R"cpp(
286         #define BAR
287         [[BAR]])cpp",
288           R"cpp(#line 0 ".*main.cpp"
289 #undef BAR
290 #line 2
291 #define         BAR
292 )cpp",
293       },
294       // multiline macro
295       {
296           R"cpp(
297         #define BAR \
298 
299         [[BAR]])cpp",
300           R"cpp(#line 0 ".*main.cpp"
301 #undef BAR
302 #line 2
303 #define         BAR
304 )cpp",
305       },
306       // multiline macro
307       {
308           R"cpp(
309         #define \
310                 BAR
311         [[BAR]])cpp",
312           R"cpp(#line 0 ".*main.cpp"
313 #undef BAR
314 #line 3
315 #define         BAR
316 )cpp",
317       },
318   };
319 
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));
325 
326     auto AST = createPatchedAST("", Modified.code());
327     ASSERT_TRUE(AST);
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});
332     }
333     EXPECT_THAT(MacroRefRanges, Contains(Modified.range()));
334   }
335 }
336 
337 TEST(PreamblePatchTest, OrderingPreserved) {
338   llvm::StringLiteral Baseline = "#define BAR(X) X";
339   Annotations Modified(R"cpp(
340     #define BAR(X, Y) X Y
341     #define BAR(X) X
342     [[BAR]](int y);
343   )cpp");
344 
345   llvm::StringLiteral ExpectedPatch(R"cpp(#line 0 ".*main.cpp"
346 #undef BAR
347 #line 2
348 #define     BAR\(X, Y\) X Y
349 #undef BAR
350 #line 3
351 #define     BAR\(X\) X
352 )cpp");
353   EXPECT_THAT(getPreamblePatch(Baseline, Modified.code()),
354               MatchesRegex(ExpectedPatch.str()));
355 
356   auto AST = createPatchedAST(Baseline, Modified.code());
357   ASSERT_TRUE(AST);
358 }
359 
360 TEST(PreamblePatchTest, LocateMacroAtWorks) {
361   struct {
362     const char *const Baseline;
363     const char *const Modified;
364   } Cases[] = {
365       // Addition of new directive
366       {
367           "",
368           R"cpp(
369             #define $def^FOO
370             $use^FOO)cpp",
371       },
372       // Available inside preamble section
373       {
374           "",
375           R"cpp(
376             #define $def^FOO
377             #undef $use^FOO)cpp",
378       },
379       // Available after undef, as we don't patch those
380       {
381           "",
382           R"cpp(
383             #define $def^FOO
384             #undef FOO
385             $use^FOO)cpp",
386       },
387       // Identifier on a different line
388       {
389           "",
390           R"cpp(
391             #define \
392               $def^FOO
393             $use^FOO)cpp",
394       },
395       // In presence of comment tokens
396       {
397           "",
398           R"cpp(
399             #\
400               define /* FOO */\
401               /* FOO */ $def^FOO
402             $use^FOO)cpp",
403       },
404       // Moved around
405       {
406           "#define FOO",
407           R"cpp(
408             #define BAR
409             #define $def^FOO
410             $use^FOO)cpp",
411       },
412   };
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());
417     ASSERT_TRUE(AST);
418 
419     const auto &SM = AST->getSourceManager();
420     auto *MacroTok = AST->getTokens().spelledTokenContaining(
421         SM.getComposedLoc(SM.getMainFileID(), Modified.point("use")));
422     ASSERT_TRUE(MacroTok);
423 
424     auto FoundMacro = locateMacroAt(*MacroTok, AST->getPreprocessor());
425     ASSERT_TRUE(FoundMacro);
426     EXPECT_THAT(FoundMacro->Name, "FOO");
427 
428     auto MacroLoc = FoundMacro->NameLoc;
429     EXPECT_EQ(SM.getFileID(MacroLoc), SM.getMainFileID());
430     EXPECT_EQ(SM.getFileOffset(MacroLoc), Modified.point("def"));
431   }
432 }
433 
434 TEST(PreamblePatchTest, LocateMacroAtDeletion) {
435   {
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");
439 
440     auto AST = createPatchedAST(Baseline, Modified.code());
441     ASSERT_TRUE(AST);
442 
443     const auto &SM = AST->getSourceManager();
444     auto *MacroTok = AST->getTokens().spelledTokenContaining(
445         SM.getComposedLoc(SM.getMainFileID(), Modified.point()));
446     ASSERT_TRUE(MacroTok);
447 
448     auto FoundMacro = locateMacroAt(*MacroTok, AST->getPreprocessor());
449     ASSERT_TRUE(FoundMacro);
450     EXPECT_THAT(FoundMacro->Name, "FOO");
451     auto HI =
452         getHover(*AST, offsetToPosition(Modified.code(), Modified.point()),
453                  format::getLLVMStyle(), nullptr);
454     ASSERT_TRUE(HI);
455     EXPECT_THAT(HI->Definition, testing::IsEmpty());
456   }
457 
458   {
459     // Offset is valid, but underlying text is different.
460     llvm::StringLiteral Baseline = "#define FOO";
461     Annotations Modified(R"cpp(#define BAR
462     ^FOO")cpp");
463 
464     auto AST = createPatchedAST(Baseline, Modified.code());
465     ASSERT_TRUE(AST);
466 
467     auto HI = getHover(*AST, Modified.point(), format::getLLVMStyle(), nullptr);
468     ASSERT_TRUE(HI);
469     EXPECT_THAT(HI->Definition, "#define BAR");
470   }
471 }
472 
473 MATCHER_P(referenceRangeIs, R, "") { return arg.Loc.range == R; }
474 
475 TEST(PreamblePatchTest, RefsToMacros) {
476   struct {
477     const char *const Baseline;
478     const char *const Modified;
479   } Cases[] = {
480       // Newly added
481       {
482           "",
483           R"cpp(
484             #define ^FOO
485             ^[[FOO]])cpp",
486       },
487       // Moved around
488       {
489           "#define FOO",
490           R"cpp(
491             #define BAR
492             #define ^FOO
493             ^[[FOO]])cpp",
494       },
495       // Ref in preamble section
496       {
497           "",
498           R"cpp(
499             #define ^FOO
500             #undef ^FOO)cpp",
501       },
502   };
503 
504   for (const auto &Case : Cases) {
505     Annotations Modified(Case.Modified);
506     auto AST = createPatchedAST("", Modified.code());
507     ASSERT_TRUE(AST);
508 
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));
513 
514     for (const auto &P : Modified.points()) {
515       auto *MacroTok =
516           AST->getTokens().spelledTokenContaining(SM.getComposedLoc(
517               SM.getMainFileID(),
518               llvm::cantFail(positionToOffset(Modified.code(), P))));
519       ASSERT_TRUE(MacroTok);
520       EXPECT_THAT(findReferences(*AST, P, 0).References,
521                   testing::ElementsAreArray(ExpectedLocations));
522     }
523   }
524 }
525 
526 TEST(TranslatePreamblePatchLocation, Simple) {
527   auto TU = TestTU::withHeaderCode(R"cpp(
528     #line 3 "main.cpp"
529     int foo();)cpp");
530   // Presumed line/col needs to be valid in the main file.
531   TU.Code = R"cpp(// line 1
532     // line 2
533     // line 3
534     // line 4)cpp";
535   TU.Filename = "main.cpp";
536   TU.HeaderFilename = "__preamble_patch__.h";
537   TU.ImplicitHeaderGuard = false;
538 
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());
543 
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);
548 }
549 
550 TEST(PreamblePatch, ModifiedBounds) {
551   struct {
552     const char *const Baseline;
553     const char *const Modified;
554   } Cases[] = {
555       // Size increased
556       {
557           "",
558           R"cpp(
559             #define FOO
560             FOO)cpp",
561       },
562       // Stayed same
563       {"#define FOO", "#define BAR"},
564       // Got smaller
565       {
566           R"cpp(
567             #define FOO
568             #undef FOO)cpp",
569           "#define FOO"},
570   };
571 
572   for (const auto &Case : Cases) {
573     auto TU = TestTU::withCode(Case.Baseline);
574     auto BaselinePreamble = TU.preamble();
575     ASSERT_TRUE(BaselinePreamble);
576 
577     Annotations Modified(Case.Modified);
578     TU.Code = Modified.code().str();
579     MockFS FS;
580     auto PP = PreamblePatch::createFullPatch(testPath(TU.Filename),
581                                              TU.inputs(FS), *BaselinePreamble);
582 
583     IgnoreDiagnostics Diags;
584     auto CI = buildCompilerInvocation(TU.inputs(FS), Diags);
585     ASSERT_TRUE(CI);
586 
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);
592   }
593 }
594 
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);
599   ASSERT_TRUE(AST);
600 }
601 
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);
608 
609   TU.Code = Modified.str();
610   MockFS FS;
611   auto PP = PreamblePatch::createMacroPatch(testPath(TU.Filename),
612                                             TU.inputs(FS), *BaselinePreamble);
613   EXPECT_TRUE(PP.text().empty());
614 }
615 
616 ::testing::Matcher<const Diag &>
617 withNote(::testing::Matcher<Note> NoteMatcher) {
618   return Field(&Diag::Notes, ElementsAre(NoteMatcher));
619 }
620 MATCHER_P(Diag, Range, "Diag at " + llvm::to_string(Range)) {
621   return arg.Range == Range;
622 }
623 MATCHER_P2(Diag, Range, Name,
624            "Diag at " + llvm::to_string(Range) + " = [" + Name + "]") {
625   return arg.Range == Range && arg.Name == Name;
626 }
627 
628 TEST(PreamblePatch, DiagnosticsFromMainASTAreInRightPlace) {
629   {
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")));
636   }
637   {
638     // Check with additions to preamble.
639     Annotations Code("#define FOO");
640     Annotations NewCode(R"(
641 #define FOO
642 #define BAR
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")));
647   }
648 }
649 
650 TEST(PreamblePatch, DiagnosticsToPreamble) {
651   Config Cfg;
652   Cfg.Diagnostics.UnusedIncludes = Config::IncludesPolicy::Strict;
653   Cfg.Diagnostics.MissingIncludes = Config::IncludesPolicy::Strict;
654   WithContextValue WithCfg(Config::Key, std::move(Cfg));
655 
656   llvm::StringMap<std::string> AdditionalFiles;
657   AdditionalFiles["foo.h"] = "#pragma once";
658   AdditionalFiles["bar.h"] = "#pragma once";
659   {
660     Annotations Code(R"(
661 // Test comment
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")));
668   }
669   {
670     // Check with additions to preamble.
671     Annotations Code(R"(
672 // Test comment
673 [[#include "foo.h"]])");
674     Annotations NewCode(R"(
675 $bar[[#include "bar.h"]]
676 // Test comment
677 $foo[[#include "foo.h"]])");
678     auto AST = createPatchedAST(Code.code(), NewCode.code(), AdditionalFiles);
679     EXPECT_THAT(
680         AST->getDiagnostics(),
681         UnorderedElementsAre(Diag(NewCode.range("bar"), "unused-includes"),
682                              Diag(NewCode.range("foo"), "unused-includes")));
683   }
684   {
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
691 void foo();
692 #define $foo2[[FOO]] 2)");
693     auto AST = createPatchedAST(Code.code(), NewCode.code(), AdditionalFiles);
694     EXPECT_THAT(
695         AST->getDiagnostics(),
696         ElementsAre(AllOf(Diag(NewCode.range("foo2"), "-Wmacro-redefined"),
697                           withNote(Diag(NewCode.range("foo1"))))));
698   }
699 }
700 
701 TEST(PreamblePatch, TranslatesDiagnosticsInPreamble) {
702   {
703     // Check with additions to preamble.
704     Annotations Code("#include [[<foo>]]");
705     Annotations NewCode(R"(
706 #define BAR
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")));
711   }
712   {
713     // Check with removals from preamble.
714     Annotations Code(R"(
715 #define BAR
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")));
721   }
722   {
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());
728   }
729   {
730     // Picks closest line in case of multiple alternatives.
731     Annotations Code("#include [[<foo>]]");
732     Annotations NewCode(R"(
733 #define BAR
734 #include [[<foo>]]
735 #define BAR
736 #include <foo>)");
737     auto AST = createPatchedAST(Code.code(), NewCode.code());
738     EXPECT_THAT(AST->getDiagnostics(),
739                 ElementsAre(Diag(NewCode.range(), "pp_file_not_found")));
740   }
741   {
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());
747   }
748   {
749     // Multiple lines.
750     Annotations Code(R"(
751 #define BAR
752 #include [[<fo\
753 o>]])");
754     Annotations NewCode(R"(#include [[<fo\
755 o>]])");
756     auto AST = createPatchedAST(Code.code(), NewCode.code());
757     EXPECT_THAT(AST->getDiagnostics(),
758                 ElementsAre(Diag(NewCode.range(), "pp_file_not_found")));
759   }
760   {
761     // Multiple lines with change.
762     Annotations Code(R"(
763 #define BAR
764 #include <fox>
765 #include [[<fo\
766 o>]])");
767     Annotations NewCode(R"(#include <fo\
768 x>)");
769     auto AST = createPatchedAST(Code.code(), NewCode.code());
770     EXPECT_THAT(AST->getDiagnostics(), IsEmpty());
771   }
772   {
773     // Preserves notes.
774     Annotations Code(R"(
775 #define $note[[BAR]] 1
776 #define $main[[BAR]] 2)");
777     Annotations NewCode(R"(
778 #define BAZ 0
779 #define $note[[BAR]] 1
780 #define BAZ 0
781 #define $main[[BAR]] 2)");
782     auto AST = createPatchedAST(Code.code(), NewCode.code());
783     EXPECT_THAT(
784         AST->getDiagnostics(),
785         ElementsAre(AllOf(Diag(NewCode.range("main"), "-Wmacro-redefined"),
786                           withNote(Diag(NewCode.range("note"))))));
787   }
788   {
789     // Preserves diag without note.
790     Annotations Code(R"(
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());
796     EXPECT_THAT(
797         AST->getDiagnostics(),
798         ElementsAre(AllOf(Diag(NewCode.range("main"), "-Wmacro-redefined"),
799                           Field(&Diag::Notes, IsEmpty()))));
800   }
801   {
802     // Make sure orphaned notes are not promoted to diags.
803     Annotations Code(R"(
804 #define $note[[BAR]] 1
805 #define $main[[BAR]] 2)");
806     Annotations NewCode(R"(
807 #define BAZ 0
808 #define BAR 1)");
809     auto AST = createPatchedAST(Code.code(), NewCode.code());
810     EXPECT_THAT(AST->getDiagnostics(), IsEmpty());
811   }
812   {
813     Annotations Code(R"(
814 #ifndef FOO
815 #define FOO
816 void foo();
817 #endif)");
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());
824   }
825   {
826     Annotations Code(R"(
827 #ifndef FOO
828 #define FOO
829 void foo();
830 #endif)");
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"(
836 i[[nt]] xyz;
837     )");
838     auto AST = createPatchedAST(Code.code(), NewCode.code());
839     EXPECT_THAT(
840         AST->getDiagnostics(),
841         ElementsAre(Diag(NewCode.range(), "pp_unterminated_conditional")));
842   }
843 }
844 
845 MATCHER_P2(Mark, Range, Text, "") {
846   return std::tie(arg.Rng, arg.Trivia) == std::tie(Range, Text);
847 }
848 
849 TEST(PreamblePatch, MacroAndMarkHandling) {
850   {
851     Annotations Code(R"cpp(
852 #ifndef FOO
853 #define FOO
854 // Some comments
855 #pragma mark XX
856 #define BAR
857 
858 #endif)cpp");
859     Annotations NewCode(R"cpp(
860 #ifndef FOO
861 #define FOO
862 #define BAR
863 #pragma $x[[mark XX
864 ]]
865 #pragma $y[[mark YY
866 ]]
867 #define BAZ
868 
869 #endif)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")));
876   }
877 }
878 
879 TEST(PreamblePatch, PatchFileEntry) {
880   Annotations Code(R"cpp(#define FOO)cpp");
881   Annotations NewCode(R"cpp(
882 #define BAR
883 #define FOO)cpp");
884   {
885     auto AST = createPatchedAST(Code.code(), Code.code());
886     EXPECT_EQ(
887         PreamblePatch::getPatchEntry(AST->tuPath(), AST->getSourceManager()),
888         nullptr);
889   }
890   {
891     auto AST = createPatchedAST(Code.code(), NewCode.code());
892     auto FE =
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()));
897   }
898 }
899 
900 } // namespace
901 } // namespace clangd
902 } // namespace clang
903