Index: clang-tools-extra/clangd/CodeComplete.cpp =================================================================== --- clang-tools-extra/clangd/CodeComplete.cpp +++ clang-tools-extra/clangd/CodeComplete.cpp @@ -1866,9 +1866,10 @@ ? std::move(Flow).runWithoutSema(ParseInput.Contents, *Offset, *ParseInput.TFS) : std::move(Flow).run({FileName, *Offset, *Preamble, - // We want to serve code completions with - // low latency, so don't bother patching. - /*PreamblePatch=*/llvm::None, ParseInput}); + /*PreamblePatch=*/ + PreamblePatch::createMacroPatch( + FileName, ParseInput, *Preamble), + ParseInput}); } SignatureHelp signatureHelp(PathRef FileName, Position Pos, Index: clang-tools-extra/clangd/Preamble.h =================================================================== --- clang-tools-extra/clangd/Preamble.h +++ clang-tools-extra/clangd/Preamble.h @@ -106,6 +106,9 @@ static PreamblePatch create(llvm::StringRef FileName, const ParseInputs &Modified, const PreambleData &Baseline); + static PreamblePatch createMacroPatch(llvm::StringRef FileName, + const ParseInputs &Modified, + const PreambleData &Baseline); /// Adjusts CI (which compiles the modified inputs) to be used with the /// baseline preamble. This is done by inserting an artifical include to the /// \p CI that contains new directives calculated in create. Index: clang-tools-extra/clangd/Preamble.cpp =================================================================== --- clang-tools-extra/clangd/Preamble.cpp +++ clang-tools-extra/clangd/Preamble.cpp @@ -142,10 +142,11 @@ unsigned DirectiveLine; // Full text that's representing the directive, including the `#`. std::string Text; + unsigned Offset; bool operator==(const TextualPPDirective &RHS) const { - return std::tie(DirectiveLine, Text) == - std::tie(RHS.DirectiveLine, RHS.Text); + return std::tie(DirectiveLine, Text, Offset) == + std::tie(RHS.DirectiveLine, RHS.Text, RHS.Offset); } }; @@ -155,7 +156,7 @@ std::string spellDirective(llvm::StringRef Prefix, CharSourceRange DirectiveRange, const LangOptions &LangOpts, const SourceManager &SM, - unsigned &DirectiveLine) { + unsigned &DirectiveLine, unsigned &Offset) { std::string SpelledDirective; llvm::raw_string_ostream OS(SpelledDirective); OS << Prefix; @@ -169,6 +170,7 @@ auto DecompLoc = SM.getDecomposedLoc(DirectiveRange.getBegin()); DirectiveLine = SM.getLineNumber(DecompLoc.first, DecompLoc.second); + Offset = DecompLoc.second; auto TargetColumn = SM.getColumnNumber(DecompLoc.first, DecompLoc.second) - 1; // Pad with spaces before DirectiveRange to make sure it will be on right @@ -217,7 +219,7 @@ spellDirective("#define ", CharSourceRange::getTokenRange( MI->getDefinitionLoc(), MI->getDefinitionEndLoc()), - LangOpts, SM, TD.DirectiveLine); + LangOpts, SM, TD.DirectiveLine, TD.Offset); } private: @@ -528,6 +530,49 @@ return PP; } +PreamblePatch PreamblePatch::createMacroPatch(llvm::StringRef FileName, + const ParseInputs &Modified, + const PreambleData &Baseline) { + + auto BaselineScan = scanPreamble( + // Contents needs to be null-terminated. + Baseline.Preamble.getContents().str(), Modified.CompileCommand); + if (!BaselineScan) { + elog("Failed to scan baseline of {0}: {1}", FileName, + BaselineScan.takeError()); + return PreamblePatch::unmodified(Baseline); + } + auto ModifiedScan = scanPreamble(Modified.Contents, Modified.CompileCommand); + if (!ModifiedScan) { + elog("Failed to scan modified contents of {0}: {1}", FileName, + ModifiedScan.takeError()); + return PreamblePatch::unmodified(Baseline); + } + PreamblePatch PP; + llvm::SmallString<128> PatchName; + llvm::sys::path::append(PatchName, llvm::sys::path::parent_path(FileName), + PreamblePatchHeaderName); + PP.PatchFileName = PatchName.str().str(); + PP.ModifiedBounds = ModifiedScan->Bounds; + + llvm::raw_string_ostream Patch(PP.PatchContents); + Patch << "#line 0 \""; + + escapeBackslashAndQuotes(FileName, Patch); + Patch << "\"\n"; + + if (BaselineScan->TextualDirectives != ModifiedScan->TextualDirectives) { + for (const auto &TD : ModifiedScan->TextualDirectives) { + Patch << "#line " << TD.DirectiveLine << '\n'; + Patch << TD.Text << '\n'; + } + dlog("Created macro only preamble patch: {0}", Patch.str()); + Patch.flush(); + return PP; + } + return PreamblePatch::unmodified(Baseline); +} + void PreamblePatch::apply(CompilerInvocation &CI) const { // No need to map an empty file. if (PatchContents.empty()) Index: clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp =================================================================== --- clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -3260,6 +3260,23 @@ EXPECT_THAT(Results.Completions, UnorderedElementsAre(Labeled("BarExt"))); } } + +TEST(CompletionTest, PreambleCodeComplete) { + llvm::StringLiteral Baseline = "\n#define MACRO 12\nint num = MACRO;"; + llvm::StringLiteral ModifiedCC = + "#include \"header.h\"\n#define MACRO 12\nint num = MACRO; int num2 = M^"; + + Annotations Test(ModifiedCC); + auto BaselineTU = TestTU::withCode(Baseline); + auto ModifiedTU = TestTU::withCode(Test.code()); + + MockFS FS; + auto Inputs = ModifiedTU.inputs(FS); + auto Result = codeComplete(testPath(ModifiedTU.Filename), Test.point(), + BaselineTU.preamble().get(), Inputs, {}); + EXPECT_THAT(Result.Completions, Not(testing::IsEmpty())); +} + } // namespace } // namespace clangd } // namespace clang Index: clang-tools-extra/clangd/unittests/PreambleTests.cpp =================================================================== --- clang-tools-extra/clangd/unittests/PreambleTests.cpp +++ clang-tools-extra/clangd/unittests/PreambleTests.cpp @@ -546,6 +546,13 @@ // Ensure they are dropeed when a patched preamble is used. EXPECT_FALSE(createPatchedAST("", Code)->getDiagnostics()); } + +TEST(PreamblePatch, MacroLoc) { + llvm::StringLiteral Baseline = "\n#define MACRO 12\nint num = MACRO;"; + llvm::StringLiteral Modified = " \n#define MACRO 12\nint num = MACRO;"; + auto AST = createPatchedAST(Baseline, Modified); + ASSERT_TRUE(AST); +} } // namespace } // namespace clangd } // namespace clang