clang-tools 24.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
57// Builds a preamble for BaselineContents, patches it for ModifiedContents and
58// returns the includes in the patch.
60collectPatchedIncludes(llvm::StringRef ModifiedContents,
61 llvm::StringRef BaselineContents,
62 llvm::StringRef MainFileName = "main.cpp") {
63 MockFS FS;
64 auto TU = TestTU::withCode(BaselineContents);
65 TU.Filename = MainFileName.str();
66 // ms-compatibility changes meaning of #import, make sure it is turned off.
67 TU.ExtraArgs = {"-fno-ms-compatibility"};
68 auto BaselinePreamble = TU.preamble();
69 // Create the patch.
70 TU.Code = ModifiedContents.str();
71 auto PI = TU.inputs(FS);
72 auto PP = PreamblePatch::createFullPatch(testPath(TU.Filename), PI,
73 *BaselinePreamble);
74 // Collect patch contents.
76 auto CI = buildCompilerInvocation(PI, Diags);
77 PP.apply(*CI);
78 // Run preprocessor over the modified contents with patched Invocation. We
79 // provide a preamble and trim contents to ensure only the implicit header
80 // introduced by the patch is parsed and nothing else.
81 // We don't run PP directly over the patch cotents to test production
82 // behaviour.
83 auto Bounds = Lexer::ComputePreamble(ModifiedContents, CI->getLangOpts());
84 auto Clang =
85 prepareCompilerInstance(std::move(CI), &BaselinePreamble->Preamble,
86 llvm::MemoryBuffer::getMemBufferCopy(
87 ModifiedContents.slice(0, Bounds.Size).str()),
88 PI.TFS->view(PI.CompileCommand.Directory), Diags);
89 PreprocessOnlyAction Action;
90 if (!Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0])) {
91 ADD_FAILURE() << "failed begin source file";
92 return {};
93 }
94 IncludeStructure Includes;
95 Includes.collect(*Clang);
96 if (llvm::Error Err = Action.Execute()) {
97 ADD_FAILURE() << "failed to execute action: " << std::move(Err);
98 return {};
99 }
100 Action.EndSourceFile();
101 return Includes;
102}
103
104// Check preamble lexing logic by building an empty preamble and patching it
105// with all the contents.
106TEST(PreamblePatchTest, IncludeParsing) {
107 // We expect any line with a point to show up in the patch.
108 llvm::StringRef Cases[] = {
109 // Only preamble
110 R"cpp(^#include "a.h")cpp",
111 // Both preamble and mainfile
112 R"cpp(
113 ^#include "a.h"
114 garbage, finishes preamble
115 #include "a.h")cpp",
116 // Mixed directives
117 R"cpp(
118 ^#include "a.h"
119 #pragma directive
120 // some comments
121 ^#include_next <a.h>
122 #ifdef skipped
123 ^#import "a.h"
124 #endif)cpp",
125 // Broken directives
126 R"cpp(
127 #include "a
128 ^#include "a.h"
129 #include <b
130 ^#include <b.h>)cpp",
131 // Directive is not part of preamble if it is not the token immediately
132 // followed by the hash (#).
133 R"cpp(
134 ^#include "a.h"
135 #/**/include <b.h>)cpp",
136 };
137
138 for (const auto &Case : Cases) {
139 Annotations Test(Case);
140 const auto Code = Test.code();
141 SCOPED_TRACE(Code);
142
143 auto Includes =
144 collectPatchedIncludes(Code, /*BaselineContents=*/"").MainFileIncludes;
145 auto Points = Test.points();
146 ASSERT_EQ(Includes.size(), Points.size());
147 for (size_t I = 0, E = Includes.size(); I != E; ++I)
148 EXPECT_EQ(Includes[I].HashLine, Points[I].line);
149 }
150}
151
152TEST(PreamblePatchTest, ContainsNewIncludes) {
153 constexpr llvm::StringLiteral BaselineContents = R"cpp(
154 #include <a.h>
155 #include <b.h> // This will be removed
156 #include <c.h>
157 )cpp";
158 constexpr llvm::StringLiteral ModifiedContents = R"cpp(
159 #include <a.h>
160 #include <c.h> // This has changed a line.
161 #include <c.h> // This is a duplicate.
162 #include <d.h> // This is newly introduced.
163 )cpp";
164 auto Includes = collectPatchedIncludes(ModifiedContents, BaselineContents)
165 .MainFileIncludes;
166 EXPECT_THAT(Includes, ElementsAre(AllOf(Field(&Inclusion::Written, "<d.h>"),
168}
169
170TEST(PreamblePatchTest, PatchesPreambleIncludes) {
171 MockFS FS;
172 IgnoreDiagnostics Diags;
173 auto TU = TestTU::withCode(R"cpp(
174 #include "a.h" // IWYU pragma: keep
175 #include "c.h"
176 #ifdef FOO
177 #include "d.h"
178 #endif
179 )cpp");
180 TU.AdditionalFiles["a.h"] = "#include \"b.h\"";
181 TU.AdditionalFiles["b.h"] = "";
182 TU.AdditionalFiles["c.h"] = "";
183 auto PI = TU.inputs(FS);
184 auto BaselinePreamble = buildPreamble(
185 TU.Filename, *buildCompilerInvocation(PI, Diags), PI, true, nullptr);
186 // We drop c.h from modified and add a new header. Since the latter is patched
187 // we should only get a.h in preamble includes. d.h shouldn't be part of the
188 // preamble, as it's coming from a disabled region.
189 TU.Code = R"cpp(
190 #include "a.h"
191 #include "b.h"
192 #ifdef FOO
193 #include "d.h"
194 #endif
195 )cpp";
196 auto PP = PreamblePatch::createFullPatch(testPath(TU.Filename), TU.inputs(FS),
197 *BaselinePreamble);
198 // Only a.h should exists in the preamble, as c.h has been dropped and b.h was
199 // newly introduced.
200 EXPECT_THAT(
201 PP.preambleIncludes(),
202 ElementsAre(AllOf(
203 Field(&Inclusion::Written, "\"a.h\""),
205 Field(&Inclusion::HeaderID, testing::Not(testing::Eq(std::nullopt))),
206 Field(&Inclusion::FileKind, SrcMgr::CharacteristicKind::C_User))));
207}
208
209std::optional<ParsedAST>
210createPatchedAST(llvm::StringRef Baseline, llvm::StringRef Modified,
211 llvm::StringMap<std::string> AdditionalFiles = {}) {
212 auto TU = TestTU::withCode(Baseline);
213 TU.AdditionalFiles = std::move(AdditionalFiles);
214 auto BaselinePreamble = TU.preamble();
215 if (!BaselinePreamble) {
216 ADD_FAILURE() << "Failed to build baseline preamble";
217 return std::nullopt;
218 }
219
220 IgnoreDiagnostics Diags;
221 MockFS FS;
222 TU.Code = Modified.str();
223 auto CI = buildCompilerInvocation(TU.inputs(FS), Diags);
224 if (!CI) {
225 ADD_FAILURE() << "Failed to build compiler invocation";
226 return std::nullopt;
227 }
228 return ParsedAST::build(testPath(TU.Filename), TU.inputs(FS), std::move(CI),
229 {}, BaselinePreamble);
230}
231
232std::string getPreamblePatch(llvm::StringRef Baseline,
233 llvm::StringRef Modified) {
234 auto BaselinePreamble = TestTU::withCode(Baseline).preamble();
235 if (!BaselinePreamble) {
236 ADD_FAILURE() << "Failed to build baseline preamble";
237 return "";
238 }
239 MockFS FS;
240 auto TU = TestTU::withCode(Modified);
241 return PreamblePatch::createFullPatch(testPath("main.cpp"), TU.inputs(FS),
242 *BaselinePreamble)
243 .text()
244 .str();
245}
246
247TEST(PreamblePatchTest, IncludesArePreserved) {
248 llvm::StringLiteral Baseline = R"(//error-ok
249#include <foo>
250#include <bar>
251)";
252 llvm::StringLiteral Modified = R"(//error-ok
253#include <foo>
254#include <bar>
255#define FOO)";
256
257 auto Includes = createPatchedAST(Baseline, Modified.str())
258 ->getIncludeStructure()
259 .MainFileIncludes;
260 EXPECT_TRUE(!Includes.empty());
261 EXPECT_EQ(Includes, TestTU::withCode(Baseline)
262 .build()
263 .getIncludeStructure()
264 .MainFileIncludes);
265}
266
267TEST(PreamblePatchTest, Define) {
268 // BAR should be defined while parsing the AST.
269 struct {
270 const char *const Contents;
271 const char *const ExpectedPatch;
272 } Cases[] = {
273 {
274 R"cpp(
275 #define BAR
276 [[BAR]])cpp",
277 R"cpp(#line 0 ".*main.cpp"
278#undef BAR
279#line 2
280#define BAR
281)cpp",
282 },
283 // multiline macro
284 {
285 R"cpp(
286 #define BAR \
287
288 [[BAR]])cpp",
289 R"cpp(#line 0 ".*main.cpp"
290#undef BAR
291#line 2
292#define BAR
293)cpp",
294 },
295 // multiline macro
296 {
297 R"cpp(
298 #define \
299 BAR
300 [[BAR]])cpp",
301 R"cpp(#line 0 ".*main.cpp"
302#undef BAR
303#line 3
304#define BAR
305)cpp",
306 },
307 };
308
309 for (const auto &Case : Cases) {
310 SCOPED_TRACE(Case.Contents);
311 llvm::Annotations Modified(Case.Contents);
312 EXPECT_THAT(getPreamblePatch("", Modified.code()),
313 MatchesRegex(Case.ExpectedPatch));
314
315 auto AST = createPatchedAST("", Modified.code());
316 ASSERT_TRUE(AST);
317 std::vector<llvm::Annotations::Range> MacroRefRanges;
318 for (auto &M : AST->getMacros().MacroRefs) {
319 for (auto &O : M.getSecond())
320 MacroRefRanges.push_back({O.StartOffset, O.EndOffset});
321 }
322 EXPECT_THAT(MacroRefRanges, Contains(Modified.range()));
323 }
324}
325
326TEST(PreamblePatchTest, OrderingPreserved) {
327 llvm::StringLiteral Baseline = "#define BAR(X) X";
328 Annotations Modified(R"cpp(
329 #define BAR(X, Y) X Y
330 #define BAR(X) X
331 [[BAR]](int y);
332 )cpp");
333
334 llvm::StringLiteral ExpectedPatch(R"cpp(#line 0 ".*main.cpp"
335#undef BAR
336#line 2
337#define BAR\‍(X, Y\) X Y
338#undef BAR
339#line 3
340#define BAR\‍(X\) X
341)cpp");
342 EXPECT_THAT(getPreamblePatch(Baseline, Modified.code()),
343 MatchesRegex(ExpectedPatch.str()));
344
345 auto AST = createPatchedAST(Baseline, Modified.code());
346 ASSERT_TRUE(AST);
347}
348
349TEST(PreamblePatchTest, LocateMacroAtWorks) {
350 struct {
351 const char *const Baseline;
352 const char *const Modified;
353 } Cases[] = {
354 // Addition of new directive
355 {
356 "",
357 R"cpp(
358 #define $def^FOO
359 $use^FOO)cpp",
360 },
361 // Available inside preamble section
362 {
363 "",
364 R"cpp(
365 #define $def^FOO
366 #undef $use^FOO)cpp",
367 },
368 // Available after undef, as we don't patch those
369 {
370 "",
371 R"cpp(
372 #define $def^FOO
373 #undef FOO
374 $use^FOO)cpp",
375 },
376 // Identifier on a different line
377 {
378 "",
379 R"cpp(
380 #define \
381 $def^FOO
382 $use^FOO)cpp",
383 },
384 // In presence of comment tokens
385 {
386 "",
387 R"cpp(
388 #\
389 define /* FOO */\
390 /* FOO */ $def^FOO
391 $use^FOO)cpp",
392 },
393 // Moved around
394 {
395 "#define FOO",
396 R"cpp(
397 #define BAR
398 #define $def^FOO
399 $use^FOO)cpp",
400 },
401 };
402 for (const auto &Case : Cases) {
403 SCOPED_TRACE(Case.Modified);
404 llvm::Annotations Modified(Case.Modified);
405 auto AST = createPatchedAST(Case.Baseline, Modified.code());
406 ASSERT_TRUE(AST);
407
408 const auto &SM = AST->getSourceManager();
409 auto *MacroTok = AST->getTokens().spelledTokenContaining(
410 SM.getComposedLoc(SM.getMainFileID(), Modified.point("use")));
411 ASSERT_TRUE(MacroTok);
412
413 auto FoundMacro = locateMacroAt(*MacroTok, AST->getPreprocessor());
414 ASSERT_TRUE(FoundMacro);
415 EXPECT_THAT(FoundMacro->Name, "FOO");
416
417 auto MacroLoc = FoundMacro->NameLoc;
418 EXPECT_EQ(SM.getFileID(MacroLoc), SM.getMainFileID());
419 EXPECT_EQ(SM.getFileOffset(MacroLoc), Modified.point("def"));
420 }
421}
422
423TEST(PreamblePatchTest, LocateMacroAtDeletion) {
424 {
425 // We don't patch deleted define directives, make sure we don't crash.
426 llvm::StringLiteral Baseline = "#define FOO";
427 llvm::Annotations Modified("^FOO");
428
429 auto AST = createPatchedAST(Baseline, Modified.code());
430 ASSERT_TRUE(AST);
431
432 const auto &SM = AST->getSourceManager();
433 auto *MacroTok = AST->getTokens().spelledTokenContaining(
434 SM.getComposedLoc(SM.getMainFileID(), Modified.point()));
435 ASSERT_TRUE(MacroTok);
436
437 auto FoundMacro = locateMacroAt(*MacroTok, AST->getPreprocessor());
438 ASSERT_TRUE(FoundMacro);
439 EXPECT_THAT(FoundMacro->Name, "FOO");
440 auto HI =
441 getHover(*AST, offsetToPosition(Modified.code(), Modified.point()),
442 format::getLLVMStyle(), nullptr);
443 ASSERT_TRUE(HI);
444 EXPECT_THAT(HI->Definition, testing::IsEmpty());
445 }
446
447 {
448 // Offset is valid, but underlying text is different.
449 llvm::StringLiteral Baseline = "#define FOO";
450 Annotations Modified(R"cpp(#define BAR
451 ^FOO")cpp");
452
453 auto AST = createPatchedAST(Baseline, Modified.code());
454 ASSERT_TRUE(AST);
455
456 auto HI = getHover(*AST, Modified.point(), format::getLLVMStyle(), nullptr);
457 ASSERT_TRUE(HI);
458 EXPECT_THAT(HI->Definition, "#define BAR");
459 }
460}
461
462MATCHER_P(referenceRangeIs, R, "") { return arg.Loc.range == R; }
463
464TEST(PreamblePatchTest, RefsToMacros) {
465 struct {
466 const char *const Baseline;
467 const char *const Modified;
468 } Cases[] = {
469 // Newly added
470 {
471 "",
472 R"cpp(
473 #define ^FOO
474 ^[[FOO]])cpp",
475 },
476 // Moved around
477 {
478 "#define FOO",
479 R"cpp(
480 #define BAR
481 #define ^FOO
482 ^[[FOO]])cpp",
483 },
484 // Ref in preamble section
485 {
486 "",
487 R"cpp(
488 #define ^FOO
489 #undef ^FOO)cpp",
490 },
491 };
492
493 for (const auto &Case : Cases) {
494 Annotations Modified(Case.Modified);
495 auto AST = createPatchedAST("", Modified.code());
496 ASSERT_TRUE(AST);
497
498 const auto &SM = AST->getSourceManager();
499 std::vector<Matcher<ReferencesResult::Reference>> ExpectedLocations;
500 for (const auto &R : Modified.ranges())
501 ExpectedLocations.push_back(referenceRangeIs(R));
502
503 for (const auto &P : Modified.points()) {
504 auto *MacroTok =
505 AST->getTokens().spelledTokenContaining(SM.getComposedLoc(
506 SM.getMainFileID(),
507 llvm::cantFail(positionToOffset(Modified.code(), P))));
508 ASSERT_TRUE(MacroTok);
509 EXPECT_THAT(findReferences(*AST, P, 0).References,
510 testing::ElementsAreArray(ExpectedLocations));
511 }
512 }
513}
514
515TEST(TranslatePreamblePatchLocation, Simple) {
516 auto TU = TestTU::withHeaderCode(R"cpp(
517 #line 3 "main.cpp"
518 int foo();)cpp");
519 // Presumed line/col needs to be valid in the main file.
520 TU.Code = R"cpp(// line 1
521 // line 2
522 // line 3
523 // line 4)cpp";
524 TU.Filename = "main.cpp";
525 TU.HeaderFilename = "__preamble_patch__.h";
526 TU.ImplicitHeaderGuard = false;
527
528 auto AST = TU.build();
529 auto &SM = AST.getSourceManager();
530 auto &ND = findDecl(AST, "foo");
531 EXPECT_NE(SM.getFileID(ND.getLocation()), SM.getMainFileID());
532
533 auto TranslatedLoc = translatePreamblePatchLocation(ND.getLocation(), SM);
534 auto DecompLoc = SM.getDecomposedLoc(TranslatedLoc);
535 EXPECT_EQ(DecompLoc.first, SM.getMainFileID());
536 EXPECT_EQ(SM.getLineNumber(DecompLoc.first, DecompLoc.second), 3U);
537}
538
539TEST(PreamblePatch, ModifiedBounds) {
540 struct {
541 const char *const Baseline;
542 const char *const Modified;
543 } Cases[] = {
544 // Size increased
545 {
546 "",
547 R"cpp(
548 #define FOO
549 FOO)cpp",
550 },
551 // Stayed same
552 {"#define FOO", "#define BAR"},
553 // Got smaller
554 {
555 R"cpp(
556 #define FOO
557 #undef FOO)cpp",
558 "#define FOO"},
559 };
560
561 for (const auto &Case : Cases) {
562 auto TU = TestTU::withCode(Case.Baseline);
563 auto BaselinePreamble = TU.preamble();
564 ASSERT_TRUE(BaselinePreamble);
565
566 Annotations Modified(Case.Modified);
567 TU.Code = Modified.code().str();
568 MockFS FS;
569 auto PP = PreamblePatch::createFullPatch(testPath(TU.Filename),
570 TU.inputs(FS), *BaselinePreamble);
571
572 IgnoreDiagnostics Diags;
573 auto CI = buildCompilerInvocation(TU.inputs(FS), Diags);
574 ASSERT_TRUE(CI);
575
576 const auto ExpectedBounds =
577 Lexer::ComputePreamble(Case.Modified, CI->getLangOpts());
578 EXPECT_EQ(PP.modifiedBounds().Size, ExpectedBounds.Size);
579 EXPECT_EQ(PP.modifiedBounds().PreambleEndsAtStartOfLine,
580 ExpectedBounds.PreambleEndsAtStartOfLine);
581 }
582}
583
584TEST(PreamblePatch, MacroLoc) {
585 llvm::StringLiteral Baseline = "\n#define MACRO 12\nint num = MACRO;";
586 llvm::StringLiteral Modified = " \n#define MACRO 12\nint num = MACRO;";
587 auto AST = createPatchedAST(Baseline, Modified);
588 ASSERT_TRUE(AST);
589}
590
591TEST(PreamblePatch, NoopWhenNotRequested) {
592 llvm::StringLiteral Baseline = "#define M\nint num = M;";
593 llvm::StringLiteral Modified = "#define M\n#include <foo.h>\nint num = M;";
594 auto TU = TestTU::withCode(Baseline);
595 auto BaselinePreamble = TU.preamble();
596 ASSERT_TRUE(BaselinePreamble);
597
598 TU.Code = Modified.str();
599 MockFS FS;
600 auto PP = PreamblePatch::createMacroPatch(testPath(TU.Filename),
601 TU.inputs(FS), *BaselinePreamble);
602 EXPECT_TRUE(PP.text().empty());
603}
604
605::testing::Matcher<const Diag &>
606withNote(::testing::Matcher<Note> NoteMatcher) {
607 return Field(&Diag::Notes, ElementsAre(NoteMatcher));
608}
609MATCHER_P(Diag, Range, "Diag at " + llvm::to_string(Range)) {
610 return arg.Range == Range;
611}
612MATCHER_P2(Diag, Range, Name,
613 "Diag at " + llvm::to_string(Range) + " = [" + Name + "]") {
614 return arg.Range == Range && arg.Name == Name;
615}
616
617TEST(PreamblePatch, DiagnosticsFromMainASTAreInRightPlace) {
618 {
619 Annotations Code("#define FOO");
620 // Check with removals from preamble.
621 Annotations NewCode("[[x]];/* error-ok */");
622 auto AST = createPatchedAST(Code.code(), NewCode.code());
623 EXPECT_THAT(AST->getDiagnostics(),
624 ElementsAre(Diag(NewCode.range(), "missing_type_specifier")));
625 }
626 {
627 // Check with additions to preamble.
628 Annotations Code("#define FOO");
629 Annotations NewCode(R"(
630#define FOO
631#define BAR
632[[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
639TEST(PreamblePatch, DiagnosticsToPreamble) {
640 Config Cfg;
642 Cfg.Diagnostics.MissingIncludes = Config::IncludesPolicy::Strict;
643 WithContextValue WithCfg(Config::Key, std::move(Cfg));
644
645 llvm::StringMap<std::string> AdditionalFiles;
646 AdditionalFiles["foo.h"] = "#pragma once";
647 AdditionalFiles["bar.h"] = "#pragma once";
648 {
649 Annotations Code(R"(
650// Test comment
651[[#include "foo.h"]])");
652 // Check with removals from preamble.
653 Annotations NewCode(R"([[# include "foo.h"]])");
654 auto AST = createPatchedAST(Code.code(), NewCode.code(), AdditionalFiles);
655 EXPECT_THAT(AST->getDiagnostics(),
656 ElementsAre(Diag(NewCode.range(), "unused-includes")));
657 }
658 {
659 // Check with additions to preamble.
660 Annotations Code(R"(
661// Test comment
662[[#include "foo.h"]])");
663 Annotations NewCode(R"(
664$bar[[#include "bar.h"]]
665// Test comment
666$foo[[#include "foo.h"]])");
667 auto AST = createPatchedAST(Code.code(), NewCode.code(), AdditionalFiles);
668 EXPECT_THAT(
669 AST->getDiagnostics(),
670 UnorderedElementsAre(Diag(NewCode.range("bar"), "unused-includes"),
671 Diag(NewCode.range("foo"), "unused-includes")));
672 }
673 {
674 Annotations Code("#define [[FOO]] 1\n");
675 // Check ranges for notes.
676 // This also makes sure we don't generate missing-include diagnostics
677 // because macros are redefined in preamble-patch.
678 Annotations NewCode(R"(#define BARXYZ 1
679#define $foo1[[FOO]] 1
680void foo();
681#define $foo2[[FOO]] 2)");
682 auto AST = createPatchedAST(Code.code(), NewCode.code(), AdditionalFiles);
683 EXPECT_THAT(
684 AST->getDiagnostics(),
685 ElementsAre(AllOf(Diag(NewCode.range("foo2"), "-Wmacro-redefined"),
686 withNote(Diag(NewCode.range("foo1"))))));
687 }
688}
689
690TEST(PreamblePatch, TranslatesDiagnosticsInPreamble) {
691 {
692 // Check with additions to preamble.
693 Annotations Code("#include [[<foo>]]");
694 Annotations NewCode(R"(
695#define BAR
696#include [[<foo>]])");
697 auto AST = createPatchedAST(Code.code(), NewCode.code());
698 EXPECT_THAT(AST->getDiagnostics(),
699 ElementsAre(Diag(NewCode.range(), "pp_file_not_found")));
700 }
701 {
702 // Check with removals from preamble.
703 Annotations Code(R"(
704#define BAR
705#include [[<foo>]])");
706 Annotations NewCode("#include [[<foo>]]");
707 auto AST = createPatchedAST(Code.code(), NewCode.code());
708 EXPECT_THAT(AST->getDiagnostics(),
709 ElementsAre(Diag(NewCode.range(), "pp_file_not_found")));
710 }
711 {
712 // Drop line with diags.
713 Annotations Code("#include [[<foo>]]");
714 Annotations NewCode("#define BAR\n#define BAZ\n");
715 auto AST = createPatchedAST(Code.code(), NewCode.code());
716 EXPECT_THAT(AST->getDiagnostics(), IsEmpty());
717 }
718 {
719 // Picks closest line in case of multiple alternatives.
720 Annotations Code("#include [[<foo>]]");
721 Annotations NewCode(R"(
722#define BAR
723#include [[<foo>]]
724#define BAR
725#include <foo>)");
726 auto AST = createPatchedAST(Code.code(), NewCode.code());
727 EXPECT_THAT(AST->getDiagnostics(),
728 ElementsAre(Diag(NewCode.range(), "pp_file_not_found")));
729 }
730 {
731 // Drop diag if line spelling has changed.
732 Annotations Code("#include [[<foo>]]");
733 Annotations NewCode(" # include <foo>");
734 auto AST = createPatchedAST(Code.code(), NewCode.code());
735 EXPECT_THAT(AST->getDiagnostics(), IsEmpty());
736 }
737 {
738 // Multiple lines.
739 Annotations Code(R"(
740#define BAR
741#include [[<fo\
742o>]])");
743 Annotations NewCode(R"(#include [[<fo\
744o>]])");
745 auto AST = createPatchedAST(Code.code(), NewCode.code());
746 EXPECT_THAT(AST->getDiagnostics(),
747 ElementsAre(Diag(NewCode.range(), "pp_file_not_found")));
748 }
749 {
750 // Multiple lines with change.
751 Annotations Code(R"(
752#define BAR
753#include <fox>
754#include [[<fo\
755o>]])");
756 Annotations NewCode(R"(#include <fo\
757x>)");
758 auto AST = createPatchedAST(Code.code(), NewCode.code());
759 EXPECT_THAT(AST->getDiagnostics(), IsEmpty());
760 }
761 {
762 // Preserves notes.
763 Annotations Code(R"(
764#define $note[[BAR]] 1
765#define $main[[BAR]] 2)");
766 Annotations NewCode(R"(
767#define BAZ 0
768#define $note[[BAR]] 1
769#define BAZ 0
770#define $main[[BAR]] 2)");
771 auto AST = createPatchedAST(Code.code(), NewCode.code());
772 EXPECT_THAT(
773 AST->getDiagnostics(),
774 ElementsAre(AllOf(Diag(NewCode.range("main"), "-Wmacro-redefined"),
775 withNote(Diag(NewCode.range("note"))))));
776 }
777 {
778 // Preserves diag without note.
779 Annotations Code(R"(
780#define $note[[BAR]] 1
781#define $main[[BAR]] 2)");
782 Annotations NewCode(R"(
783#define $main[[BAR]] 2)");
784 auto AST = createPatchedAST(Code.code(), NewCode.code());
785 EXPECT_THAT(
786 AST->getDiagnostics(),
787 ElementsAre(AllOf(Diag(NewCode.range("main"), "-Wmacro-redefined"),
788 Field(&Diag::Notes, IsEmpty()))));
789 }
790 {
791 // Make sure orphaned notes are not promoted to diags.
792 Annotations Code(R"(
793#define $note[[BAR]] 1
794#define $main[[BAR]] 2)");
795 Annotations NewCode(R"(
796#define BAZ 0
797#define BAR 1)");
798 auto AST = createPatchedAST(Code.code(), NewCode.code());
799 EXPECT_THAT(AST->getDiagnostics(), IsEmpty());
800 }
801 {
802 Annotations Code(R"(
803#ifndef FOO
804#define FOO
805void foo();
806#endif)");
807 // This code will emit a diagnostic for unterminated #ifndef (as stale
808 // preamble has the conditional but main file doesn't terminate it).
809 // We shouldn't emit any diagnotiscs (and shouldn't crash).
810 Annotations NewCode("");
811 auto AST = createPatchedAST(Code.code(), NewCode.code());
812 EXPECT_THAT(AST->getDiagnostics(), IsEmpty());
813 }
814 {
815 Annotations Code(R"(
816#ifndef FOO
817#define FOO
818void foo();
819#endif)");
820 // This code will emit a diagnostic for unterminated #ifndef (as stale
821 // preamble has the conditional but main file doesn't terminate it).
822 // We shouldn't emit any diagnotiscs (and shouldn't crash).
823 // FIXME: Patch/ignore diagnostics in such cases.
824 Annotations NewCode(R"(
825i[[nt]] xyz;
826 )");
827 auto AST = createPatchedAST(Code.code(), NewCode.code());
828 EXPECT_THAT(
829 AST->getDiagnostics(),
830 ElementsAre(Diag(NewCode.range(), "pp_unterminated_conditional")));
831 }
832}
833
834MATCHER_P2(Mark, Range, Text, "") {
835 return std::tie(arg.Rng, arg.Trivia) == std::tie(Range, Text);
836}
837
838TEST(PreamblePatch, MacroAndMarkHandling) {
839 {
840 Annotations Code(R"cpp(
841#ifndef FOO
842#define FOO
843// Some comments
844#pragma mark XX
845#define BAR
846
847#endif)cpp");
848 Annotations NewCode(R"cpp(
849#ifndef FOO
850#define FOO
851#define BAR
852#pragma $x[[mark XX
853]]
854#pragma $y[[mark YY
855]]
856#define BAZ
857
858#endif)cpp");
859 auto AST = createPatchedAST(Code.code(), NewCode.code());
860 EXPECT_THAT(AST->getMacros().Names.keys(),
861 UnorderedElementsAreArray({"FOO", "BAR", "BAZ"}));
862 EXPECT_THAT(AST->getMarks(),
863 UnorderedElementsAre(Mark(NewCode.range("x"), " XX"),
864 Mark(NewCode.range("y"), " YY")));
865 }
866}
867
868TEST(PreamblePatch, PatchFileEntry) {
869 Annotations Code(R"cpp(#define FOO)cpp");
870 Annotations NewCode(R"cpp(
871#define BAR
872#define FOO)cpp");
873 {
874 auto AST = createPatchedAST(Code.code(), Code.code());
875 EXPECT_EQ(
876 PreamblePatch::getPatchEntry(AST->tuPath(), AST->getSourceManager()),
877 nullptr);
878 }
879 {
880 auto AST = createPatchedAST(Code.code(), NewCode.code());
881 auto FE =
882 PreamblePatch::getPatchEntry(AST->tuPath(), AST->getSourceManager());
883 ASSERT_NE(FE, std::nullopt);
884 EXPECT_THAT(FE->getName().str(),
885 testing::EndsWith(PreamblePatch::HeaderName.str()));
886 }
887}
888
889} // namespace
890} // namespace clangd
891} // namespace clang
Same as llvm::Annotations, but adjusts functions to LSP-specific types for positions and ranges.
Definition Annotations.h:23
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.
Stores information required to parse a TU using a (possibly stale) Baseline preamble.
Definition Preamble.h:178
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:976
llvm::StringRef text() const
Returns textual patch contents.
Definition Preamble.h:215
static PreamblePatch createMacroPatch(llvm::StringRef FileName, const ParseInputs &Modified, const PreambleData &Baseline)
Definition Preamble.cpp:921
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:915
static constexpr llvm::StringLiteral HeaderName
Definition Preamble.h:220
WithContextValue extends Context::current() with a single value.
Definition Context.h:200
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
Definition AST.cpp:44
const NamedDecl & findDecl(ParsedAST &AST, llvm::StringRef QName)
Definition TestTU.cpp:220
Position offsetToPosition(llvm::StringRef Code, size_t Offset)
Turn an offset in Code into a [line, column] pair.
std::unique_ptr< CompilerInvocation > buildCompilerInvocation(const ParseInputs &Inputs, clang::DiagnosticConsumer &D, std::vector< std::string > *CC1Args)
Builds compiler invocation that could be used to build AST or preamble.
Definition Compiler.cpp:96
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:1677
std::string testPath(PathRef File, llvm::sys::path::Style Style)
Definition TestFS.cpp:94
std::optional< DefinedMacro > locateMacroAt(const syntax::Token &SpelledTok, Preprocessor &PP)
Gets the macro referenced by SpelledTok.
std::optional< HoverInfo > getHover(ParsedAST &AST, Position Pos, const format::FormatStyle &Style, const SymbolIndex *Index)
Get the hover information when hovering at Pos.
Definition Hover.cpp:1313
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:573
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:131
llvm::Expected< size_t > positionToOffset(llvm::StringRef Code, Position P, bool AllowColumnsBeyondLineLength)
Turn a [line, column] pair into an offset in Code.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Settings that express user/project preferences and control clangd behavior.
Definition Config.h:45
static clangd::Key< Config > Key
Context key which can be used to set the current Config.
Definition Config.h:49
@ Strict
Diagnose missing and unused includes.
Definition Config.h:99
struct clang::clangd::Config::@343034053122374337352226322054223376344037116252 Diagnostics
Controls warnings and errors when parsing code.
IncludesPolicy UnusedIncludes
Definition Config.h:117
A top-level diagnostic that may have Notes and Fixes.
Definition Diagnostics.h:98
std::vector< Note > Notes
Elaborate on the problem, usually pointing to a related piece of code.
std::string Written
Definition Headers.h:72
SrcMgr::CharacteristicKind FileKind
Definition Headers.h:76
std::optional< unsigned > HeaderID
Definition Headers.h:77
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:101