Skip to content

Commit 04b4190

Browse files
committed
[Driver] Make "upgrade" of -include to include-pch optional; disable in clangd
If clang is passed "-include foo.h", it will rewrite to "-include-pch foo.h.pch" before passing it to cc1, if foo.h.pch exists. Existence is checked, but validity is not. This is probably a reasonable assumption for the compiler itself, but not for clang-based tools where the actual compiler may be a different version of clang, or even GCC. In the end, we lose our -include, we gain a -include-pch that can't be used, and the file often fails to parse. I would like to turn this off for all non-clang invocations (i.e. createInvocationFromCommandLine), but we have explicit tests of this behavior for libclang and I can't work out the implications of changing it. Instead this patch: - makes it optional in the driver, default on (no change) - makes it optional in createInvocationFromCommandLine, default on (no change) - changes driver to do IO through the VFS so it can be tested - tests the option - turns the option off in clangd where the problem was reported Subsequent patches should make libclang opt in explicitly and flip the default for all other tools. It's probably also time to extract an options struct for createInvocationFromCommandLine. Fixes clangd/clangd#856 Fixes clangd/vscode-clangd#324 Differential Revision: https://reviews.llvm.org/D124970
1 parent 042a7a5 commit 04b4190

File tree

7 files changed

+52
-8
lines changed

7 files changed

+52
-8
lines changed

