diff --git a/clang-tools-extra/clangd/Compiler.cpp b/clang-tools-extra/clangd/Compiler.cpp --- a/clang-tools-extra/clangd/Compiler.cpp +++ b/clang-tools-extra/clangd/Compiler.cpp @@ -96,7 +96,7 @@ CompilerInstance::createDiagnostics(new DiagnosticOptions, &D, false); std::unique_ptr CI = createInvocationFromCommandLine( ArgStrs, CommandLineDiagsEngine, std::move(VFS), - /*ShouldRecoverOnErrors=*/true, CC1Args); + /*ShouldRecoverOnErrors=*/true, CC1Args, /*ProbePrecompiled=*/false); if (!CI) return nullptr; // createInvocationFromCommandLine sets DisableFree. diff --git a/clang/include/clang/Driver/Driver.h b/clang/include/clang/Driver/Driver.h --- a/clang/include/clang/Driver/Driver.h +++ b/clang/include/clang/Driver/Driver.h @@ -271,6 +271,9 @@ /// Whether to check that input files exist when constructing compilation /// jobs. unsigned CheckInputsExist : 1; + /// Whether to probe for PCH files on disk, + /// in order to upgrade -include foo.h to -include-pch foo.h.pch. + unsigned ProbePrecompiled : 1; public: /// Force clang to emit reproducer for driver invocation. This is enabled @@ -357,6 +360,9 @@ void setCheckInputsExist(bool Value) { CheckInputsExist = Value; } + bool getProbePrecompiled() const { return ProbePrecompiled; } + void setProbePrecompiled(bool Value) { ProbePrecompiled = Value; } + void setTargetAndMode(const ParsedClangName &TM) { ClangNameParts = TM; } const std::string &getTitle() { return DriverTitle; } diff --git a/clang/include/clang/Frontend/Utils.h b/clang/include/clang/Frontend/Utils.h --- a/clang/include/clang/Frontend/Utils.h +++ b/clang/include/clang/Frontend/Utils.h @@ -200,6 +200,10 @@ /// \param CC1Args - if non-null, will be populated with the args to cc1 /// expanded from \p Args. May be set even if nullptr is returned. /// +/// \param ProbePrecompiled - allow the driver to probe the filesystem for +/// PCH files to replace -include with. +/// FIXME: ProbePrecompiled=true is a poor, historical default. +/// /// \return A CompilerInvocation, or nullptr if none was built for the given /// argument vector. std::unique_ptr createInvocationFromCommandLine( @@ -208,7 +212,7 @@ IntrusiveRefCntPtr(), IntrusiveRefCntPtr VFS = nullptr, bool ShouldRecoverOnErrors = false, - std::vector *CC1Args = nullptr); + std::vector *CC1Args = nullptr, bool ProbePrecompiled = true); // Frontend timing utils diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -198,7 +198,8 @@ CCPrintOptions(false), CCPrintHeaders(false), CCLogDiagnostics(false), CCGenDiagnostics(false), CCPrintProcessStats(false), TargetTriple(TargetTriple), Saver(Alloc), CheckInputsExist(true), - GenReproducer(false), SuppressMissingInputWarning(false) { + ProbePrecompiled(true), GenReproducer(false), + SuppressMissingInputWarning(false) { // Provide a sane fallback if no VFS is specified. if (!this->VFS) this->VFS = llvm::vfs::getRealFileSystem(); diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -1364,7 +1364,8 @@ bool RenderedImplicitInclude = false; for (const Arg *A : Args.filtered(options::OPT_clang_i_Group)) { - if (A->getOption().matches(options::OPT_include)) { + if (A->getOption().matches(options::OPT_include) && + D.getProbePrecompiled()) { // Handling of gcc-style gch precompiled headers. bool IsFirstImplicitInclude = !RenderedImplicitInclude; RenderedImplicitInclude = true; @@ -1375,12 +1376,12 @@ // so that replace_extension does the right thing. P += ".dummy"; llvm::sys::path::replace_extension(P, "pch"); - if (llvm::sys::fs::exists(P)) + if (D.getVFS().exists(P)) FoundPCH = true; if (!FoundPCH) { llvm::sys::path::replace_extension(P, "gch"); - if (llvm::sys::fs::exists(P)) { + if (D.getVFS().exists(P)) { FoundPCH = true; } } diff --git a/clang/lib/Frontend/CreateInvocationFromCommandLine.cpp b/clang/lib/Frontend/CreateInvocationFromCommandLine.cpp --- a/clang/lib/Frontend/CreateInvocationFromCommandLine.cpp +++ b/clang/lib/Frontend/CreateInvocationFromCommandLine.cpp @@ -29,7 +29,7 @@ std::unique_ptr clang::createInvocationFromCommandLine( ArrayRef ArgList, IntrusiveRefCntPtr Diags, IntrusiveRefCntPtr VFS, bool ShouldRecoverOnErorrs, - std::vector *CC1Args) { + std::vector *CC1Args, bool ProbePrecompiled) { assert(!ArgList.empty()); if (!Diags.get()) { // No diagnostics engine was provided, so create our own diagnostics object @@ -51,6 +51,9 @@ // Don't check that inputs exist, they may have been remapped. TheDriver.setCheckInputsExist(false); + // Don't probe on the filesystem for PCHes of -include'd headers - we may have + // a VFS, they may be misversioned, etc. + TheDriver.setProbePrecompiled(ProbePrecompiled); std::unique_ptr C(TheDriver.BuildCompilation(Args)); if (!C) diff --git a/clang/unittests/Frontend/UtilsTest.cpp b/clang/unittests/Frontend/UtilsTest.cpp --- a/clang/unittests/Frontend/UtilsTest.cpp +++ b/clang/unittests/Frontend/UtilsTest.cpp @@ -11,12 +11,15 @@ #include "clang/Basic/TargetOptions.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/Support/VirtualFileSystem.h" #include "gmock/gmock.h" #include "gtest/gtest.h" namespace clang { namespace { +using testing::ElementsAre; TEST(BuildCompilerInvocationTest, RecoverMultipleJobs) { // This generates multiple jobs and we recover by using the first. @@ -33,5 +36,31 @@ EXPECT_THAT(CI->TargetOpts->Triple, testing::StartsWith("i386-")); } +// buildInvocationFromCommandLine should not translate -include to -include-pch, +// even if the PCH file exists. +TEST(BuildCompilerInvocationTest, ProbePrecompiled) { + std::vector Args = {"clang", "-include", "foo.h", "foo.cpp"}; + auto FS = llvm::makeIntrusiveRefCnt(); + FS->addFile("foo.h", 0, llvm::MemoryBuffer::getMemBuffer("")); + FS->addFile("foo.h.pch", 0, llvm::MemoryBuffer::getMemBuffer("")); + + clang::IgnoringDiagConsumer D; + llvm::IntrusiveRefCntPtr CommandLineDiagsEngine = + clang::CompilerInstance::createDiagnostics(new DiagnosticOptions, &D, + false); + // Default: ProbePrecompiled is true. + std::unique_ptr CI = createInvocationFromCommandLine( + Args, CommandLineDiagsEngine, FS, false, nullptr); + ASSERT_TRUE(CI); + EXPECT_THAT(CI->getPreprocessorOpts().Includes, ElementsAre()); + EXPECT_EQ(CI->getPreprocessorOpts().ImplicitPCHInclude, "foo.h.pch"); + + CI = createInvocationFromCommandLine(Args, CommandLineDiagsEngine, FS, false, + nullptr, /*ProbePrecompiled=*/false); + ASSERT_TRUE(CI); + EXPECT_THAT(CI->getPreprocessorOpts().Includes, ElementsAre("foo.h")); + EXPECT_EQ(CI->getPreprocessorOpts().ImplicitPCHInclude, ""); +} + } // namespace } // namespace clang