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.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 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 DebugDumpParseTreeNoSemaAction::ExecuteAction() { 309 auto &parseTree{instance().parsing().parseTree()}; 310 Fortran::parser::AnalyzedObjectsAsFortran asFortran = 311 Fortran::frontend::getBasicAsFortran(); 312 313 // Dump parse tree 314 Fortran::parser::DumpTree(llvm::outs(), parseTree, &asFortran); 315 } 316 317 void DebugDumpParseTreeAction::ExecuteAction() { 318 auto &parseTree{instance().parsing().parseTree()}; 319 Fortran::parser::AnalyzedObjectsAsFortran asFortran = 320 Fortran::frontend::getBasicAsFortran(); 321 322 // Dump parse tree 323 Fortran::parser::DumpTree(llvm::outs(), parseTree, &asFortran); 324 // Report fatal semantic errors 325 reportFatalSemanticErrors(semantics(), this->instance().diagnostics(), 326 GetCurrentFileOrBufferName()); 327 } 328 329 void DebugMeasureParseTreeAction::ExecuteAction() { 330 CompilerInstance &ci = this->instance(); 331 332 // Parse. In case of failure, report and return. 333 ci.parsing().Parse(llvm::outs()); 334 335 if (!ci.parsing().messages().empty() && 336 (ci.invocation().warnAsErr() || 337 ci.parsing().messages().AnyFatalError())) { 338 unsigned diagID = ci.diagnostics().getCustomDiagID( 339 clang::DiagnosticsEngine::Error, "Could not parse %0"); 340 ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName(); 341 342 ci.parsing().messages().Emit( 343 llvm::errs(), this->instance().allCookedSources()); 344 return; 345 } 346 347 // Report the diagnostics from parsing 348 ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); 349 350 auto &parseTree{*ci.parsing().parseTree()}; 351 352 // Measure the parse tree 353 MeasurementVisitor visitor; 354 Fortran::parser::Walk(parseTree, visitor); 355 llvm::outs() << "Parse tree comprises " << visitor.objects 356 << " objects and occupies " << visitor.bytes 357 << " total bytes.\n"; 358 } 359 360 void DebugPreFIRTreeAction::ExecuteAction() { 361 CompilerInstance &ci = this->instance(); 362 // Report and exit if fatal semantic errors are present 363 if (reportFatalSemanticErrors( 364 semantics(), ci.diagnostics(), GetCurrentFileOrBufferName())) { 365 return; 366 } 367 368 auto &parseTree{*ci.parsing().parseTree()}; 369 370 // Dump pre-FIR tree 371 if (auto ast{Fortran::lower::createPFT( 372 parseTree, ci.invocation().semanticsContext())}) { 373 Fortran::lower::dumpPFT(llvm::outs(), *ast); 374 } else { 375 unsigned diagID = ci.diagnostics().getCustomDiagID( 376 clang::DiagnosticsEngine::Error, "Pre FIR Tree is NULL."); 377 ci.diagnostics().Report(diagID); 378 } 379 } 380 381 void DebugDumpParsingLogAction::ExecuteAction() { 382 CompilerInstance &ci = this->instance(); 383 384 ci.parsing().Parse(llvm::errs()); 385 ci.parsing().DumpParsingLog(llvm::outs()); 386 } 387 388 void GetDefinitionAction::ExecuteAction() { 389 // Report and exit if fatal semantic errors are present 390 if (reportFatalSemanticErrors(semantics(), this->instance().diagnostics(), 391 GetCurrentFileOrBufferName())) 392 return; 393 394 CompilerInstance &ci = this->instance(); 395 parser::AllCookedSources &cs = ci.allCookedSources(); 396 unsigned diagID = ci.diagnostics().getCustomDiagID( 397 clang::DiagnosticsEngine::Error, "Symbol not found"); 398 399 auto gdv = ci.invocation().frontendOpts().getDefVals_; 400 auto charBlock{cs.GetCharBlockFromLineAndColumns( 401 gdv.line, gdv.startColumn, gdv.endColumn)}; 402 if (!charBlock) { 403 ci.diagnostics().Report(diagID); 404 return; 405 } 406 407 llvm::outs() << "String range: >" << charBlock->ToString() << "<\n"; 408 409 auto *symbol{ci.invocation() 410 .semanticsContext() 411 .FindScope(*charBlock) 412 .FindSymbol(*charBlock)}; 413 if (!symbol) { 414 ci.diagnostics().Report(diagID); 415 return; 416 } 417 418 llvm::outs() << "Found symbol name: " << symbol->name().ToString() << "\n"; 419 420 auto sourceInfo{cs.GetSourcePositionRange(symbol->name())}; 421 if (!sourceInfo) { 422 llvm_unreachable( 423 "Failed to obtain SourcePosition." 424 "TODO: Please, write a test and replace this with a diagnostic!"); 425 return; 426 } 427 428 llvm::outs() << "Found symbol name: " << symbol->name().ToString() << "\n"; 429 llvm::outs() << symbol->name().ToString() << ": " 430 << sourceInfo->first.file.path() << ", " 431 << sourceInfo->first.line << ", " << sourceInfo->first.column 432 << "-" << sourceInfo->second.column << "\n"; 433 } 434 435 void GetSymbolsSourcesAction::ExecuteAction() { 436 // Report and exit if fatal semantic errors are present 437 if (reportFatalSemanticErrors(semantics(), this->instance().diagnostics(), 438 GetCurrentFileOrBufferName())) 439 return; 440 441 semantics().DumpSymbolsSources(llvm::outs()); 442 } 443 444 void EmitObjAction::ExecuteAction() { 445 CompilerInstance &ci = this->instance(); 446 unsigned DiagID = ci.diagnostics().getCustomDiagID( 447 clang::DiagnosticsEngine::Error, "code-generation is not available yet"); 448 ci.diagnostics().Report(DiagID); 449 } 450