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