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