//===--- FrontendAction.cpp -----------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "flang/Frontend/FrontendAction.h" #include "flang/Frontend/CompilerInstance.h" #include "flang/Frontend/FrontendActions.h" #include "flang/Frontend/FrontendOptions.h" #include "flang/Frontend/FrontendPluginRegistry.h" #include "flang/FrontendTool/Utils.h" #include "clang/Basic/DiagnosticFrontend.h" #include "llvm/Support/Errc.h" #include "llvm/Support/VirtualFileSystem.h" using namespace Fortran::frontend; LLVM_INSTANTIATE_REGISTRY(FrontendPluginRegistry) void FrontendAction::set_currentInput(const FrontendInputFile ¤tInput) { this->currentInput_ = currentInput; } // Call this method if BeginSourceFile fails. // Deallocate compiler instance, input and output descriptors static void BeginSourceFileCleanUp(FrontendAction &fa, CompilerInstance &ci) { ci.ClearOutputFiles(/*EraseFiles=*/true); fa.set_currentInput(FrontendInputFile()); fa.set_instance(nullptr); } bool FrontendAction::BeginSourceFile( CompilerInstance &ci, const FrontendInputFile &realInput) { FrontendInputFile input(realInput); // Return immediately if the input file does not exist or is not a file. Note // that we cannot check this for input from stdin. if (input.file() != "-") { if (!llvm::sys::fs::is_regular_file(input.file())) { // Create an diagnostic ID to report unsigned diagID; if (llvm::vfs::getRealFileSystem()->exists(input.file())) { ci.diagnostics().Report(clang::diag::err_fe_error_reading) << input.file(); diagID = ci.diagnostics().getCustomDiagID( clang::DiagnosticsEngine::Error, "%0 is not a regular file"); } else { diagID = ci.diagnostics().getCustomDiagID( clang::DiagnosticsEngine::Error, "%0 does not exist"); } // Report the diagnostic and return ci.diagnostics().Report(diagID) << input.file(); BeginSourceFileCleanUp(*this, ci); return false; } } assert(!instance_ && "Already processing a source file!"); assert(!realInput.IsEmpty() && "Unexpected empty filename!"); set_currentInput(realInput); set_instance(&ci); if (!ci.HasAllSources()) { BeginSourceFileCleanUp(*this, ci); return false; } auto &invoc = ci.invocation(); // Include command-line and predefined preprocessor macros. Use either: // * `-cpp/-nocpp`, or // * the file extension (if the user didn't express any preference) // to decide whether to include them or not. if ((invoc.preprocessorOpts().macrosFlag == PPMacrosFlag::Include) || (invoc.preprocessorOpts().macrosFlag == PPMacrosFlag::Unknown && currentInput().MustBePreprocessed())) { invoc.setDefaultPredefinitions(); invoc.collectMacroDefinitions(); } // Decide between fixed and free form (if the user didn't express any // preference, use the file extension to decide) if (invoc.frontendOpts().fortranForm == FortranForm::Unknown) { invoc.fortranOpts().isFixedForm = currentInput().IsFixedForm(); } if (!BeginSourceFileAction()) { BeginSourceFileCleanUp(*this, ci); return false; } return true; } bool FrontendAction::ShouldEraseOutputFiles() { return instance().diagnostics().hasErrorOccurred(); } llvm::Error FrontendAction::Execute() { ExecuteAction(); return llvm::Error::success(); } void FrontendAction::EndSourceFile() { CompilerInstance &ci = instance(); // Cleanup the output streams, and erase the output files if instructed by the // FrontendAction. ci.ClearOutputFiles(/*EraseFiles=*/ShouldEraseOutputFiles()); 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; }