[PCH] Mark a PCH file with a flag to indicate if the serialized AST had

compiler errors or not.

-Control whether ASTReader should reject such a PCH by a boolean flag at ASTReader's creation time.
By default, such a PCH file will be rejected with an error when trying to load it.

[libclang] Allow clang_saveTranslationUnit to create a PCH file even if compiler errors
occurred.
-Have libclang API calls accept a PCH that had compiler errors.

The general idea is that we want libclang to stay functional even if a PCH had a compiler error.
rdar://10976363.

llvm-svn: 152192
This commit is contained in:
Argyrios Kyrtzidis 2012-03-07 01:51:17 +00:00
parent 9b2ab81a0c
commit 4a280ff48f
15 changed files with 97 additions and 26 deletions

View File

@ -37,6 +37,8 @@ def warn_pch_version_too_new : Error<
"PCH file uses a newer PCH format that cannot be read">;
def warn_pch_different_branch : Error<
"PCH file built from a different branch (%0) than the compiler (%1)">;
def err_pch_with_compiler_errors : Error<
"PCH file contains compiler errors">;
def warn_cmdline_conflicting_macro_def : Error<
"definition of the macro '%0' conflicts with the definition used to "
"build the precompiled header">;

View File

@ -642,7 +642,8 @@ public:
bool OnlyLocalDecls = false,
RemappedFile *RemappedFiles = 0,
unsigned NumRemappedFiles = 0,
bool CaptureDiagnostics = false);
bool CaptureDiagnostics = false,
bool AllowPCHWithCompilerErrors = false);
private:
/// \brief Helper function for \c LoadFromCompilerInvocation() and
@ -730,7 +731,8 @@ public:
bool RemappedFilesKeepOriginalName = true,
bool PrecompilePreamble = false,
TranslationUnitKind TUKind = TU_Complete,
bool CacheCodeCompletionResults = false);
bool CacheCodeCompletionResults = false,
bool AllowPCHWithCompilerErrors = false);
/// \brief Reparse the source files using the same command-line options that
/// were originally used to produce this translation unit.

View File

@ -535,6 +535,7 @@ public:
void createPCHExternalASTSource(StringRef Path,
bool DisablePCHValidation,
bool DisableStatCache,
bool AllowPCHWithCompilerErrors,
void *DeserializationListener);
/// Create an external AST source to read a PCH file.
@ -544,6 +545,7 @@ public:
createPCHExternalASTSource(StringRef Path, const std::string &Sysroot,
bool DisablePCHValidation,
bool DisableStatCache,
bool AllowPCHWithCompilerErrors,
Preprocessor &PP, ASTContext &Context,
void *DeserializationListener, bool Preamble);

View File

@ -69,6 +69,9 @@ public:
/// precompiled header or AST file.
bool DisableStatCache;
/// \brief When true, a PCH with compiler errors will not be rejected.
bool AllowPCHWithCompilerErrors;
/// \brief Dump declarations that are deserialized from PCH, for testing.
bool DumpDeserializedPCHDecls;
@ -165,6 +168,7 @@ public:
PreprocessorOptions() : UsePredefines(true), DetailedRecord(false),
DetailedRecordConditionalDirectives(false),
DisablePCHValidation(false), DisableStatCache(false),
AllowPCHWithCompilerErrors(false),
DumpDeserializedPCHDecls(false),
PrecompiledPreambleBytes(0, true),
RemappedFilesKeepOriginalName(true),

View File

@ -579,6 +579,9 @@ private:
/// \brief Whether to disable the use of stat caches in AST files.
bool DisableStatCache;
/// \brief Whether to accept an AST file with compiler errors.
bool AllowASTWithCompilerErrors;
/// \brief The current "generation" of the module file import stack, which
/// indicates how many separate module file load operations have occurred.
unsigned CurrentGeneration;
@ -875,8 +878,13 @@ public:
/// help when an AST file is being used in cases where the
/// underlying files in the file system may have changed, but
/// parsing should still continue.
///
/// \param AllowASTWithCompilerErrors If true, the AST reader will accept an
/// AST file the was created out of an AST with compiler errors,
/// otherwise it will reject it.
ASTReader(Preprocessor &PP, ASTContext &Context, StringRef isysroot = "",
bool DisableValidation = false, bool DisableStatCache = false);
bool DisableValidation = false, bool DisableStatCache = false,
bool AllowASTWithCompilerErrors = false);
~ASTReader();

