clang-tools 20.0.0git
PreambleTests.cpp
Go to the documentation of this file.
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
43using testing::AllOf;
44using testing::Contains;
45using testing::ElementsAre;
46using testing::Field;
47using testing::IsEmpty;
48using testing::Matcher;
49using testing::MatchesRegex;
50using testing::UnorderedElementsAre;
51using testing::UnorderedElementsAreArray;
52
53namespace clang {
54namespace clangd {
55namespace {
56
57MATCHER_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.
63IncludeStructure
64collectPatchedIncludes(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.
110TEST(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
156TEST(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)
170 EXPECT_THAT(Includes, ElementsAre(AllOf(Field(&Inclusion::Written, "<d.h>"),
172}
173
174TEST(PreamblePatchTest, MainFileIsEscaped) {
175 auto Includes = collectPatchedIncludes("#include <a.h>", "", "file\"name.cpp")
177 EXPECT_THAT(Includes, ElementsAre(AllOf(Field(&Inclusion::Written, "<a.h>"),
179}
180
181TEST(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\""),
216 Field(&Inclusion::HeaderID, testing::Not(testing::Eq(std::nullopt))),
217 Field(&Inclusion::FileKind, SrcMgr::CharacteristicKind::C_User))));
218}
219
220std::optional<ParsedAST>
221createPatchedAST(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
243std::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
258TEST(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()
271 EXPECT_TRUE(!Includes.empty());
272 EXPECT_EQ(Includes, TestTU::withCode(Baseline)
273 .build()
274 .getIncludeStructure()
275 .MainFileIncludes);
276}
277
278TEST(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
337TEST(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
360TEST(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
434TEST(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
473MATCHER_P(referenceRangeIs, R, "") { return arg.Loc.range == R; }
474
475TEST(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
526TEST(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
550TEST(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
595TEST(PreamblePatch, MacroLoc) {
596 llvm::StringLiteral Baseline = "\n#define MACRO 12\nint num = MACRO;";
597 llvm::StringLiteral Modified = " \n#define MACRO 12\nint num = MACRO;";
598 auto AST = createPatchedAST(Baseline, Modified);
599 ASSERT_TRUE(AST);
600}
601
602TEST(PreamblePatch, NoopWhenNotRequested) {
603 llvm::StringLiteral Baseline = "#define M\nint num = M;";
604 llvm::StringLiteral Modified = "#define M\n#include <foo.h>\nint num = M;";
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 &>
617withNote(::testing::Matcher<Note> NoteMatcher) {
618 return Field(&Diag::Notes, ElementsAre(NoteMatcher));
619}
620MATCHER_P(Diag, Range, "Diag at " + llvm::to_string(Range)) {
621 return arg.Range == Range;
622}
623MATCHER_P2(Diag, Range, Name,
624 "Diag at " + llvm::to_string(Range) + " = [" + Name + "]") {
625 return arg.Range == Range && arg.Name == Name;
626}
627
628TEST(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
650TEST(PreamblePatch, DiagnosticsToPreamble) {
651 Config Cfg;
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
691void 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
701TEST(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\
753o>]])");
754 Annotations NewCode(R"(#include [[<fo\
755o>]])");
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\
766o>]])");
767 Annotations NewCode(R"(#include <fo\
768x>)");
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
816void 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
829void 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"(
836i[[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
845MATCHER_P2(Mark, Range, Text, "") {
846 return std::tie(arg.Rng, arg.Trivia) == std::tie(Range, Text);
847}
848
849TEST(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
879TEST(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
const Expr * E
llvm::SmallString< 256U > Name
std::string Code
std::unique_ptr< CompilerInstance > Clang
CharSourceRange Range
SourceRange for the file name.
FieldAction Action
PreambleBounds Bounds
Definition: Preamble.cpp:334
const google::protobuf::Message & M
Definition: Server.cpp:309
std::unique_ptr< CompilerInvocation > CI
std::vector< Inclusion > MainFileIncludes
Definition: Headers.h:174
void collect(const CompilerInstance &CI)
Definition: Headers.cpp:178
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.
Definition: ParsedAST.cpp:403
static OptionalFileEntryRef getPatchEntry(llvm::StringRef MainFilePath, const SourceManager &SM)
Returns the FileEntry for the preamble patch of MainFilePath in SM, if any.
Definition: Preamble.cpp:981
llvm::StringRef text() const
Returns textual patch contents.
Definition: Preamble.h:212
static PreamblePatch createMacroPatch(llvm::StringRef FileName, const ParseInputs &Modified, const PreambleData &Baseline)
Definition: Preamble.cpp:926
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...
Definition: Preamble.cpp:920
static constexpr llvm::StringLiteral HeaderName
Definition: Preamble.h:217
const NamedDecl & findDecl(ParsedAST &AST, llvm::StringRef QName)
Definition: TestTU.cpp:219
Position offsetToPosition(llvm::StringRef Code, size_t Offset)
Turn an offset in Code into a [line, column] pair.
Definition: SourceCode.cpp:202
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.
Definition: Compiler.cpp:95
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.
MATCHER_P(named, N, "")
ReferencesResult findReferences(ParsedAST &AST, Position Pos, uint32_t Limit, const SymbolIndex *Index, bool AddContext)
Returns references of the symbol at a specified Pos.
Definition: XRefs.cpp:1376
std::string testPath(PathRef File, llvm::sys::path::Style Style)
Definition: TestFS.cpp:93
std::optional< DefinedMacro > locateMacroAt(const syntax::Token &SpelledTok, Preprocessor &PP)
Gets the macro referenced by SpelledTok.
Definition: SourceCode.cpp:999
std::optional< HoverInfo > getHover(ParsedAST &AST, Position Pos, const format::FormatStyle &Style, const SymbolIndex *Index)
Get the hover information when hovering at Pos.
Definition: Hover.cpp:1283
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.
Definition: Preamble.cpp:591
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)
Definition: Compiler.cpp:129
llvm::Expected< size_t > positionToOffset(llvm::StringRef Code, Position P, bool AllowColumnsBeyondLineLength)
Turn a [line, column] pair into an offset in Code.
Definition: SourceCode.cpp:173
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static clangd::Key< Config > Key
Context key which can be used to set the current Config.
Definition: Config.h:48
@ Strict
Diagnose missing and unused includes.
IncludesPolicy UnusedIncludes
Definition: Config.h:110
IncludesPolicy MissingIncludes
Definition: Config.h:111
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.
Definition: Diagnostics.h:109
std::string Written
Definition: Headers.h:70
SrcMgr::CharacteristicKind FileKind
Definition: Headers.h:74
std::optional< unsigned > HeaderID
Definition: Headers.h:75
static TestTU withHeaderCode(llvm::StringRef HeaderCode)
Definition: TestTU.h:42
static TestTU withCode(llvm::StringRef Code)
Definition: TestTU.h:36
std::shared_ptr< const PreambleData > preamble(PreambleParsedCallback PreambleCallback=nullptr) const
Definition: TestTU.cpp:100