clang-tools-extra/clangd/Compiler.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ buildCompilerInvocation(const ParseInputs &Inputs, clang::DiagnosticConsumer &D,
9797
CIOpts.RecoverOnError = true;
9898
CIOpts.Diags =
9999
CompilerInstance::createDiagnostics(new DiagnosticOptions, &D, false);
100+
CIOpts.ProbePrecompiled = false;
100101
std::unique_ptr<CompilerInvocation> CI = createInvocation(ArgStrs, CIOpts);
101102
if (!CI)
102103
return nullptr;

clang/include/clang/Driver/Driver.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,9 @@ class Driver {
271271
/// Whether to check that input files exist when constructing compilation
272272
/// jobs.
273273
unsigned CheckInputsExist : 1;
274+
/// Whether to probe for PCH files on disk, in order to upgrade
275+
/// -include foo.h to -include-pch foo.h.pch.
276+
unsigned ProbePrecompiled : 1;
274277

275278
public:
276279
/// Force clang to emit reproducer for driver invocation. This is enabled
@@ -357,6 +360,9 @@ class Driver {
357360

358361
void setCheckInputsExist(bool Value) { CheckInputsExist = Value; }
359362

363+
bool getProbePrecompiled() const { return ProbePrecompiled; }
364+
void setProbePrecompiled(bool Value) { ProbePrecompiled = Value; }
365+
360366
void setTargetAndMode(const ParsedClangName &TM) { ClangNameParts = TM; }
361367

362368
const std::string &getTitle() { return DriverTitle; }

clang/include/clang/Frontend/Utils.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,11 @@ struct CreateInvocationOptions {
202202
/// if any errors were encountered.
203203
/// By default, always return null on errors.
204204
bool RecoverOnError = false;
205+
/// Allow the driver to probe the filesystem for PCH files.
206+
/// This is used to replace -include with -include-pch in the cc1 args.
207+
/// FIXME: ProbePrecompiled=true is a poor, historical default.
208+
/// It misbehaves if the PCH file is from GCC, has the wrong version, etc.
209+
bool ProbePrecompiled = true;
205210
/// If set, the target is populated with the cc1 args produced by the driver.
206211
/// This may be populated even if createInvocation returns nullptr.
207212
std::vector<std::string> *CC1Args = nullptr;
@@ -236,7 +241,7 @@ std::unique_ptr<CompilerInvocation> createInvocationFromCommandLine(
236241
IntrusiveRefCntPtr<DiagnosticsEngine>(),
237242
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS = nullptr,
238243
bool ShouldRecoverOnErrors = false,
239-
std::vector<std::string> *CC1Args = nullptr);
244+
std::vector<std::string> *CC1Args = nullptr, bool ProbePrecompiled = true);
240245

241246
} // namespace clang
242247

clang/lib/Driver/Driver.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,8 @@ Driver::Driver(StringRef ClangExecutable, StringRef TargetTriple,
198198
CCPrintOptions(false), CCPrintHeaders(false), CCLogDiagnostics(false),
199199
CCGenDiagnostics(false), CCPrintProcessStats(false),
200200
TargetTriple(TargetTriple), Saver(Alloc), CheckInputsExist(true),
201-
GenReproducer(false), SuppressMissingInputWarning(false) {
201+
ProbePrecompiled(true), GenReproducer(false),
202+
SuppressMissingInputWarning(false) {
202203
// Provide a sane fallback if no VFS is specified.
203204
if (!this->VFS)
204205
this->VFS = llvm::vfs::getRealFileSystem();

clang/lib/Driver/ToolChains/Clang.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1364,7 +1364,8 @@ void Clang::AddPreprocessingOptions(Compilation &C, const JobAction &JA,
13641364

13651365
bool RenderedImplicitInclude = false;
13661366
for (const Arg *A : Args.filtered(options::OPT_clang_i_Group)) {
1367-
if (A->getOption().matches(options::OPT_include)) {
1367+
if (A->getOption().matches(options::OPT_include) &&
1368+
D.getProbePrecompiled()) {
13681369
// Handling of gcc-style gch precompiled headers.
13691370
bool IsFirstImplicitInclude = !RenderedImplicitInclude;
13701371
RenderedImplicitInclude = true;
@@ -1375,12 +1376,12 @@ void Clang::AddPreprocessingOptions(Compilation &C, const JobAction &JA,
13751376
// so that replace_extension does the right thing.
13761377
P += ".dummy";
13771378
llvm::sys::path::replace_extension(P, "pch");
1378-
if (llvm::sys::fs::exists(P))
1379+
if (D.getVFS().exists(P))
13791380
FoundPCH = true;
13801381

13811382
if (!FoundPCH) {
13821383
llvm::sys::path::replace_extension(P, "gch");
1383-
if (llvm::sys::fs::exists(P)) {
1384+
if (D.getVFS().exists(P)) {
13841385
FoundPCH = true;
13851386
}
13861387
}

clang/lib/Frontend/CreateInvocationFromCommandLine.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ clang::createInvocation(ArrayRef<const char *> ArgList,
4848

4949
// Don't check that inputs exist, they may have been remapped.
5050
TheDriver.setCheckInputsExist(false);
51+
TheDriver.setProbePrecompiled(Opts.ProbePrecompiled);
5152

5253
std::unique_ptr<driver::Compilation> C(TheDriver.BuildCompilation(Args));
5354
if (!C)
@@ -107,8 +108,8 @@ clang::createInvocation(ArrayRef<const char *> ArgList,
107108
std::unique_ptr<CompilerInvocation> clang::createInvocationFromCommandLine(
108109
ArrayRef<const char *> Args, IntrusiveRefCntPtr<DiagnosticsEngine> Diags,
109110
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, bool ShouldRecoverOnErrors,
110-
std::vector<std::string> *CC1Args) {
111+
std::vector<std::string> *CC1Args, bool ProbePrecompiled) {
111112
return createInvocation(
112-
Args,
113-
CreateInvocationOptions{Diags, VFS, ShouldRecoverOnErrors, CC1Args});
113+
Args, CreateInvocationOptions{Diags, VFS, ShouldRecoverOnErrors,
114+
ProbePrecompiled, CC1Args});
114115
}

clang/unittests/Frontend/UtilsTest.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,15 @@
1111
#include "clang/Basic/TargetOptions.h"
1212
#include "clang/Frontend/CompilerInstance.h"
1313
#include "clang/Frontend/CompilerInvocation.h"
14+
#include "clang/Lex/PreprocessorOptions.h"
15+
#include "llvm/ADT/IntrusiveRefCntPtr.h"
1416
#include "llvm/Support/VirtualFileSystem.h"
1517
#include "gmock/gmock.h"
1618
#include "gtest/gtest.h"
1719

1820
namespace clang {
1921
namespace {
22+
using testing::ElementsAre;
2023

2124
TEST(BuildCompilerInvocationTest, RecoverMultipleJobs) {
2225
// This generates multiple jobs and we recover by using the first.
@@ -33,5 +36,31 @@ TEST(BuildCompilerInvocationTest, RecoverMultipleJobs) {
3336
EXPECT_THAT(CI->TargetOpts->Triple, testing::StartsWith("i386-"));
3437
}
3538

39+
// buildInvocationFromCommandLine should not translate -include to -include-pch,
40+
// even if the PCH file exists.
41+
TEST(BuildCompilerInvocationTest, ProbePrecompiled) {
42+
std::vector<const char *> Args = {"clang", "-include", "foo.h", "foo.cpp"};
43+
auto FS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
44+
FS->addFile("foo.h", 0, llvm::MemoryBuffer::getMemBuffer(""));
45+
FS->addFile("foo.h.pch", 0, llvm::MemoryBuffer::getMemBuffer(""));
46+
47+
clang::IgnoringDiagConsumer D;
48+
llvm::IntrusiveRefCntPtr<DiagnosticsEngine> CommandLineDiagsEngine =
49+
clang::CompilerInstance::createDiagnostics(new DiagnosticOptions, &D,
50+
false);
51+
// Default: ProbePrecompiled is true.
52+
std::unique_ptr<CompilerInvocation> CI = createInvocationFromCommandLine(
53+
Args, CommandLineDiagsEngine, FS, false, nullptr);
54+
ASSERT_TRUE(CI);
55+
EXPECT_THAT(CI->getPreprocessorOpts().Includes, ElementsAre());
56+
EXPECT_EQ(CI->getPreprocessorOpts().ImplicitPCHInclude, "foo.h.pch");
57+
58+
CI = createInvocationFromCommandLine(Args, CommandLineDiagsEngine, FS, false,
59+
nullptr, /*ProbePrecompiled=*/false);
60+
ASSERT_TRUE(CI);
61+
EXPECT_THAT(CI->getPreprocessorOpts().Includes, ElementsAre("foo.h"));
62+
EXPECT_EQ(CI->getPreprocessorOpts().ImplicitPCHInclude, "");
63+
}
64+
3665
} // namespace
3766
} // namespace clang

0 commit comments

Comments
 (0)