<div dir="ltr">Thanks Mikael, sent out an ugly fix at 89cb5d558895706e053bc3af972aa5b15aa82863 to get sanitizer build bots running.<div><br></div><div>Will change the testing scheme for a proper fix.</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Thu, Apr 23, 2020 at 4:53 PM Mikael Holmén <<a href="mailto:mikael.holmen@ericsson.com">mikael.holmen@ericsson.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Hi Kadir,<br>
<br>
I start seeing some sanitizer complaints with this commit for the<br>
following two testcases:<br>
  ./ClangdTests/PreamblePatchTest.ContainsNewIncludes<br>
  ./ClangdTests/PreamblePatchTest.IncludeParsing<br>
<br>
The complaints look like this:<br>
<br>
=================================================================<br>
==75126==ERROR: LeakSanitizer: detected memory leaks<br>
<br>
Direct leak of 369 byte(s) in 4 object(s) allocated from:<br>
    #0 0x5bbe40 in operator new(unsigned long, std::nothrow_t const&)<br>
/repo/uabkaka/tmp/llvm/projects/compiler-<br>
rt/lib/asan/asan_new_delete.cc:112<br>
    #1 0x149a3e9 in<br>
llvm::WritableMemoryBuffer::getNewUninitMemBuffer(unsigned long,<br>
llvm::Twine const&) /repo/bbiswjenk/fem023-<br>
eiffel003/workspace/llvm/llvm-master-sanitize-asan/llvm/build-all-<br>
asan/../lib/Support/MemoryBuffer.cpp:298:34<br>
    #2 0x1498db3 in getMemBufferCopyImpl /repo/bbiswjenk/fem023-<br>
eiffel003/workspace/llvm/llvm-master-sanitize-asan/llvm/build-all-<br>
asan/../lib/Support/MemoryBuffer.cpp:127:14<br>
    #3 0x1498db3 in<br>
llvm::MemoryBuffer::getMemBufferCopy(llvm::StringRef, llvm::Twine<br>
const&) /repo/bbiswjenk/fem023-eiffel003/workspace/llvm/llvm-master-<br>
sanitize-asan/llvm/build-all-asan/../lib/Support/MemoryBuffer.cpp:136<br>
    #4 0x506012e in<br>
clang::clangd::PreamblePatch::apply(clang::CompilerInvocation&) const<br>
/repo/bbiswjenk/fem023-eiffel003/workspace/llvm/llvm-master-sanitize-<br>
asan/llvm/build-all-asan/../../clang-tools-<br>
extra/clangd/Preamble.cpp:337:7<br>
    #5 0xf910e6 in clang::clangd::(anonymous<br>
namespace)::PreamblePatchTest_IncludeParsing_Test::TestBody()<br>
/repo/bbiswjenk/fem023-eiffel003/workspace/llvm/llvm-master-sanitize-<br>
asan/llvm/build-all-asan/../../clang-tools-<br>
extra/clangd/unittests/PreambleTests.cpp:91:57<br>
    #6 0x164e8e0 in HandleExceptionsInMethodIfSupported<testing::Test,<br>
void> /repo/bbiswjenk/fem023-eiffel003/workspace/llvm/llvm-master-<br>
sanitize-asan/llvm/build-all-<br>
asan/../utils/unittest/googletest/src/gtest.cc<br>
    #7 0x164e8e0 in testing::Test::Run() /repo/bbiswjenk/fem023-<br>
eiffel003/workspace/llvm/llvm-master-sanitize-asan/llvm/build-all-<br>
asan/../utils/unittest/googletest/src/gtest.cc:2474<br>
    #8 0x1651fc5 in testing::TestInfo::Run() /repo/bbiswjenk/fem023-<br>
eiffel003/workspace/llvm/llvm-master-sanitize-asan/llvm/build-all-<br>
asan/../utils/unittest/googletest/src/gtest.cc:2656:11<br>
    #9 0x1653440 in testing::TestCase::Run() /repo/bbiswjenk/fem023-<br>
eiffel003/workspace/llvm/llvm-master-sanitize-asan/llvm/build-all-<br>
asan/../utils/unittest/googletest/src/gtest.cc:2774:28<br>
    #10 0x1671e64 in testing::internal::UnitTestImpl::RunAllTests()<br>
/repo/bbiswjenk/fem023-eiffel003/workspace/llvm/llvm-master-sanitize-<br>
asan/llvm/build-all-<br>
asan/../utils/unittest/googletest/src/gtest.cc:4649:43<br>
    #11 0x1671010 in<br>
HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl,<br>
bool> /repo/bbiswjenk/fem023-eiffel003/workspace/llvm/llvm-master-<br>
sanitize-asan/llvm/build-all-<br>
asan/../utils/unittest/googletest/src/gtest.cc<br>
    #12 0x1671010 in testing::UnitTest::Run() /repo/bbiswjenk/fem023-<br>
eiffel003/workspace/llvm/llvm-master-sanitize-asan/llvm/build-all-<br>
asan/../utils/unittest/googletest/src/gtest.cc:4257<br>
    #13 0x1633040 in RUN_ALL_TESTS /repo/bbiswjenk/fem023-<br>
eiffel003/workspace/llvm/llvm-master-sanitize-asan/llvm/build-all-<br>
asan/../utils/unittest/googletest/include/gtest/gtest.h:2233:46<br>
    #14 0x1633040 in main /repo/bbiswjenk/fem023-<br>
eiffel003/workspace/llvm/llvm-master-sanitize-asan/llvm/build-all-<br>
asan/../utils/unittest/UnitTestMain/TestMain.cpp:50<br>
    #15 0x7f7dfdfe6544 in __libc_start_main (/lib64/libc.so.6+0x22544)<br>
