forked from OSchip/llvm-project
Optionally demote fatal errors to non-fatal errors.
This behavior is enabled when the new CXTranslationUnit_KeepGoing option is passed to clang_parseTranslationUnit{,2}. It is geared towards use by IDEs and similar consumers of the clang-c API where fatal errors may arise when parsing incomplete code mid-edit, or when include paths are not properly configured yet. In such situations one still wants to get as much information as possible about a TU. Previously, the semantic analysis would not instantiate templates or report additional fatal errors after the first fatal error was encountered. Fixes PR24268. Patch by Milian Wolff. llvm-svn: 262318
This commit is contained in:
parent
255689c3ad
commit
016c024ca4
|
@ -1208,7 +1208,18 @@ enum CXTranslationUnit_Flags {
|
||||||
* trades runtime on the first parse (serializing the preamble takes time) for
|
* trades runtime on the first parse (serializing the preamble takes time) for
|
||||||
* reduced runtime on the second parse (can now reuse the preamble).
|
* reduced runtime on the second parse (can now reuse the preamble).
|
||||||
*/
|
*/
|
||||||
CXTranslationUnit_CreatePreambleOnFirstParse = 0x100
|
CXTranslationUnit_CreatePreambleOnFirstParse = 0x100,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Do not stop processing when fatal errors are encountered.
|
||||||
|
*
|
||||||
|
* When fatal errors are encountered while parsing a translation unit,
|
||||||
|
* semantic analysis is typically stopped early when compiling code. A common
|
||||||
|
* source for fatal errors are unresolvable include files. For the
|
||||||
|
* purposes of an IDE, this is undesirable behavior and as much information
|
||||||
|
* as possible should be reported. Use this flag to enable this behavior.
|
||||||
|
*/
|
||||||
|
CXTranslationUnit_KeepGoing = 0x200
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -173,6 +173,7 @@ private:
|
||||||
bool WarningsAsErrors; // Treat warnings like errors.
|
bool WarningsAsErrors; // Treat warnings like errors.
|
||||||
bool EnableAllWarnings; // Enable all warnings.
|
bool EnableAllWarnings; // Enable all warnings.
|
||||||
bool ErrorsAsFatal; // Treat errors like fatal errors.
|
bool ErrorsAsFatal; // Treat errors like fatal errors.
|
||||||
|
bool FatalsAsError; // Treat fatal errors like errors.
|
||||||
bool SuppressSystemWarnings; // Suppress warnings in system headers.
|
bool SuppressSystemWarnings; // Suppress warnings in system headers.
|
||||||
bool SuppressAllDiagnostics; // Suppress all diagnostics.
|
bool SuppressAllDiagnostics; // Suppress all diagnostics.
|
||||||
bool ElideType; // Elide common types of templates.
|
bool ElideType; // Elide common types of templates.
|
||||||
|
@ -455,6 +456,12 @@ public:
|
||||||
void setErrorsAsFatal(bool Val) { ErrorsAsFatal = Val; }
|
void setErrorsAsFatal(bool Val) { ErrorsAsFatal = Val; }
|
||||||
bool getErrorsAsFatal() const { return ErrorsAsFatal; }
|
bool getErrorsAsFatal() const { return ErrorsAsFatal; }
|
||||||
|
|
||||||
|
/// \brief When set to true, any fatal error reported is made an error.
|
||||||
|
///
|
||||||
|
/// This setting takes precedence over the setErrorsAsFatal setting above.
|
||||||
|
void setFatalsAsError(bool Val) { FatalsAsError = Val; }
|
||||||
|
bool getFatalsAsError() const { return FatalsAsError; }
|
||||||
|
|
||||||
/// \brief When set to true mask warnings that come from system headers.
|
/// \brief When set to true mask warnings that come from system headers.
|
||||||
void setSuppressSystemWarnings(bool Val) { SuppressSystemWarnings = Val; }
|
void setSuppressSystemWarnings(bool Val) { SuppressSystemWarnings = Val; }
|
||||||
bool getSuppressSystemWarnings() const { return SuppressSystemWarnings; }
|
bool getSuppressSystemWarnings() const { return SuppressSystemWarnings; }
|
||||||
|
|
|
@ -68,6 +68,7 @@ DiagnosticsEngine::DiagnosticsEngine(
|
||||||
WarningsAsErrors = false;
|
WarningsAsErrors = false;
|
||||||
EnableAllWarnings = false;
|
EnableAllWarnings = false;
|
||||||
ErrorsAsFatal = false;
|
ErrorsAsFatal = false;
|
||||||
|
FatalsAsError = false;
|
||||||
SuppressSystemWarnings = false;
|
SuppressSystemWarnings = false;
|
||||||
SuppressAllDiagnostics = false;
|
SuppressAllDiagnostics = false;
|
||||||
ElideType = true;
|
ElideType = true;
|
||||||
|
|
|
@ -462,6 +462,12 @@ DiagnosticIDs::getDiagnosticSeverity(unsigned DiagID, SourceLocation Loc,
|
||||||
Result = diag::Severity::Fatal;
|
Result = diag::Severity::Fatal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If explicitly requested, map fatal errors to errors.
|
||||||
|
if (Result == diag::Severity::Fatal) {
|
||||||
|
if (Diag.FatalsAsError)
|
||||||
|
Result = diag::Severity::Error;
|
||||||
|
}
|
||||||
|
|
||||||
// Custom diagnostics always are emitted in system headers.
|
// Custom diagnostics always are emitted in system headers.
|
||||||
bool ShowInSystemHeader =
|
bool ShowInSystemHeader =
|
||||||
!GetDiagInfo(DiagID) || GetDiagInfo(DiagID)->WarnShowInSystemHeader;
|
!GetDiagInfo(DiagID) || GetDiagInfo(DiagID)->WarnShowInSystemHeader;
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
#include "missing1.h"
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class A { T a; };
|
||||||
|
|
||||||
|
class B : public A<int> { };
|
||||||
|
|
||||||
|
#include "missing2.h"
|
||||||
|
|
||||||
|
class C : public A<float> { };
|
||||||
|
|
||||||
|
// RUN: env CINDEXTEST_EDITING=1 CINDEXTEST_KEEP_GOING=1 c-index-test -test-print-type %s 2> %t.stderr.txt | FileCheck %s
|
||||||
|
// RUN: FileCheck -check-prefix CHECK-DIAG %s < %t.stderr.txt
|
||||||
|
|
||||||
|
// CHECK: inclusion directive=missing1.h ((null)) [type=] [typekind=Invalid] [isPOD=0]
|
||||||
|
// CHECK: inclusion directive=missing2.h ((null)) [type=] [typekind=Invalid] [isPOD=0]
|
||||||
|
// CHECK: ClassTemplate=A:4:7 (Definition) [type=] [typekind=Invalid] [isPOD=0]
|
||||||
|
// CHECK: TemplateTypeParameter=T:3:16 (Definition) [type=T] [typekind=Unexposed] [canonicaltype=type-parameter-0-0] [canonicaltypekind=Unexposed] [isPOD=0]
|
||||||
|
// CHECK: FieldDecl=a:4:13 (Definition) [type=T] [typekind=Unexposed] [canonicaltype=type-parameter-0-0] [canonicaltypekind=Unexposed] [isPOD=0]
|
||||||
|
// CHECK: TypeRef=T:3:16 [type=T] [typekind=Unexposed] [canonicaltype=type-parameter-0-0] [canonicaltypekind=Unexposed] [isPOD=0]
|
||||||
|
// CHECK: ClassDecl=B:6:7 (Definition) [type=B] [typekind=Record] [isPOD=0]
|
||||||
|
// CHECK: C++ base class specifier=A<int>:4:7 [access=public isVirtual=false] [type=A<int>] [typekind=Unexposed] [canonicaltype=A<int>] [canonicaltypekind=Record] [templateargs/1= [type=int] [typekind=Int]] [isPOD=0] [nbFields=1]
|
||||||
|
// CHECK: TemplateRef=A:4:7 [type=] [typekind=Invalid] [isPOD=0]
|
||||||
|
// CHECK: ClassDecl=C:10:7 (Definition) [type=C] [typekind=Record] [isPOD=0]
|
||||||
|
// CHECK: C++ base class specifier=A<float>:4:7 [access=public isVirtual=false] [type=A<float>] [typekind=Unexposed] [canonicaltype=A<float>] [canonicaltypekind=Record] [templateargs/1= [type=float] [typekind=Float]] [isPOD=0] [nbFields=1]
|
||||||
|
// CHECK: TemplateRef=A:4:7 [type=] [typekind=Invalid] [isPOD=0]
|
||||||
|
|
||||||
|
// CHECK-DIAG: keep-going.cpp:1:10: error: 'missing1.h' file not found
|
||||||
|
// CHECK-DIAG: keep-going.cpp:8:10: error: 'missing2.h' file not found
|
|
@ -80,6 +80,8 @@ static unsigned getDefaultParsingOptions() {
|
||||||
options |= CXTranslationUnit_IncludeBriefCommentsInCodeCompletion;
|
options |= CXTranslationUnit_IncludeBriefCommentsInCodeCompletion;
|
||||||
if (getenv("CINDEXTEST_CREATE_PREAMBLE_ON_FIRST_PARSE"))
|
if (getenv("CINDEXTEST_CREATE_PREAMBLE_ON_FIRST_PARSE"))
|
||||||
options |= CXTranslationUnit_CreatePreambleOnFirstParse;
|
options |= CXTranslationUnit_CreatePreambleOnFirstParse;
|
||||||
|
if (getenv("CINDEXTEST_KEEP_GOING"))
|
||||||
|
options |= CXTranslationUnit_KeepGoing;
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3158,6 +3158,9 @@ clang_parseTranslationUnit_Impl(CXIndex CIdx, const char *source_filename,
|
||||||
IntrusiveRefCntPtr<DiagnosticsEngine>
|
IntrusiveRefCntPtr<DiagnosticsEngine>
|
||||||
Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions));
|
Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions));
|
||||||
|
|
||||||
|
if (options & CXTranslationUnit_KeepGoing)
|
||||||
|
Diags->setFatalsAsError(true);
|
||||||
|
|
||||||
// Recover resources if we crash before exiting this function.
|
// Recover resources if we crash before exiting this function.
|
||||||
llvm::CrashRecoveryContextCleanupRegistrar<DiagnosticsEngine,
|
llvm::CrashRecoveryContextCleanupRegistrar<DiagnosticsEngine,
|
||||||
llvm::CrashRecoveryContextReleaseRefCleanup<DiagnosticsEngine> >
|
llvm::CrashRecoveryContextReleaseRefCleanup<DiagnosticsEngine> >
|
||||||
|
|
|
@ -46,4 +46,27 @@ TEST(DiagnosticTest, suppressAndTrap) {
|
||||||
EXPECT_FALSE(Diags.hasUnrecoverableErrorOccurred());
|
EXPECT_FALSE(Diags.hasUnrecoverableErrorOccurred());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check that FatalsAsErrors works as intended
|
||||||
|
TEST(DiagnosticTest, fatalsAsErrors) {
|
||||||
|
DiagnosticsEngine Diags(new DiagnosticIDs(),
|
||||||
|
new DiagnosticOptions,
|
||||||
|
new IgnoringDiagConsumer());
|
||||||
|
Diags.setFatalsAsError(true);
|
||||||
|
|
||||||
|
// Diag that would set UncompilableErrorOccurred and ErrorOccurred.
|
||||||
|
Diags.Report(diag::err_target_unknown_triple) << "unknown";
|
||||||
|
|
||||||
|
// Diag that would set UnrecoverableErrorOccurred and ErrorOccurred.
|
||||||
|
Diags.Report(diag::err_cannot_open_file) << "file" << "error";
|
||||||
|
|
||||||
|
// Diag that would set FatalErrorOccurred
|
||||||
|
// (via non-note following a fatal error).
|
||||||
|
Diags.Report(diag::warn_mt_message) << "warning";
|
||||||
|
|
||||||
|
EXPECT_TRUE(Diags.hasErrorOccurred());
|
||||||
|
EXPECT_FALSE(Diags.hasFatalErrorOccurred());
|
||||||
|
EXPECT_TRUE(Diags.hasUncompilableErrorOccurred());
|
||||||
|
EXPECT_TRUE(Diags.hasUnrecoverableErrorOccurred());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue