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
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.
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.
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)
169 .MainFileIncludes;
170 EXPECT_THAT(Includes, ElementsAre(AllOf(Field(&Inclusion::Written, "<d.h>"),
172}
173
174TEST(PreamblePatchTest, PatchesPreambleIncludes) {
175 MockFS FS;
176 IgnoreDiagnostics Diags;
177 auto TU = TestTU::withCode(R"cpp(
178 #include "a.h" // IWYU pragma: keep
179 #include "c.h"
180 #ifdef FOO
181 #include "d.h"
182 #endif
183 )cpp");
184 TU.AdditionalFiles["a.h"] = "#include \"b.h\"";
185 TU.AdditionalFiles["b.h"] = "";
186 TU.AdditionalFiles["c.h"] = "";
187 auto PI = TU.inputs(FS);
188 auto BaselinePreamble = buildPreamble(
189 TU.Filename, *buildCompilerInvocation(PI, Diags), PI, true, nullptr);
190 // We drop c.h from modified and add a new header. Since the latter is patched
191 // we should only get a.h in preamble includes. d.h shouldn't be part of the
192 // preamble, as it's coming from a disabled region.
193 TU.Code = R"cpp(
194 #include "a.h"
195 #include "b.h"
196 #ifdef FOO
197 #include "d.h"
198 #endif
199 )cpp";
200 auto PP = PreamblePatch::createFullPatch(testPath(TU.Filename), TU.inputs(FS),
201 *BaselinePreamble);
202 // Only a.h should exists in the preamble, as c.h has been dropped and b.h was
203 // newly introduced.
204 EXPECT_THAT(
205 PP.preambleIncludes(),
206 ElementsAre(AllOf(
207 Field(&Inclusion::Written, "\"a.h\""),
209 Field(&Inclusion::HeaderID, testing::Not(testing::Eq(std::nullopt))),
210 Field(&Inclusion::FileKind, SrcMgr::CharacteristicKind::C_User))));
211}
212
213std::optional<ParsedAST>
214createPatchedAST(llvm::StringRef Baseline, llvm::StringRef Modified,
215 llvm::StringMap<std::string> AdditionalFiles = {}) {
216 auto TU = TestTU::withCode(Baseline);
217 TU.AdditionalFiles = std::move(AdditionalFiles);
218 auto BaselinePreamble = TU.preamble();
219 if (!BaselinePreamble) {
220 ADD_FAILURE() << "Failed to build baseline preamble";
221 return std::nullopt;
222 }
223
224 IgnoreDiagnostics Diags;
225 MockFS FS;
226 TU.Code = Modified.str();
227 auto CI = buildCompilerInvocation(TU.inputs(FS), Diags);
228 if (!CI) {
229 ADD_FAILURE() << "Failed to build compiler invocation";
230 return std::nullopt;
231 }
232 return ParsedAST::build(testPath(TU.Filename), TU.inputs(FS), std::move(CI),
233 {}, BaselinePreamble);
234}
235
236std::string getPreamblePatch(llvm::StringRef Baseline,
237 llvm::StringRef Modified) {
238 auto BaselinePreamble = TestTU::withCode(Baseline).preamble();
239 if (!BaselinePreamble) {
240 ADD_FAILURE() << "Failed to build baseline preamble";
241 return "";
242 }
243 MockFS FS;
244 auto TU = TestTU::withCode(Modified);
245 return PreamblePatch::createFullPatch(testPath("main.cpp"), TU.inputs(FS),
246 *BaselinePreamble)
247 .text()
248 .str();
249}
250
251TEST(PreamblePatchTest, IncludesArePreserved) {
252 llvm::StringLiteral Baseline = R"(//error-ok
253#include <foo>
254#include <bar>
255)";
256 llvm::StringLiteral Modified = R"(//error-ok
257#include <foo>
258#include <bar>
259#define FOO)";
260
261 auto Includes = createPatchedAST(Baseline, Modified.str())
262 ->getIncludeStructure()
263 .MainFileIncludes;
264 EXPECT_TRUE(!Includes.empty());
265 EXPECT_EQ(Includes, TestTU::withCode(Baseline)
266 .build()
267 .getIncludeStructure()
268 .MainFileIncludes);
269}
270
271TEST(PreamblePatchTest, Define) {
272 // BAR should be defined while parsing the AST.
273 struct {
274 const char *const Contents;
275 const char *const ExpectedPatch;
276 } Cases[] = {
277 {
278 R"cpp(
279 #define BAR
280 [[BAR]])cpp",
281 R"cpp(#line 0 ".*main.cpp"
282#undef BAR
283#line 2
284#define BAR
285)cpp",
286 },
287 // multiline macro
288 {
289 R"cpp(
290 #define BAR \
291
292 [[BAR]])cpp",
293 R"cpp(#line 0 ".*main.cpp"
294#undef BAR
295#line 2
296#define BAR
297)cpp",
298 },
299 // multiline macro
300 {
301 R"cpp(
302 #define \
303 BAR
304 [[BAR]])cpp",
305 R"cpp(#line 0 ".*main.cpp"
306#undef BAR
307#line 3
308#define BAR
309)cpp",
310 },
311 };
312
313 for (const auto &Case : Cases) {
314 SCOPED_TRACE(Case.Contents);
315 llvm::Annotations Modified(Case.Contents);
316 EXPECT_THAT(getPreamblePatch("", Modified.code()),
317 MatchesRegex(Case.ExpectedPatch));
318
319 auto AST = createPatchedAST("", Modified.code());
320 ASSERT_TRUE(AST);
321 std::vector<llvm::Annotations::Range> MacroRefRanges;
322 for (auto &M : AST->getMacros().MacroRefs) {
323 for (auto &O : M.getSecond())
324 MacroRefRanges.push_back({O.StartOffset, O.EndOffset});
325 }
326 EXPECT_THAT(MacroRefRanges, Contains(Modified.range()));
327 }
328}
329
330TEST(PreamblePatchTest, OrderingPreserved) {
331 llvm::StringLiteral Baseline = "#define BAR(X) X";
332 Annotations Modified(R"cpp(
333 #define BAR(X, Y) X Y
334 #define BAR(X) X
335 [[BAR]](int y);
336 )cpp");
337
338 llvm::StringLiteral ExpectedPatch(R"cpp(#line 0 ".*main.cpp"
339#undef BAR
340#line 2
341#define BAR\‍(X, Y\) X Y
342#undef BAR
343#line 3
344#define BAR\‍(X\) X
345)cpp");
346 EXPECT_THAT(getPreamblePatch(Baseline, Modified.code()),
347 MatchesRegex(ExpectedPatch.str()));
348
349 auto AST = createPatchedAST(Baseline, Modified.code());
350 ASSERT_TRUE(AST);
351}
352
353TEST(PreamblePatchTest, LocateMacroAtWorks) {
354 struct {
355 const char *const Baseline;
356 const char *const Modified;
357 } Cases[] = {
358 // Addition of new directive
359 {
360 "",
361 R"cpp(
362 #define $def^FOO
363 $use^FOO)cpp",
364 },
365 // Available inside preamble section
366 {
367 "",
368 R"cpp(
369 #define $def^FOO
370 #undef $use^FOO)cpp",
371 },
372 // Available after undef, as we don't patch those
373 {
374 "",
375 R"cpp(
376 #define $def^FOO
377 #undef FOO
378 $use^FOO)cpp",
379 },
380 // Identifier on a different line
381 {
382 "",
383 R"cpp(
384 #define \
385 $def^FOO
386 $use^FOO)cpp",
387 },
388 // In presence of comment tokens
389 {
390 "",
391 R"cpp(
392 #\
393 define /* FOO */\
394 /* FOO */ $def^FOO
395 $use^FOO)cpp",
396 },
397 // Moved around
398 {
399 "#define FOO",
400 R"cpp(
401 #define BAR
402 #define $def^FOO
403 $use^FOO)cpp",
404 },
405 };
406 for (const auto &Case : Cases) {
407 SCOPED_TRACE(Case.Modified);
408 llvm::Annotations Modified(Case.Modified);
409 auto AST = createPatchedAST(Case.Baseline, Modified.code());
410 ASSERT_TRUE(AST);
411
412 const auto &SM = AST->getSourceManager();
413 auto *MacroTok = AST->getTokens().spelledTokenContaining(
414 SM.getComposedLoc(SM.getMainFileID(), Modified.point("use")));
415 ASSERT_TRUE(MacroTok);
416
417 auto FoundMacro = locateMacroAt(*MacroTok, AST->getPreprocessor());
418 ASSERT_TRUE(FoundMacro);
419 EXPECT_THAT(FoundMacro->Name, "FOO");
420
421 auto MacroLoc = FoundMacro->NameLoc;
422 EXPECT_EQ(SM.getFileID(MacroLoc), SM.getMainFileID());
423 EXPECT_EQ(SM.getFileOffset(MacroLoc), Modified.point("def"));
424 }
425}
426
427TEST(PreamblePatchTest, LocateMacroAtDeletion) {
428 {
429 // We don't patch deleted define directives, make sure we don't crash.
430 llvm::StringLiteral Baseline = "#define FOO";
431 llvm::Annotations Modified("^FOO");
432
433 auto AST = createPatchedAST(Baseline, Modified.code());
434 ASSERT_TRUE(AST);
435
436 const auto &SM = AST->getSourceManager();
437 auto *MacroTok = AST->getTokens().spelledTokenContaining(
438 SM.getComposedLoc(SM.getMainFileID(), Modified.point()));
439 ASSERT_TRUE(MacroTok);
440
441 auto FoundMacro = locateMacroAt(*MacroTok, AST->getPreprocessor());
442 ASSERT_TRUE(FoundMacro);
443 EXPECT_THAT(FoundMacro->Name, "FOO");
444 auto HI =
445 getHover(*AST, offsetToPosition(Modified.code(), Modified.point()),
446 format::getLLVMStyle(), nullptr);
447 ASSERT_TRUE(HI);
448 EXPECT_THAT(HI->Definition, testing::IsEmpty());
449 }
450
451 {
452 // Offset is valid, but underlying text is different.
453 llvm::StringLiteral Baseline = "#define FOO";
454 Annotations Modified(R"cpp(#define BAR
455 ^FOO")cpp");
456
457 auto AST = createPatchedAST(Baseline, Modified.code());
458 ASSERT_TRUE(AST);
459
460 auto HI = getHover(*AST, Modified.point(), format::getLLVMStyle(), nullptr);
461 ASSERT_TRUE(HI);
462 EXPECT_THAT(HI->Definition, "#define BAR");
463 }
464}
465
466MATCHER_P(referenceRangeIs, R, "") { return arg.Loc.range == R; }
467
468TEST(PreamblePatchTest, RefsToMacros) {
469 struct {
470 const char *const Baseline;
471 const char *const Modified;
472 } Cases[] = {
473 // Newly added
474 {
475 "",
476 R"cpp(
477 #define ^FOO
478 ^[[FOO]])cpp",
479 },
480 // Moved around
481 {
482 "#define FOO",
483 R"cpp(
484 #define BAR
485 #define ^FOO
486 ^[[FOO]])cpp",
487 },
488 // Ref in preamble section
489 {
490 "",
491 R"cpp(
492 #define ^FOO
493 #undef ^FOO)cpp",
494 },
495 };
496
497 for (const auto &Case : Cases) {
498 Annotations Modified(Case.Modified);
499 auto AST = createPatchedAST("", Modified.code());
500 ASSERT_TRUE(AST);
501
502 const auto &SM = AST->getSourceManager();
503 std::vector<Matcher<ReferencesResult::Reference>> ExpectedLocations;
504 for (const auto &R : Modified.ranges())
505 ExpectedLocations.push_back(referenceRangeIs(R));
506
507 for (const auto &P : Modified.points()) {
508 auto *MacroTok =
509 AST->getTokens().spelledTokenContaining(SM.getComposedLoc(
510 SM.getMainFileID(),
511 llvm::cantFail(positionToOffset(Modified.code(), P))));
512 ASSERT_TRUE(MacroTok);
513 EXPECT_THAT(findReferences(*AST, P, 0).References,
514 testing::ElementsAreArray(ExpectedLocations));
515 }
516 }
517}
518
519TEST(TranslatePreamblePatchLocation, Simple) {
520 auto TU = TestTU::withHeaderCode(R"cpp(
521 #line 3 "main.cpp"
522 int foo();)cpp");
523 // Presumed line/col needs to be valid in the main file.
524 TU.Code = R"cpp(// line 1
525 // line 2
526 // line 3
527 // line 4)cpp";
528 TU.Filename = "main.cpp";
529 TU.HeaderFilename = "__preamble_patch__.h";
530 TU.ImplicitHeaderGuard = false;
531
532 auto AST = TU.build();
533 auto &SM = AST.getSourceManager();
534 auto &ND = findDecl(AST, "foo");
535 EXPECT_NE(SM.getFileID(ND.getLocation()), SM.getMainFileID());
536
537 auto TranslatedLoc = translatePreamblePatchLocation(ND.getLocation(), SM);
538 auto DecompLoc = SM.getDecomposedLoc(TranslatedLoc);
539 EXPECT_EQ(DecompLoc.first, SM.getMainFileID());
540 EXPECT_EQ(SM.getLineNumber(DecompLoc.first, DecompLoc.second), 3U);
541}
542
543TEST(PreamblePatch, ModifiedBounds) {
544 struct {
545 const char *const Baseline;
546 const char *const Modified;
547 } Cases[] = {
548 // Size increased
549 {
550 "",
551 R"cpp(
552 #define FOO
553 FOO)cpp",
554 },
555 // Stayed same
556 {"#define FOO", "#define BAR"},
557 // Got smaller
558 {
559 R"cpp(
560 #define FOO
561 #undef FOO)cpp",
562 "#define FOO"},
563 };
564
565 for (const auto &Case : Cases) {
566 auto TU = TestTU::withCode(Case.Baseline);
567 auto BaselinePreamble = TU.preamble();
568 ASSERT_TRUE(BaselinePreamble);
569
570 Annotations Modified(Case.Modified);
571 TU.Code = Modified.code().str();
572 MockFS FS;
573 auto PP = PreamblePatch::createFullPatch(testPath(TU.Filename),
574 TU.inputs(FS), *BaselinePreamble);
575
576 IgnoreDiagnostics Diags;
577 auto CI = buildCompilerInvocation(TU.inputs(FS), Diags);
578 ASSERT_TRUE(CI);
579
580 const auto ExpectedBounds =
581 Lexer::ComputePreamble(Case.Modified, CI->getLangOpts());
582 EXPECT_EQ(PP.modifiedBounds().Size, ExpectedBounds.Size);
583 EXPECT_EQ(PP.modifiedBounds().PreambleEndsAtStartOfLine,
584 ExpectedBounds.PreambleEndsAtStartOfLine);
585 }
586}
587
588TEST(PreamblePatch, MacroLoc) {
589 llvm::StringLiteral Baseline = "\n#define MACRO 12\nint num = MACRO;";
590 llvm::StringLiteral Modified = " \n#define MACRO 12\nint num = MACRO;";
591 auto AST = createPatchedAST(Baseline, Modified);
592 ASSERT_TRUE(AST);
593}
594
595TEST(PreamblePatch, NoopWhenNotRequested) {
596 llvm::StringLiteral Baseline = "#define M\nint num = M;";
597 llvm::StringLiteral Modified = "#define M\n#include <foo.h>\nint num = M;";
598 auto TU = TestTU::withCode(Baseline);
599 auto BaselinePreamble = TU.preamble();
600 ASSERT_TRUE(BaselinePreamble);
601
602 TU.Code = Modified.str();
603 MockFS FS;
604 auto PP = PreamblePatch::createMacroPatch(testPath(TU.Filename),
605 TU.inputs(FS), *BaselinePreamble);
606 EXPECT_TRUE(PP.text().empty());
607}
608
609::testing::Matcher<const Diag &>
610withNote(::testing::Matcher<Note> NoteMatcher) {
611 return Field(&Diag::Notes, ElementsAre(NoteMatcher));
612}
613MATCHER_P(Diag, Range, "Diag at " + llvm::to_string(Range)) {
614 return arg.Range == Range;
615}
616MATCHER_P2(Diag, Range, Name,
617 "Diag at " + llvm::to_string(Range) + " = [" + Name + "]") {
618 return arg.Range == Range && arg.Name == Name;
619}
620
621TEST(PreamblePatch, DiagnosticsFromMainASTAreInRightPlace) {
622 {
623 Annotations Code("#define FOO");
624 // Check with removals from preamble.
625 Annotations NewCode("[[x]];/* error-ok */");
626 auto AST = createPatchedAST(Code.code(), NewCode.code());
627 EXPECT_THAT(AST->getDiagnostics(),
628 ElementsAre(Diag(NewCode.range(), "missing_type_specifier")));
629 }
630 {
631 // Check with additions to preamble.
632 Annotations Code("#define FOO");
633 Annotations NewCode(R"(
634#define FOO
635#define BAR
636[[x]];/* error-ok */)");
637 auto AST = createPatchedAST(Code.code(), NewCode.code());
638 EXPECT_THAT(AST->getDiagnostics(),
639 ElementsAre(Diag(NewCode.range(), "missing_type_specifier")));
640 }
641}
642
643TEST(PreamblePatch, DiagnosticsToPreamble) {
644 Config Cfg;
646 Cfg.Diagnostics.MissingIncludes = Config::IncludesPolicy::Strict;
647 WithContextValue WithCfg(Config::Key, std::move(Cfg));
648
649 llvm::StringMap<std::string> AdditionalFiles;
650 AdditionalFiles["foo.h"] = "#pragma once";
651 AdditionalFiles["bar.h"] = "#pragma once";
652 {
653 Annotations Code(R"(
654// Test comment
655[[#include "foo.h"]])");
656 // Check with removals from preamble.
657 Annotations NewCode(R"([[# include "foo.h"]])");
658 auto AST = createPatchedAST(Code.code(), NewCode.code(), AdditionalFiles);
659 EXPECT_THAT(AST->getDiagnostics(),
660 ElementsAre(Diag(NewCode.range(), "unused-includes")));
661 }
662 {
663 // Check with additions to preamble.
664 Annotations Code(R"(
665// Test comment
666[[#include "foo.h"]])");
667 Annotations NewCode(R"(
668$bar[[#include "bar.h"]]
669// Test comment
670$foo[[#include "foo.h"]])");
671 auto AST = createPatchedAST(Code.code(), NewCode.code(), AdditionalFiles);
672 EXPECT_THAT(
673 AST->getDiagnostics(),
674 UnorderedElementsAre(Diag(NewCode.range("bar"), "unused-includes"),
675 Diag(NewCode.range("foo"), "unused-includes")));
676 }
677 {
678 Annotations Code("#define [[FOO]] 1\n");
679 // Check ranges for notes.
680 // This also makes sure we don't generate missing-include diagnostics
681 // because macros are redefined in preamble-patch.
682 Annotations NewCode(R"(#define BARXYZ 1
683#define $foo1[[FOO]] 1
684void foo();
685#define $foo2[[FOO]] 2)");
686 auto AST = createPatchedAST(Code.code(), NewCode.code(), AdditionalFiles);
687 EXPECT_THAT(
688 AST->getDiagnostics(),
689 ElementsAre(AllOf(Diag(NewCode.range("foo2"), "-Wmacro-redefined"),
690 withNote(Diag(NewCode.range("foo1"))))));
691 }
692}
693
694TEST(PreamblePatch, TranslatesDiagnosticsInPreamble) {
695 {
696 // Check with additions to preamble.
697 Annotations Code("#include [[<foo>]]");
698 Annotations NewCode(R"(
699#define BAR
700#include [[<foo>]])");
701 auto AST = createPatchedAST(Code.code(), NewCode.code());
702 EXPECT_THAT(AST->getDiagnostics(),
703 ElementsAre(Diag(NewCode.range(), "pp_file_not_found")));
704 }
705 {
706 // Check with removals from preamble.
707 Annotations Code(R"(
708#define BAR
709#include [[<foo>]])");
710 Annotations NewCode("#include [[<foo>]]");
711 auto AST = createPatchedAST(Code.code(), NewCode.code());
712 EXPECT_THAT(AST->getDiagnostics(),
713 ElementsAre(Diag(NewCode.range(), "pp_file_not_found")));
714 }
715 {
716 // Drop line with diags.
717 Annotations Code("#include [[<foo>]]");
718 Annotations NewCode("#define BAR\n#define BAZ\n");
719 auto AST = createPatchedAST(Code.code(), NewCode.code());
720 EXPECT_THAT(AST->getDiagnostics(), IsEmpty());
721 }
722 {
723 // Picks closest line in case of multiple alternatives.
724 Annotations Code("#include [[<foo>]]");
725 Annotations NewCode(R"(
726#define BAR
727#include [[<foo>]]
728#define BAR
729#include <foo>)");
730 auto AST = createPatchedAST(Code.code(), NewCode.code());
731 EXPECT_THAT(AST->getDiagnostics(),
732 ElementsAre(Diag(NewCode.range(), "pp_file_not_found")));
733 }
734 {
735 // Drop diag if line spelling has changed.
736 Annotations Code("#include [[<foo>]]");
737 Annotations NewCode(" # include <foo>");
738 auto AST = createPatchedAST(Code.code(), NewCode.code());
739 EXPECT_THAT(AST->getDiagnostics(), IsEmpty());
740 }
741 {
742 // Multiple lines.
743 Annotations Code(R"(
744#define BAR
745#include [[<fo\
746o>]])");
747 Annotations NewCode(R"(#include [[<fo\
748o>]])");
749 auto AST = createPatchedAST(Code.code(), NewCode.code());
750 EXPECT_THAT(AST->getDiagnostics(),
751 ElementsAre(Diag(NewCode.range(), "pp_file_not_found")));
752 }
753 {
754 // Multiple lines with change.
755 Annotations Code(R"(
756#define BAR
757#include <fox>
758#include [[<fo\
759o>]])");
760 Annotations NewCode(R"(#include <fo\
761x>)");
762 auto AST = createPatchedAST(Code.code(), NewCode.code());
763 EXPECT_THAT(AST->getDiagnostics(), IsEmpty());
764 }
765 {
766 // Preserves notes.
767 Annotations Code(R"(
768#define $note[[BAR]] 1
769#define $main[[BAR]] 2)");
770 Annotations NewCode(R"(
771#define BAZ 0
772#define $note[[BAR]] 1
773#define BAZ 0
774#define $main[[BAR]] 2)");
775 auto AST = createPatchedAST(Code.code(), NewCode.code());
776 EXPECT_THAT(
777 AST->getDiagnostics(),
778 ElementsAre(AllOf(Diag(NewCode.range("main"), "-Wmacro-redefined"),
779 withNote(Diag(NewCode.range("note"))))));
780 }
781 {
782 // Preserves diag without note.
783 Annotations Code(R"(
784#define $note[[BAR]] 1
785#define $main[[BAR]] 2)");
786 Annotations NewCode(R"(
787#define $main[[BAR]] 2)");
788 auto AST = createPatchedAST(Code.code(), NewCode.code());
789 EXPECT_THAT(
790 AST->getDiagnostics(),
791 ElementsAre(AllOf(Diag(NewCode.range("main"), "-Wmacro-redefined"),
792 Field(&Diag::Notes, IsEmpty()))));
793 }
794 {
795 // Make sure orphaned notes are not promoted to diags.
796 Annotations Code(R"(
797#define $note[[BAR]] 1
798#define $main[[BAR]] 2)");
799 Annotations NewCode(R"(
800#define BAZ 0
801#define BAR 1)");
802 auto AST = createPatchedAST(Code.code(), NewCode.code());
803 EXPECT_THAT(AST->getDiagnostics(), IsEmpty());
804 }
805 {
806 Annotations Code(R"(
807#ifndef FOO
808#define FOO
809void foo();
810#endif)");
811 // This code will emit a diagnostic for unterminated #ifndef (as stale
812 // preamble has the conditional but main file doesn't terminate it).
813 // We shouldn't emit any diagnotiscs (and shouldn't crash).
814 Annotations NewCode("");
815 auto AST = createPatchedAST(Code.code(), NewCode.code());
816 EXPECT_THAT(AST->getDiagnostics(), IsEmpty());
817 }
818 {
819 Annotations Code(R"(
820#ifndef FOO
821#define FOO
822void foo();
823#endif)");
824 // This code will emit a diagnostic for unterminated #ifndef (as stale
825 // preamble has the conditional but main file doesn't terminate it).
826 // We shouldn't emit any diagnotiscs (and shouldn't crash).
827 // FIXME: Patch/ignore diagnostics in such cases.
828 Annotations NewCode(R"(
829i[[nt]] xyz;
830 )");
831 auto AST = createPatchedAST(Code.code(), NewCode.code());
832 EXPECT_THAT(
833 AST->getDiagnostics(),
834 ElementsAre(Diag(NewCode.range(), "pp_unterminated_conditional")));
835 }
836}
837
838MATCHER_P2(Mark, Range, Text, "") {
839 return std::tie(arg.Rng, arg.Trivia) == std::tie(Range, Text);
840}
841
842TEST(PreamblePatch, MacroAndMarkHandling) {
843 {
844 Annotations Code(R"cpp(
845#ifndef FOO
846#define FOO
847// Some comments
848#pragma mark XX
849#define BAR
850
851#endif)cpp");
852 Annotations NewCode(R"cpp(
853#ifndef FOO
854#define FOO
855#define BAR
856#pragma $x[[mark XX
857]]
858#pragma $y[[mark YY
859]]
860#define BAZ
861
862#endif)cpp");
863 auto AST = createPatchedAST(Code.code(), NewCode.code());
864 EXPECT_THAT(AST->getMacros().Names.keys(),
865 UnorderedElementsAreArray({"FOO", "BAR", "BAZ"}));
866 EXPECT_THAT(AST->getMarks(),
867 UnorderedElementsAre(Mark(NewCode.range("x"), " XX"),
868 Mark(NewCode.range("y"), " YY")));
869 }
870}
871
872TEST(PreamblePatch, PatchFileEntry) {
873 Annotations Code(R"cpp(#define FOO)cpp");
874 Annotations NewCode(R"cpp(
875#define BAR
876#define FOO)cpp");
877 {
878 auto AST = createPatchedAST(Code.code(), Code.code());
879 EXPECT_EQ(
880 PreamblePatch::getPatchEntry(AST->tuPath(), AST->getSourceManager()),
881 nullptr);
882 }
883 {
884 auto AST = createPatchedAST(Code.code(), NewCode.code());
885 auto FE =
886 PreamblePatch::getPatchEntry(AST->tuPath(), AST->getSourceManager());
887 ASSERT_NE(FE, std::nullopt);
888 EXPECT_THAT(FE->getName().str(),
889 testing::EndsWith(PreamblePatch::HeaderName.str()));
890 }
891}
892
893} // namespace
894} // namespace clangd
895} // 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:1513
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:1308
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:116
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