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 "llvm/Support/ErrorHandling.h" 25 #include <clang/Basic/Diagnostic.h> 26 #include <memory> 27 28 using namespace Fortran::frontend; 29 30 /// Report fatal semantic errors if present. 31 /// 32 /// \param semantics The semantics instance 33 /// \param diags The diagnostics engine instance 34 /// \param bufferName The file or buffer name 35 /// 36 /// \return True if fatal semantic errors are present, false if not 37 bool reportFatalSemanticErrors(const Fortran::semantics::Semantics &semantics, 38 clang::DiagnosticsEngine &diags, const llvm::StringRef &bufferName) { 39 if (semantics.AnyFatalError()) { 40 unsigned DiagID = diags.getCustomDiagID( 41 clang::DiagnosticsEngine::Error, "Semantic errors in %0"); 42 diags.Report(DiagID) << bufferName; 43 return true; 44 } 45 return false; 46 } 47 48 bool PrescanAction::BeginSourceFileAction(CompilerInstance &c1) { 49 CompilerInstance &ci = this->instance(); 50 std::string currentInputPath{GetCurrentFileOrBufferName()}; 51 Fortran::parser::Options parserOptions = ci.invocation().fortranOpts(); 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.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 &invoc = this->instance().invocation(); 246 auto &parseTree{instance().parsing().parseTree()}; 247 248 // TODO: Options should come from CompilerInvocation 249 Unparse(llvm::outs(), *parseTree, 250 /*encoding=*/Fortran::parser::Encoding::UTF_8, 251 /*capitalizeKeywords=*/true, /*backslashEscapes=*/false, 252 /*preStatement=*/nullptr, 253 invoc.useAnalyzedObjectsForUnparse() ? &invoc.asFortran() : nullptr); 254 } 255 256 void DebugUnparseAction::ExecuteAction() { 257 auto &invoc = this->instance().invocation(); 258 auto &parseTree{instance().parsing().parseTree()}; 259 260 CompilerInstance &ci = this->instance(); 261 auto os{ci.CreateDefaultOutputFile( 262 /*Binary=*/false, /*InFile=*/GetCurrentFileOrBufferName())}; 263 264 // TODO: Options should come from CompilerInvocation 265 Unparse(*os, *parseTree, 266 /*encoding=*/Fortran::parser::Encoding::UTF_8, 267 /*capitalizeKeywords=*/true, /*backslashEscapes=*/false, 268 /*preStatement=*/nullptr, 269 invoc.useAnalyzedObjectsForUnparse() ? &invoc.asFortran() : nullptr); 270 271 // Report fatal semantic errors 272 reportFatalSemanticErrors(semantics(), this->instance().diagnostics(), 273 GetCurrentFileOrBufferName()); 274 } 275 276 void DebugUnparseWithSymbolsAction::ExecuteAction() { 277 auto &parseTree{*instance().parsing().parseTree()}; 278 279 Fortran::semantics::UnparseWithSymbols( 280 llvm::outs(), parseTree, /*encoding=*/Fortran::parser::Encoding::UTF_8); 281 282 // Report fatal semantic errors 283 reportFatalSemanticErrors(semantics(), this->instance().diagnostics(), 284 GetCurrentFileOrBufferName()); 285 } 286 287 void DebugDumpSymbolsAction::ExecuteAction() { 288 CompilerInstance &ci = this->instance(); 289 auto &semantics = this->semantics(); 290 291 auto tables{Fortran::semantics::BuildRuntimeDerivedTypeTables( 292 instance().invocation().semanticsContext())}; 293 // The runtime derived type information table builder may find and report 294 // semantic errors. So it is important that we report them _after_ 295 // BuildRuntimeDerivedTypeTables is run. 296 reportFatalSemanticErrors( 297 semantics, this->instance().diagnostics(), GetCurrentFileOrBufferName()); 298 299 if (!tables.schemata) { 300 unsigned DiagID = 301 ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, 302 "could not find module file for __fortran_type_info"); 303 ci.diagnostics().Report(DiagID); 304 llvm::errs() << "\n"; 305 } 306 307 // Dump symbols 308 semantics.DumpSymbols(llvm::outs()); 309 } 310 311 void DebugDumpAllAction::ExecuteAction() { 312 CompilerInstance &ci = this->instance(); 313 314 // Dump parse tree 315 auto &parseTree{instance().parsing().parseTree()}; 316 llvm::outs() << "========================"; 317 llvm::outs() << " Flang: parse tree dump "; 318 llvm::outs() << "========================\n"; 319 Fortran::parser::DumpTree( 320 llvm::outs(), parseTree, &ci.invocation().asFortran()); 321 322 auto &semantics = this->semantics(); 323 auto tables{Fortran::semantics::BuildRuntimeDerivedTypeTables( 324 instance().invocation().semanticsContext())}; 325 // The runtime derived type information table builder may find and report 326 // semantic errors. So it is important that we report them _after_ 327 // BuildRuntimeDerivedTypeTables is run. 328 reportFatalSemanticErrors( 329 semantics, this->instance().diagnostics(), GetCurrentFileOrBufferName()); 330 331 if (!tables.schemata) { 332 unsigned DiagID = 333 ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, 334 "could not find module file for __fortran_type_info"); 335 ci.diagnostics().Report(DiagID); 336 llvm::errs() << "\n"; 337 } 338 339 // Dump symbols 340 llvm::outs() << "====================="; 341 llvm::outs() << " Flang: symbols dump "; 342 llvm::outs() << "=====================\n"; 343 semantics.DumpSymbols(llvm::outs()); 344 } 345 346 void DebugDumpParseTreeNoSemaAction::ExecuteAction() { 347 auto &parseTree{instance().parsing().parseTree()}; 348 349 // Dump parse tree 350 Fortran::parser::DumpTree( 351 llvm::outs(), parseTree, &this->instance().invocation().asFortran()); 352 } 353 354 void DebugDumpParseTreeAction::ExecuteAction() { 355 auto &parseTree{instance().parsing().parseTree()}; 356 357 // Dump parse tree 358 Fortran::parser::DumpTree( 359 llvm::outs(), parseTree, &this->instance().invocation().asFortran()); 360 361 // Report fatal semantic errors 362 reportFatalSemanticErrors(semantics(), this->instance().diagnostics(), 363 GetCurrentFileOrBufferName()); 364 } 365 366 void DebugMeasureParseTreeAction::ExecuteAction() { 367 CompilerInstance &ci = this->instance(); 368 369 // Parse. In case of failure, report and return. 370 ci.parsing().Parse(llvm::outs()); 371 372 if (!ci.parsing().messages().empty() && 373 (ci.invocation().warnAsErr() || 374 ci.parsing().messages().AnyFatalError())) { 375 unsigned diagID = ci.diagnostics().getCustomDiagID( 376 clang::DiagnosticsEngine::Error, "Could not parse %0"); 377 ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName(); 378 379 ci.parsing().messages().Emit( 380 llvm::errs(), this->instance().allCookedSources()); 381 return; 382 } 383 384 // Report the diagnostics from parsing 385 ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); 386 387 auto &parseTree{*ci.parsing().parseTree()}; 388 389 // Measure the parse tree 390 MeasurementVisitor visitor; 391 Fortran::parser::Walk(parseTree, visitor); 392 llvm::outs() << "Parse tree comprises " << visitor.objects 393 << " objects and occupies " << visitor.bytes 394 << " total bytes.\n"; 395 } 396 397 void DebugPreFIRTreeAction::ExecuteAction() { 398 CompilerInstance &ci = this->instance(); 399 // Report and exit if fatal semantic errors are present 400 if (reportFatalSemanticErrors( 401 semantics(), ci.diagnostics(), GetCurrentFileOrBufferName())) { 402 return; 403 } 404 405 auto &parseTree{*ci.parsing().parseTree()}; 406 407 // Dump pre-FIR tree 408 if (auto ast{Fortran::lower::createPFT( 409 parseTree, ci.invocation().semanticsContext())}) { 410 Fortran::lower::dumpPFT(llvm::outs(), *ast); 411 } else { 412 unsigned diagID = ci.diagnostics().getCustomDiagID( 413 clang::DiagnosticsEngine::Error, "Pre FIR Tree is NULL."); 414 ci.diagnostics().Report(diagID); 415 } 416 } 417 418 void DebugDumpParsingLogAction::ExecuteAction() { 419 CompilerInstance &ci = this->instance(); 420 421 ci.parsing().Parse(llvm::errs()); 422 ci.parsing().DumpParsingLog(llvm::outs()); 423 } 424 425 void GetDefinitionAction::ExecuteAction() { 426 // Report and exit if fatal semantic errors are present 427 if (reportFatalSemanticErrors(semantics(), this->instance().diagnostics(), 428 GetCurrentFileOrBufferName())) 429 return; 430 431 CompilerInstance &ci = this->instance(); 432 parser::AllCookedSources &cs = ci.allCookedSources(); 433 unsigned diagID = ci.diagnostics().getCustomDiagID( 434 clang::DiagnosticsEngine::Error, "Symbol not found"); 435 436 auto gdv = ci.invocation().frontendOpts().getDefVals_; 437 auto charBlock{cs.GetCharBlockFromLineAndColumns( 438 gdv.line, gdv.startColumn, gdv.endColumn)}; 439 if (!charBlock) { 440 ci.diagnostics().Report(diagID); 441 return; 442 } 443 444 llvm::outs() << "String range: >" << charBlock->ToString() << "<\n"; 445 446 auto *symbol{ci.invocation() 447 .semanticsContext() 448 .FindScope(*charBlock) 449 .FindSymbol(*charBlock)}; 450 if (!symbol) { 451 ci.diagnostics().Report(diagID); 452 return; 453 } 454 455 llvm::outs() << "Found symbol name: " << symbol->name().ToString() << "\n"; 456 457 auto sourceInfo{cs.GetSourcePositionRange(symbol->name())}; 458 if (!sourceInfo) { 459 llvm_unreachable( 460 "Failed to obtain SourcePosition." 461 "TODO: Please, write a test and replace this with a diagnostic!"); 462 return; 463 } 464 465 llvm::outs() << "Found symbol name: " << symbol->name().ToString() << "\n"; 466 llvm::outs() << symbol->name().ToString() << ": " 467 << sourceInfo->first.file.path() << ", " 468 << sourceInfo->first.line << ", " << sourceInfo->first.column 469 << "-" << sourceInfo->second.column << "\n"; 470 } 471 472 void GetSymbolsSourcesAction::ExecuteAction() { 473 // Report and exit if fatal semantic errors are present 474 if (reportFatalSemanticErrors(semantics(), this->instance().diagnostics(), 475 GetCurrentFileOrBufferName())) 476 return; 477 478 semantics().DumpSymbolsSources(llvm::outs()); 479 } 480 481 void EmitObjAction::ExecuteAction() { 482 CompilerInstance &ci = this->instance(); 483 unsigned DiagID = ci.diagnostics().getCustomDiagID( 484 clang::DiagnosticsEngine::Error, "code-generation is not available yet"); 485 ci.diagnostics().Report(DiagID); 486 } 487 488 void InitOnlyAction::ExecuteAction() { 489 CompilerInstance &ci = this->instance(); 490 unsigned DiagID = 491 ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Warning, 492 "Use `-init-only` for testing purposes only"); 493 ci.diagnostics().Report(DiagID); 494 } 495