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