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;
58 return arg.first() ==
File && arg.second ==
D;
64collectPatchedIncludes(llvm::StringRef ModifiedContents,
65 llvm::StringRef BaselineContents,
66 llvm::StringRef MainFileName =
"main.cpp") {
69 TU.Filename = MainFileName.str();
71 TU.ExtraArgs = {
"-fno-ms-compatibility"};
72 auto BaselinePreamble = TU.preamble();
74 TU.Code = ModifiedContents.str();
75 auto PI = TU.inputs(FS);
87 auto Bounds = Lexer::ComputePreamble(ModifiedContents, CI->getLangOpts());
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";
100 if (llvm::Error Err = Action.Execute()) {
101 ADD_FAILURE() <<
"failed to execute action: " << std::move(Err);
104 Action.EndSourceFile();
110TEST(PreamblePatchTest, IncludeParsing) {
112 llvm::StringRef Cases[] = {
114 R
"cpp(^#include "a.h")cpp",
118 garbage, finishes preamble
134 ^#include <b.h>)cpp",
139 #/**/include <b.h>)cpp",
142 for (
const auto &Case : Cases) {
144 const auto Code = Test.code();
148 collectPatchedIncludes(Code,
"").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);
156TEST(PreamblePatchTest, ContainsNewIncludes) {
157 constexpr llvm::StringLiteral BaselineContents = R
"cpp(
159 #include <b.h> // This will be removed
162 constexpr llvm::StringLiteral ModifiedContents = R
"cpp(
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.
168 auto Includes = collectPatchedIncludes(ModifiedContents, BaselineContents)
174TEST(PreamblePatchTest, PatchesPreambleIncludes) {
178 #include "a.h" // IWYU pragma: keep
184 TU.AdditionalFiles["a.h"] =
"#include \"b.h\"";
185 TU.AdditionalFiles[
"b.h"] =
"";
186 TU.AdditionalFiles[
"c.h"] =
"";
187 auto PI = TU.inputs(FS);
205 PP.preambleIncludes(),
213std::optional<ParsedAST>
214createPatchedAST(llvm::StringRef Baseline, llvm::StringRef Modified,
215 llvm::StringMap<std::string> AdditionalFiles = {}) {
217 TU.AdditionalFiles = std::move(AdditionalFiles);
218 auto BaselinePreamble = TU.preamble();
219 if (!BaselinePreamble) {
220 ADD_FAILURE() <<
"Failed to build baseline preamble";
226 TU.Code = Modified.str();
229 ADD_FAILURE() <<
"Failed to build compiler invocation";
233 {}, BaselinePreamble);
236std::string getPreamblePatch(llvm::StringRef Baseline,
237 llvm::StringRef Modified) {
239 if (!BaselinePreamble) {
240 ADD_FAILURE() <<
"Failed to build baseline preamble";
251TEST(PreamblePatchTest, IncludesArePreserved) {
252 llvm::StringLiteral Baseline = R
"(//error-ok
256 llvm::StringLiteral Modified = R"(//error-ok
261 auto Includes = createPatchedAST(Baseline, Modified.str())
262 ->getIncludeStructure()
264 EXPECT_TRUE(!Includes.empty());
267 .getIncludeStructure()
271TEST(PreamblePatchTest, Define) {
274 const char *
const Contents;
275 const char *
const ExpectedPatch;
281 R"cpp(#line 0 ".*main.cpp"
293 R"cpp(#line 0 ".*main.cpp"
305 R"cpp(#line 0 ".*main.cpp"
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));
319 auto AST = createPatchedAST(
"", Modified.code());
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});
326 EXPECT_THAT(MacroRefRanges, Contains(Modified.range()));
330TEST(PreamblePatchTest, OrderingPreserved) {
331 llvm::StringLiteral Baseline =
"#define BAR(X) X";
333 #define BAR(X, Y) X Y
338 llvm::StringLiteral ExpectedPatch(R"cpp(#line 0 ".*main.cpp"
341#define BAR\(X, Y\) X Y
346 EXPECT_THAT(getPreamblePatch(Baseline, Modified.code()),
347 MatchesRegex(ExpectedPatch.str()));
349 auto AST = createPatchedAST(Baseline, Modified.code());
353TEST(PreamblePatchTest, LocateMacroAtWorks) {
355 const char *
const Baseline;
356 const char *
const Modified;
370 #undef $use^FOO)cpp",
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());
412 const auto &SM =
AST->getSourceManager();
413 auto *MacroTok =
AST->getTokens().spelledTokenContaining(
414 SM.getComposedLoc(SM.getMainFileID(), Modified.point(
"use")));
415 ASSERT_TRUE(MacroTok);
418 ASSERT_TRUE(FoundMacro);
419 EXPECT_THAT(FoundMacro->Name,
"FOO");
421 auto MacroLoc = FoundMacro->NameLoc;
422 EXPECT_EQ(SM.getFileID(MacroLoc), SM.getMainFileID());
423 EXPECT_EQ(SM.getFileOffset(MacroLoc), Modified.point(
"def"));
427TEST(PreamblePatchTest, LocateMacroAtDeletion) {
430 llvm::StringLiteral Baseline =
"#define FOO";
431 llvm::Annotations Modified(
"^FOO");
433 auto AST = createPatchedAST(Baseline, Modified.code());
436 const auto &SM =
AST->getSourceManager();
437 auto *MacroTok =
AST->getTokens().spelledTokenContaining(
438 SM.getComposedLoc(SM.getMainFileID(), Modified.point()));
439 ASSERT_TRUE(MacroTok);
442 ASSERT_TRUE(FoundMacro);
443 EXPECT_THAT(FoundMacro->Name,
"FOO");
446 format::getLLVMStyle(),
nullptr);
448 EXPECT_THAT(HI->Definition, testing::IsEmpty());
453 llvm::StringLiteral Baseline =
"#define FOO";
457 auto AST = createPatchedAST(Baseline, Modified.code());
460 auto HI =
getHover(*
AST, Modified.point(), format::getLLVMStyle(),
nullptr);
462 EXPECT_THAT(HI->Definition,
"#define BAR");
466MATCHER_P(referenceRangeIs, R,
"") {
return arg.Loc.range == R; }
468TEST(PreamblePatchTest, RefsToMacros) {
470 const char *
const Baseline;
471 const char *
const Modified;
497 for (
const auto &Case : Cases) {
499 auto AST = createPatchedAST(
"", Modified.code());
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));
507 for (
const auto &P : Modified.points()) {
509 AST->getTokens().spelledTokenContaining(SM.getComposedLoc(
512 ASSERT_TRUE(MacroTok);
514 testing::ElementsAreArray(ExpectedLocations));
519TEST(TranslatePreamblePatchLocation, Simple) {
524 TU.Code = R
"cpp(// line 1
528 TU.Filename = "main.cpp";
529 TU.HeaderFilename =
"__preamble_patch__.h";
530 TU.ImplicitHeaderGuard =
false;
532 auto AST = TU.build();
533 auto &SM =
AST.getSourceManager();
535 EXPECT_NE(SM.getFileID(ND.getLocation()), SM.getMainFileID());
538 auto DecompLoc = SM.getDecomposedLoc(TranslatedLoc);
539 EXPECT_EQ(DecompLoc.first, SM.getMainFileID());
540 EXPECT_EQ(SM.getLineNumber(DecompLoc.first, DecompLoc.second), 3U);
545 const char *
const Baseline;
546 const char *
const Modified;
556 {
"#define FOO",
"#define BAR"},
565 for (
const auto &Case : Cases) {
567 auto BaselinePreamble = TU.preamble();
568 ASSERT_TRUE(BaselinePreamble);
571 TU.Code = Modified.code().str();
574 TU.inputs(FS), *BaselinePreamble);
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);
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);
596 llvm::StringLiteral Baseline =
"#define M\nint num = M;";
597 llvm::StringLiteral Modified =
"#define M\n#include <foo.h>\nint num = M;";
599 auto BaselinePreamble = TU.preamble();
600 ASSERT_TRUE(BaselinePreamble);
602 TU.Code = Modified.str();
605 TU.inputs(FS), *BaselinePreamble);
606 EXPECT_TRUE(PP.text().empty());
609::testing::Matcher<const Diag &>
610withNote(::testing::Matcher<Note> NoteMatcher) {
614 return arg.Range ==
Range;
617 "Diag at " + llvm::to_string(
Range) +
" = [" + Name +
"]") {
618 return arg.Range ==
Range && arg.Name == Name;
626 auto AST = createPatchedAST(Code.code(), NewCode.code());
627 EXPECT_THAT(
AST->getDiagnostics(),
628 ElementsAre(
Diag(NewCode.range(),
"missing_type_specifier")));
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")));
649 llvm::StringMap<std::string> AdditionalFiles;
650 AdditionalFiles[
"foo.h"] =
"#pragma once";
651 AdditionalFiles[
"bar.h"] =
"#pragma once";
655[[#include "foo.h"]])");
658 auto AST = createPatchedAST(Code.code(), NewCode.code(), AdditionalFiles);
659 EXPECT_THAT(
AST->getDiagnostics(),
660 ElementsAre(
Diag(NewCode.range(),
"unused-includes")));
666[[#include "foo.h"]])");
668$bar[[#include "bar.h"]]
670$foo[[#include "foo.h"]])");
671 auto AST = createPatchedAST(Code.code(), NewCode.code(), AdditionalFiles);
673 AST->getDiagnostics(),
674 UnorderedElementsAre(
Diag(NewCode.range(
"bar"),
"unused-includes"),
675 Diag(NewCode.range(
"foo"),
"unused-includes")));
683#define $foo1[[FOO]] 1
685#define $foo2[[FOO]] 2)");
686 auto AST = createPatchedAST(Code.code(), NewCode.code(), AdditionalFiles);
688 AST->getDiagnostics(),
689 ElementsAre(AllOf(
Diag(NewCode.range(
"foo2"),
"-Wmacro-redefined"),
690 withNote(
Diag(NewCode.range(
"foo1"))))));
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")));
709#include [[<foo>]])");
711 auto AST = createPatchedAST(Code.code(), NewCode.code());
712 EXPECT_THAT(
AST->getDiagnostics(),
713 ElementsAre(
Diag(NewCode.range(),
"pp_file_not_found")));
719 auto AST = createPatchedAST(Code.code(), NewCode.code());
720 EXPECT_THAT(
AST->getDiagnostics(), IsEmpty());
730 auto AST = createPatchedAST(Code.code(), NewCode.code());
731 EXPECT_THAT(
AST->getDiagnostics(),
732 ElementsAre(
Diag(NewCode.range(),
"pp_file_not_found")));
738 auto AST = createPatchedAST(Code.code(), NewCode.code());
739 EXPECT_THAT(
AST->getDiagnostics(), IsEmpty());
749 auto AST = createPatchedAST(Code.code(), NewCode.code());
750 EXPECT_THAT(
AST->getDiagnostics(),
751 ElementsAre(
Diag(NewCode.range(),
"pp_file_not_found")));
762 auto AST = createPatchedAST(Code.code(), NewCode.code());
763 EXPECT_THAT(
AST->getDiagnostics(), IsEmpty());
768#define $note[[BAR]] 1
769#define $main[[BAR]] 2)");
772#define $note[[BAR]] 1
774#define $main[[BAR]] 2)");
775 auto AST = createPatchedAST(Code.code(), NewCode.code());
777 AST->getDiagnostics(),
778 ElementsAre(AllOf(
Diag(NewCode.range(
"main"),
"-Wmacro-redefined"),
779 withNote(
Diag(NewCode.range(
"note"))))));
784#define $note[[BAR]] 1
785#define $main[[BAR]] 2)");
787#define $main[[BAR]] 2)");
788 auto AST = createPatchedAST(Code.code(), NewCode.code());
790 AST->getDiagnostics(),
791 ElementsAre(AllOf(
Diag(NewCode.range(
"main"),
"-Wmacro-redefined"),
797#define $note[[BAR]] 1
798#define $main[[BAR]] 2)");
802 auto AST = createPatchedAST(Code.code(), NewCode.code());
803 EXPECT_THAT(
AST->getDiagnostics(), IsEmpty());
815 auto AST = createPatchedAST(Code.code(), NewCode.code());
816 EXPECT_THAT(
AST->getDiagnostics(), IsEmpty());
831 auto AST = createPatchedAST(Code.code(), NewCode.code());
833 AST->getDiagnostics(),
834 ElementsAre(
Diag(NewCode.range(),
"pp_unterminated_conditional")));
839 return std::tie(arg.Rng, arg.Trivia) == std::tie(
Range,
Text);
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")));
878 auto AST = createPatchedAST(Code.code(), Code.code());
884 auto AST = createPatchedAST(Code.code(), NewCode.code());
887 ASSERT_NE(FE, std::nullopt);
888 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