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