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"
44using testing::Contains;
45using testing::ElementsAre;
47using testing::IsEmpty;
48using testing::Matcher;
49using testing::MatchesRegex;
50using testing::UnorderedElementsAre;
51using testing::UnorderedElementsAreArray;
60collectPatchedIncludes(llvm::StringRef ModifiedContents,
61 llvm::StringRef BaselineContents,
62 llvm::StringRef MainFileName =
"main.cpp") {
65 TU.Filename = MainFileName.str();
67 TU.ExtraArgs = {
"-fno-ms-compatibility"};
68 auto BaselinePreamble = TU.preamble();
70 TU.Code = ModifiedContents.str();
71 auto PI = TU.inputs(FS);
83 auto Bounds = Lexer::ComputePreamble(ModifiedContents, CI->getLangOpts());
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";
96 if (llvm::Error Err = Action.Execute()) {
97 ADD_FAILURE() <<
"failed to execute action: " << std::move(Err);
100 Action.EndSourceFile();
106TEST(PreamblePatchTest, IncludeParsing) {
108 llvm::StringRef Cases[] = {
110 R
"cpp(^#include "a.h")cpp",
114 garbage, finishes preamble
130 ^#include <b.h>)cpp",
135 #/**/include <b.h>)cpp",
138 for (
const auto &Case : Cases) {
140 const auto Code = Test.code();
144 collectPatchedIncludes(Code,
"").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);
152TEST(PreamblePatchTest, ContainsNewIncludes) {
153 constexpr llvm::StringLiteral BaselineContents = R
"cpp(
155 #include <b.h> // This will be removed
158 constexpr llvm::StringLiteral ModifiedContents = R
"cpp(
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.
164 auto Includes = collectPatchedIncludes(ModifiedContents, BaselineContents)
170TEST(PreamblePatchTest, PatchesPreambleIncludes) {
174 #include "a.h" // IWYU pragma: keep
180 TU.AdditionalFiles["a.h"] =
"#include \"b.h\"";
181 TU.AdditionalFiles[
"b.h"] =
"";
182 TU.AdditionalFiles[
"c.h"] =
"";
183 auto PI = TU.inputs(FS);
201 PP.preambleIncludes(),
209std::optional<ParsedAST>
210createPatchedAST(llvm::StringRef Baseline, llvm::StringRef Modified,
211 llvm::StringMap<std::string> AdditionalFiles = {}) {
213 TU.AdditionalFiles = std::move(AdditionalFiles);
214 auto BaselinePreamble = TU.preamble();
215 if (!BaselinePreamble) {
216 ADD_FAILURE() <<
"Failed to build baseline preamble";
222 TU.Code = Modified.str();
225 ADD_FAILURE() <<
"Failed to build compiler invocation";
229 {}, BaselinePreamble);
232std::string getPreamblePatch(llvm::StringRef Baseline,
233 llvm::StringRef Modified) {
235 if (!BaselinePreamble) {
236 ADD_FAILURE() <<
"Failed to build baseline preamble";
247TEST(PreamblePatchTest, IncludesArePreserved) {
248 llvm::StringLiteral Baseline = R
"(//error-ok
252 llvm::StringLiteral Modified = R"(//error-ok
257 auto Includes = createPatchedAST(Baseline, Modified.str())
258 ->getIncludeStructure()
260 EXPECT_TRUE(!Includes.empty());
263 .getIncludeStructure()
267TEST(PreamblePatchTest, Define) {
270 const char *
const Contents;
271 const char *
const ExpectedPatch;
277 R"cpp(#line 0 ".*main.cpp"
289 R"cpp(#line 0 ".*main.cpp"
301 R"cpp(#line 0 ".*main.cpp"
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));
315 auto AST = createPatchedAST(
"", Modified.code());
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});
322 EXPECT_THAT(MacroRefRanges, Contains(Modified.range()));
326TEST(PreamblePatchTest, OrderingPreserved) {
327 llvm::StringLiteral Baseline =
"#define BAR(X) X";
329 #define BAR(X, Y) X Y
334 llvm::StringLiteral ExpectedPatch(R"cpp(#line 0 ".*main.cpp"
337#define BAR\(X, Y\) X Y
342 EXPECT_THAT(getPreamblePatch(Baseline, Modified.code()),
343 MatchesRegex(ExpectedPatch.str()));
345 auto AST = createPatchedAST(Baseline, Modified.code());
349TEST(PreamblePatchTest, LocateMacroAtWorks) {
351 const char *
const Baseline;
352 const char *
const Modified;
366 #undef $use^FOO)cpp",
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());
408 const auto &SM =
AST->getSourceManager();
409 auto *MacroTok =
AST->getTokens().spelledTokenContaining(
410 SM.getComposedLoc(SM.getMainFileID(), Modified.point(
"use")));
411 ASSERT_TRUE(MacroTok);
414 ASSERT_TRUE(FoundMacro);
415 EXPECT_THAT(FoundMacro->Name,
"FOO");
417 auto MacroLoc = FoundMacro->NameLoc;
418 EXPECT_EQ(SM.getFileID(MacroLoc), SM.getMainFileID());
419 EXPECT_EQ(SM.getFileOffset(MacroLoc), Modified.point(
"def"));
423TEST(PreamblePatchTest, LocateMacroAtDeletion) {
426 llvm::StringLiteral Baseline =
"#define FOO";
427 llvm::Annotations Modified(
"^FOO");
429 auto AST = createPatchedAST(Baseline, Modified.code());
432 const auto &SM =
AST->getSourceManager();
433 auto *MacroTok =
AST->getTokens().spelledTokenContaining(
434 SM.getComposedLoc(SM.getMainFileID(), Modified.point()));
435 ASSERT_TRUE(MacroTok);
438 ASSERT_TRUE(FoundMacro);
439 EXPECT_THAT(FoundMacro->Name,
"FOO");
442 format::getLLVMStyle(),
nullptr);
444 EXPECT_THAT(HI->Definition, testing::IsEmpty());
449 llvm::StringLiteral Baseline =
"#define FOO";
453 auto AST = createPatchedAST(Baseline, Modified.code());
456 auto HI =
getHover(*
AST, Modified.point(), format::getLLVMStyle(),
nullptr);
458 EXPECT_THAT(HI->Definition,
"#define BAR");
462MATCHER_P(referenceRangeIs, R,
"") {
return arg.Loc.range == R; }
464TEST(PreamblePatchTest, RefsToMacros) {
466 const char *
const Baseline;
467 const char *
const Modified;
493 for (
const auto &Case : Cases) {
495 auto AST = createPatchedAST(
"", Modified.code());
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));
503 for (
const auto &P : Modified.points()) {
505 AST->getTokens().spelledTokenContaining(SM.getComposedLoc(
508 ASSERT_TRUE(MacroTok);
510 testing::ElementsAreArray(ExpectedLocations));
515TEST(TranslatePreamblePatchLocation, Simple) {
520 TU.Code = R
"cpp(// line 1
524 TU.Filename = "main.cpp";
525 TU.HeaderFilename =
"__preamble_patch__.h";
526 TU.ImplicitHeaderGuard =
false;
528 auto AST = TU.build();
529 auto &SM =
AST.getSourceManager();
531 EXPECT_NE(SM.getFileID(ND.getLocation()), SM.getMainFileID());
534 auto DecompLoc = SM.getDecomposedLoc(TranslatedLoc);
535 EXPECT_EQ(DecompLoc.first, SM.getMainFileID());
536 EXPECT_EQ(SM.getLineNumber(DecompLoc.first, DecompLoc.second), 3U);
541 const char *
const Baseline;
542 const char *
const Modified;
552 {
"#define FOO",
"#define BAR"},
561 for (
const auto &Case : Cases) {
563 auto BaselinePreamble = TU.preamble();
564 ASSERT_TRUE(BaselinePreamble);
567 TU.Code = Modified.code().str();
570 TU.inputs(FS), *BaselinePreamble);
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);
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);
592 llvm::StringLiteral Baseline =
"#define M\nint num = M;";
593 llvm::StringLiteral Modified =
"#define M\n#include <foo.h>\nint num = M;";
595 auto BaselinePreamble = TU.preamble();
596 ASSERT_TRUE(BaselinePreamble);
598 TU.Code = Modified.str();
601 TU.inputs(FS), *BaselinePreamble);
602 EXPECT_TRUE(PP.text().empty());
605::testing::Matcher<const Diag &>
606withNote(::testing::Matcher<Note> NoteMatcher) {
610 return arg.Range ==
Range;
613 "Diag at " + llvm::to_string(
Range) +
" = [" + Name +
"]") {
614 return arg.Range ==
Range && arg.Name == Name;
622 auto AST = createPatchedAST(Code.code(), NewCode.code());
623 EXPECT_THAT(
AST->getDiagnostics(),
624 ElementsAre(
Diag(NewCode.range(),
"missing_type_specifier")));
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")));
645 llvm::StringMap<std::string> AdditionalFiles;
646 AdditionalFiles[
"foo.h"] =
"#pragma once";
647 AdditionalFiles[
"bar.h"] =
"#pragma once";
651[[#include "foo.h"]])");
654 auto AST = createPatchedAST(Code.code(), NewCode.code(), AdditionalFiles);
655 EXPECT_THAT(
AST->getDiagnostics(),
656 ElementsAre(
Diag(NewCode.range(),
"unused-includes")));
662[[#include "foo.h"]])");
664$bar[[#include "bar.h"]]
666$foo[[#include "foo.h"]])");
667 auto AST = createPatchedAST(Code.code(), NewCode.code(), AdditionalFiles);
669 AST->getDiagnostics(),
670 UnorderedElementsAre(
Diag(NewCode.range(
"bar"),
"unused-includes"),
671 Diag(NewCode.range(
"foo"),
"unused-includes")));
679#define $foo1[[FOO]] 1
681#define $foo2[[FOO]] 2)");
682 auto AST = createPatchedAST(Code.code(), NewCode.code(), AdditionalFiles);
684 AST->getDiagnostics(),
685 ElementsAre(AllOf(
Diag(NewCode.range(
"foo2"),
"-Wmacro-redefined"),
686 withNote(
Diag(NewCode.range(
"foo1"))))));
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")));
705#include [[<foo>]])");
707 auto AST = createPatchedAST(Code.code(), NewCode.code());
708 EXPECT_THAT(
AST->getDiagnostics(),
709 ElementsAre(
Diag(NewCode.range(),
"pp_file_not_found")));
715 auto AST = createPatchedAST(Code.code(), NewCode.code());
716 EXPECT_THAT(
AST->getDiagnostics(), IsEmpty());
726 auto AST = createPatchedAST(Code.code(), NewCode.code());
727 EXPECT_THAT(
AST->getDiagnostics(),
728 ElementsAre(
Diag(NewCode.range(),
"pp_file_not_found")));
734 auto AST = createPatchedAST(Code.code(), NewCode.code());
735 EXPECT_THAT(
AST->getDiagnostics(), IsEmpty());
745 auto AST = createPatchedAST(Code.code(), NewCode.code());
746 EXPECT_THAT(
AST->getDiagnostics(),
747 ElementsAre(
Diag(NewCode.range(),
"pp_file_not_found")));
758 auto AST = createPatchedAST(Code.code(), NewCode.code());
759 EXPECT_THAT(
AST->getDiagnostics(), IsEmpty());
764#define $note[[BAR]] 1
765#define $main[[BAR]] 2)");
768#define $note[[BAR]] 1
770#define $main[[BAR]] 2)");
771 auto AST = createPatchedAST(Code.code(), NewCode.code());
773 AST->getDiagnostics(),
774 ElementsAre(AllOf(
Diag(NewCode.range(
"main"),
"-Wmacro-redefined"),
775 withNote(
Diag(NewCode.range(
"note"))))));
780#define $note[[BAR]] 1
781#define $main[[BAR]] 2)");
783#define $main[[BAR]] 2)");
784 auto AST = createPatchedAST(Code.code(), NewCode.code());
786 AST->getDiagnostics(),
787 ElementsAre(AllOf(
Diag(NewCode.range(
"main"),
"-Wmacro-redefined"),
793#define $note[[BAR]] 1
794#define $main[[BAR]] 2)");
798 auto AST = createPatchedAST(Code.code(), NewCode.code());
799 EXPECT_THAT(
AST->getDiagnostics(), IsEmpty());
811 auto AST = createPatchedAST(Code.code(), NewCode.code());
812 EXPECT_THAT(
AST->getDiagnostics(), IsEmpty());
827 auto AST = createPatchedAST(Code.code(), NewCode.code());
829 AST->getDiagnostics(),
830 ElementsAre(
Diag(NewCode.range(),
"pp_unterminated_conditional")));
835 return std::tie(arg.Rng, arg.Trivia) == std::tie(
Range,
Text);
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")));
874 auto AST = createPatchedAST(Code.code(), Code.code());
880 auto AST = createPatchedAST(Code.code(), NewCode.code());
883 ASSERT_NE(FE, std::nullopt);
884 EXPECT_THAT(FE->getName().str(),
Same as llvm::Annotations, but adjusts functions to LSP-specific types for positions and ranges.
void collect(const CompilerInstance &CI)
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.
static OptionalFileEntryRef getPatchEntry(llvm::StringRef MainFilePath, const SourceManager &SM)
Returns the FileEntry for the preamble patch of MainFilePath in SM, if any.
llvm::StringRef text() const
Returns textual patch contents.
static PreamblePatch createMacroPatch(llvm::StringRef FileName, const ParseInputs &Modified, const PreambleData &Baseline)
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...
static constexpr llvm::StringLiteral HeaderName
WithContextValue extends Context::current() with a single value.
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
const NamedDecl & findDecl(ParsedAST &AST, llvm::StringRef QName)
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.
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.
ReferencesResult findReferences(ParsedAST &AST, Position Pos, uint32_t Limit, const SymbolIndex *Index, bool AddContext)
Returns references of the symbol at a specified Pos.
std::string testPath(PathRef File, llvm::sys::path::Style Style)
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.
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.
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)
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.
static clangd::Key< Config > Key
Context key which can be used to set the current Config.
@ Strict
Diagnose missing and unused includes.
struct clang::clangd::Config::@343034053122374337352226322054223376344037116252 Diagnostics
Controls warnings and errors when parsing code.
IncludesPolicy UnusedIncludes
A top-level diagnostic that may have Notes and Fixes.
std::vector< Note > Notes
Elaborate on the problem, usually pointing to a related piece of code.
SrcMgr::CharacteristicKind FileKind
std::optional< unsigned > HeaderID
static TestTU withHeaderCode(llvm::StringRef HeaderCode)
static TestTU withCode(llvm::StringRef Code)
std::shared_ptr< const PreambleData > preamble(PreambleParsedCallback PreambleCallback=nullptr) const