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