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 &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 CompilerInstance &ci = this->instance(); 286 auto &semantics = this->semantics(); 287 288 auto tables{Fortran::semantics::BuildRuntimeDerivedTypeTables( 289 instance().invocation().semanticsContext())}; 290 // The runtime derived type information table builder may find and report 291 // semantic errors. So it is important that we report them _after_ 292 // BuildRuntimeDerivedTypeTables is run. 293 reportFatalSemanticErrors( 294 semantics, this->instance().diagnostics(), GetCurrentFileOrBufferName()); 295 296 if (!tables.schemata) { 297 unsigned DiagID = 298 ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, 299 "could not find module file for __fortran_type_info"); 300 ci.diagnostics().Report(DiagID); 301 llvm::errs() << "\n"; 302 } 303 304 // Dump symbols 305 semantics.DumpSymbols(llvm::outs()); 306 } 307 308 void DebugDumpAllAction::ExecuteAction() { 309 CompilerInstance &ci = this->instance(); 310 311 // Dump parse tree 312 auto &parseTree{instance().parsing().parseTree()}; 313 Fortran::parser::AnalyzedObjectsAsFortran asFortran = 314 Fortran::frontend::getBasicAsFortran(); 315 llvm::outs() << "========================"; 316 llvm::outs() << " Flang: parse tree dump "; 317 llvm::outs() << "========================\n"; 318 Fortran::parser::DumpTree(llvm::outs(), parseTree, &asFortran); 319 320 auto &semantics = this->semantics(); 321 auto tables{Fortran::semantics::BuildRuntimeDerivedTypeTables( 322 instance().invocation().semanticsContext())}; 323 // The runtime derived type information table builder may find and report 324 // semantic errors. So it is important that we report them _after_ 325 // BuildRuntimeDerivedTypeTables is run. 326 reportFatalSemanticErrors( 327 semantics, this->instance().diagnostics(), GetCurrentFileOrBufferName()); 328 329 if (!tables.schemata) { 330 unsigned DiagID = 331 ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, 332 "could not find module file for __fortran_type_info"); 333 ci.diagnostics().Report(DiagID); 334 llvm::errs() << "\n"; 335 } 336 337 // Dump symbols 338 llvm::outs() << "====================="; 339 llvm::outs() << " Flang: symbols dump "; 340 llvm::outs() << "=====================\n"; 341 semantics.DumpSymbols(llvm::outs()); 342 } 343 344 void DebugDumpParseTreeNoSemaAction::ExecuteAction() { 345 auto &parseTree{instance().parsing().parseTree()}; 346 Fortran::parser::AnalyzedObjectsAsFortran asFortran = 347 Fortran::frontend::getBasicAsFortran(); 348 349 // Dump parse tree 350 Fortran::parser::DumpTree(llvm::outs(), parseTree, &asFortran); 351 } 352 353 void DebugDumpParseTreeAction::ExecuteAction() { 354 auto &parseTree{instance().parsing().parseTree()}; 355 Fortran::parser::AnalyzedObjectsAsFortran asFortran = 356 Fortran::frontend::getBasicAsFortran(); 357 358 // Dump parse tree 359 Fortran::parser::DumpTree(llvm::outs(), parseTree, &asFortran); 360 // Report fatal semantic errors 361 reportFatalSemanticErrors(semantics(), this->instance().diagnostics(), 362 GetCurrentFileOrBufferName()); 363 } 364 365 void DebugMeasureParseTreeAction::ExecuteAction() { 366 CompilerInstance &ci = this->instance(); 367 368 // Parse. In case of failure, report and return. 369 ci.parsing().Parse(llvm::outs()); 370 371 if (!ci.parsing().messages().empty() && 372 (ci.invocation().warnAsErr() || 373 ci.parsing().messages().AnyFatalError())) { 374 unsigned diagID = ci.diagnostics().getCustomDiagID( 375 clang::DiagnosticsEngine::Error, "Could not parse %0"); 376 ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName(); 377 378 ci.parsing().messages().Emit( 379 llvm::errs(), this->instance().allCookedSources()); 380 return; 381 } 382 383 // Report the diagnostics from parsing 384 ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); 385 386 auto &parseTree{*ci.parsing().parseTree()}; 387 388 // Measure the parse tree 389 MeasurementVisitor visitor; 390 Fortran::parser::Walk(parseTree, visitor); 391 llvm::outs() << "Parse tree comprises " << visitor.objects 392 << " objects and occupies " << visitor.bytes 393 << " total bytes.\n"; 394 } 395 396 void DebugPreFIRTreeAction::ExecuteAction() { 397 CompilerInstance &ci = this->instance(); 398 // Report and exit if fatal semantic errors are present 399 if (reportFatalSemanticErrors( 400 semantics(), ci.diagnostics(), GetCurrentFileOrBufferName())) { 401 return; 402 } 403 404 auto &parseTree{*ci.parsing().parseTree()}; 405 406 // Dump pre-FIR tree 407 if (auto ast{Fortran::lower::createPFT( 408 parseTree, ci.invocation().semanticsContext())}) { 409 Fortran::lower::dumpPFT(llvm::outs(), *ast); 410 } else { 411 unsigned diagID = ci.diagnostics().getCustomDiagID( 412 clang::DiagnosticsEngine::Error, "Pre FIR Tree is NULL."); 413 ci.diagnostics().Report(diagID); 414 } 415 } 416 417 void DebugDumpParsingLogAction::ExecuteAction() { 418 CompilerInstance &ci = this->instance(); 419 420 ci.parsing().Parse(llvm::errs()); 421 ci.parsing().DumpParsingLog(llvm::outs()); 422 } 423 424 void GetDefinitionAction::ExecuteAction() { 425 // Report and exit if fatal semantic errors are present 426 if (reportFatalSemanticErrors(semantics(), this->instance().diagnostics(), 427 GetCurrentFileOrBufferName())) 428 return; 429 430 CompilerInstance &ci = this->instance(); 431 parser::AllCookedSources &cs = ci.allCookedSources(); 432 unsigned diagID = ci.diagnostics().getCustomDiagID( 433 clang::DiagnosticsEngine::Error, "Symbol not found"); 434 435 auto gdv = ci.invocation().frontendOpts().getDefVals_; 436 auto charBlock{cs.GetCharBlockFromLineAndColumns( 437 gdv.line, gdv.startColumn, gdv.endColumn)}; 438 if (!charBlock) { 439 ci.diagnostics().Report(diagID); 440 return; 441 } 442 443 llvm::outs() << "String range: >" << charBlock->ToString() << "<\n"; 444 445 auto *symbol{ci.invocation() 446 .semanticsContext() 447 .FindScope(*charBlock) 448 .FindSymbol(*charBlock)}; 449 if (!symbol) { 450 ci.diagnostics().Report(diagID); 451 return; 452 } 453 454 llvm::outs() << "Found symbol name: " << symbol->name().ToString() << "\n"; 455 456 auto sourceInfo{cs.GetSourcePositionRange(symbol->name())}; 457 if (!sourceInfo) { 458 llvm_unreachable( 459 "Failed to obtain SourcePosition." 460 "TODO: Please, write a test and replace this with a diagnostic!"); 461 return; 462 } 463 464 llvm::outs() << "Found symbol name: " << symbol->name().ToString() << "\n"; 465 llvm::outs() << symbol->name().ToString() << ": " 466 << sourceInfo->first.file.path() << ", " 467 << sourceInfo->first.line << ", " << sourceInfo->first.column 468 << "-" << sourceInfo->second.column << "\n"; 469 } 470 471 void GetSymbolsSourcesAction::ExecuteAction() { 472 // Report and exit if fatal semantic errors are present 473 if (reportFatalSemanticErrors(semantics(), this->instance().diagnostics(), 474 GetCurrentFileOrBufferName())) 475 return; 476 477 semantics().DumpSymbolsSources(llvm::outs()); 478 } 479 480 void EmitObjAction::ExecuteAction() { 481 CompilerInstance &ci = this->instance(); 482 unsigned DiagID = ci.diagnostics().getCustomDiagID( 483 clang::DiagnosticsEngine::Error, "code-generation is not available yet"); 484 ci.diagnostics().Report(DiagID); 485 } 486 487 void InitOnlyAction::ExecuteAction() { 488 CompilerInstance &ci = this->instance(); 489 unsigned DiagID = 490 ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Warning, 491 "Use `-init-only` for testing purposes only"); 492 ci.diagnostics().Report(DiagID); 493 } 494