IntrusiveRefCntPtr<TargetOptions> TargetOpts;
IntrusiveRefCntPtr<HeaderSearchOptions> HSOpts;
ASTReader *Reader;
+ bool HadModuleLoaderFatalFailure;
struct ASTWriterData;
OwningPtr<ASTWriterData> WriterData;
SourceLocation ImportLoc,
bool Complain);
+ bool hadModuleLoaderFatalFailure() const {
+ return ModuleLoader::HadFatalFailure;
+ }
+
};
} // end namespace clang
/// then loading that module.
class ModuleLoader {
public:
+ ModuleLoader() : HadFatalFailure(false) {}
+
virtual ~ModuleLoader();
/// \brief Attempt to load the given module.
Module::NameVisibilityKind Visibility,
SourceLocation ImportLoc,
bool Complain) = 0;
+
+ bool HadFatalFailure;
};
}
/// \brief Retrieve the module loader associated with this preprocessor.
ModuleLoader &getModuleLoader() const { return TheModuleLoader; }
+ bool hadModuleLoaderFatalFailure() const {
+ return TheModuleLoader.HadFatalFailure;
+ }
+
/// \brief True if we are currently preprocessing a #if or #elif directive
bool isParsingIfOrElifDirective() const {
return ParsingIfOrElifDirective;
/// \brief Abruptly cut off parsing; mainly used when we have reached the
/// code-completion point.
void cutOffParsing() {
- PP.setCodeCompletionReached();
+ if (PP.isCodeCompletionEnabled())
+ PP.setCodeCompletionReached();
// Cut off parsing by acting as if we reached the end-of-file.
Tok.setKind(tok::eof);
}
/// \brief The module manager which manages modules and their dependencies
ModuleManager ModuleMgr;
+ /// \brief The location where the module file will be considered as
+ /// imported from. For non-module AST types it should be invalid.
+ SourceLocation CurrentImportLoc;
+
/// \brief The global module index, if loaded.
llvm::OwningPtr<GlobalModuleIndex> GlobalIndex;
static llvm::sys::cas_flag ActiveASTUnitObjects;
ASTUnit::ASTUnit(bool _MainFileIsAST)
- : Reader(0), OnlyLocalDecls(false), CaptureDiagnostics(false),
+ : Reader(0), HadModuleLoaderFatalFailure(false),
+ OnlyLocalDecls(false), CaptureDiagnostics(false),
MainFileIsAST(_MainFileIsAST),
TUKind(TU_Complete), WantTiming(getenv("LIBCLANG_TIMING")),
OwnsRemappedFileBuffers(true),
CI.setFileManager(0);
Target = &CI.getTarget();
Reader = CI.getModuleManager();
+ HadModuleLoaderFatalFailure = CI.hadModuleLoaderFatalFailure();
}
StringRef ASTUnit::getMainFileName() const {
}
bool ASTUnit::Save(StringRef File) {
+ if (HadModuleLoaderFatalFailure)
+ return true;
+
// Write to a temporary file and later rename it to the actual file, to avoid
// possible race conditions.
SmallString<128> TempPath;
case ASTReader::VersionMismatch:
case ASTReader::ConfigurationMismatch:
case ASTReader::HadErrors:
+ ModuleLoader::HadFatalFailure = true;
// FIXME: The ASTReader will already have complained, but can we showhorn
// that diagnostic information into a more useful form?
KnownModules[Path[0].first] = 0;
return ModuleLoadResult();
case ASTReader::Failure:
+ ModuleLoader::HadFatalFailure = true;
// Already complained, but note now that we failed.
KnownModules[Path[0].first] = 0;
ModuleBuildFailed = true;
FormTokenWithChars(Result, CurPtr, tok::hash);
PP->HandleDirective(Result);
+ if (PP->hadModuleLoaderFatalFailure()) {
+ // With a fatal failure in the module loader, we abort parsing.
+ assert(Result.is(tok::eof) && "Preprocessor did not set tok:eof");
+ return;
+ }
+
// As an optimization, if the preprocessor didn't switch lexers, tail
// recurse.
if (PP->isCurrentLexer(this)) {
/*IsIncludeDirective=*/true);
assert((Imported == 0 || Imported == SuggestedModule) &&
"the imported module is different than the suggested one");
+
+ if (!Imported && hadModuleLoaderFatalFailure()) {
+ // With a fatal failure in the module loader, we abort parsing.
+ Token &Result = IncludeTok;
+ if (CurLexer) {
+ Result.startToken();
+ CurLexer->FormTokenWithChars(Result, CurLexer->BufferEnd, tok::eof);
+ CurLexer->cutOffLexing();
+ } else {
+ assert(CurPTHLexer && "#include but no current lexer set!");
+ CurPTHLexer->getEOF(Result);
+ }
+ return;
+ }
// If this header isn't part of the module we're building, we're done.
if (!BuildingImportedModule && Imported) {
break;
} while (true);
-
+
+ if (PP.hadModuleLoaderFatalFailure()) {
+ // With a fatal failure in the module loader, we abort parsing.
+ cutOffParsing();
+ return DeclGroupPtrTy();
+ }
+
DeclResult Import = Actions.ActOnModuleImport(AtLoc, ImportLoc, Path);
ExpectAndConsumeSemi(diag::err_module_expected_semi);
if (Import.isInvalid())
ModuleKind Type,
SourceLocation ImportLoc,
unsigned ClientLoadCapabilities) {
+ llvm::SaveAndRestore<SourceLocation>
+ SetCurImportLocRAII(CurrentImportLoc, ImportLoc);
+
// Bump the generation number.
unsigned PreviousGeneration = CurrentGeneration++;
}
DiagnosticBuilder ASTReader::Diag(unsigned DiagID) {
- return Diag(SourceLocation(), DiagID);
+ return Diag(CurrentImportLoc, DiagID);
}
DiagnosticBuilder ASTReader::Diag(SourceLocation Loc, unsigned DiagID) {
--- /dev/null
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: touch %t/Module.pcm
+// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t %s -fdisable-module-hash -F %S/Inputs -verify
+// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t %s -fdisable-module-hash -F %S/Inputs -DIMPLICIT -verify
+
+// This tests that after a fatal module loader error, we do not continue parsing.
+
+#ifdef IMPLICIT
+
+// expected-error@+1{{does not appear to be}}
+#import <Module/Module.h>
+#pragma clang __debug crash;
+
+#else
+
+// expected-error@+1{{does not appear to be}}
+@import Module;
+#pragma clang __debug crash;
+
+#endif
+
+// Also check that libclang does not create a PCH with such an error.
+// RUN: c-index-test -write-pch %t.pch -fmodules -fmodules-cache-path=%t %s \
+// RUN: -Xclang -fdisable-module-hash -F %S/Inputs 2>&1 | Filecheck %s
+// CHECK: Unable to write PCH file