[Frontend] Allow additional files for diagnostic verifier

This change adds a frontend flag, -verify-additional-file, which can be used to pass extra files directly to the diagnostic verifier. These files are not otherwise considered to be Swift source files; they are not compiled or even properly parsed.

This feature can be used to verify diagnostics emitted in non-source files, such as in module interfaces or header files.
This commit is contained in:
Brent Royal-Gordon
2020-09-18 17:52:38 -07:00
committed by Alexis Laferrière
parent ba00258c66
commit 5036a55550
7 changed files with 50 additions and 12 deletions

View File

@@ -41,6 +41,10 @@ public:
/// Indicates whether diagnostic passes should be skipped. /// Indicates whether diagnostic passes should be skipped.
bool SkipDiagnosticPasses = false; bool SkipDiagnosticPasses = false;
/// Additional non-source files which will have diagnostics emitted in them,
/// and which should be scanned for expectations by the diagnostic verifier.
std::vector<std::string> AdditionalVerifierFiles;
/// Keep emitting subsequent diagnostics after a fatal error. /// Keep emitting subsequent diagnostics after a fatal error.
bool ShowDiagnosticsAfterFatalError = false; bool ShowDiagnosticsAfterFatalError = false;
@@ -54,22 +58,23 @@ public:
/// Treat all warnings as errors /// Treat all warnings as errors
bool WarningsAsErrors = false; bool WarningsAsErrors = false;
// When printing diagnostics, include the diagnostic name at the end /// When printing diagnostics, include the diagnostic name (diag::whatever) at
/// the end.
bool PrintDiagnosticNames = false; bool PrintDiagnosticNames = false;
/// If set to true, include educational notes in printed output if available. /// If set to true, include educational notes in printed output if available.
/// Educational notes are documentation which supplement diagnostics. /// Educational notes are documentation which supplement diagnostics.
bool PrintEducationalNotes = false; bool PrintEducationalNotes = false;
// If set to true, use the more descriptive experimental formatting style for /// Whether to emit diagnostics in the terse LLVM style or in a more
// diagnostics. /// descriptive style that's specific to Swift (currently experimental).
FormattingStyle PrintedFormattingStyle = FormattingStyle::LLVM; FormattingStyle PrintedFormattingStyle = FormattingStyle::LLVM;
std::string DiagnosticDocumentationPath = ""; std::string DiagnosticDocumentationPath = "";
std::string LocalizationCode = ""; std::string LocalizationCode = "";
// Diagnostic messages directory path. /// Path to a directory of diagnostic localization tables.
std::string LocalizationPath = ""; std::string LocalizationPath = "";
/// Return a hash code of any components from these options that should /// Return a hash code of any components from these options that should

View File

