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