<br>
SUMMARY: AddressSanitizer: 369 byte(s) leaked in 4 allocation(s).<br>
<br>
Regards,<br>
Mikael<br>
<br>
On Tue, 2020-04-21 at 01:34 -0700, Kadir Cetinkaya via cfe-commits<br>
wrote:<br>
> Author: Kadir Cetinkaya<br>
> Date: 2020-04-21T10:27:26+02:00<br>
> New Revision: 2214b9076f1d3a4784820c4479e2417685e5c980<br>
> <br>
> URL: <br>
> <a href="https://github.com/llvm/llvm-project/commit/2214b9076f1d3a4784820c4479e2417685e5c980" rel="noreferrer" target="_blank">https://github.com/llvm/llvm-project/commit/2214b9076f1d3a4784820c4479e2417685e5c980</a><br>
> DIFF: <br>
> <a href="https://github.com/llvm/llvm-project/commit/2214b9076f1d3a4784820c4479e2417685e5c980.diff" rel="noreferrer" target="_blank">https://github.com/llvm/llvm-project/commit/2214b9076f1d3a4784820c4479e2417685e5c980.diff</a><br>
> <br>
> LOG: [clangd] Make signatureHelp work with stale preambles<br>
> <br>
> Summary:<br>
> This is achieved by calculating newly added includes and implicitly<br>
> parsing them as if they were part of the main file.<br>
> <br>
> This also gets rid of the need for consistent preamble reads.<br>
> <br>
> Reviewers: sammccall<br>
> <br>
> Subscribers: ilya-biryukov, javed.absar, MaskRay, jkorous, mgrang,<br>
> arphaman, jfb, usaxena95, cfe-commits<br>
> <br>
> Tags: #clang<br>
> <br>
> Differential Revision: <a href="https://reviews.llvm.org/D77392" rel="noreferrer" target="_blank">https://reviews.llvm.org/D77392</a><br>
> <br>
> Added: <br>
>     clang-tools-extra/clangd/unittests/PreambleTests.cpp<br>
> <br>
> Modified: <br>
>     clang-tools-extra/clangd/ClangdServer.cpp<br>
>     clang-tools-extra/clangd/CodeComplete.cpp<br>
>     clang-tools-extra/clangd/Preamble.cpp<br>
>     clang-tools-extra/clangd/Preamble.h<br>
>     clang-tools-extra/clangd/TUScheduler.cpp<br>
>     clang-tools-extra/clangd/TUScheduler.h<br>
>     clang-tools-extra/clangd/unittests/CMakeLists.txt<br>
>     clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp<br>
>     clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp<br>
>     clang/include/clang/Basic/TokenKinds.h<br>
>     clang/include/clang/Frontend/PrecompiledPreamble.h<br>
> <br>
> Removed: <br>
>     <br>
> <br>
> <br>
> #####################################################################<br>
> ###########<br>
> diff  --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-<br>
> tools-extra/clangd/ClangdServer.cpp<br>
> index f82bfa4f2682..c5148f81f3df 100644<br>
> --- a/clang-tools-extra/clangd/ClangdServer.cpp<br>
> +++ b/clang-tools-extra/clangd/ClangdServer.cpp<br>
> @@ -280,11 +280,8 @@ void ClangdServer::signatureHelp(PathRef File,<br>
> Position Pos,<br>
>                               Pos, FS, Index));<br>
>    };<br>
>  <br>
> -  // Unlike code completion, we wait for an up-to-date preamble<br>
> here.<br>
> -  // Signature help is often triggered after code completion. If the<br>
> code<br>
> -  // completion inserted a header to make the symbol available, then<br>
> using<br>
> -  // the old preamble would yield useless results.<br>
> -  WorkScheduler.runWithPreamble("SignatureHelp", File,<br>
> TUScheduler::Consistent,<br>
> +  // Unlike code completion, we wait for a preamble here.<br>
> +  WorkScheduler.runWithPreamble("SignatureHelp", File,<br>
> TUScheduler::Stale,<br>
>                                  std::move(Action));<br>
>  }<br>
>  <br>
> <br>
> diff  --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-<br>
> tools-extra/clangd/CodeComplete.cpp<br>
> index 7dbb4f5b78a3..3fa5b53ebaad 100644<br>
> --- a/clang-tools-extra/clangd/CodeComplete.cpp<br>
> +++ b/clang-tools-extra/clangd/CodeComplete.cpp<br>
> @@ -1023,6 +1023,7 @@ struct SemaCompleteInput {<br>
>    PathRef FileName;<br>
>    const tooling::CompileCommand &Command;<br>
>    const PreambleData &Preamble;<br>
> +  const PreamblePatch &PreamblePatch;<br>
>    llvm::StringRef Contents;<br>
>    size_t Offset;<br>
>    llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS;<br>
> @@ -1060,7 +1061,6 @@ bool<br>
> semaCodeComplete(std::unique_ptr<CodeCompleteConsumer> Consumer,<br>
>    ParseInput.CompileCommand = Input.Command;<br>
>    ParseInput.FS = VFS;<br>
>    ParseInput.Contents = std::string(Input.Contents);<br>
> -  ParseInput.Opts = ParseOptions();<br>
>  <br>
>    IgnoreDiagnostics IgnoreDiags;<br>
>    auto CI = buildCompilerInvocation(ParseInput, IgnoreDiags);<br>
> @@ -1096,6 +1096,7 @@ bool<br>
> semaCodeComplete(std::unique_ptr<CodeCompleteConsumer> Consumer,<br>
>    PreambleBounds PreambleRegion =<br>
>        ComputePreambleBounds(*CI->getLangOpts(),<br>
> ContentsBuffer.get(), 0);<br>
>    bool CompletingInPreamble = PreambleRegion.Size > Input.Offset;<br>
> +  Input.PreamblePatch.apply(*CI);<br>
>    // NOTE: we must call BeginSourceFile after<br>
> prepareCompilerInstance. Otherwise<br>
>    // the remapped buffers do not get freed.<br>
>    auto Clang = prepareCompilerInstance(<br>
> @@ -1754,8 +1755,10 @@ codeComplete(PathRef FileName, const<br>
> tooling::CompileCommand &Command,<br>
>        SpecFuzzyFind, Opts);<br>
>    return (!Preamble || Opts.RunParser ==<br>
> CodeCompleteOptions::NeverParse)<br>
>               ? std::move(Flow).runWithoutSema(Contents, *Offset,<br>
> VFS)<br>
> -             : std::move(Flow).run(<br>
> -                   {FileName, Command, *Preamble, Contents, *Offset,<br>
> VFS});<br>
> +             : std::move(Flow).run({FileName, Command, *Preamble,<br>
> +                                    // We want to serve code<br>
> completions with<br>
> +                                    // low latency, so don't bother<br>
> patching.<br>
> +                                    PreamblePatch(), Contents,<br>
> *Offset, VFS});<br>
>  }<br>
>  <br>
>  SignatureHelp signatureHelp(PathRef FileName,<br>
> @@ -1775,10 +1778,15 @@ SignatureHelp signatureHelp(PathRef FileName,<br>
>    Options.IncludeMacros = false;<br>
>    Options.IncludeCodePatterns = false;<br>
>    Options.IncludeBriefComments = false;<br>
> -  IncludeStructure PreambleInclusions; // Unused for signatureHelp<br>
> +<br>
> +  ParseInputs PI;<br>
> +  PI.CompileCommand = Command;<br>
> +  PI.Contents = Contents.str();<br>
> +  PI.FS = std::move(VFS);<br>
> +  auto PP = PreamblePatch::create(FileName, PI, Preamble);<br>
>    semaCodeComplete(<br>
>        std::make_unique<SignatureHelpCollector>(Options, Index,<br>
> Result), Options,<br>
> -      {FileName, Command, Preamble, Contents, *Offset,<br>
> std::move(VFS)});<br>
> +      {FileName, Command, Preamble, PP, Contents, *Offset,<br>
> std::move(PI.FS)});<br>
>    return Result;<br>
>  }<br>
>  <br>
> <br>
> diff  --git a/clang-tools-extra/clangd/Preamble.cpp b/clang-tools-<br>
> extra/clangd/Preamble.cpp<br>
> index 97cd22a5d1fc..8392748227b4 100644<br>
> --- a/clang-tools-extra/clangd/Preamble.cpp<br>
> +++ b/clang-tools-extra/clangd/Preamble.cpp<br>
> @@ -8,11 +8,39 @@<br>
>  <br>
>  #include "Preamble.h"<br>
>  #include "Compiler.h"<br>
> +#include "Headers.h"<br>
>  #include "Logger.h"<br>
>  #include "Trace.h"<br>
> +#include "clang/Basic/Diagnostic.h"<br>
> +#include "clang/Basic/LangOptions.h"<br>
>  #include "clang/Basic/SourceLocation.h"<br>
> +#include "clang/Basic/TokenKinds.h"<br>
> +#include "clang/Frontend/CompilerInvocation.h"<br>
> +#include "clang/Frontend/FrontendActions.h"<br>
> +#include "clang/Lex/Lexer.h"<br>
>  #include "clang/Lex/PPCallbacks.h"<br>
> +#include "clang/Lex/Preprocessor.h"<br>
>  #include "clang/Lex/PreprocessorOptions.h"<br>
> +#include "clang/Tooling/CompilationDatabase.h"<br>
> +#include "llvm/ADT/ArrayRef.h"<br>
> +#include "llvm/ADT/IntrusiveRefCntPtr.h"<br>
> +#include "llvm/ADT/STLExtras.h"<br>
> +#include "llvm/ADT/SmallString.h"<br>
> +#include "llvm/ADT/StringRef.h"<br>
> +#include "llvm/ADT/StringSet.h"<br>
> +#include "llvm/Support/Error.h"<br>
> +#include "llvm/Support/ErrorHandling.h"<br>
> +#include "llvm/Support/FormatVariadic.h"<br>
> +#include "llvm/Support/MemoryBuffer.h"<br>
> +#include "llvm/Support/Path.h"<br>
> +#include "llvm/Support/VirtualFileSystem.h"<br>
> +#include "llvm/Support/raw_ostream.h"<br>
> +#include <iterator><br>
> +#include <memory><br>
> +#include <string><br>
> +#include <system_error><br>
> +#include <utility><br>
> +#include <vector><br>
>  <br>
>  namespace clang {<br>
>  namespace clangd {<br>
> @@ -74,6 +102,81 @@ class CppFilePreambleCallbacks : public<br>
> PreambleCallbacks {<br>
>    const SourceManager *SourceMgr = nullptr;<br>
>  };<br>
>  <br>
> +// Runs preprocessor over preamble section.<br>
> +class PreambleOnlyAction : public PreprocessorFrontendAction {<br>
> +protected:<br>
> +  void ExecuteAction() override {<br>
> +    Preprocessor &PP = getCompilerInstance().getPreprocessor();<br>
> +    auto &SM = PP.getSourceManager();<br>
> +    PP.EnterMainSourceFile();<br>
> +    auto Bounds =<br>
> ComputePreambleBounds(getCompilerInstance().getLangOpts(),<br>
> +                                        SM.getBuffer(SM.getMainFileI<br>
> D()), 0);<br>
> +    Token Tok;<br>
> +    do {<br>
> +      PP.Lex(Tok);<br>
> +      assert(SM.isInMainFile(Tok.getLocation()));<br>
> +    } while (Tok.isNot(tok::eof) &&<br>
> +             SM.getDecomposedLoc(Tok.getLocation()).second <<br>
> Bounds.Size);<br>
> +  }<br>
> +};<br>
> +<br>
> +/// Gets the includes in the preamble section of the file by running<br>
> +/// preprocessor over \p Contents. Returned includes do not contain<br>
> resolved<br>
> +/// paths. \p VFS and \p Cmd is used to build the compiler<br>
> invocation, which<br>
> +/// might stat/read files.<br>
> +llvm::Expected<std::vector<Inclusion>><br>
> +scanPreambleIncludes(llvm::StringRef Contents,<br>
> +                     llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem><br>
> VFS,<br>
> +                     const tooling::CompileCommand &Cmd) {<br>
> +  // Build and run Preprocessor over the preamble.<br>
> +  ParseInputs PI;<br>
> +  PI.Contents = Contents.str();<br>
> +  PI.FS = std::move(VFS);<br>
> +  PI.CompileCommand = Cmd;<br>
> +  IgnoringDiagConsumer IgnoreDiags;<br>
> +  auto CI = buildCompilerInvocation(PI, IgnoreDiags);<br>
> +  if (!CI)<br>
> +    return llvm::createStringError(llvm::inconvertibleErrorCode(),<br>
> +                                   "failed to create compiler<br>
> invocation");<br>
> +  CI->getDiagnosticOpts().IgnoreWarnings = true;<br>
> +  auto ContentsBuffer = llvm::MemoryBuffer::getMemBuffer(Contents);<br>
> +  auto Clang = prepareCompilerInstance(<br>
> +      std::move(CI), nullptr, std::move(ContentsBuffer),<br>
> +      // Provide an empty FS to prevent preprocessor from performing<br>
> IO. This<br>
> +      // also implies missing resolved paths for includes.<br>
> +      new llvm::vfs::InMemoryFileSystem, IgnoreDiags);<br>
> +  if (Clang->getFrontendOpts().Inputs.empty())<br>
> +    return llvm::createStringError(llvm::inconvertibleErrorCode(),<br>
> +                                   "compiler instance had no<br>
> inputs");<br>
> +  // We are only interested in main file includes.<br>
> +  Clang->getPreprocessorOpts().SingleFileParseMode = true;<br>
> +  PreambleOnlyAction Action;<br>
> +  if (!Action.BeginSourceFile(*Clang, Clang-<br>
> >getFrontendOpts().Inputs[0]))<br>
> +    return llvm::createStringError(llvm::inconvertibleErrorCode(),<br>
> +                                   "failed BeginSourceFile");<br>
> +  Preprocessor &PP = Clang->getPreprocessor();<br>
> +  IncludeStructure Includes;<br>
> +  PP.addPPCallbacks(<br>
> +      collectIncludeStructureCallback(Clang->getSourceManager(),<br>
> &Includes));<br>
> +  if (llvm::Error Err = Action.Execute())<br>
> +    return std::move(Err);<br>
> +  Action.EndSourceFile();<br>
> +  return Includes.MainFileIncludes;<br>
> +}<br>
> +<br>
> +const char *spellingForIncDirective(tok::PPKeywordKind<br>
> IncludeDirective) {<br>
> +  switch (IncludeDirective) {<br>
> +  case tok::pp_include:<br>
> +    return "include";<br>
> +  case tok::pp_import:<br>
> +    return "import";<br>
> +  case tok::pp_include_next:<br>
> +    return "include_next";<br>
> +  default:<br>
> +    break;<br>
> +  }<br>
> +  llvm_unreachable("not an include directive");<br>
> +}<br>
>  } // namespace<br>
>  <br>
>  PreambleData::PreambleData(const ParseInputs &Inputs,<br>
> @@ -166,5 +269,78 @@ bool isPreambleCompatible(const PreambleData<br>
> &Preamble,<br>
>           Preamble.Preamble.CanReuse(CI, ContentsBuffer.get(),<br>
> Bounds,<br>
>                                      Inputs.FS.get());<br>
>  }<br>
> +<br>
> +PreamblePatch PreamblePatch::create(llvm::StringRef FileName,<br>
> +                                    const ParseInputs &Modified,<br>
> +                                    const PreambleData &Baseline) {<br>
> +  // First scan the include directives in Baseline and Modified.<br>
> These will be<br>
> +  // used to figure out newly added directives in Modified. Scanning<br>
> can fail,<br>
> +  // the code just bails out and creates an empty patch in such<br>
> cases, as:<br>
> +  // - If scanning for Baseline fails, no knowledge of existing<br>
> includes hence<br>
> +  //   patch will contain all the includes in Modified. Leading to<br>
> rebuild of<br>
> +  //   whole preamble, which is terribly slow.<br>
> +  // - If scanning for Modified fails, cannot figure out newly added<br>
> ones so<br>
> +  //   there's nothing to do but generate an empty patch.<br>
> +  auto BaselineIncludes = scanPreambleIncludes(<br>
> +      // Contents needs to be null-terminated.<br>
> +      Baseline.Preamble.getContents().str(),<br>
> +      Baseline.StatCache->getConsumingFS(Modified.FS),<br>
> Modified.CompileCommand);<br>
> +  if (!BaselineIncludes) {<br>
> +    elog("Failed to scan includes for baseline of {0}: {1}",<br>
> FileName,<br>
> +         BaselineIncludes.takeError());<br>
> +    return {};<br>
> +  }<br>
> +  auto ModifiedIncludes = scanPreambleIncludes(<br>
> +      Modified.Contents, Baseline.StatCache-<br>
> >getConsumingFS(Modified.FS),<br>
> +      Modified.CompileCommand);<br>
> +  if (!ModifiedIncludes) {<br>
> +    elog("Failed to scan includes for modified contents of {0}:<br>
> {1}", FileName,<br>
> +         ModifiedIncludes.takeError());<br>
> +    return {};<br>
> +  }<br>
> +<br>
> +  PreamblePatch PP;<br>
> +  // This shouldn't coincide with any real file name.<br>
> +  llvm::SmallString<128> PatchName;<br>
> +  llvm::sys::path::append(PatchName,<br>
> llvm::sys::path::parent_path(FileName),<br>
> +                          "__preamble_patch__.h");<br>
> +  PP.PatchFileName = PatchName.str().str();<br>
> +<br>
> +  // We are only interested in newly added includes, record the ones<br>
> in Baseline<br>
> +  // for exclusion.<br>
> +  llvm::DenseSet<std::pair<tok::PPKeywordKind, llvm::StringRef>><br>
> +      ExistingIncludes;<br>
> +  for (const auto &Inc : *BaselineIncludes)<br>
> +    ExistingIncludes.insert({Inc.Directive, Inc.Written});<br>
> +  // Calculate extra includes that needs to be inserted.<br>
> +  llvm::raw_string_ostream Patch(PP.PatchContents);<br>
> +  for (const auto &Inc : *ModifiedIncludes) {<br>
> +    if (ExistingIncludes.count({Inc.Directive, Inc.Written}))<br>
> +      continue;<br>
> +    Patch << llvm::formatv("#{0} {1}\n",<br>
> spellingForIncDirective(Inc.Directive),<br>
> +                           Inc.Written);<br>
> +  }<br>
> +  Patch.flush();<br>
> +<br>
> +  // FIXME: Handle more directives, e.g. define/undef.<br>
> +  return PP;<br>
> +}<br>
> +<br>
> +void PreamblePatch::apply(CompilerInvocation &CI) const {<br>
> +  // No need to map an empty file.<br>
> +  if (PatchContents.empty())<br>
> +    return;<br>
> +  auto &PPOpts = CI.getPreprocessorOpts();<br>
> +  auto PatchBuffer =<br>
> +      // we copy here to ensure contents are still valid if CI<br>
> outlives the<br>
> +      // PreamblePatch.<br>
> +      llvm::MemoryBuffer::getMemBufferCopy(PatchContents,<br>
> PatchFileName);<br>
> +  // CI will take care of the lifetime of the buffer.<br>
> +  PPOpts.addRemappedFile(PatchFileName, PatchBuffer.release());<br>
> +  // The patch will be parsed after loading the preamble ast and<br>
> before parsing<br>
> +  // the main file.<br>
> +  PPOpts.Includes.push_back(PatchFileName);<br>
> +}<br>
> +<br>
>  } // namespace clangd<br>
>  } // namespace clang<br>
> <br>
> diff  --git a/clang-tools-extra/clangd/Preamble.h b/clang-tools-<br>
> extra/clangd/Preamble.h<br>
> index bd67610e0ad7..10c292a71f38 100644<br>
> --- a/clang-tools-extra/clangd/Preamble.h<br>
> +++ b/clang-tools-extra/clangd/Preamble.h<br>
> @@ -32,6 +32,7 @@<br>
>  #include "clang/Frontend/CompilerInvocation.h"<br>
>  #include "clang/Frontend/PrecompiledPreamble.h"<br>
>  #include "clang/Tooling/CompilationDatabase.h"<br>
> +#include "llvm/ADT/StringRef.h"<br>
>  <br>
>  #include <memory><br>
>  #include <string><br>
> @@ -88,6 +89,31 @@ buildPreamble(PathRef FileName, CompilerInvocation<br>
> CI,<br>
>  bool isPreambleCompatible(const PreambleData &Preamble,<br>
>                            const ParseInputs &Inputs, PathRef<br>
> FileName,<br>
>                            const CompilerInvocation &CI);<br>
> +<br>
> +/// Stores information required to parse a TU using a (possibly<br>
> stale) Baseline<br>
> +/// preamble. Updates compiler invocation to approximately reflect<br>
> additions to<br>
> +/// the preamble section of Modified contents, e.g. new include<br>
> directives.<br>
> +class PreamblePatch {<br>
> +public:<br>
> +  // With an empty patch, the preamble is used verbatim.<br>
> +  PreamblePatch() = default;<br>
> +  /// Builds a patch that contains new PP directives introduced to<br>
> the preamble<br>
> +  /// section of \p Modified compared to \p Baseline.<br>
> +  /// FIXME: This only handles include directives, we should at<br>
> least handle<br>
> +  /// define/undef.<br>
> +  static PreamblePatch create(llvm::StringRef FileName,<br>
> +                              const ParseInputs &Modified,<br>
> +                              const PreambleData &Baseline);<br>
> +  /// Adjusts CI (which compiles the modified inputs) to be used<br>
> with the<br>
> +  /// baseline preamble. This is done by inserting an artifical<br>
> include to the<br>
> +  /// \p CI that contains new directives calculated in create.<br>
> +  void apply(CompilerInvocation &CI) const;<br>
> +<br>
> +private:<br>
> +  std::string PatchContents;<br>
> +  std::string PatchFileName;<br>
> +};<br>
> +<br>
>  } // namespace clangd<br>
>  } // namespace clang<br>
>  <br>
> <br>
> diff  --git a/clang-tools-extra/clangd/TUScheduler.cpp b/clang-tools-<br>
> extra/clangd/TUScheduler.cpp<br>
> index 26adcfd2b8f2..2f2abb59ab3c 100644<br>
> --- a/clang-tools-extra/clangd/TUScheduler.cpp<br>
> +++ b/clang-tools-extra/clangd/TUScheduler.cpp<br>
> @@ -866,36 +866,6 @@ ASTWorker::getPossiblyStalePreamble() const {<br>
>    return LatestPreamble;<br>
>  }<br>
>  <br>
> -void ASTWorker::getCurrentPreamble(<br>
> -    llvm::unique_function<void(std::shared_ptr<const PreambleData>)><br>
> Callback) {<br>
> -  // We could just call startTask() to throw the read on the queue,<br>
> knowing<br>
> -  // it will run after any updates. But we know this task is cheap,<br>
> so to<br>
> -  // improve latency we cheat: insert it on the queue after the last<br>
> update.<br>
> -  std::unique_lock<std::mutex> Lock(Mutex);<br>
> -  auto LastUpdate =<br>
> -      std::find_if(Requests.rbegin(), Requests.rend(),<br>
> -                   [](const Request &R) { return<br>
> R.UpdateType.hasValue(); });<br>
> -  // If there were no writes in the queue, and CurrentRequest is not<br>
> a write,<br>
> -  // the preamble is ready now.<br>
> -  if (LastUpdate == Requests.rend() &&<br>
> -      (!CurrentRequest || CurrentRequest->UpdateType.hasValue())) {<br>
> -    Lock.unlock();<br>
> -    return Callback(getPossiblyStalePreamble());<br>
> -  }<br>
> -  assert(!RunSync && "Running synchronously, but queue is non-<br>
> empty!");<br>
> -  Requests.insert(LastUpdate.base(),<br>
> -                  Request{[Callback = std::move(Callback), this]()<br>
> mutable {<br>
> -                            Callback(getPossiblyStalePreamble());<br>
> -                          },<br>
> -                          "GetPreamble", steady_clock::now(),<br>
> -                          Context::current().clone(),<br>
> -                          /*UpdateType=*/None,<br>
> -                          /*InvalidationPolicy=*/TUScheduler::NoInva<br>
> lidation,<br>
> -                          /*Invalidate=*/nullptr});<br>
> -  Lock.unlock();<br>
> -  RequestsCV.notify_all();<br>
> -}<br>
> -<br>
>  void ASTWorker::waitForFirstPreamble() const {<br>
> BuiltFirstPreamble.wait(); }<br>
>  <br>
>  tooling::CompileCommand ASTWorker::getCurrentCompileCommand() const<br>
> {<br>
> @@ -1307,41 +1277,21 @@ void<br>
> TUScheduler::runWithPreamble(llvm::StringRef Name, PathRef File,<br>
>      return;<br>
>    }<br>
>  <br>
> -  // Future is populated if the task needs a specific preamble.<br>
> -  std::future<std::shared_ptr<const PreambleData>><br>
> ConsistentPreamble;<br>
> -  // FIXME: Currently this only holds because ASTWorker blocks after<br>
> issuing a<br>
> -  // preamble build. Get rid of consistent reads or make them build<br>
> on the<br>
> -  // calling thread instead.<br>
> -  if (Consistency == Consistent) {<br>
> -    std::promise<std::shared_ptr<const PreambleData>> Promise;<br>
> -    ConsistentPreamble = Promise.get_future();<br>
> -    It->second->Worker->getCurrentPreamble(<br>
> -        [Promise = std::move(Promise)](<br>
> -            std::shared_ptr<const PreambleData> Preamble) mutable {<br>
> -          Promise.set_value(std::move(Preamble));<br>
> -        });<br>
> -  }<br>
> -<br>
>    std::shared_ptr<const ASTWorker> Worker = It->second-<br>
> >Worker.lock();<br>
>    auto Task =<br>
>        [Worker, Consistency, Name = Name.str(), File = File.str(),<br>
>         Contents = It->second->Contents,<br>
>         Command = Worker->getCurrentCompileCommand(),<br>
>         Ctx = Context::current().derive(kFileBeingProcessed,<br>
> std::string(File)),<br>
> -       ConsistentPreamble = std::move(ConsistentPreamble),<br>
>         Action = std::move(Action), this]() mutable {<br>
>          std::shared_ptr<const PreambleData> Preamble;<br>
> -        if (ConsistentPreamble.valid()) {<br>
> -          Preamble = ConsistentPreamble.get();<br>
> -        } else {<br>
> -          if (Consistency != PreambleConsistency::StaleOrAbsent) {<br>
> -            // Wait until the preamble is built for the first time,<br>
> if preamble<br>
> -            // is required. This avoids extra work of processing the<br>
> preamble<br>
> -            // headers in parallel multiple times.<br>
> -            Worker->waitForFirstPreamble();<br>
> -          }<br>
> -          Preamble = Worker->getPossiblyStalePreamble();<br>
> +        if (Consistency == PreambleConsistency::Stale) {<br>
> +          // Wait until the preamble is built for the first time, if<br>
> preamble<br>
> +          // is required. This avoids extra work of processing the<br>
> preamble<br>
> +          // headers in parallel multiple times.<br>
> +          Worker->waitForFirstPreamble();<br>
>          }<br>
> +        Preamble = Worker->getPossiblyStalePreamble();<br>
>  <br>
>          std::lock_guard<Semaphore> BarrierLock(Barrier);<br>
>          WithContext Guard(std::move(Ctx));<br>
> <br>
> diff  --git a/clang-tools-extra/clangd/TUScheduler.h b/clang-tools-<br>
> extra/clangd/TUScheduler.h<br>
> index a7f5d2f493fb..48ed2c76f546 100644<br>
> --- a/clang-tools-extra/clangd/TUScheduler.h<br>
> +++ b/clang-tools-extra/clangd/TUScheduler.h<br>
> @@ -256,11 +256,6 @@ class TUScheduler {<br>
>  <br>
>    /// Controls whether preamble reads wait for the preamble to be<br>
> up-to-date.<br>
>    enum PreambleConsistency {<br>
> -    /// The preamble is generated from the current version of the<br>
> file.<br>
> -    /// If the content was recently updated, we will wait until we<br>
> have a<br>
> -    /// preamble that reflects that update.<br>
> -    /// This is the slowest option, and may be delayed by other<br>
> tasks.<br>
> -    Consistent,<br>
>      /// The preamble may be generated from an older version of the<br>
> file.<br>
>      /// Reading from locations in the preamble may cause files to be<br>
> re-read.<br>
>      /// This gives callers two options:<br>
> <br>
> diff  --git a/clang-tools-extra/clangd/unittests/CMakeLists.txt<br>
> b/clang-tools-extra/clangd/unittests/CMakeLists.txt<br>
> index 541367a1d96a..4119445b85a0 100644<br>
> --- a/clang-tools-extra/clangd/unittests/CMakeLists.txt<br>
> +++ b/clang-tools-extra/clangd/unittests/CMakeLists.txt<br>
> @@ -59,6 +59,7 @@ add_unittest(ClangdUnitTests ClangdTests<br>
>    LSPClient.cpp<br>
>    ParsedASTTests.cpp<br>
>    PathMappingTests.cpp<br>
> +  PreambleTests.cpp<br>
>    PrintASTTests.cpp<br>
>    QualityTests.cpp<br>
>    RenameTests.cpp<br>
> <br>
> diff  --git a/clang-tools-<br>
> extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-<br>
> extra/clangd/unittests/CodeCompleteTests.cpp<br>
> index 794b21ee61a5..47637024ab91 100644<br>
> --- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp<br>
> +++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp<br>
> @@ -1186,6 +1186,31 @@ TEST(SignatureHelpTest, OpeningParen) {<br>
>    }<br>
>  }<br>
>  <br>
> +TEST(SignatureHelpTest, StalePreamble) {<br>
> +  TestTU TU;<br>
> +  TU.Code = "";<br>
> +  IgnoreDiagnostics Diags;<br>
> +  auto Inputs = TU.inputs();<br>
> +  auto CI = buildCompilerInvocation(Inputs, Diags);<br>
> +  ASSERT_TRUE(CI);<br>
> +  auto EmptyPreamble = buildPreamble(testPath(TU.Filename), *CI,<br>
> Inputs,<br>
> +                                     /*InMemory=*/true,<br>
> /*Callback=*/nullptr);<br>
> +  ASSERT_TRUE(EmptyPreamble);<br>
> +<br>
> +  TU.AdditionalFiles["a.h"] = "int foo(int x);";<br>
> +  const Annotations Test(R"cpp(<br>
> +    #include "a.h"<br>
> +    void bar() { foo(^2); })cpp");<br>
> +  TU.Code = Test.code().str();<br>
> +  Inputs = TU.inputs();<br>
> +  auto Results =<br>
> +      signatureHelp(testPath(TU.Filename), Inputs.CompileCommand,<br>
> +                    *EmptyPreamble, TU.Code, Test.point(),<br>
> Inputs.FS, nullptr);<br>
> +  EXPECT_THAT(Results.signatures, ElementsAre(Sig("foo([[int x]]) -><br>
> int")));<br>
> +  EXPECT_EQ(0, Results.activeSignature);<br>
> +  EXPECT_EQ(0, Results.activeParameter);<br>
> +}<br>
> +<br>
>  class IndexRequestCollector : public SymbolIndex {<br>
>  public:<br>
>    bool<br>
> <br>
> diff  --git a/clang-tools-extra/clangd/unittests/PreambleTests.cpp<br>
> b/clang-tools-extra/clangd/unittests/PreambleTests.cpp<br>
> new file mode 100644<br>
> index 000000000000..2815ca0a46f1<br>
> --- /dev/null<br>
> +++ b/clang-tools-extra/clangd/unittests/PreambleTests.cpp<br>
> @@ -0,0 +1,123 @@<br>
> +//===--- PreambleTests.cpp --------------------------------------*-<br>
> C++ -*-===//<br>
> +//<br>
> +// Part of the LLVM Project, under the Apache License v2.0 with LLVM<br>
> Exceptions.<br>
> +// See <a href="https://llvm.org/LICENSE.txt" rel="noreferrer" target="_blank">https://llvm.org/LICENSE.txt</a> for license information.<br>
> +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception<br>
> +//<br>
> +//===---------------------------------------------------------------<br>
> -------===//<br>
> +<br>
> +#include "Annotations.h"<br>
> +#include "Compiler.h"<br>
> +#include "Preamble.h"<br>
> +#include "TestFS.h"<br>
> +#include "clang/Lex/PreprocessorOptions.h"<br>
> +#include "llvm/ADT/STLExtras.h"<br>
> +#include "llvm/ADT/StringRef.h"<br>
> +#include "gmock/gmock.h"<br>
> +#include "gtest/gtest.h"<br>
> +#include <string><br>
> +#include <vector><br>
> +<br>
> +namespace clang {<br>
> +namespace clangd {<br>
> +namespace {<br>
> +<br>
> +using testing::_;<br>
> +using testing::Contains;<br>
> +using testing::Pair;<br>
> +<br>
> +MATCHER_P(HasContents, Contents, "") { return arg->getBuffer() ==<br>
> Contents; }<br>
> +<br>
> +TEST(PreamblePatchTest, IncludeParsing) {<br>
> +  MockFSProvider FS;<br>
> +  MockCompilationDatabase CDB;<br>
> +  IgnoreDiagnostics Diags;<br>
> +  ParseInputs PI;<br>
> +  PI.FS = FS.getFileSystem();<br>
> +<br>
> +  // We expect any line with a point to show up in the patch.<br>
> +  llvm::StringRef Cases[] = {<br>
> +      // Only preamble<br>
> +      R"cpp(^#include "a.h")cpp",<br>
> +      // Both preamble and mainfile<br>
> +      R"cpp(<br>
> +        ^#include "a.h"<br>
> +        garbage, finishes preamble<br>
> +        #include "a.h")cpp",<br>
> +      // Mixed directives<br>
> +      R"cpp(<br>
> +        ^#include "a.h"<br>
> +        #pragma directive<br>
> +        // some comments<br>
> +        ^#include_next <a.h><br>
> +        #ifdef skipped<br>
> +        ^#import "a.h"<br>
> +        #endif)cpp",<br>
> +      // Broken directives<br>
> +      R"cpp(<br>
> +        #include "a<br>
> +        ^#include "a.h"<br>
> +        #include <b<br>
> +        ^#include <b.h>)cpp",<br>
> +  };<br>
> +<br>
> +  const auto FileName = testPath("foo.cc");<br>
> +  for (const auto Case : Cases) {<br>
> +    Annotations Test(Case);<br>
> +    const auto Code = Test.code();<br>
> +    PI.CompileCommand = *CDB.getCompileCommand(FileName);<br>
> +<br>
> +    SCOPED_TRACE(Code);<br>
> +    // Check preamble lexing logic by building an empty preamble and<br>
> patching it<br>
> +    // with all the contents.<br>
> +    PI.Contents = "";<br>
> +    const auto CI = buildCompilerInvocation(PI, Diags);<br>
> +    const auto EmptyPreamble = buildPreamble(FileName, *CI, PI,<br>
> true, nullptr);<br>
> +    PI.Contents = Code.str();<br>
> +<br>
> +    std::string ExpectedBuffer;<br>
> +    const auto Points = Test.points();<br>
> +    for (const auto &P : Points) {<br>
> +      // Copy the whole line.<br>
> +      auto StartOffset = llvm::cantFail(positionToOffset(Code, P));<br>
> +      ExpectedBuffer.append(Code.substr(StartOffset)<br>
> +                                .take_until([](char C) { return C ==<br>
> '\n'; })<br>
> +                                .str());<br>
> +      ExpectedBuffer += '\n';<br>
> +    }<br>
> +<br>
> +    PreamblePatch::create(FileName, PI, *EmptyPreamble).apply(*CI);<br>
> +    EXPECT_THAT(CI->getPreprocessorOpts().RemappedFileBuffers,<br>
> +                Contains(Pair(_, HasContents(ExpectedBuffer))));<br>
> +  }<br>
> +}<br>
> +<br>
> +TEST(PreamblePatchTest, ContainsNewIncludes) {<br>
> +  MockFSProvider FS;<br>
> +  MockCompilationDatabase CDB;<br>
> +  IgnoreDiagnostics Diags;<br>
> +<br>
> +  const auto FileName = testPath("foo.cc");<br>
> +  ParseInputs PI;<br>
> +  PI.FS = FS.getFileSystem();<br>
> +  PI.CompileCommand = *CDB.getCompileCommand(FileName);<br>
> +  PI.Contents = "#include <existing.h>\n";<br>
> +<br>
> +  // Check <br>
> diff ing logic by adding a new header to the preamble and ensuring<br>
> +  // only it is patched.<br>
> +  const auto CI = buildCompilerInvocation(PI, Diags);<br>
> +  const auto FullPreamble = buildPreamble(FileName, *CI, PI, true,<br>
> nullptr);<br>
> +<br>
> +  constexpr llvm::StringLiteral Patch =<br>
> +      "#include <test>\n#import <existing.h>\n";<br>
> +  // We provide the same includes twice, they shouldn't be included<br>
> in the<br>
> +  // patch.<br>
> +  PI.Contents = (Patch + PI.Contents + PI.Contents).str();<br>
> +  PreamblePatch::create(FileName, PI, *FullPreamble).apply(*CI);<br>
> +  EXPECT_THAT(CI->getPreprocessorOpts().RemappedFileBuffers,<br>
> +              Contains(Pair(_, HasContents(Patch))));<br>
> +}<br>
> +<br>
> +} // namespace<br>
> +} // namespace clangd<br>
> +} // namespace clang<br>
> <br>
> diff  --git a/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp <br>
> b/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp<br>
> index 7106e01a10e4..6f50a5acd4e3 100644<br>
> --- a/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp<br>
> +++ b/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp<br>
> @@ -246,66 +246,6 @@ TEST_F(TUSchedulerTests, Debounce) {<br>
>    EXPECT_EQ(2, CallbackCount);<br>
>  }<br>
>  <br>
> -static std::vector<std::string> includes(const PreambleData<br>
> *Preamble) {<br>
> -  std::vector<std::string> Result;<br>
> -  if (Preamble)<br>
> -    for (const auto &Inclusion : Preamble-<br>
> >Includes.MainFileIncludes)<br>
> -      Result.push_back(Inclusion.Written);<br>
> -  return Result;<br>
> -}<br>
> -<br>
> -TEST_F(TUSchedulerTests, PreambleConsistency) {<br>
> -  std::atomic<int> CallbackCount(0);<br>
> -  {<br>
> -    Notification InconsistentReadDone; // Must live longest.<br>
> -    TUScheduler S(CDB, optsForTest());<br>
> -    auto Path = testPath("foo.cpp");<br>
> -    // Schedule two updates (A, B) and two preamble reads (stale,<br>
> consistent).<br>
> -    // The stale read should see A, and the consistent read should<br>
> see B.<br>
> -    // (We recognize the preambles by their included files).<br>
> -    auto Inputs = getInputs(Path, "#include <A>");<br>
> -    Inputs.Version = "A";<br>
> -    updateWithCallback(S, Path, Inputs, WantDiagnostics::Yes, [&]()<br>
> {<br>
> -      // This callback runs in between the two preamble updates.<br>
> -<br>
> -      // This blocks update B, preventing it from winning the race<br>
> -      // against the stale read.<br>
> -      // If the first read was instead consistent, this would<br>
> deadlock.<br>
> -      InconsistentReadDone.wait();<br>
> -      // This delays update B, preventing it from winning a race<br>
> -      // against the consistent read. The consistent read sees B<br>
> -      // only because it waits for it.<br>
> -      // If the second read was stale, it would usually see A.<br>
> -      std::this_thread::sleep_for(std::chrono::milliseconds(100));<br>
> -    });<br>
> -    Inputs.Contents = "#include <B>";<br>
> -    Inputs.Version = "B";<br>
> -    S.update(Path, Inputs, WantDiagnostics::Yes);<br>
> -<br>
> -    S.runWithPreamble("StaleRead", Path, TUScheduler::Stale,<br>
> -                      [&](Expected<InputsAndPreamble> Pre) {<br>
> -                        ASSERT_TRUE(bool(Pre));<br>
> -                        ASSERT_TRUE(Pre->Preamble);<br>
> -                        EXPECT_EQ(Pre->Preamble->Version, "A");<br>
> -                        EXPECT_THAT(includes(Pre->Preamble),<br>
> -                                    ElementsAre("<A>"));<br>
> -                        InconsistentReadDone.notify();<br>
> -                        ++CallbackCount;<br>
> -                      });<br>
> -    S.runWithPreamble("ConsistentRead", Path,<br>
> TUScheduler::Consistent,<br>
> -                      [&](Expected<InputsAndPreamble> Pre) {<br>
> -                        ASSERT_TRUE(bool(Pre));<br>
> -                        ASSERT_TRUE(Pre->Preamble);<br>
> -                        EXPECT_EQ(Pre->Preamble->Version, "B");<br>
> -                        EXPECT_THAT(includes(Pre->Preamble),<br>
> -                                    ElementsAre("<B>"));<br>
> -                        ++CallbackCount;<br>
> -                      });<br>
> -    S.blockUntilIdle(timeoutSeconds(10));<br>
> -  }<br>
> -  EXPECT_EQ(2, CallbackCount);<br>
> -}<br>
> -<br>
>  TEST_F(TUSchedulerTests, Cancellation) {<br>
>    // We have the following update/read sequence<br>
>    //   U0<br>
> <br>
> diff  --git a/clang/include/clang/Basic/TokenKinds.h<br>
> b/clang/include/clang/Basic/TokenKinds.h<br>
> index c25181e6827c..4e66aa1c8c2d 100644<br>
> --- a/clang/include/clang/Basic/TokenKinds.h<br>
> +++ b/clang/include/clang/Basic/TokenKinds.h<br>
> @@ -14,6 +14,7 @@<br>
>  #ifndef LLVM_CLANG_BASIC_TOKENKINDS_H<br>
>  #define LLVM_CLANG_BASIC_TOKENKINDS_H<br>
>  <br>
> +#include "llvm/ADT/DenseMapInfo.h"<br>
>  #include "llvm/Support/Compiler.h"<br>
>  <br>
>  namespace clang {<br>
> @@ -95,7 +96,25 @@ bool isAnnotation(TokenKind K);<br>
>  /// Return true if this is an annotation token representing a<br>
> pragma.<br>
>  bool isPragmaAnnotation(TokenKind K);<br>
>  <br>
> -}  // end namespace tok<br>
> -}  // end namespace clang<br>
> +} // end namespace tok<br>
> +} // end namespace clang<br>
> +<br>
> +namespace llvm {<br>
> +template <> struct DenseMapInfo<clang::tok::PPKeywordKind> {<br>
> +  static inline clang::tok::PPKeywordKind getEmptyKey() {<br>
> +    return clang::tok::PPKeywordKind::pp_not_keyword;<br>
> +  }<br>
> +  static inline clang::tok::PPKeywordKind getTombstoneKey() {<br>
> +    return clang::tok::PPKeywordKind::NUM_PP_KEYWORDS;<br>
> +  }<br>
> +  static unsigned getHashValue(const clang::tok::PPKeywordKind &Val)<br>
> {<br>
> +    return static_cast<unsigned>(Val);<br>
> +  }<br>
> +  static bool isEqual(const clang::tok::PPKeywordKind &LHS,<br>
> +                      const clang::tok::PPKeywordKind &RHS) {<br>
> +    return LHS == RHS;<br>
> +  }<br>
> +};<br>
> +} // namespace llvm<br>
>  <br>
>  #endif<br>
> <br>
> diff  --git a/clang/include/clang/Frontend/PrecompiledPreamble.h<br>
> b/clang/include/clang/Frontend/PrecompiledPreamble.h<br>
> index 0d95ee683eee..538f6c92ad55 100644<br>
> --- a/clang/include/clang/Frontend/PrecompiledPreamble.h<br>
> +++ b/clang/include/clang/Frontend/PrecompiledPreamble.h<br>
> @@ -16,6 +16,7 @@<br>
>  #include "clang/Lex/Lexer.h"<br>
>  #include "clang/Lex/Preprocessor.h"<br>
>  #include "llvm/ADT/IntrusiveRefCntPtr.h"<br>
> +#include "llvm/ADT/StringRef.h"<br>
>  #include "llvm/Support/AlignOf.h"<br>
>  #include "llvm/Support/MD5.h"<br>
>  #include <cstddef><br>
> @@ -94,6 +95,11 @@ class PrecompiledPreamble {<br>
>    /// be used for logging and debugging purposes only.<br>
>    std::size_t getSize() const;<br>
>  <br>
> +  /// Returned string is not null-terminated.<br>
> +  llvm::StringRef getContents() const {<br>
> +    return {PreambleBytes.data(), PreambleBytes.size()};<br>
> +  }<br>
> +<br>
>    /// Check whether PrecompiledPreamble can be reused for the new<br>
> contents(\p<br>
>    /// MainFileBuffer) of the main file.<br>
>    bool CanReuse(const CompilerInvocation &Invocation,<br>
> <br>
> <br>
>         <br>
> _______________________________________________<br>
> cfe-commits mailing list<br>
> <a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a><br>
> <a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits</a><br>
</blockquote></div>