Index: include/clang/Basic/Diagnostic.h =================================================================== --- include/clang/Basic/Diagnostic.h +++ include/clang/Basic/Diagnostic.h @@ -750,8 +750,21 @@ bool hasUncompilableErrorOccurred() const { return UncompilableErrorOccurred; } + + /// Determine whether a fatal error (either a proper one or + /// promoted from another diagnostic) has occurred. + /// + /// Mainly to be used by clients which are known to have + /// unwanted behavior on severely ill-formed code. bool hasFatalErrorOccurred() const { return FatalErrorOccurred; } + /// Determine whether no more diagnostics are expected, + /// because a fatal error was issued, so clients can cut + /// off extra processing that might be wasteful in this case. + bool areDiagnosticsSuppressedAfterFatalError() const { + return FatalErrorOccurred && SuppressAfterFatalError; + } + /// Determine whether any kind of unrecoverable error has occurred. bool hasUnrecoverableErrorOccurred() const { return FatalErrorOccurred || UnrecoverableErrorOccurred; Index: lib/Lex/PPDirectives.cpp =================================================================== --- lib/Lex/PPDirectives.cpp +++ lib/Lex/PPDirectives.cpp @@ -1899,7 +1899,7 @@ // Any diagnostics after the fatal error will not be visible. As the // compilation failed already and errors in subsequently included files won't // be visible, avoid preprocessing those files. - if (ShouldEnter && Diags->hasFatalErrorOccurred()) + if (ShouldEnter && Diags->areDiagnosticsSuppressedAfterFatalError()) ShouldEnter = false; // Determine whether we should try to import the module for this #include, if Index: lib/Sema/SemaTemplateInstantiate.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiate.cpp +++ lib/Sema/SemaTemplateInstantiate.cpp @@ -218,7 +218,7 @@ // Don't allow further instantiation if a fatal error and an uncompilable // error have occurred. Any diagnostics we might have raised will not be // visible, and we do not need to construct a correct AST. - if (SemaRef.Diags.hasFatalErrorOccurred() && + if (SemaRef.Diags.areDiagnosticsSuppressedAfterFatalError() && SemaRef.Diags.hasUncompilableErrorOccurred()) { Invalid = true; return; Index: unittests/Frontend/PCHPreambleTest.cpp =================================================================== --- unittests/Frontend/PCHPreambleTest.cpp +++ unittests/Frontend/PCHPreambleTest.cpp @@ -77,7 +77,8 @@ RemappedFiles[Filename] = Contents; } - std::unique_ptr ParseAST(const std::string &EntryFile) { + std::unique_ptr ParseAST(const std::string &EntryFile, + bool SuppressAfterFatalError = true) { PCHContainerOpts = std::make_shared(); std::shared_ptr CI(new CompilerInvocation); CI->getFrontendOpts().Inputs.push_back( @@ -88,11 +89,15 @@ CI->getPreprocessorOpts().RemappedFileBuffers = GetRemappedFiles(); + CI->getLangOpts()->CPlusPlus = true; + CI->getLangOpts()->CPlusPlus11 = true; + PreprocessorOptions &PPOpts = CI->getPreprocessorOpts(); PPOpts.RemappedFilesKeepOriginalName = true; IntrusiveRefCntPtr Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions, new DiagnosticConsumer)); + Diags->setSuppressAfterFatalError(SuppressAfterFatalError); FileManager *FileMgr = new FileManager(FSOpts, VFS); @@ -197,4 +202,33 @@ ASSERT_LE(HeaderReadCount, GetFileReadCount(Header)); } +TEST_F(PCHPreambleTest, MissingHeader) { + std::string Header1 = "//./header1.h"; + AddFile(Header1, + "template class C;\n" + "template class C{};\n"); + + std::string Header2 = "//./header2.h"; + AddFile(Header2, "using Alias = C;\n"); + + std::string Main = "//./main.cpp"; + AddFile(Main, + "#include \"nonexistent1\"\n" + "#include \"//./header1.h\"\n" + "#include \"nonexistent2\"\n" + "#include \"//./header2.h\"\n" + "Alias Var;"); + + std::unique_ptr AST(ParseAST(Main, /*SuppressAfterFatalError=*/false)); + ASSERT_TRUE(AST.get()); + + // only "file not found" errors should be emitted, + // "Alias" should be visible for lookup. + auto ExpectedErrorsCount = 2u; + + ASSERT_EQ(AST->getDiagnostics().getClient()->getNumErrors(), ExpectedErrorsCount); + ASSERT_TRUE(ReparseAST(AST)); + ASSERT_EQ(AST->getDiagnostics().getClient()->getNumErrors(), ExpectedErrorsCount); +} + } // anonymous namespace