@@ -65,6 +65,7 @@ class DiagnosticVerifier : public DiagnosticConsumer {
SourceManager &SM; SourceManager &SM;
std::vector<CapturedDiagnosticInfo> CapturedDiagnostics; std::vector<CapturedDiagnosticInfo> CapturedDiagnostics;
ArrayRef<unsigned> BufferIDs; ArrayRef<unsigned> BufferIDs;
SmallVector<unsigned, 4> AdditionalBufferIDs;
bool AutoApplyFixes; bool AutoApplyFixes;
bool IgnoreUnknown; bool IgnoreUnknown;
@@ -74,6 +75,10 @@ public:
: SM(SM), BufferIDs(BufferIDs), AutoApplyFixes(AutoApplyFixes), : SM(SM), BufferIDs(BufferIDs), AutoApplyFixes(AutoApplyFixes),
IgnoreUnknown(IgnoreUnknown) {} IgnoreUnknown(IgnoreUnknown) {}
void appendAdditionalBufferID(unsigned bufferID) {
AdditionalBufferIDs.push_back(bufferID);
}
virtual void handleDiagnostic(SourceManager &SM, virtual void handleDiagnostic(SourceManager &SM,
const DiagnosticInfo &Info) override; const DiagnosticInfo &Info) override;

View File

@@ -560,8 +560,11 @@ private:
bool setUpInputs(); bool setUpInputs();
bool setUpASTContextIfNeeded(); bool setUpASTContextIfNeeded();
void setupStatsReporter(); void setupStatsReporter();
void setupDiagnosticVerifierIfNeeded();
void setupDependencyTrackerIfNeeded(); void setupDependencyTrackerIfNeeded();
/// \return false if successsful, true on error.
bool setupDiagnosticVerifierIfNeeded();
Optional<unsigned> setUpCodeCompletionBuffer(); Optional<unsigned> setUpCodeCompletionBuffer();
/// Find a buffer for a given input file and ensure it is recorded in /// Find a buffer for a given input file and ensure it is recorded in

View File

@@ -114,6 +114,8 @@ def tbd_is_installapi: Flag<["-"], "tbd-is-installapi">,
def verify : Flag<["-"], "verify">, def verify : Flag<["-"], "verify">,
HelpText<"Verify diagnostics against expected-{error|warning|note} " HelpText<"Verify diagnostics against expected-{error|warning|note} "
"annotations">; "annotations">;
def verify_additional_file : Separate<["-"], "verify-additional-file">,
HelpText<"Verify diagnostics in this file in addition to source files">;
def verify_apply_fixes : Flag<["-"], "verify-apply-fixes">, def verify_apply_fixes : Flag<["-"], "verify-apply-fixes">,
HelpText<"Like -verify, but updates the original source file">; HelpText<"Like -verify, but updates the original source file">;
def verify_ignore_unknown: Flag<["-"], "verify-ignore-unknown">, def verify_ignore_unknown: Flag<["-"], "verify-ignore-unknown">,

View File

@@ -957,6 +957,9 @@ static bool ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args,
Opts.ShowDiagnosticsAfterFatalError |= Opts.ShowDiagnosticsAfterFatalError |=
Args.hasArg(OPT_show_diagnostics_after_fatal); Args.hasArg(OPT_show_diagnostics_after_fatal);
for (Arg *A : Args.filtered(OPT_verify_additional_file))
Opts.AdditionalVerifierFiles.push_back(A->getValue());
Opts.UseColor |= Opts.UseColor |=
Args.hasFlag(OPT_color_diagnostics, Args.hasFlag(OPT_color_diagnostics,
OPT_no_color_diagnostics, OPT_no_color_diagnostics,

View File

@@ -978,7 +978,9 @@ void DiagnosticVerifier::handleDiagnostic(SourceManager &SM,
bool DiagnosticVerifier::finishProcessing() { bool DiagnosticVerifier::finishProcessing() {
DiagnosticVerifier::Result Result = {false, false}; DiagnosticVerifier::Result Result = {false, false};
for (auto &BufferID : BufferIDs) { ArrayRef<unsigned> BufferIDLists[2] = { BufferIDs, AdditionalBufferIDs };
for (ArrayRef<unsigned> BufferIDList : BufferIDLists)
for (auto &BufferID : BufferIDList) {
DiagnosticVerifier::Result FileResult = verifyFile(BufferID); DiagnosticVerifier::Result FileResult = verifyFile(BufferID);
Result.HadError |= FileResult.HadError; Result.HadError |= FileResult.HadError;
Result.HadUnexpectedDiag |= FileResult.HadUnexpectedDiag; Result.HadUnexpectedDiag |= FileResult.HadUnexpectedDiag;

View File

@@ -290,15 +290,31 @@ void CompilerInstance::setupStatsReporter() {
Stats = std::move(Reporter); Stats = std::move(Reporter);
} }
void CompilerInstance::setupDiagnosticVerifierIfNeeded() { bool CompilerInstance::setupDiagnosticVerifierIfNeeded() {
auto &diagOpts = Invocation.getDiagnosticOptions(); auto &diagOpts = Invocation.getDiagnosticOptions();
bool hadError = false;
if (diagOpts.VerifyMode != DiagnosticOptions::NoVerify) { if (diagOpts.VerifyMode != DiagnosticOptions::NoVerify) {
DiagVerifier = std::make_unique<DiagnosticVerifier>( DiagVerifier = std::make_unique<DiagnosticVerifier>(
SourceMgr, InputSourceCodeBufferIDs, SourceMgr, InputSourceCodeBufferIDs,
diagOpts.VerifyMode == DiagnosticOptions::VerifyAndApplyFixes, diagOpts.VerifyMode == DiagnosticOptions::VerifyAndApplyFixes,
diagOpts.VerifyIgnoreUnknown); diagOpts.VerifyIgnoreUnknown);
for (const auto &filename : diagOpts.AdditionalVerifierFiles) {
auto result = getFileSystem().getBufferForFile(filename);
if (!result) {
Diagnostics.diagnose(SourceLoc(), diag::error_open_input_file,
filename, result.getError().message());
hadError |= true;
}
auto bufferID = SourceMgr.addNewSourceBuffer(std::move(result.get()));
DiagVerifier->appendAdditionalBufferID(bufferID);
}
addDiagnosticConsumer(DiagVerifier.get()); addDiagnosticConsumer(DiagVerifier.get());
} }
return hadError;
} }
void CompilerInstance::setupDependencyTrackerIfNeeded() { void CompilerInstance::setupDependencyTrackerIfNeeded() {
@@ -347,7 +363,9 @@ bool CompilerInstance::setup(const CompilerInvocation &Invok) {
return true; return true;
setupStatsReporter(); setupStatsReporter();
setupDiagnosticVerifierIfNeeded();
if (setupDiagnosticVerifierIfNeeded())
return true;
return false; return false;
} }