1 //===--- FrontendActions.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/FrontendActions.h" 10 #include "flang/Common/default-kinds.h" 11 #include "flang/Frontend/CompilerInstance.h" 12 #include "flang/Frontend/FrontendOptions.h" 13 #include "flang/Lower/PFTBuilder.h" 14 #include "flang/Parser/dump-parse-tree.h" 15 #include "flang/Parser/parsing.h" 16 #include "flang/Parser/provenance.h" 17 #include "flang/Parser/source.h" 18 #include "flang/Parser/unparse.h" 19 #include "flang/Semantics/runtime-type-info.h" 20 #include "flang/Semantics/semantics.h" 21 #include "flang/Semantics/unparse-with-symbols.h" 22 #include "llvm/ADT/StringRef.h" 23 #include <clang/Basic/Diagnostic.h> 24 #include <memory> 25 26 using namespace Fortran::frontend; 27 28 /// Report fatal semantic errors if present. 29 /// 30 /// \param semantics The semantics instance 31 /// \param diags The diagnostics engine instance 32 /// \param bufferName The file or buffer name 33 /// 34 /// \return True if fatal semantic errors are present, false if not 35 bool reportFatalSemanticErrors(const Fortran::semantics::Semantics &semantics, 36 clang::DiagnosticsEngine &diags, const llvm::StringRef &bufferName) { 37 if (semantics.AnyFatalError()) { 38 unsigned DiagID = diags.getCustomDiagID( 39 clang::DiagnosticsEngine::Error, "Semantic errors in %0"); 40 diags.Report(DiagID) << bufferName; 41 return true; 42 } 43 return false; 44 } 45 46 bool PrescanAction::BeginSourceFileAction(CompilerInstance &c1) { 47 CompilerInstance &ci = this->instance(); 48 49 std::string currentInputPath{GetCurrentFileOrBufferName()}; 50 51 Fortran::parser::Options parserOptions = ci.invocation().fortranOpts(); 52 53 if (ci.invocation().frontendOpts().fortranForm_ == FortranForm::Unknown) { 54 // Switch between fixed and free form format based on the input file 55 // extension. 56 // 57 // Ideally we should have all Fortran options set before entering this 58 // method (i.e. before processing any specific input files). However, we 59 // can't decide between fixed and free form based on the file extension 60 // earlier than this. 61 parserOptions.isFixedForm = currentInput().IsFixedForm(); 62 } 63 64 // Prescan. In case of failure, report and return. 65 ci.parsing().Prescan(currentInputPath, parserOptions); 66 67 if (ci.parsing().messages().AnyFatalError()) { 68 const unsigned diagID = ci.diagnostics().getCustomDiagID( 69 clang::DiagnosticsEngine::Error, "Could not scan %0"); 70 ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName(); 71 ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); 72 73 return false; 74 } 75 76 return true; 77 } 78 79 bool PrescanAndSemaAction::BeginSourceFileAction(CompilerInstance &c1) { 80 CompilerInstance &ci = this->instance(); 81 82 std::string currentInputPath{GetCurrentFileOrBufferName()}; 83 84 Fortran::parser::Options parserOptions = ci.invocation().fortranOpts(); 85 86 if (ci.invocation().frontendOpts().fortranForm_ == FortranForm::Unknown) { 87 // Switch between fixed and free form format based on the input file 88 // extension. 89 // 90 // Ideally we should have all Fortran options set before entering this 91 // method (i.e. before processing any specific input files). However, we 92 // can't decide between fixed and free form based on the file extension 93 // earlier than this. 94 parserOptions.isFixedForm = currentInput().IsFixedForm(); 95 } 96 97 // Prescan. In case of failure, report and return. 98 ci.parsing().Prescan(currentInputPath, parserOptions); 99 100 if (ci.parsing().messages().AnyFatalError()) { 101 const unsigned diagID = ci.diagnostics().getCustomDiagID( 102 clang::DiagnosticsEngine::Error, "Could not scan %0"); 103 ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName(); 104 ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); 105 106 return false; 107 } 108 109 // Parse. In case of failure, report and return. 110 ci.parsing().Parse(llvm::outs()); 111 112 if (ci.parsing().messages().AnyFatalError()) { 113 unsigned diagID = ci.diagnostics().getCustomDiagID( 114 clang::DiagnosticsEngine::Error, "Could not parse %0"); 115 ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName(); 116 117 ci.parsing().messages().Emit( 118 llvm::errs(), this->instance().allCookedSources()); 119 return false; 120 } 121 122 // Report the diagnostics from parsing 123 ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); 124 125 auto &parseTree{*ci.parsing().parseTree()}; 126 127 // Prepare semantics 128 setSemantics(std::make_unique<Fortran::semantics::Semantics>( 129 ci.invocation().semanticsContext(), parseTree, 130 ci.parsing().cooked().AsCharBlock(), ci.invocation().debugModuleDir())); 131 auto &semantics = this->semantics(); 132 133 // Run semantic checks 134 semantics.Perform(); 135 136 // Report the diagnostics from the semantic checks 137 semantics.EmitMessages(ci.semaOutputStream()); 138 return true; 139 } 140 141 void InputOutputTestAction::ExecuteAction() { 142 CompilerInstance &ci = instance(); 143 144 // Create a stream for errors 145 std::string buf; 146 llvm::raw_string_ostream error_stream{buf}; 147 148 // Read the input file 149 Fortran::parser::AllSources &allSources{ci.allSources()}; 150 std::string path{GetCurrentFileOrBufferName()}; 151 const Fortran::parser::SourceFile *sf; 152 if (path == "-") 153 sf = allSources.ReadStandardInput(error_stream); 154 else 155 sf = allSources.Open(path, error_stream, std::optional<std::string>{"."s}); 156 llvm::ArrayRef<char> fileContent = sf->content(); 157 158 // Output file descriptor to receive the contents of the input file. 159 std::unique_ptr<llvm::raw_ostream> os; 160 161 // Copy the contents from the input file to the output file 162 if (!ci.IsOutputStreamNull()) { 163 // An output stream (outputStream_) was set earlier 164 ci.WriteOutputStream(fileContent.data()); 165 } else { 166 // No pre-set output stream - create an output file 167 os = ci.CreateDefaultOutputFile( 168 /*binary=*/true, GetCurrentFileOrBufferName(), "txt"); 169 if (!os) 170 return; 171 (*os) << fileContent.data(); 172 } 173 } 174 175 void PrintPreprocessedAction::ExecuteAction() { 176 std::string buf; 177 llvm::raw_string_ostream outForPP{buf}; 178 179 // Run the preprocessor 180 CompilerInstance &ci = this->instance(); 181 ci.parsing().DumpCookedChars(outForPP); 182 183 // If a pre-defined output stream exists, dump the preprocessed content there 184 if (!ci.IsOutputStreamNull()) { 185 // Send the output to the pre-defined output buffer. 186 ci.WriteOutputStream(outForPP.str()); 187 return; 188 } 189 190 // Print diagnostics from the preprocessor 191 ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); 192 193 // Create a file and save the preprocessed output there 194 if (auto os{ci.CreateDefaultOutputFile( 195 /*Binary=*/true, /*InFile=*/GetCurrentFileOrBufferName())}) { 196 (*os) << outForPP.str(); 197 } else { 198 llvm::errs() << "Unable to create the output file\n"; 199 return; 200 } 201 } 202 203 void DebugDumpProvenanceAction::ExecuteAction() { 204 this->instance().parsing().DumpProvenance(llvm::outs()); 205 } 206 207 void ParseSyntaxOnlyAction::ExecuteAction() { 208 reportFatalSemanticErrors(semantics(), this->instance().diagnostics(), 209 GetCurrentFileOrBufferName()); 210 } 211 212 void DebugUnparseAction::ExecuteAction() { 213 auto &parseTree{instance().parsing().parseTree()}; 214 Fortran::parser::AnalyzedObjectsAsFortran asFortran = 215 Fortran::frontend::getBasicAsFortran(); 216 217 // TODO: Options should come from CompilerInvocation 218 Unparse(llvm::outs(), *parseTree, 219 /*encoding=*/Fortran::parser::Encoding::UTF_8, 220 /*capitalizeKeywords=*/true, /*backslashEscapes=*/false, 221 /*preStatement=*/nullptr, &asFortran); 222 223 // Report fatal semantic errors 224 reportFatalSemanticErrors(semantics(), this->instance().diagnostics(), 225 GetCurrentFileOrBufferName()); 226 } 227 228 void DebugUnparseWithSymbolsAction::ExecuteAction() { 229 auto &parseTree{*instance().parsing().parseTree()}; 230 231 Fortran::semantics::UnparseWithSymbols( 232 llvm::outs(), parseTree, /*encoding=*/Fortran::parser::Encoding::UTF_8); 233 234 // Report fatal semantic errors 235 reportFatalSemanticErrors(semantics(), this->instance().diagnostics(), 236 GetCurrentFileOrBufferName()); 237 } 238 239 void DebugDumpSymbolsAction::ExecuteAction() { 240 auto &semantics = this->semantics(); 241 242 // Dump symbols 243 semantics.DumpSymbols(llvm::outs()); 244 // Report fatal semantic errors 245 reportFatalSemanticErrors( 246 semantics, this->instance().diagnostics(), GetCurrentFileOrBufferName()); 247 } 248 249 void DebugDumpParseTreeAction::ExecuteAction() { 250 auto &parseTree{instance().parsing().parseTree()}; 251 Fortran::parser::AnalyzedObjectsAsFortran asFortran = 252 Fortran::frontend::getBasicAsFortran(); 253 254 // Dump parse tree 255 Fortran::parser::DumpTree(llvm::outs(), parseTree, &asFortran); 256 // Report fatal semantic errors 257 reportFatalSemanticErrors(semantics(), this->instance().diagnostics(), 258 GetCurrentFileOrBufferName()); 259 } 260 261 void DebugMeasureParseTreeAction::ExecuteAction() { 262 CompilerInstance &ci = this->instance(); 263 264 // Parse. In case of failure, report and return. 265 ci.parsing().Parse(llvm::outs()); 266 267 if (ci.parsing().messages().AnyFatalError()) { 268 unsigned diagID = ci.diagnostics().getCustomDiagID( 269 clang::DiagnosticsEngine::Error, "Could not parse %0"); 270 ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName(); 271 272 ci.parsing().messages().Emit( 273 llvm::errs(), this->instance().allCookedSources()); 274 return; 275 } 276 277 // Report the diagnostics from parsing 278 ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); 279 280 auto &parseTree{*ci.parsing().parseTree()}; 281 282 // Measure the parse tree 283 MeasurementVisitor visitor; 284 Fortran::parser::Walk(parseTree, visitor); 285 llvm::outs() << "Parse tree comprises " << visitor.objects 286 << " objects and occupies " << visitor.bytes 287 << " total bytes.\n"; 288 } 289 290 void DebugPreFIRTreeAction::ExecuteAction() { 291 CompilerInstance &ci = this->instance(); 292 // Report and exit if fatal semantic errors are present 293 if (reportFatalSemanticErrors( 294 semantics(), ci.diagnostics(), GetCurrentFileOrBufferName())) { 295 return; 296 } 297 298 auto &parseTree{*ci.parsing().parseTree()}; 299 300 // Dump pre-FIR tree 301 if (auto ast{Fortran::lower::createPFT( 302 parseTree, ci.invocation().semanticsContext())}) { 303 Fortran::lower::dumpPFT(llvm::outs(), *ast); 304 } else { 305 unsigned diagID = ci.diagnostics().getCustomDiagID( 306 clang::DiagnosticsEngine::Error, "Pre FIR Tree is NULL."); 307 ci.diagnostics().Report(diagID); 308 } 309 } 310 311 void DebugDumpParsingLogAction::ExecuteAction() { 312 CompilerInstance &ci = this->instance(); 313 314 ci.parsing().Parse(llvm::errs()); 315 ci.parsing().DumpParsingLog(llvm::outs()); 316 } 317 318 void GetSymbolsSourcesAction::ExecuteAction() { 319 // Report and exit if fatal semantic errors are present 320 if (reportFatalSemanticErrors(semantics(), this->instance().diagnostics(), 321 GetCurrentFileOrBufferName())) 322 return; 323 324 semantics().DumpSymbolsSources(llvm::outs()); 325 } 326 327 void EmitObjAction::ExecuteAction() { 328 CompilerInstance &ci = this->instance(); 329 unsigned DiagID = ci.diagnostics().getCustomDiagID( 330 clang::DiagnosticsEngine::Error, "code-generation is not available yet"); 331 ci.diagnostics().Report(DiagID); 332 } 333