1 //===--- FrontendAction.cpp -----------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "flang/Frontend/FrontendAction.h" 10 #include "flang/Frontend/CompilerInstance.h" 11 #include "flang/Frontend/FrontendActions.h" 12 #include "flang/Frontend/FrontendOptions.h" 13 #include "flang/Frontend/FrontendPluginRegistry.h" 14 #include "flang/FrontendTool/Utils.h" 15 #include "clang/Basic/DiagnosticFrontend.h" 16 #include "llvm/Support/Errc.h" 17 #include "llvm/Support/VirtualFileSystem.h" 18 19 using namespace Fortran::frontend; 20 21 LLVM_INSTANTIATE_REGISTRY(FrontendPluginRegistry) 22 23 void FrontendAction::set_currentInput(const FrontendInputFile ¤tInput) { 24 this->currentInput_ = currentInput; 25 } 26 27 // Call this method if BeginSourceFile fails. 28 // Deallocate compiler instance, input and output descriptors 29 static void BeginSourceFileCleanUp(FrontendAction &fa, CompilerInstance &ci) { 30 ci.ClearOutputFiles(/*EraseFiles=*/true); 31 fa.set_currentInput(FrontendInputFile()); 32 fa.set_instance(nullptr); 33 } 34 35 bool FrontendAction::BeginSourceFile( 36 CompilerInstance &ci, const FrontendInputFile &realInput) { 37 38 FrontendInputFile input(realInput); 39 40 // Return immediately if the input file does not exist or is not a file. Note 41 // that we cannot check this for input from stdin. 42 if (input.file() != "-") { 43 if (!llvm::sys::fs::is_regular_file(input.file())) { 44 // Create an diagnostic ID to report 45 unsigned diagID; 46 if (llvm::vfs::getRealFileSystem()->exists(input.file())) { 47 ci.diagnostics().Report(clang::diag::err_fe_error_reading) 48 << input.file(); 49 diagID = ci.diagnostics().getCustomDiagID( 50 clang::DiagnosticsEngine::Error, "%0 is not a regular file"); 51 } else { 52 diagID = ci.diagnostics().getCustomDiagID( 53 clang::DiagnosticsEngine::Error, "%0 does not exist"); 54 } 55 56 // Report the diagnostic and return 57 ci.diagnostics().Report(diagID) << input.file(); 58 BeginSourceFileCleanUp(*this, ci); 59 return false; 60 } 61 } 62 63 assert(!instance_ && "Already processing a source file!"); 64 assert(!realInput.IsEmpty() && "Unexpected empty filename!"); 65 set_currentInput(realInput); 66 set_instance(&ci); 67 68 if (!ci.HasAllSources()) { 69 BeginSourceFileCleanUp(*this, ci); 70 return false; 71 } 72 73 auto &invoc = ci.invocation(); 74 75 // Include command-line and predefined preprocessor macros. Use either: 76 // * `-cpp/-nocpp`, or 77 // * the file extension (if the user didn't express any preference) 78 // to decide whether to include them or not. 79 if ((invoc.preprocessorOpts().macrosFlag == PPMacrosFlag::Include) || 80 (invoc.preprocessorOpts().macrosFlag == PPMacrosFlag::Unknown && 81 currentInput().MustBePreprocessed())) { 82 invoc.setDefaultPredefinitions(); 83 invoc.collectMacroDefinitions(); 84 } 85 86 // Decide between fixed and free form (if the user didn't express any 87 // preference, use the file extension to decide) 88 if (invoc.frontendOpts().fortranForm == FortranForm::Unknown) { 89 invoc.fortranOpts().isFixedForm = currentInput().IsFixedForm(); 90 } 91 92 if (!BeginSourceFileAction()) { 93 BeginSourceFileCleanUp(*this, ci); 94 return false; 95 } 96 97 return true; 98 } 99 100 bool FrontendAction::ShouldEraseOutputFiles() { 101 return instance().diagnostics().hasErrorOccurred(); 102 } 103 104 llvm::Error FrontendAction::Execute() { 105 ExecuteAction(); 106 107 return llvm::Error::success(); 108 } 109 110 void FrontendAction::EndSourceFile() { 111 CompilerInstance &ci = instance(); 112 113 // Cleanup the output streams, and erase the output files if instructed by the 114 // FrontendAction. 115 ci.ClearOutputFiles(/*EraseFiles=*/ShouldEraseOutputFiles()); 116 117 set_instance(nullptr); 118 set_currentInput(FrontendInputFile()); 119 } 120 121 bool FrontendAction::RunPrescan() { 122 CompilerInstance &ci = this->instance(); 123 std::string currentInputPath{GetCurrentFileOrBufferName()}; 124 Fortran::parser::Options parserOptions = ci.invocation().fortranOpts(); 125 126 if (ci.invocation().frontendOpts().fortranForm == FortranForm::Unknown) { 127 // Switch between fixed and free form format based on the input file 128 // extension. 129 // 130 // Ideally we should have all Fortran options set before entering this 131 // method (i.e. before processing any specific input files). However, we 132 // can't decide between fixed and free form based on the file extension 133 // earlier than this. 134 parserOptions.isFixedForm = currentInput().IsFixedForm(); 135 } 136 137 // Prescan. In case of failure, report and return. 138 ci.parsing().Prescan(currentInputPath, parserOptions); 139 140 return !reportFatalScanningErrors(); 141 } 142 143 bool FrontendAction::RunParse() { 144 CompilerInstance &ci = this->instance(); 145 146 // Parse. In case of failure, report and return. 147 ci.parsing().Parse(llvm::outs()); 148 149 if (reportFatalParsingErrors()) { 150 return false; 151 } 152 153 // Report the diagnostics from parsing 154 ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); 155 156 return true; 157 } 158 159 bool FrontendAction::RunSemanticChecks() { 160 CompilerInstance &ci = this->instance(); 161 std::optional<parser::Program> &parseTree{ci.parsing().parseTree()}; 162 assert(parseTree && "Cannot run semantic checks without a parse tree!"); 163 164 // Prepare semantics 165 ci.setSemantics(std::make_unique<Fortran::semantics::Semantics>( 166 ci.invocation().semanticsContext(), *parseTree, 167 ci.invocation().debugModuleDir())); 168 auto &semantics = ci.semantics(); 169 170 // Run semantic checks 171 semantics.Perform(); 172 173 if (reportFatalSemanticErrors()) { 174 return false; 175 } 176 177 // Report the diagnostics from the semantic checks 178 semantics.EmitMessages(ci.semaOutputStream()); 179 180 return true; 181 } 182 183 template <unsigned N> 184 bool FrontendAction::reportFatalErrors(const char (&message)[N]) { 185 if (!instance_->parsing().messages().empty() && 186 (instance_->invocation().warnAsErr() || 187 instance_->parsing().messages().AnyFatalError())) { 188 const unsigned diagID = instance_->diagnostics().getCustomDiagID( 189 clang::DiagnosticsEngine::Error, message); 190 instance_->diagnostics().Report(diagID) << GetCurrentFileOrBufferName(); 191 instance_->parsing().messages().Emit( 192 llvm::errs(), instance_->allCookedSources()); 193 return true; 194 } 195 return false; 196 } 197 198 bool FrontendAction::reportFatalSemanticErrors() { 199 auto &diags = instance_->diagnostics(); 200 auto &sema = instance_->semantics(); 201 202 if (instance_->semantics().AnyFatalError()) { 203 unsigned DiagID = diags.getCustomDiagID( 204 clang::DiagnosticsEngine::Error, "Semantic errors in %0"); 205 diags.Report(DiagID) << GetCurrentFileOrBufferName(); 206 sema.EmitMessages(instance_->semaOutputStream()); 207 208 return true; 209 } 210 211 return false; 212 } 213