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 template <unsigned N> 49 static bool reportFatalErrors( 50 const FrontendAction *act, const char (&message)[N]) { 51 CompilerInstance &ci = act->instance(); 52 if (!ci.parsing().messages().empty() && 53 (ci.invocation().warnAsErr() || 54 ci.parsing().messages().AnyFatalError())) { 55 const unsigned diagID = ci.diagnostics().getCustomDiagID( 56 clang::DiagnosticsEngine::Error, message); 57 ci.diagnostics().Report(diagID) << act->GetCurrentFileOrBufferName(); 58 ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); 59 return true; 60 } 61 return false; 62 } 63 64 inline bool reportFatalScanningErrors(const FrontendAction *act) { 65 return reportFatalErrors(act, "Could not scan %0"); 66 } 67 68 inline bool reportFatalParsingErrors(const FrontendAction *act) { 69 return reportFatalErrors(act, "Could not parse %0"); 70 } 71 72 bool PrescanAction::BeginSourceFileAction(CompilerInstance &c1) { 73 CompilerInstance &ci = this->instance(); 74 std::string currentInputPath{GetCurrentFileOrBufferName()}; 75 Fortran::parser::Options parserOptions = ci.invocation().fortranOpts(); 76 77 // Prescan. In case of failure, report and return. 78 ci.parsing().Prescan(currentInputPath, parserOptions); 79 80 return !reportFatalScanningErrors(this); 81 } 82 83 bool PrescanAndParseAction::BeginSourceFileAction(CompilerInstance &c1) { 84 CompilerInstance &ci = this->instance(); 85 86 std::string currentInputPath{GetCurrentFileOrBufferName()}; 87 88 Fortran::parser::Options parserOptions = ci.invocation().fortranOpts(); 89 90 if (ci.invocation().frontendOpts().fortranForm == FortranForm::Unknown) { 91 // Switch between fixed and free form format based on the input file 92 // extension. 93 // 94 // Ideally we should have all Fortran options set before entering this 95 // method (i.e. before processing any specific input files). However, we 96 // can't decide between fixed and free form based on the file extension 97 // earlier than this. 98 parserOptions.isFixedForm = currentInput().IsFixedForm(); 99 } 100 101 // Prescan. In case of failure, report and return. 102 ci.parsing().Prescan(currentInputPath, parserOptions); 103 104 if (reportFatalScanningErrors(this)) 105 return false; 106 107 // Parse. In case of failure, report and return. 108 ci.parsing().Parse(llvm::outs()); 109 110 if (reportFatalParsingErrors(this)) 111 return false; 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 (reportFatalScanningErrors(this)) 128 return false; 129 130 // Parse. In case of failure, report and return. 131 ci.parsing().Parse(llvm::outs()); 132 133 if (reportFatalParsingErrors(this)) 134 return false; 135 136 // Report the diagnostics from parsing 137 ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); 138 139 auto &parseTree{*ci.parsing().parseTree()}; 140 141 // Prepare semantics 142 setSemantics(std::make_unique<Fortran::semantics::Semantics>( 143 ci.invocation().semanticsContext(), parseTree, 144 ci.invocation().debugModuleDir())); 145 auto &semantics = this->semantics(); 146 147 // Run semantic checks 148 semantics.Perform(); 149 150 // Report the diagnostics from the semantic checks 151 semantics.EmitMessages(ci.semaOutputStream()); 152 153 return true; 154 } 155 156 void InputOutputTestAction::ExecuteAction() { 157 CompilerInstance &ci = instance(); 158 159 // Create a stream for errors 160 std::string buf; 161 llvm::raw_string_ostream error_stream{buf}; 162 163 // Read the input file 164 Fortran::parser::AllSources &allSources{ci.allSources()}; 165 std::string path{GetCurrentFileOrBufferName()}; 166 const Fortran::parser::SourceFile *sf; 167 if (path == "-") 168 sf = allSources.ReadStandardInput(error_stream); 169 else 170 sf = allSources.Open(path, error_stream, std::optional<std::string>{"."s}); 171 llvm::ArrayRef<char> fileContent = sf->content(); 172 173 // Output file descriptor to receive the contents of the input file. 174 std::unique_ptr<llvm::raw_ostream> os; 175 176 // Copy the contents from the input file to the output file 177 if (!ci.IsOutputStreamNull()) { 178 // An output stream (outputStream_) was set earlier 179 ci.WriteOutputStream(fileContent.data()); 180 } else { 181 // No pre-set output stream - create an output file 182 os = ci.CreateDefaultOutputFile( 183 /*binary=*/true, GetCurrentFileOrBufferName(), "txt"); 184 if (!os) 185 return; 186 (*os) << fileContent.data(); 187 } 188 } 189 190 void PrintPreprocessedAction::ExecuteAction() { 191 std::string buf; 192 llvm::raw_string_ostream outForPP{buf}; 193 194 // Format or dump the prescanner's output 195 CompilerInstance &ci = this->instance(); 196 if (ci.invocation().preprocessorOpts().noReformat) { 197 ci.parsing().DumpCookedChars(outForPP); 198 } else { 199 ci.parsing().EmitPreprocessedSource( 200 outForPP, !ci.invocation().preprocessorOpts().noLineDirectives); 201 } 202 203 // If a pre-defined output stream exists, dump the preprocessed content there 204 if (!ci.IsOutputStreamNull()) { 205 // Send the output to the pre-defined output buffer. 206 ci.WriteOutputStream(outForPP.str()); 207 return; 208 } 209 210 // Print diagnostics from the prescanner 211 ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); 212 213 // Create a file and save the preprocessed output there 214 if (auto os{ci.CreateDefaultOutputFile( 215 /*Binary=*/true, /*InFile=*/GetCurrentFileOrBufferName())}) { 216 (*os) << outForPP.str(); 217 } else { 218 llvm::errs() << "Unable to create the output file\n"; 219 } 220 } 221 222 void DebugDumpProvenanceAction::ExecuteAction() { 223 this->instance().parsing().DumpProvenance(llvm::outs()); 224 } 225 226 void ParseSyntaxOnlyAction::ExecuteAction() { 227 reportFatalSemanticErrors(semantics(), this->instance().diagnostics(), 228 GetCurrentFileOrBufferName()); 229 } 230 231 void DebugUnparseNoSemaAction::ExecuteAction() { 232 auto &invoc = this->instance().invocation(); 233 auto &parseTree{instance().parsing().parseTree()}; 234 235 // TODO: Options should come from CompilerInvocation 236 Unparse(llvm::outs(), *parseTree, 237 /*encoding=*/Fortran::parser::Encoding::UTF_8, 238 /*capitalizeKeywords=*/true, /*backslashEscapes=*/false, 239 /*preStatement=*/nullptr, 240 invoc.useAnalyzedObjectsForUnparse() ? &invoc.asFortran() : nullptr); 241 } 242 243 void DebugUnparseAction::ExecuteAction() { 244 auto &invoc = this->instance().invocation(); 245 auto &parseTree{instance().parsing().parseTree()}; 246 247 CompilerInstance &ci = this->instance(); 248 auto os{ci.CreateDefaultOutputFile( 249 /*Binary=*/false, /*InFile=*/GetCurrentFileOrBufferName())}; 250 251 // TODO: Options should come from CompilerInvocation 252 Unparse(*os, *parseTree, 253 /*encoding=*/Fortran::parser::Encoding::UTF_8, 254 /*capitalizeKeywords=*/true, /*backslashEscapes=*/false, 255 /*preStatement=*/nullptr, 256 invoc.useAnalyzedObjectsForUnparse() ? &invoc.asFortran() : nullptr); 257 258 // Report fatal semantic errors 259 reportFatalSemanticErrors(semantics(), this->instance().diagnostics(), 260 GetCurrentFileOrBufferName()); 261 } 262 263 void DebugUnparseWithSymbolsAction::ExecuteAction() { 264 auto &parseTree{*instance().parsing().parseTree()}; 265 266 Fortran::semantics::UnparseWithSymbols( 267 llvm::outs(), parseTree, /*encoding=*/Fortran::parser::Encoding::UTF_8); 268 269 // Report fatal semantic errors 270 reportFatalSemanticErrors(semantics(), this->instance().diagnostics(), 271 GetCurrentFileOrBufferName()); 272 } 273 274 void DebugDumpSymbolsAction::ExecuteAction() { 275 CompilerInstance &ci = this->instance(); 276 auto &semantics = this->semantics(); 277 278 auto tables{Fortran::semantics::BuildRuntimeDerivedTypeTables( 279 instance().invocation().semanticsContext())}; 280 // The runtime derived type information table builder may find and report 281 // semantic errors. So it is important that we report them _after_ 282 // BuildRuntimeDerivedTypeTables is run. 283 reportFatalSemanticErrors( 284 semantics, this->instance().diagnostics(), GetCurrentFileOrBufferName()); 285 286 if (!tables.schemata) { 287 unsigned DiagID = 288 ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, 289 "could not find module file for __fortran_type_info"); 290 ci.diagnostics().Report(DiagID); 291 llvm::errs() << "\n"; 292 } 293 294 // Dump symbols 295 semantics.DumpSymbols(llvm::outs()); 296 } 297 298 void DebugDumpAllAction::ExecuteAction() { 299 CompilerInstance &ci = this->instance(); 300 301 // Dump parse tree 302 auto &parseTree{instance().parsing().parseTree()}; 303 llvm::outs() << "========================"; 304 llvm::outs() << " Flang: parse tree dump "; 305 llvm::outs() << "========================\n"; 306 Fortran::parser::DumpTree( 307 llvm::outs(), parseTree, &ci.invocation().asFortran()); 308 309 auto &semantics = this->semantics(); 310 auto tables{Fortran::semantics::BuildRuntimeDerivedTypeTables( 311 instance().invocation().semanticsContext())}; 312 // The runtime derived type information table builder may find and report 313 // semantic errors. So it is important that we report them _after_ 314 // BuildRuntimeDerivedTypeTables is run. 315 reportFatalSemanticErrors( 316 semantics, this->instance().diagnostics(), GetCurrentFileOrBufferName()); 317 318 if (!tables.schemata) { 319 unsigned DiagID = 320 ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, 321 "could not find module file for __fortran_type_info"); 322 ci.diagnostics().Report(DiagID); 323 llvm::errs() << "\n"; 324 } 325 326 // Dump symbols 327 llvm::outs() << "====================="; 328 llvm::outs() << " Flang: symbols dump "; 329 llvm::outs() << "=====================\n"; 330 semantics.DumpSymbols(llvm::outs()); 331 } 332 333 void DebugDumpParseTreeNoSemaAction::ExecuteAction() { 334 auto &parseTree{instance().parsing().parseTree()}; 335 336 // Dump parse tree 337 Fortran::parser::DumpTree( 338 llvm::outs(), parseTree, &this->instance().invocation().asFortran()); 339 } 340 341 void DebugDumpParseTreeAction::ExecuteAction() { 342 auto &parseTree{instance().parsing().parseTree()}; 343 344 // Dump parse tree 345 Fortran::parser::DumpTree( 346 llvm::outs(), parseTree, &this->instance().invocation().asFortran()); 347 348 // Report fatal semantic errors 349 reportFatalSemanticErrors(semantics(), this->instance().diagnostics(), 350 GetCurrentFileOrBufferName()); 351 } 352 353 void DebugMeasureParseTreeAction::ExecuteAction() { 354 CompilerInstance &ci = this->instance(); 355 356 // Parse. In case of failure, report and return. 357 ci.parsing().Parse(llvm::outs()); 358 359 if (!ci.parsing().messages().empty() && 360 (ci.invocation().warnAsErr() || 361 ci.parsing().messages().AnyFatalError())) { 362 unsigned diagID = ci.diagnostics().getCustomDiagID( 363 clang::DiagnosticsEngine::Error, "Could not parse %0"); 364 ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName(); 365 366 ci.parsing().messages().Emit( 367 llvm::errs(), this->instance().allCookedSources()); 368 return; 369 } 370 371 // Report the diagnostics from parsing 372 ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); 373 374 auto &parseTree{*ci.parsing().parseTree()}; 375 376 // Measure the parse tree 377 MeasurementVisitor visitor; 378 Fortran::parser::Walk(parseTree, visitor); 379 llvm::outs() << "Parse tree comprises " << visitor.objects 380 << " objects and occupies " << visitor.bytes 381 << " total bytes.\n"; 382 } 383 384 void DebugPreFIRTreeAction::ExecuteAction() { 385 CompilerInstance &ci = this->instance(); 386 // Report and exit if fatal semantic errors are present 387 if (reportFatalSemanticErrors( 388 semantics(), ci.diagnostics(), GetCurrentFileOrBufferName())) { 389 return; 390 } 391 392 auto &parseTree{*ci.parsing().parseTree()}; 393 394 // Dump pre-FIR tree 395 if (auto ast{Fortran::lower::createPFT( 396 parseTree, ci.invocation().semanticsContext())}) { 397 Fortran::lower::dumpPFT(llvm::outs(), *ast); 398 } else { 399 unsigned diagID = ci.diagnostics().getCustomDiagID( 400 clang::DiagnosticsEngine::Error, "Pre FIR Tree is NULL."); 401 ci.diagnostics().Report(diagID); 402 } 403 } 404 405 void DebugDumpParsingLogAction::ExecuteAction() { 406 CompilerInstance &ci = this->instance(); 407 408 ci.parsing().Parse(llvm::errs()); 409 ci.parsing().DumpParsingLog(llvm::outs()); 410 } 411 412 void GetDefinitionAction::ExecuteAction() { 413 // Report and exit if fatal semantic errors are present 414 if (reportFatalSemanticErrors(semantics(), this->instance().diagnostics(), 415 GetCurrentFileOrBufferName())) 416 return; 417 418 CompilerInstance &ci = this->instance(); 419 parser::AllCookedSources &cs = ci.allCookedSources(); 420 unsigned diagID = ci.diagnostics().getCustomDiagID( 421 clang::DiagnosticsEngine::Error, "Symbol not found"); 422 423 auto gdv = ci.invocation().frontendOpts().getDefVals; 424 auto charBlock{cs.GetCharBlockFromLineAndColumns( 425 gdv.line, gdv.startColumn, gdv.endColumn)}; 426 if (!charBlock) { 427 ci.diagnostics().Report(diagID); 428 return; 429 } 430 431 llvm::outs() << "String range: >" << charBlock->ToString() << "<\n"; 432 433 auto *symbol{ci.invocation() 434 .semanticsContext() 435 .FindScope(*charBlock) 436 .FindSymbol(*charBlock)}; 437 if (!symbol) { 438 ci.diagnostics().Report(diagID); 439 return; 440 } 441 442 llvm::outs() << "Found symbol name: " << symbol->name().ToString() << "\n"; 443 444 auto sourceInfo{cs.GetSourcePositionRange(symbol->name())}; 445 if (!sourceInfo) { 446 llvm_unreachable( 447 "Failed to obtain SourcePosition." 448 "TODO: Please, write a test and replace this with a diagnostic!"); 449 return; 450 } 451 452 llvm::outs() << "Found symbol name: " << symbol->name().ToString() << "\n"; 453 llvm::outs() << symbol->name().ToString() << ": " 454 << sourceInfo->first.file.path() << ", " 455 << sourceInfo->first.line << ", " << sourceInfo->first.column 456 << "-" << sourceInfo->second.column << "\n"; 457 } 458 459 void GetSymbolsSourcesAction::ExecuteAction() { 460 // Report and exit if fatal semantic errors are present 461 if (reportFatalSemanticErrors(semantics(), this->instance().diagnostics(), 462 GetCurrentFileOrBufferName())) 463 return; 464 465 semantics().DumpSymbolsSources(llvm::outs()); 466 } 467 468 void EmitObjAction::ExecuteAction() { 469 CompilerInstance &ci = this->instance(); 470 unsigned DiagID = ci.diagnostics().getCustomDiagID( 471 clang::DiagnosticsEngine::Error, "code-generation is not available yet"); 472 ci.diagnostics().Report(DiagID); 473 } 474 475 void InitOnlyAction::ExecuteAction() { 476 CompilerInstance &ci = this->instance(); 477 unsigned DiagID = 478 ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Warning, 479 "Use `-init-only` for testing purposes only"); 480 ci.diagnostics().Report(DiagID); 481 } 482