diff --git a/flang/include/flang/Frontend/FrontendAction.h b/flang/include/flang/Frontend/FrontendAction.h --- a/flang/include/flang/Frontend/FrontendAction.h +++ b/flang/include/flang/Frontend/FrontendAction.h @@ -43,7 +43,7 @@ /// /// \return True on success; on failure ExecutionAction() and /// EndSourceFileAction() will not be called. - virtual bool BeginSourceFileAction(CompilerInstance &ci) { return true; } + virtual bool BeginSourceFileAction() { return true; } /// @} @@ -100,6 +100,34 @@ /// Perform any per-file post processing, deallocate per-file /// objects, and run statistics and output file cleanup code. void EndSourceFile(); + + /// @} +protected: + // Prescan the current input file. Return False if fatal errors are reported, + // True otherwise. + bool RunPrescan(); + // Parse the current input file. Return False if fatal errors are reported, + // True otherwise. + bool RunParse(); + // Run semantic checks for the current input file. Return False if fatal + // errors are reported, True otherwise. + bool RunSemanticChecks(); + + // Report fatal semantic errors. Return True if present, false otherwise. + bool reportFatalSemanticErrors(); + + // Report fatal scanning errors. Return True if present, false otherwise. + inline bool reportFatalScanningErrors() { + return reportFatalErrors("Could not scan %0"); + } + + // Report fatal parsing errors. Return True if present, false otherwise + inline bool reportFatalParsingErrors() { + return reportFatalErrors("Could not parse %0"); + } + +private: + template bool reportFatalErrors(const char (&message)[N]); }; } // namespace Fortran::frontend diff --git a/flang/include/flang/Frontend/FrontendActions.h b/flang/include/flang/Frontend/FrontendActions.h --- a/flang/include/flang/Frontend/FrontendActions.h +++ b/flang/include/flang/Frontend/FrontendActions.h @@ -51,7 +51,7 @@ //===----------------------------------------------------------------------===// class PrescanAction : public FrontendAction { void ExecuteAction() override = 0; - bool BeginSourceFileAction(CompilerInstance &ci) override; + bool BeginSourceFileAction() override; }; class PrintPreprocessedAction : public PrescanAction { @@ -75,7 +75,7 @@ //===----------------------------------------------------------------------===// class PrescanAndParseAction : public FrontendAction { void ExecuteAction() override = 0; - bool BeginSourceFileAction(CompilerInstance &ci) override; + bool BeginSourceFileAction() override; }; class DebugUnparseNoSemaAction : public PrescanAndParseAction { @@ -92,7 +92,7 @@ class PrescanAndSemaAction : public FrontendAction { void ExecuteAction() override = 0; - bool BeginSourceFileAction(CompilerInstance &ci) override; + bool BeginSourceFileAction() override; }; class DebugUnparseWithSymbolsAction : public PrescanAndSemaAction { diff --git a/flang/lib/Frontend/FrontendAction.cpp b/flang/lib/Frontend/FrontendAction.cpp --- a/flang/lib/Frontend/FrontendAction.cpp +++ b/flang/lib/Frontend/FrontendAction.cpp @@ -89,7 +89,7 @@ invoc.fortranOpts().isFixedForm = currentInput().IsFixedForm(); } - if (!BeginSourceFileAction(ci)) { + if (!BeginSourceFileAction()) { BeginSourceFileCleanUp(*this, ci); return false; } @@ -117,3 +117,96 @@ set_instance(nullptr); set_currentInput(FrontendInputFile()); } + +bool FrontendAction::RunPrescan() { + CompilerInstance &ci = this->instance(); + std::string currentInputPath{GetCurrentFileOrBufferName()}; + Fortran::parser::Options parserOptions = ci.invocation().fortranOpts(); + + if (ci.invocation().frontendOpts().fortranForm == FortranForm::Unknown) { + // Switch between fixed and free form format based on the input file + // extension. + // + // Ideally we should have all Fortran options set before entering this + // method (i.e. before processing any specific input files). However, we + // can't decide between fixed and free form based on the file extension + // earlier than this. + parserOptions.isFixedForm = currentInput().IsFixedForm(); + } + + // Prescan. In case of failure, report and return. + ci.parsing().Prescan(currentInputPath, parserOptions); + + return !reportFatalScanningErrors(); +} + +bool FrontendAction::RunParse() { + CompilerInstance &ci = this->instance(); + + // Parse. In case of failure, report and return. + ci.parsing().Parse(llvm::outs()); + + if (reportFatalParsingErrors()) { + return false; + } + + // Report the diagnostics from parsing + ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); + + return true; +} + +bool FrontendAction::RunSemanticChecks() { + CompilerInstance &ci = this->instance(); + std::optional &parseTree{ci.parsing().parseTree()}; + assert(parseTree && "Cannot run semantic checks without a parse tree!"); + + // Prepare semantics + ci.setSemantics(std::make_unique( + ci.invocation().semanticsContext(), *parseTree, + ci.invocation().debugModuleDir())); + auto &semantics = ci.semantics(); + + // Run semantic checks + semantics.Perform(); + + if (reportFatalSemanticErrors()) { + return false; + } + + // Report the diagnostics from the semantic checks + semantics.EmitMessages(ci.semaOutputStream()); + + return true; +} + +template +bool FrontendAction::reportFatalErrors(const char (&message)[N]) { + if (!instance_->parsing().messages().empty() && + (instance_->invocation().warnAsErr() || + instance_->parsing().messages().AnyFatalError())) { + const unsigned diagID = instance_->diagnostics().getCustomDiagID( + clang::DiagnosticsEngine::Error, message); + instance_->diagnostics().Report(diagID) << GetCurrentFileOrBufferName(); + instance_->parsing().messages().Emit( + llvm::errs(), instance_->allCookedSources()); + return true; + } + return false; +} + +bool FrontendAction::reportFatalSemanticErrors() { + auto &diags = instance_->diagnostics(); + auto &sema = instance_->semantics(); + + if (instance_->semantics().AnyFatalError()) { + unsigned DiagID = diags.getCustomDiagID( + clang::DiagnosticsEngine::Error, "Semantic errors in %0"); + diags.Report(DiagID) << GetCurrentFileOrBufferName(); + sema.EmitMessages(instance_->semaOutputStream()); + + return true; + } + + return false; +} diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp --- a/flang/lib/Frontend/FrontendActions.cpp +++ b/flang/lib/Frontend/FrontendActions.cpp @@ -27,132 +27,22 @@ using namespace Fortran::frontend; -/// Report fatal semantic errors if present. -/// -/// \param semantics The semantics instance -/// \param diags The diagnostics engine instance -/// \param bufferName The file or buffer name -/// -/// \return True if fatal semantic errors are present, false if not -bool reportFatalSemanticErrors(const Fortran::semantics::Semantics &semantics, - clang::DiagnosticsEngine &diags, const llvm::StringRef &bufferName) { - if (semantics.AnyFatalError()) { - unsigned DiagID = diags.getCustomDiagID( - clang::DiagnosticsEngine::Error, "Semantic errors in %0"); - diags.Report(DiagID) << bufferName; - return true; - } - return false; -} - -template -static bool reportFatalErrors( - const FrontendAction *act, const char (&message)[N]) { - CompilerInstance &ci = act->instance(); - if (!ci.parsing().messages().empty() && - (ci.invocation().warnAsErr() || - ci.parsing().messages().AnyFatalError())) { - const unsigned diagID = ci.diagnostics().getCustomDiagID( - clang::DiagnosticsEngine::Error, message); - ci.diagnostics().Report(diagID) << act->GetCurrentFileOrBufferName(); - ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); - return true; - } - return false; -} - -inline bool reportFatalScanningErrors(const FrontendAction *act) { - return reportFatalErrors(act, "Could not scan %0"); -} - -inline bool reportFatalParsingErrors(const FrontendAction *act) { - return reportFatalErrors(act, "Could not parse %0"); -} - -bool PrescanAction::BeginSourceFileAction(CompilerInstance &c1) { - CompilerInstance &ci = this->instance(); - std::string currentInputPath{GetCurrentFileOrBufferName()}; - Fortran::parser::Options parserOptions = ci.invocation().fortranOpts(); - - // Prescan. In case of failure, report and return. - ci.parsing().Prescan(currentInputPath, parserOptions); - - return !reportFatalScanningErrors(this); -} - -bool PrescanAndParseAction::BeginSourceFileAction(CompilerInstance &c1) { - CompilerInstance &ci = this->instance(); - - std::string currentInputPath{GetCurrentFileOrBufferName()}; - - Fortran::parser::Options parserOptions = ci.invocation().fortranOpts(); - - if (ci.invocation().frontendOpts().fortranForm == FortranForm::Unknown) { - // Switch between fixed and free form format based on the input file - // extension. - // - // Ideally we should have all Fortran options set before entering this - // method (i.e. before processing any specific input files). However, we - // can't decide between fixed and free form based on the file extension - // earlier than this. - parserOptions.isFixedForm = currentInput().IsFixedForm(); - } - - // Prescan. In case of failure, report and return. - ci.parsing().Prescan(currentInputPath, parserOptions); - - if (reportFatalScanningErrors(this)) - return false; - - // Parse. In case of failure, report and return. - ci.parsing().Parse(llvm::outs()); - - if (reportFatalParsingErrors(this)) - return false; - - // Report the diagnostics from parsing - ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); +//===----------------------------------------------------------------------===// +// Custom BeginSourceFileAction +//===----------------------------------------------------------------------===// +bool PrescanAction::BeginSourceFileAction() { return RunPrescan(); } - return true; +bool PrescanAndParseAction::BeginSourceFileAction() { + return RunPrescan() && RunParse(); } -bool PrescanAndSemaAction::BeginSourceFileAction(CompilerInstance &c1) { - CompilerInstance &ci = this->instance(); - std::string currentInputPath{GetCurrentFileOrBufferName()}; - Fortran::parser::Options parserOptions = ci.invocation().fortranOpts(); - - // Prescan. In case of failure, report and return. - ci.parsing().Prescan(currentInputPath, parserOptions); - - if (reportFatalScanningErrors(this)) - return false; - - // Parse. In case of failure, report and return. - ci.parsing().Parse(llvm::outs()); - - if (reportFatalParsingErrors(this)) - return false; - - // Report the diagnostics from parsing - ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); - - auto &parseTree{*ci.parsing().parseTree()}; - - // Prepare semantics - ci.setSemantics(std::make_unique( - ci.invocation().semanticsContext(), parseTree, - ci.invocation().debugModuleDir())); - auto &semantics = ci.semantics(); - - // Run semantic checks - semantics.Perform(); - - // Report the diagnostics from the semantic checks - semantics.EmitMessages(ci.semaOutputStream()); - - return true; +bool PrescanAndSemaAction::BeginSourceFileAction() { + return RunPrescan() & RunParse() && RunSemanticChecks(); } +//===----------------------------------------------------------------------===// +// Custom ExecuteAction +//===----------------------------------------------------------------------===// void InputOutputTestAction::ExecuteAction() { CompilerInstance &ci = instance(); @@ -224,10 +114,6 @@ } void ParseSyntaxOnlyAction::ExecuteAction() { - CompilerInstance &ci = this->instance(); - - reportFatalSemanticErrors( - ci.semantics(), ci.diagnostics(), GetCurrentFileOrBufferName()); } void DebugUnparseNoSemaAction::ExecuteAction() { @@ -258,20 +144,17 @@ invoc.useAnalyzedObjectsForUnparse() ? &invoc.asFortran() : nullptr); // Report fatal semantic errors - reportFatalSemanticErrors(ci.semantics(), this->instance().diagnostics(), - GetCurrentFileOrBufferName()); + reportFatalSemanticErrors(); } void DebugUnparseWithSymbolsAction::ExecuteAction() { - CompilerInstance &ci = this->instance(); auto &parseTree{*instance().parsing().parseTree()}; Fortran::semantics::UnparseWithSymbols( llvm::outs(), parseTree, /*encoding=*/Fortran::parser::Encoding::UTF_8); // Report fatal semantic errors - reportFatalSemanticErrors( - ci.semantics(), ci.diagnostics(), GetCurrentFileOrBufferName()); + reportFatalSemanticErrors(); } void DebugDumpSymbolsAction::ExecuteAction() { @@ -283,8 +166,7 @@ // The runtime derived type information table builder may find and report // semantic errors. So it is important that we report them _after_ // BuildRuntimeDerivedTypeTables is run. - reportFatalSemanticErrors( - semantics, this->instance().diagnostics(), GetCurrentFileOrBufferName()); + reportFatalSemanticErrors(); if (!tables.schemata) { unsigned DiagID = @@ -315,8 +197,7 @@ // The runtime derived type information table builder may find and report // semantic errors. So it is important that we report them _after_ // BuildRuntimeDerivedTypeTables is run. - reportFatalSemanticErrors( - semantics, this->instance().diagnostics(), GetCurrentFileOrBufferName()); + reportFatalSemanticErrors(); if (!tables.schemata) { unsigned DiagID = @@ -342,7 +223,6 @@ } void DebugDumpParseTreeAction::ExecuteAction() { - CompilerInstance &ci = this->instance(); auto &parseTree{instance().parsing().parseTree()}; // Dump parse tree @@ -350,8 +230,7 @@ llvm::outs(), parseTree, &this->instance().invocation().asFortran()); // Report fatal semantic errors - reportFatalSemanticErrors( - ci.semantics(), ci.diagnostics(), GetCurrentFileOrBufferName()); + reportFatalSemanticErrors(); } void DebugMeasureParseTreeAction::ExecuteAction() { @@ -388,8 +267,7 @@ void DebugPreFIRTreeAction::ExecuteAction() { CompilerInstance &ci = this->instance(); // Report and exit if fatal semantic errors are present - if (reportFatalSemanticErrors( - ci.semantics(), ci.diagnostics(), GetCurrentFileOrBufferName())) { + if (reportFatalSemanticErrors()) { return; } @@ -417,9 +295,9 @@ CompilerInstance &ci = this->instance(); // Report and exit if fatal semantic errors are present - if (reportFatalSemanticErrors( - ci.semantics(), ci.diagnostics(), GetCurrentFileOrBufferName())) + if (reportFatalSemanticErrors()) { return; + } parser::AllCookedSources &cs = ci.allCookedSources(); unsigned diagID = ci.diagnostics().getCustomDiagID( @@ -465,9 +343,9 @@ CompilerInstance &ci = this->instance(); // Report and exit if fatal semantic errors are present - if (reportFatalSemanticErrors( - ci.semantics(), ci.diagnostics(), GetCurrentFileOrBufferName())) + if (reportFatalSemanticErrors()) { return; + } ci.semantics().DumpSymbolsSources(llvm::outs()); }