View File

@ -109,6 +109,9 @@ private:
/// serialization, rather than just queueing updates.
bool WritingAST;
/// \brief Indicates that the AST contained compiler errors.
bool ASTHasCompilerErrors;
/// \brief Stores a declaration or a type to be written to the AST file.
class DeclOrType {
public:
@ -467,7 +470,8 @@ public:
/// are relative to the given system root.
void WriteAST(Sema &SemaRef, MemorizeStatCalls *StatCalls,
const std::string &OutputFile,
Module *WritingModule, StringRef isysroot);
Module *WritingModule, StringRef isysroot,
bool hasErrors = false);
/// \brief Emit a source location.
void AddSourceLocation(SourceLocation Loc, RecordDataImpl &Record);

View File

@ -652,7 +652,8 @@ ASTUnit *ASTUnit::LoadFromASTFile(const std::string &Filename,
bool OnlyLocalDecls,
RemappedFile *RemappedFiles,
unsigned NumRemappedFiles,
bool CaptureDiagnostics) {
bool CaptureDiagnostics,
bool AllowPCHWithCompilerErrors) {
OwningPtr<ASTUnit> AST(new ASTUnit(true));
// Recover resources if we crash before exiting this method.
@ -748,7 +749,11 @@ ASTUnit *ASTUnit::LoadFromASTFile(const std::string &Filename,
/*DelayInitialization=*/true);
ASTContext &Context = *AST->Ctx;
Reader.reset(new ASTReader(PP, Context));
Reader.reset(new ASTReader(PP, Context,
/*isysroot=*/"",
/*DisableValidation=*/false,
/*DisableStatCache=*/false,
AllowPCHWithCompilerErrors));
// Recover resources if we crash before exiting this method.
llvm::CrashRecoveryContextCleanupRegistrar<ASTReader>
@ -1862,7 +1867,8 @@ ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin,
bool RemappedFilesKeepOriginalName,
bool PrecompilePreamble,
TranslationUnitKind TUKind,
bool CacheCodeCompletionResults) {
bool CacheCodeCompletionResults,
bool AllowPCHWithCompilerErrors) {
if (!Diags.getPtr()) {
// No diagnostics engine was provided, so create our own diagnostics object
// with the default options.
@ -1898,8 +1904,9 @@ ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin,
CI->getPreprocessorOpts().addRemappedFile(RemappedFiles[I].first, fname);
}
}
CI->getPreprocessorOpts().RemappedFilesKeepOriginalName =
RemappedFilesKeepOriginalName;
PreprocessorOptions &PPOpts = CI->getPreprocessorOpts();
PPOpts.RemappedFilesKeepOriginalName = RemappedFilesKeepOriginalName;
PPOpts.AllowPCHWithCompilerErrors = AllowPCHWithCompilerErrors;
// Override the resources path.
CI->getHeaderSearchOpts().ResourceDir = ResourceFilesPath;
@ -2388,9 +2395,6 @@ void ASTUnit::CodeComplete(StringRef File, unsigned Line, unsigned Column,
}
CXSaveError ASTUnit::Save(StringRef File) {
if (getDiagnostics().hasUnrecoverableErrorOccurred())
return CXSaveError_TranslationErrors;
// Write to a temporary file and later rename it to the actual file, to avoid
// possible race conditions.
SmallString<128> TempPath;
@ -2420,14 +2424,13 @@ CXSaveError ASTUnit::Save(StringRef File) {
}
bool ASTUnit::serialize(raw_ostream &OS) {
if (getDiagnostics().hasErrorOccurred())
return true;
bool hasErrors = getDiagnostics().hasErrorOccurred();
SmallString<128> Buffer;
llvm::BitstreamWriter Stream(Buffer);
ASTWriter Writer(Stream);
// FIXME: Handle modules
Writer.WriteAST(getSema(), 0, std::string(), 0, "");
Writer.WriteAST(getSema(), 0, std::string(), 0, "", hasErrors);
// Write the generated bitstream to "Out".
if (!Buffer.empty())

View File

@ -306,12 +306,14 @@ void CompilerInstance::createASTContext() {
void CompilerInstance::createPCHExternalASTSource(StringRef Path,
bool DisablePCHValidation,
bool DisableStatCache,
bool AllowPCHWithCompilerErrors,
void *DeserializationListener){
OwningPtr<ExternalASTSource> Source;
bool Preamble = getPreprocessorOpts().PrecompiledPreambleBytes.first != 0;
Source.reset(createPCHExternalASTSource(Path, getHeaderSearchOpts().Sysroot,
DisablePCHValidation,
DisableStatCache,
AllowPCHWithCompilerErrors,
getPreprocessor(), getASTContext(),
DeserializationListener,
Preamble));
@ -324,6 +326,7 @@ CompilerInstance::createPCHExternalASTSource(StringRef Path,
const std::string &Sysroot,
bool DisablePCHValidation,
bool DisableStatCache,
bool AllowPCHWithCompilerErrors,
Preprocessor &PP,
ASTContext &Context,
void *DeserializationListener,
@ -331,7 +334,8 @@ CompilerInstance::createPCHExternalASTSource(StringRef Path,
OwningPtr<ASTReader> Reader;
Reader.reset(new ASTReader(PP, Context,
Sysroot.empty() ? "" : Sysroot.c_str(),
DisablePCHValidation, DisableStatCache));
DisablePCHValidation, DisableStatCache,
AllowPCHWithCompilerErrors));
Reader->setDeserializationListener(
static_cast<ASTDeserializationListener *>(DeserializationListener));

View File

@ -268,6 +268,7 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
CI.getPreprocessorOpts().ImplicitPCHInclude,
CI.getPreprocessorOpts().DisablePCHValidation,
CI.getPreprocessorOpts().DisableStatCache,
CI.getPreprocessorOpts().AllowPCHWithCompilerErrors,
DeserialListener);
if (!CI.getASTContext().getExternalSource())
goto failure;

View File

@ -1807,6 +1807,12 @@ ASTReader::ReadASTBlock(ModuleFile &F) {
return IgnorePCH;
}
bool hasErrors = Record[5];
if (hasErrors && !DisableValidation && !AllowASTWithCompilerErrors) {
Diag(diag::err_pch_with_compiler_errors);
return IgnorePCH;
}
RelocatablePCH = Record[4];
if (Listener) {
std::string TargetTriple(BlobStart, BlobLen);
@ -6282,14 +6288,15 @@ void ASTReader::FinishedDeserializing() {
ASTReader::ASTReader(Preprocessor &PP, ASTContext &Context,
StringRef isysroot, bool DisableValidation,
bool DisableStatCache)
bool DisableStatCache, bool AllowASTWithCompilerErrors)
: Listener(new PCHValidator(PP, *this)), DeserializationListener(0),
SourceMgr(PP.getSourceManager()), FileMgr(PP.getFileManager()),
Diags(PP.getDiagnostics()), SemaObj(0), PP(PP), Context(Context),
Consumer(0), ModuleMgr(FileMgr.getFileSystemOptions()),
RelocatablePCH(false), isysroot(isysroot),
DisableValidation(DisableValidation),
DisableStatCache(DisableStatCache),
DisableStatCache(DisableStatCache),
AllowASTWithCompilerErrors(AllowASTWithCompilerErrors),
CurrentGeneration(0), NumStatHits(0), NumStatMisses(0),
NumSLocEntriesRead(0), TotalNumSLocEntries(0),
NumStatementsRead(0), TotalNumStatements(0), NumMacrosRead(0),

View File

@ -981,6 +981,7 @@ void ASTWriter::WriteMetadata(ASTContext &Context, StringRef isysroot,
MetaAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Clang major
MetaAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Clang minor
MetaAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // Relocatable
MetaAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // Has errors
MetaAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Target triple
unsigned MetaAbbrevCode = Stream.EmitAbbrev(MetaAbbrev);
@ -991,6 +992,7 @@ void ASTWriter::WriteMetadata(ASTContext &Context, StringRef isysroot,
Record.push_back(CLANG_VERSION_MAJOR);
Record.push_back(CLANG_VERSION_MINOR);
Record.push_back(!isysroot.empty());
Record.push_back(ASTHasCompilerErrors);
const std::string &Triple = Target.getTriple().getTriple();
Stream.EmitRecordWithBlob(MetaAbbrevCode, Record, Triple);
@ -3115,7 +3117,7 @@ void ASTWriter::SetSelectorOffset(Selector Sel, uint32_t Offset) {
ASTWriter::ASTWriter(llvm::BitstreamWriter &Stream)
: Stream(Stream), Context(0), PP(0), Chain(0), WritingModule(0),
WritingAST(false),
WritingAST(false), ASTHasCompilerErrors(false),
FirstDeclID(NUM_PREDEF_DECL_IDS), NextDeclID(FirstDeclID),
FirstTypeID(NUM_PREDEF_TYPE_IDS), NextTypeID(FirstTypeID),
FirstIdentID(NUM_PREDEF_IDENT_IDS), NextIdentID(FirstIdentID),
@ -3144,9 +3146,12 @@ ASTWriter::~ASTWriter() {
void ASTWriter::WriteAST(Sema &SemaRef, MemorizeStatCalls *StatCalls,
const std::string &OutputFile,
Module *WritingModule, StringRef isysroot) {
Module *WritingModule, StringRef isysroot,
bool hasErrors) {
WritingAST = true;
ASTHasCompilerErrors = hasErrors;
// Emit the file header.
Stream.Emit((unsigned)'C', 8);
Stream.Emit((unsigned)'P', 8);

View File

@ -0,0 +1,28 @@
#ifndef HEADER
#define HEADER
void erroneous(int);
void erroneous(float);
#else
void foo(void) {
erroneous(0);
}
#endif
// RUN: c-index-test -write-pch %t.h.pch %s
// RUN: c-index-test -test-load-source local %s -include %t.h | FileCheck -check-prefix=CHECK-PARSE %s
// RUN: c-index-test -index-file %s -include %t.h | FileCheck -check-prefix=CHECK-INDEX %s
// CHECK-PARSE: pch-with-errors.c:10:6: FunctionDecl=foo:10:6 (Definition) Extent=[10:1 - 12:2]
// CHECK-PARSE: pch-with-errors.c:11:3: CallExpr=erroneous:5:6 Extent=[11:3 - 11:15]
// CHECK-INDEX: [indexDeclaration]: kind: function | name: foo
// CHECK-INDEX: [indexEntityReference]: kind: function | name: erroneous
// RUN: %clang -fsyntax-only %s -include %t.h 2>&1 | FileCheck -check-prefix=PCH-ERR %s
// PCH-ERR: error: PCH file contains compiler errors

View File

@ -7,9 +7,6 @@ void fatal(int);
void fatal(float);
#endif
// CHECK-FATAL: translation errors
// RUN: c-index-test -write-pch %t.pch -Werror %s
// RUN: not c-index-test -write-pch %t.pch -DFATAL -Werror %s 2>%t.err
// RUN: FileCheck -check-prefix=CHECK-FATAL %s < %t.err
// RUN: c-index-test -write-pch %t.pch -DFATAL -Werror %s

View File

@ -2438,7 +2438,9 @@ CXTranslationUnit clang_createTranslationUnit(CXIndex CIdx,
IntrusiveRefCntPtr<DiagnosticsEngine> Diags;
ASTUnit *TU = ASTUnit::LoadFromASTFile(ast_filename, Diags, FileSystemOpts,
CXXIdx->getOnlyLocalDecls(),
0, 0, true);
0, 0,
/*CaptureDiagnostics=*/true,
/*AllowPCHWithCompilerErrors=*/true);
return MakeCXTranslationUnit(TU);
}
@ -2575,7 +2577,8 @@ static void clang_parseTranslationUnit_Impl(void *UserData) {
/*RemappedFilesKeepOriginalName=*/true,
PrecompilePreamble,
TUKind,
CacheCodeCompetionResults));
CacheCodeCompetionResults,
/*AllowPCHWithCompilerErrors=*/true));
if (NumErrors != Diags->getClient()->getNumErrors()) {
// Make sure to check that 'Unit' is non-NULL.

View File

@ -369,6 +369,7 @@ static void clang_indexSourceFile_Impl(void *UserData) {
bool CacheCodeCompletionResults = false;
PreprocessorOptions &PPOpts = CInvok->getPreprocessorOpts();
PPOpts.DetailedRecord = false;
PPOpts.AllowPCHWithCompilerErrors = true;
if (requestedToGetTU) {
OnlyLocalDecls = CXXIdx->getOnlyLocalDecls();