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