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/Lower/PFTBuilder.h" 14 #include "flang/Parser/dump-parse-tree.h" 15 #include "flang/Parser/parsing.h" 16 #include "flang/Parser/provenance.h" 17 #include "flang/Parser/source.h" 18 #include "flang/Parser/unparse.h" 19 #include "flang/Semantics/runtime-type-info.h" 20 #include "flang/Semantics/semantics.h" 21 #include "flang/Semantics/unparse-with-symbols.h" 22 #include "llvm/ADT/StringRef.h" 23 #include <clang/Basic/Diagnostic.h> 24 #include <memory> 25 26 using namespace Fortran::frontend; 27 28 /// Report fatal semantic errors if present. 29 /// 30 /// \param semantics The semantics instance 31 /// \param diags The diagnostics engine instance 32 /// \param bufferName The file or buffer name 33 /// 34 /// \return True if fatal semantic errors are present, false if not 35 bool reportFatalSemanticErrors(const Fortran::semantics::Semantics &semantics, 36 clang::DiagnosticsEngine &diags, const llvm::StringRef &bufferName) { 37 if (semantics.AnyFatalError()) { 38 unsigned DiagID = diags.getCustomDiagID( 39 clang::DiagnosticsEngine::Error, "Semantic errors in %0"); 40 diags.Report(DiagID) << bufferName; 41 return true; 42 } 43 return false; 44 } 45 46 bool PrescanAction::BeginSourceFileAction(CompilerInstance &c1) { 47 CompilerInstance &ci = this->instance(); 48 49 std::string currentInputPath{GetCurrentFileOrBufferName()}; 50 51 Fortran::parser::Options parserOptions = ci.invocation().fortranOpts(); 52 53 if (ci.invocation().frontendOpts().fortranForm_ == FortranForm::Unknown) { 54 // Switch between fixed and free form format based on the input file 55 // extension. 56 // 57 // Ideally we should have all Fortran options set before entering this 58 // method (i.e. before processing any specific input files). However, we 59 // can't decide between fixed and free form based on the file extension 60 // earlier than this. 61 parserOptions.isFixedForm = currentInput().IsFixedForm(); 62 } 63 64 // Prescan. In case of failure, report and return. 65 ci.parsing().Prescan(currentInputPath, parserOptions); 66 67 if (!ci.parsing().messages().empty() && 68 (ci.invocation().warnAsErr() || 69 ci.parsing().messages().AnyFatalError())) { 70 const unsigned diagID = ci.diagnostics().getCustomDiagID( 71 clang::DiagnosticsEngine::Error, "Could not scan %0"); 72 ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName(); 73 ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); 74 75 return false; 76 } 77 78 return true; 79 } 80 81 bool PrescanAndSemaAction::BeginSourceFileAction(CompilerInstance &c1) { 82 CompilerInstance &ci = this->instance(); 83 84 std::string currentInputPath{GetCurrentFileOrBufferName()}; 85 86 Fortran::parser::Options parserOptions = ci.invocation().fortranOpts(); 87 88 if (ci.invocation().frontendOpts().fortranForm_ == FortranForm::Unknown) { 89 // Switch between fixed and free form format based on the input file 90 // extension. 91 // 92 // Ideally we should have all Fortran options set before entering this 93 // method (i.e. before processing any specific input files). However, we 94 // can't decide between fixed and free form based on the file extension 95 // earlier than this. 96 parserOptions.isFixedForm = currentInput().IsFixedForm(); 97 } 98 99 // Prescan. In case of failure, report and return. 100 ci.parsing().Prescan(currentInputPath, parserOptions); 101 102 if (!ci.parsing().messages().empty() && 103 (ci.invocation().warnAsErr() || 104 ci.parsing().messages().AnyFatalError())) { 105 const unsigned diagID = ci.diagnostics().getCustomDiagID( 106 clang::DiagnosticsEngine::Error, "Could not scan %0"); 107 ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName(); 108 ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); 109 110 return false; 111 } 112 113 // Parse. In case of failure, report and return. 114 ci.parsing().Parse(llvm::outs()); 115 116 if (!ci.parsing().messages().empty() && 117 (ci.invocation().warnAsErr() || 118 ci.parsing().messages().AnyFatalError())) { 119 unsigned diagID = ci.diagnostics().getCustomDiagID( 120 clang::DiagnosticsEngine::Error, "Could not parse %0"); 121 ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName(); 122 123 ci.parsing().messages().Emit( 124 llvm::errs(), this->instance().allCookedSources()); 125 return false; 126 } 127 128 // Report the diagnostics from parsing 129 ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); 130 131 auto &parseTree{*ci.parsing().parseTree()}; 132 133 // Prepare semantics 134 setSemantics(std::make_unique<Fortran::semantics::Semantics>( 135 ci.invocation().semanticsContext(), parseTree, 136 ci.parsing().cooked().AsCharBlock(), ci.invocation().debugModuleDir())); 137 auto &semantics = this->semantics(); 138 139 // Run semantic checks 140 semantics.Perform(); 141 142 // Report the diagnostics from the semantic checks 143 semantics.EmitMessages(ci.semaOutputStream()); 144 return true; 145 } 146 147 void InputOutputTestAction::ExecuteAction() { 148 CompilerInstance &ci = instance(); 149 150 // Create a stream for errors 151 std::string buf; 152 llvm::raw_string_ostream error_stream{buf}; 153 154 // Read the input file 155 Fortran::parser::AllSources &allSources{ci.allSources()}; 156 std::string path{GetCurrentFileOrBufferName()}; 157 const Fortran::parser::SourceFile *sf; 158 if (path == "-") 159 sf = allSources.ReadStandardInput(error_stream); 160 else 161 sf = allSources.Open(path, error_stream, std::optional<std::string>{"."s}); 162 llvm::ArrayRef<char> fileContent = sf->content(); 163 164 // Output file descriptor to receive the contents of the input file. 165 std::unique_ptr<llvm::raw_ostream> os; 166 167 // Copy the contents from the input file to the output file 168 if (!ci.IsOutputStreamNull()) { 169 // An output stream (outputStream_) was set earlier 170 ci.WriteOutputStream(fileContent.data()); 171 } else { 172 // No pre-set output stream - create an output file 173 os = ci.CreateDefaultOutputFile( 174 /*binary=*/true, GetCurrentFileOrBufferName(), "txt"); 175 if (!os) 176 return; 177 (*os) << fileContent.data(); 178 } 179 } 180 181 void PrintPreprocessedAction::ExecuteAction() { 182 std::string buf; 183 llvm::raw_string_ostream outForPP{buf}; 184 185 // Run the preprocessor 186 CompilerInstance &ci = this->instance(); 187 ci.parsing().DumpCookedChars(outForPP); 188 189 // If a pre-defined output stream exists, dump the preprocessed content there 190 if (!ci.IsOutputStreamNull()) { 191 // Send the output to the pre-defined output buffer. 192 ci.WriteOutputStream(outForPP.str()); 193 return; 194 } 195 196 // Print diagnostics from the preprocessor 197 ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); 198 199 // Create a file and save the preprocessed output there 200 if (auto os{ci.CreateDefaultOutputFile( 201 /*Binary=*/true, /*InFile=*/GetCurrentFileOrBufferName())}) { 202 (*os) << outForPP.str(); 203 } else { 204 llvm::errs() << "Unable to create the output file\n"; 205 return; 206 } 207 } 208 209 void DebugDumpProvenanceAction::ExecuteAction() { 210 this->instance().parsing().DumpProvenance(llvm::outs()); 211 } 212 213 void ParseSyntaxOnlyAction::ExecuteAction() { 214 reportFatalSemanticErrors(semantics(), this->instance().diagnostics(), 215 GetCurrentFileOrBufferName()); 216 } 217 218 void DebugUnparseAction::ExecuteAction() { 219 auto &parseTree{instance().parsing().parseTree()}; 220 Fortran::parser::AnalyzedObjectsAsFortran asFortran = 221 Fortran::frontend::getBasicAsFortran(); 222 223 // TODO: Options should come from CompilerInvocation 224 Unparse(llvm::outs(), *parseTree, 225 /*encoding=*/Fortran::parser::Encoding::UTF_8, 226 /*capitalizeKeywords=*/true, /*backslashEscapes=*/false, 227 /*preStatement=*/nullptr, &asFortran); 228 229 // Report fatal semantic errors 230 reportFatalSemanticErrors(semantics(), this->instance().diagnostics(), 231 GetCurrentFileOrBufferName()); 232 } 233 234 void DebugUnparseWithSymbolsAction::ExecuteAction() { 235 auto &parseTree{*instance().parsing().parseTree()}; 236 237 Fortran::semantics::UnparseWithSymbols( 238 llvm::outs(), parseTree, /*encoding=*/Fortran::parser::Encoding::UTF_8); 239 240 // Report fatal semantic errors 241 reportFatalSemanticErrors(semantics(), this->instance().diagnostics(), 242 GetCurrentFileOrBufferName()); 243 } 244 245 void DebugDumpSymbolsAction::ExecuteAction() { 246 auto &semantics = this->semantics(); 247 248 // Dump symbols 249 semantics.DumpSymbols(llvm::outs()); 250 // Report fatal semantic errors 251 reportFatalSemanticErrors( 252 semantics, this->instance().diagnostics(), GetCurrentFileOrBufferName()); 253 } 254 255 void DebugDumpParseTreeAction::ExecuteAction() { 256 auto &parseTree{instance().parsing().parseTree()}; 257 Fortran::parser::AnalyzedObjectsAsFortran asFortran = 258 Fortran::frontend::getBasicAsFortran(); 259 260 // Dump parse tree 261 Fortran::parser::DumpTree(llvm::outs(), parseTree, &asFortran); 262 // Report fatal semantic errors 263 reportFatalSemanticErrors(semantics(), this->instance().diagnostics(), 264 GetCurrentFileOrBufferName()); 265 } 266 267 void DebugMeasureParseTreeAction::ExecuteAction() { 268 CompilerInstance &ci = this->instance(); 269 270 // Parse. In case of failure, report and return. 271 ci.parsing().Parse(llvm::outs()); 272 273 if (!ci.parsing().messages().empty() && 274 (ci.invocation().warnAsErr() || 275 ci.parsing().messages().AnyFatalError())) { 276 unsigned diagID = ci.diagnostics().getCustomDiagID( 277 clang::DiagnosticsEngine::Error, "Could not parse %0"); 278 ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName(); 279 280 ci.parsing().messages().Emit( 281 llvm::errs(), this->instance().allCookedSources()); 282 return; 283 } 284 285 // Report the diagnostics from parsing 286 ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); 287 288 auto &parseTree{*ci.parsing().parseTree()}; 289 290 // Measure the parse tree 291 MeasurementVisitor visitor; 292 Fortran::parser::Walk(parseTree, visitor); 293 llvm::outs() << "Parse tree comprises " << visitor.objects 294 << " objects and occupies " << visitor.bytes 295 << " total bytes.\n"; 296 } 297 298 void DebugPreFIRTreeAction::ExecuteAction() { 299 CompilerInstance &ci = this->instance(); 300 // Report and exit if fatal semantic errors are present 301 if (reportFatalSemanticErrors( 302 semantics(), ci.diagnostics(), GetCurrentFileOrBufferName())) { 303 return; 304 } 305 306 auto &parseTree{*ci.parsing().parseTree()}; 307 308 // Dump pre-FIR tree 309 if (auto ast{Fortran::lower::createPFT( 310 parseTree, ci.invocation().semanticsContext())}) { 311 Fortran::lower::dumpPFT(llvm::outs(), *ast); 312 } else { 313 unsigned diagID = ci.diagnostics().getCustomDiagID( 314 clang::DiagnosticsEngine::Error, "Pre FIR Tree is NULL."); 315 ci.diagnostics().Report(diagID); 316 } 317 } 318 319 void DebugDumpParsingLogAction::ExecuteAction() { 320 CompilerInstance &ci = this->instance(); 321 322 ci.parsing().Parse(llvm::errs()); 323 ci.parsing().DumpParsingLog(llvm::outs()); 324 } 325 326 void GetSymbolsSourcesAction::ExecuteAction() { 327 // Report and exit if fatal semantic errors are present 328 if (reportFatalSemanticErrors(semantics(), this->instance().diagnostics(), 329 GetCurrentFileOrBufferName())) 330 return; 331 332 semantics().DumpSymbolsSources(llvm::outs()); 333 } 334 335 void EmitObjAction::ExecuteAction() { 336 CompilerInstance &ci = this->instance(); 337 unsigned DiagID = ci.diagnostics().getCustomDiagID( 338 clang::DiagnosticsEngine::Error, "code-generation is not available yet"); 339 ci.diagnostics().Report(DiagID); 340 } 341