[clang][modules] Don't prevent translation of FW_Private includes when explicitly...
[llvm-project.git] / clang-tools-extra / clangd / unittests / PreambleTests.cpp
blob6420516e78557672aae2301d3b0c06fca62ff7e0
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 //===----------------------------------------------------------------------===//
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>
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;
53 namespace clang {
54 namespace clangd {
55 namespace {
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.
63 IncludeStructure
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 {};
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 {};
104 Action.EndSourceFile();
105 return Includes;
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[] = {
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",
142 for (const auto &Case : Cases) {
143 Annotations Test(Case);
144 const auto Code = Test.code();
145 SCOPED_TRACE(Code);
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);
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))));
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))));
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))));
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;
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;
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";
248 return "";
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();
258 TEST(PreamblePatchTest, IncludesArePreserved) {
259 llvm::StringLiteral Baseline = R"(//error-ok
260 #include <foo>
261 #include <bar>
263 llvm::StringLiteral Modified = R"(//error-ok
264 #include <foo>
265 #include <bar>
266 #define FOO)";
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);
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[] = {
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",
294 // multiline macro
296 R"cpp(
297 #define BAR \
299 [[BAR]])cpp",
300 R"cpp(#line 0 ".*main.cpp"
301 #undef BAR
302 #line 2
303 #define BAR
304 )cpp",
306 // multiline macro
308 R"cpp(
309 #define \
311 [[BAR]])cpp",
312 R"cpp(#line 0 ".*main.cpp"
313 #undef BAR
314 #line 3
315 #define BAR
316 )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());
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});
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
341 #define BAR(X) X
342 [[BAR]](int y);
343 )cpp");
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()));
356 auto AST = createPatchedAST(Baseline, Modified.code());
357 ASSERT_TRUE(AST);
360 TEST(PreamblePatchTest, LocateMacroAtWorks) {
361 struct {
362 const char *const Baseline;
363 const char *const Modified;
364 } Cases[] = {
365 // Addition of new directive
368 R"cpp(
369 #define $def^FOO
370 $use^FOO)cpp",
372 // Available inside preamble section
375 R"cpp(
376 #define $def^FOO
377 #undef $use^FOO)cpp",
379 // Available after undef, as we don't patch those
382 R"cpp(
383 #define $def^FOO
384 #undef FOO
385 $use^FOO)cpp",
387 // Identifier on a different line
390 R"cpp(
391 #define \
392 $def^FOO
393 $use^FOO)cpp",
395 // In presence of comment tokens
398 R"cpp(
400 define /* FOO */\
401 /* FOO */ $def^FOO
402 $use^FOO)cpp",
404 // Moved around
406 "#define FOO",
407 R"cpp(
408 #define BAR
409 #define $def^FOO
410 $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());
417 ASSERT_TRUE(AST);
419 const auto &SM = AST->getSourceManager();
420 auto *MacroTok = AST->getTokens().spelledTokenAt(
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());
441 ASSERT_TRUE(AST);
443 const auto &SM = AST->getSourceManager();
444 auto *MacroTok = AST->getTokens().spelledTokenAt(
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");
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());
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");
464 auto AST = createPatchedAST(Baseline, Modified.code());
465 ASSERT_TRUE(AST);
467 auto HI = getHover(*AST, Modified.point(), format::getLLVMStyle(), nullptr);
468 ASSERT_TRUE(HI);
469 EXPECT_THAT(HI->Definition, "#define BAR");
473 MATCHER_P(referenceRangeIs, R, "") { return arg.Loc.range == R; }
475 TEST(PreamblePatchTest, RefsToMacros) {
476 struct {
477 const char *const Baseline;
478 const char *const Modified;
479 } Cases[] = {
480 // Newly added
483 R"cpp(
484 #define ^FOO
485 ^[[FOO]])cpp",
487 // Moved around
489 "#define FOO",
490 R"cpp(
491 #define BAR
492 #define ^FOO
493 ^[[FOO]])cpp",
495 // Ref in preamble section
498 R"cpp(
499 #define ^FOO
500 #undef ^FOO)cpp",
504 for (const auto &Case : Cases) {
505 Annotations Modified(Case.Modified);
506 auto AST = createPatchedAST("", Modified.code());
507 ASSERT_TRUE(AST);
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(
516 SM.getMainFileID(),
517 llvm::cantFail(positionToOffset(Modified.code(), P))));
518 ASSERT_TRUE(MacroTok);
519 EXPECT_THAT(findReferences(*AST, P, 0).References,
520 testing::ElementsAreArray(ExpectedLocations));
525 TEST(TranslatePreamblePatchLocation, Simple) {
526 auto TU = TestTU::withHeaderCode(R"cpp(
527 #line 3 "main.cpp"
528 int foo();)cpp");
529 // Presumed line/col needs to be valid in the main file.
530 TU.Code = R"cpp(// line 1
531 // line 2
532 // line 3
533 // line 4)cpp";
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();
540 auto &ND = findDecl(AST, "foo");
541 EXPECT_NE(SM.getFileID(ND.getLocation()), SM.getMainFileID());
543 auto TranslatedLoc = translatePreamblePatchLocation(ND.getLocation(), SM);
544 auto DecompLoc = SM.getDecomposedLoc(TranslatedLoc);
545 EXPECT_EQ(DecompLoc.first, SM.getMainFileID());
546 EXPECT_EQ(SM.getLineNumber(DecompLoc.first, DecompLoc.second), 3U);
549 TEST(PreamblePatch, ModifiedBounds) {
550 struct {
551 const char *const Baseline;
552 const char *const Modified;
553 } Cases[] = {
554 // Size increased
557 R"cpp(
558 #define FOO
559 FOO)cpp",
561 // Stayed same
562 {"#define FOO", "#define BAR"},
563 // Got smaller
565 R"cpp(
566 #define FOO
567 #undef FOO)cpp",
568 "#define FOO"},
571 for (const auto &Case : Cases) {
572 auto TU = TestTU::withCode(Case.Baseline);
573 auto BaselinePreamble = TU.preamble();
574 ASSERT_TRUE(BaselinePreamble);
576 Annotations Modified(Case.Modified);
577 TU.Code = Modified.code().str();
578 MockFS FS;
579 auto PP = PreamblePatch::createFullPatch(testPath(TU.Filename),
580 TU.inputs(FS), *BaselinePreamble);
582 IgnoreDiagnostics Diags;
583 auto CI = buildCompilerInvocation(TU.inputs(FS), Diags);
584 ASSERT_TRUE(CI);
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);
594 TEST(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);
598 ASSERT_TRUE(AST);
601 TEST(PreamblePatch, NoopWhenNotRequested) {
602 llvm::StringLiteral Baseline = "#define M\nint num = M;";
603 llvm::StringLiteral Modified = "#define M\n#include <foo.h>\nint num = M;";
604 auto TU = TestTU::withCode(Baseline);
605 auto BaselinePreamble = TU.preamble();
606 ASSERT_TRUE(BaselinePreamble);
608 TU.Code = Modified.str();
609 MockFS FS;
610 auto PP = PreamblePatch::createMacroPatch(testPath(TU.Filename),
611 TU.inputs(FS), *BaselinePreamble);
612 EXPECT_TRUE(PP.text().empty());
615 ::testing::Matcher<const Diag &>
616 withNote(::testing::Matcher<Note> NoteMatcher) {
617 return Field(&Diag::Notes, ElementsAre(NoteMatcher));
619 MATCHER_P(Diag, Range, "Diag at " + llvm::to_string(Range)) {
620 return arg.Range == Range;
622 MATCHER_P2(Diag, Range, Name,
623 "Diag at " + llvm::to_string(Range) + " = [" + Name + "]") {
624 return arg.Range == Range && arg.Name == Name;
627 TEST(PreamblePatch, DiagnosticsFromMainASTAreInRightPlace) {
629 Annotations Code("#define FOO");
630 // Check with removals from preamble.
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")));
637 // Check with additions to preamble.
638 Annotations Code("#define FOO");
639 Annotations NewCode(R"(
640 #define FOO
641 #define BAR
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")));
649 TEST(PreamblePatch, DiagnosticsToPreamble) {
650 Config Cfg;
651 Cfg.Diagnostics.UnusedIncludes = Config::IncludesPolicy::Strict;
652 Cfg.Diagnostics.MissingIncludes = Config::IncludesPolicy::Strict;
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";
659 Annotations Code(R"(
660 // Test comment
661 [[#include "foo.h"]])");
662 // Check with removals from preamble.
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")));
669 // Check with additions to preamble.
670 Annotations Code(R"(
671 // Test comment
672 [[#include "foo.h"]])");
673 Annotations NewCode(R"(
674 $bar[[#include "bar.h"]]
675 // Test comment
676 $foo[[#include "foo.h"]])");
677 auto AST = createPatchedAST(Code.code(), NewCode.code(), AdditionalFiles);
678 EXPECT_THAT(
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");
685 // Check ranges for notes.
686 // This also makes sure we don't generate missing-include diagnostics
687 // because macros are redefined in preamble-patch.
688 Annotations NewCode(R"(#define BARXYZ 1
689 #define $foo1[[FOO]] 1
690 void foo();
691 #define $foo2[[FOO]] 2)");
692 auto AST = createPatchedAST(Code.code(), NewCode.code(), AdditionalFiles);
693 EXPECT_THAT(
694 AST->getDiagnostics(),
695 ElementsAre(AllOf(Diag(NewCode.range("foo2"), "-Wmacro-redefined"),
696 withNote(Diag(NewCode.range("foo1"))))));
700 TEST(PreamblePatch, TranslatesDiagnosticsInPreamble) {
702 // Check with additions to preamble.
703 Annotations Code("#include [[<foo>]]");
704 Annotations NewCode(R"(
705 #define BAR
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")));
712 // Check with removals from preamble.
713 Annotations Code(R"(
714 #define BAR
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")));
722 // Drop line with diags.
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());
729 // Picks closest line in case of multiple alternatives.
730 Annotations Code("#include [[<foo>]]");
731 Annotations NewCode(R"(
732 #define BAR
733 #include [[<foo>]]
734 #define BAR
735 #include <foo>)");
736 auto AST = createPatchedAST(Code.code(), NewCode.code());
737 EXPECT_THAT(AST->getDiagnostics(),
738 ElementsAre(Diag(NewCode.range(), "pp_file_not_found")));
741 // Drop diag if line spelling has changed.
742 Annotations Code("#include [[<foo>]]");
743 Annotations NewCode(" # include <foo>");
744 auto AST = createPatchedAST(Code.code(), NewCode.code());
745 EXPECT_THAT(AST->getDiagnostics(), IsEmpty());
748 // Multiple lines.
749 Annotations Code(R"(
750 #define BAR
751 #include [[<fo\
752 o>]])");
753 Annotations NewCode(R"(#include [[<fo\
754 o>]])");
755 auto AST = createPatchedAST(Code.code(), NewCode.code());
756 EXPECT_THAT(AST->getDiagnostics(),
757 ElementsAre(Diag(NewCode.range(), "pp_file_not_found")));
760 // Multiple lines with change.
761 Annotations Code(R"(
762 #define BAR
763 #include <fox>
764 #include [[<fo\
765 o>]])");
766 Annotations NewCode(R"(#include <fo\
767 x>)");
768 auto AST = createPatchedAST(Code.code(), NewCode.code());
769 EXPECT_THAT(AST->getDiagnostics(), IsEmpty());
772 // Preserves notes.
773 Annotations Code(R"(
774 #define $note[[BAR]] 1
775 #define $main[[BAR]] 2)");
776 Annotations NewCode(R"(
777 #define BAZ 0
778 #define $note[[BAR]] 1
779 #define BAZ 0
780 #define $main[[BAR]] 2)");
781 auto AST = createPatchedAST(Code.code(), NewCode.code());
782 EXPECT_THAT(
783 AST->getDiagnostics(),
784 ElementsAre(AllOf(Diag(NewCode.range("main"), "-Wmacro-redefined"),
785 withNote(Diag(NewCode.range("note"))))));
788 // Preserves diag without note.
789 Annotations Code(R"(
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());
795 EXPECT_THAT(
796 AST->getDiagnostics(),
797 ElementsAre(AllOf(Diag(NewCode.range("main"), "-Wmacro-redefined"),
798 Field(&Diag::Notes, IsEmpty()))));
801 // Make sure orphaned notes are not promoted to diags.
802 Annotations Code(R"(
803 #define $note[[BAR]] 1
804 #define $main[[BAR]] 2)");
805 Annotations NewCode(R"(
806 #define BAZ 0
807 #define BAR 1)");
808 auto AST = createPatchedAST(Code.code(), NewCode.code());
809 EXPECT_THAT(AST->getDiagnostics(), IsEmpty());
812 Annotations Code(R"(
813 #ifndef FOO
814 #define FOO
815 void foo();
816 #endif)");
817 // This code will emit a diagnostic for unterminated #ifndef (as stale
818 // preamble has the conditional but main file doesn't terminate it).
819 // We shouldn't emit any diagnotiscs (and shouldn't crash).
820 Annotations NewCode("");
821 auto AST = createPatchedAST(Code.code(), NewCode.code());
822 EXPECT_THAT(AST->getDiagnostics(), IsEmpty());
825 Annotations Code(R"(
826 #ifndef FOO
827 #define FOO
828 void foo();
829 #endif)");
830 // This code will emit a diagnostic for unterminated #ifndef (as stale
831 // preamble has the conditional but main file doesn't terminate it).
832 // We shouldn't emit any diagnotiscs (and shouldn't crash).
833 // FIXME: Patch/ignore diagnostics in such cases.
834 Annotations NewCode(R"(
835 i[[nt]] xyz;
836 )");
837 auto AST = createPatchedAST(Code.code(), NewCode.code());
838 EXPECT_THAT(
839 AST->getDiagnostics(),
840 ElementsAre(Diag(NewCode.range(), "pp_unterminated_conditional")));
844 MATCHER_P2(Mark, Range, Text, "") {
845 return std::tie(arg.Rng, arg.Trivia) == std::tie(Range, Text);
848 TEST(PreamblePatch, MacroAndMarkHandling) {
850 Annotations Code(R"cpp(
851 #ifndef FOO
852 #define FOO
853 // Some comments
854 #pragma mark XX
855 #define BAR
857 #endif)cpp");
858 Annotations NewCode(R"cpp(
859 #ifndef FOO
860 #define FOO
861 #define BAR
862 #pragma $x[[mark XX
864 #pragma $y[[mark YY
866 #define BAZ
868 #endif)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")));
878 TEST(PreamblePatch, PatchFileEntry) {
879 Annotations Code(R"cpp(#define FOO)cpp");
880 Annotations NewCode(R"cpp(
881 #define BAR
882 #define FOO)cpp");
884 auto AST = createPatchedAST(Code.code(), Code.code());
885 EXPECT_EQ(
886 PreamblePatch::getPatchEntry(AST->tuPath(), AST->getSourceManager()),
887 nullptr);
890 auto AST = createPatchedAST(Code.code(), NewCode.code());
891 auto FE =
892 PreamblePatch::getPatchEntry(AST->tuPath(), AST->getSourceManager());
893 ASSERT_NE(FE, std::nullopt);
894 EXPECT_THAT(FE->getName().str(),
895 testing::EndsWith(PreamblePatch::HeaderName.str()));
899 } // namespace
900 } // namespace clangd
901 } // namespace clang