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 //===----------------------------------------------------------------------===// 31 // Custom BeginSourceFileAction 32 //===----------------------------------------------------------------------===// 33 bool PrescanAction::BeginSourceFileAction() { return RunPrescan(); } 34 35 bool PrescanAndParseAction::BeginSourceFileAction() { 36 return RunPrescan() && RunParse(); 37 } 38 39 bool PrescanAndSemaAction::BeginSourceFileAction() { 40 return RunPrescan() && RunParse() && RunSemanticChecks(); 41 } 42 43 bool PrescanAndSemaDebugAction::BeginSourceFileAction() { 44 // Semantic checks are made to succeed unconditionally. 45 return RunPrescan() && RunParse() && (RunSemanticChecks() || true); 46 } 47 48 //===----------------------------------------------------------------------===// 49 // Custom ExecuteAction 50 //===----------------------------------------------------------------------===// 51 void InputOutputTestAction::ExecuteAction() { 52 CompilerInstance &ci = instance(); 53 54 // Create a stream for errors 55 std::string buf; 56 llvm::raw_string_ostream error_stream{buf}; 57 58 // Read the input file 59 Fortran::parser::AllSources &allSources{ci.allSources()}; 60 std::string path{GetCurrentFileOrBufferName()}; 61 const Fortran::parser::SourceFile *sf; 62 if (path == "-") 63 sf = allSources.ReadStandardInput(error_stream); 64 else 65 sf = allSources.Open(path, error_stream, std::optional<std::string>{"."s}); 66 llvm::ArrayRef<char> fileContent = sf->content(); 67 68 // Output file descriptor to receive the contents of the input file. 69 std::unique_ptr<llvm::raw_ostream> os; 70 71 // Copy the contents from the input file to the output file 72 if (!ci.IsOutputStreamNull()) { 73 // An output stream (outputStream_) was set earlier 74 ci.WriteOutputStream(fileContent.data()); 75 } else { 76 // No pre-set output stream - create an output file 77 os = ci.CreateDefaultOutputFile( 78 /*binary=*/true, GetCurrentFileOrBufferName(), "txt"); 79 if (!os) 80 return; 81 (*os) << fileContent.data(); 82 } 83 } 84 85 void PrintPreprocessedAction::ExecuteAction() { 86 std::string buf; 87 llvm::raw_string_ostream outForPP{buf}; 88 89 // Format or dump the prescanner's output 90 CompilerInstance &ci = this->instance(); 91 if (ci.invocation().preprocessorOpts().noReformat) { 92 ci.parsing().DumpCookedChars(outForPP); 93 } else { 94 ci.parsing().EmitPreprocessedSource( 95 outForPP, !ci.invocation().preprocessorOpts().noLineDirectives); 96 } 97 98 // Print diagnostics from the prescanner 99 ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); 100 101 // If a pre-defined output stream exists, dump the preprocessed content there 102 if (!ci.IsOutputStreamNull()) { 103 // Send the output to the pre-defined output buffer. 104 ci.WriteOutputStream(outForPP.str()); 105 return; 106 } 107 108 // Create a file and save the preprocessed output there 109 std::unique_ptr<llvm::raw_pwrite_stream> os{ci.CreateDefaultOutputFile( 110 /*Binary=*/true, /*InFile=*/GetCurrentFileOrBufferName())}; 111 if (!os) { 112 return; 113 } 114 115 (*os) << outForPP.str(); 116 } 117 118 void DebugDumpProvenanceAction::ExecuteAction() { 119 this->instance().parsing().DumpProvenance(llvm::outs()); 120 } 121 122 void ParseSyntaxOnlyAction::ExecuteAction() { 123 } 124 125 void DebugUnparseNoSemaAction::ExecuteAction() { 126 auto &invoc = this->instance().invocation(); 127 auto &parseTree{instance().parsing().parseTree()}; 128 129 // TODO: Options should come from CompilerInvocation 130 Unparse(llvm::outs(), *parseTree, 131 /*encoding=*/Fortran::parser::Encoding::UTF_8, 132 /*capitalizeKeywords=*/true, /*backslashEscapes=*/false, 133 /*preStatement=*/nullptr, 134 invoc.useAnalyzedObjectsForUnparse() ? &invoc.asFortran() : nullptr); 135 } 136 137 void DebugUnparseAction::ExecuteAction() { 138 auto &invoc = this->instance().invocation(); 139 auto &parseTree{instance().parsing().parseTree()}; 140 141 CompilerInstance &ci = this->instance(); 142 auto os{ci.CreateDefaultOutputFile( 143 /*Binary=*/false, /*InFile=*/GetCurrentFileOrBufferName())}; 144 145 // TODO: Options should come from CompilerInvocation 146 Unparse(*os, *parseTree, 147 /*encoding=*/Fortran::parser::Encoding::UTF_8, 148 /*capitalizeKeywords=*/true, /*backslashEscapes=*/false, 149 /*preStatement=*/nullptr, 150 invoc.useAnalyzedObjectsForUnparse() ? &invoc.asFortran() : nullptr); 151 152 // Report fatal semantic errors 153 reportFatalSemanticErrors(); 154 } 155 156 void DebugUnparseWithSymbolsAction::ExecuteAction() { 157 auto &parseTree{*instance().parsing().parseTree()}; 158 159 Fortran::semantics::UnparseWithSymbols( 160 llvm::outs(), parseTree, /*encoding=*/Fortran::parser::Encoding::UTF_8); 161 162 // Report fatal semantic errors 163 reportFatalSemanticErrors(); 164 } 165 166 void DebugDumpSymbolsAction::ExecuteAction() { 167 CompilerInstance &ci = this->instance(); 168 auto &semantics = ci.semantics(); 169 170 auto tables{Fortran::semantics::BuildRuntimeDerivedTypeTables( 171 instance().invocation().semanticsContext())}; 172 // The runtime derived type information table builder may find and report 173 // semantic errors. So it is important that we report them _after_ 174 // BuildRuntimeDerivedTypeTables is run. 175 reportFatalSemanticErrors(); 176 177 if (!tables.schemata) { 178 unsigned DiagID = 179 ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, 180 "could not find module file for __fortran_type_info"); 181 ci.diagnostics().Report(DiagID); 182 llvm::errs() << "\n"; 183 } 184 185 // Dump symbols 186 semantics.DumpSymbols(llvm::outs()); 187 } 188 189 void DebugDumpAllAction::ExecuteAction() { 190 CompilerInstance &ci = this->instance(); 191 192 // Dump parse tree 193 auto &parseTree{instance().parsing().parseTree()}; 194 llvm::outs() << "========================"; 195 llvm::outs() << " Flang: parse tree dump "; 196 llvm::outs() << "========================\n"; 197 Fortran::parser::DumpTree( 198 llvm::outs(), parseTree, &ci.invocation().asFortran()); 199 200 auto &semantics = ci.semantics(); 201 auto tables{Fortran::semantics::BuildRuntimeDerivedTypeTables( 202 instance().invocation().semanticsContext())}; 203 // The runtime derived type information table builder may find and report 204 // semantic errors. So it is important that we report them _after_ 205 // BuildRuntimeDerivedTypeTables is run. 206 reportFatalSemanticErrors(); 207 208 if (!tables.schemata) { 209 unsigned DiagID = 210 ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, 211 "could not find module file for __fortran_type_info"); 212 ci.diagnostics().Report(DiagID); 213 llvm::errs() << "\n"; 214 } 215 216 // Dump symbols 217 llvm::outs() << "====================="; 218 llvm::outs() << " Flang: symbols dump "; 219 llvm::outs() << "=====================\n"; 220 semantics.DumpSymbols(llvm::outs()); 221 } 222 223 void DebugDumpParseTreeNoSemaAction::ExecuteAction() { 224 auto &parseTree{instance().parsing().parseTree()}; 225 226 // Dump parse tree 227 Fortran::parser::DumpTree( 228 llvm::outs(), parseTree, &this->instance().invocation().asFortran()); 229 } 230 231 void DebugDumpParseTreeAction::ExecuteAction() { 232 auto &parseTree{instance().parsing().parseTree()}; 233 234 // Dump parse tree 235 Fortran::parser::DumpTree( 236 llvm::outs(), parseTree, &this->instance().invocation().asFortran()); 237 238 // Report fatal semantic errors 239 reportFatalSemanticErrors(); 240 } 241 242 void DebugMeasureParseTreeAction::ExecuteAction() { 243 CompilerInstance &ci = this->instance(); 244 245 // Parse. In case of failure, report and return. 246 ci.parsing().Parse(llvm::outs()); 247 248 if (!ci.parsing().messages().empty() && 249 (ci.invocation().warnAsErr() || 250 ci.parsing().messages().AnyFatalError())) { 251 unsigned diagID = ci.diagnostics().getCustomDiagID( 252 clang::DiagnosticsEngine::Error, "Could not parse %0"); 253 ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName(); 254 255 ci.parsing().messages().Emit( 256 llvm::errs(), this->instance().allCookedSources()); 257 return; 258 } 259 260 // Report the diagnostics from parsing 261 ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); 262 263 auto &parseTree{*ci.parsing().parseTree()}; 264 265 // Measure the parse tree 266 MeasurementVisitor visitor; 267 Fortran::parser::Walk(parseTree, visitor); 268 llvm::outs() << "Parse tree comprises " << visitor.objects 269 << " objects and occupies " << visitor.bytes 270 << " total bytes.\n"; 271 } 272 273 void DebugPreFIRTreeAction::ExecuteAction() { 274 CompilerInstance &ci = this->instance(); 275 // Report and exit if fatal semantic errors are present 276 if (reportFatalSemanticErrors()) { 277 return; 278 } 279 280 auto &parseTree{*ci.parsing().parseTree()}; 281 282 // Dump pre-FIR tree 283 if (auto ast{Fortran::lower::createPFT( 284 parseTree, ci.invocation().semanticsContext())}) { 285 Fortran::lower::dumpPFT(llvm::outs(), *ast); 286 } else { 287 unsigned diagID = ci.diagnostics().getCustomDiagID( 288 clang::DiagnosticsEngine::Error, "Pre FIR Tree is NULL."); 289 ci.diagnostics().Report(diagID); 290 } 291 } 292 293 void DebugDumpParsingLogAction::ExecuteAction() { 294 CompilerInstance &ci = this->instance(); 295 296 ci.parsing().Parse(llvm::errs()); 297 ci.parsing().DumpParsingLog(llvm::outs()); 298 } 299 300 void GetDefinitionAction::ExecuteAction() { 301 CompilerInstance &ci = this->instance(); 302 303 // Report and exit if fatal semantic errors are present 304 if (reportFatalSemanticErrors()) { 305 return; 306 } 307 308 parser::AllCookedSources &cs = ci.allCookedSources(); 309 unsigned diagID = ci.diagnostics().getCustomDiagID( 310 clang::DiagnosticsEngine::Error, "Symbol not found"); 311 312 auto gdv = ci.invocation().frontendOpts().getDefVals; 313 auto charBlock{cs.GetCharBlockFromLineAndColumns( 314 gdv.line, gdv.startColumn, gdv.endColumn)}; 315 if (!charBlock) { 316 ci.diagnostics().Report(diagID); 317 return; 318 } 319 320 llvm::outs() << "String range: >" << charBlock->ToString() << "<\n"; 321 322 auto *symbol{ci.invocation() 323 .semanticsContext() 324 .FindScope(*charBlock) 325 .FindSymbol(*charBlock)}; 326 if (!symbol) { 327 ci.diagnostics().Report(diagID); 328 return; 329 } 330 331 llvm::outs() << "Found symbol name: " << symbol->name().ToString() << "\n"; 332 333 auto sourceInfo{cs.GetSourcePositionRange(symbol->name())}; 334 if (!sourceInfo) { 335 llvm_unreachable( 336 "Failed to obtain SourcePosition." 337 "TODO: Please, write a test and replace this with a diagnostic!"); 338 return; 339 } 340 341 llvm::outs() << "Found symbol name: " << symbol->name().ToString() << "\n"; 342 llvm::outs() << symbol->name().ToString() << ": " 343 << sourceInfo->first.file.path() << ", " 344 << sourceInfo->first.line << ", " << sourceInfo->first.column 345 << "-" << sourceInfo->second.column << "\n"; 346 } 347 348 void GetSymbolsSourcesAction::ExecuteAction() { 349 CompilerInstance &ci = this->instance(); 350 351 // Report and exit if fatal semantic errors are present 352 if (reportFatalSemanticErrors()) { 353 return; 354 } 355 356 ci.semantics().DumpSymbolsSources(llvm::outs()); 357 } 358 359 void EmitObjAction::ExecuteAction() { 360 CompilerInstance &ci = this->instance(); 361 unsigned DiagID = ci.diagnostics().getCustomDiagID( 362 clang::DiagnosticsEngine::Error, "code-generation is not available yet"); 363 ci.diagnostics().Report(DiagID); 364 } 365 366 void InitOnlyAction::ExecuteAction() { 367 CompilerInstance &ci = this->instance(); 368 unsigned DiagID = 369 ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Warning, 370 "Use `-init-only` for testing purposes only"); 371 ci.diagnostics().Report(DiagID); 372 } 373 374 void PluginParseTreeAction::ExecuteAction() {} 375