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