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/Parser/parsing.h" 14 #include "flang/Parser/provenance.h" 15 #include "flang/Parser/source.h" 16 #include "flang/Parser/unparse.h" 17 #include "flang/Semantics/semantics.h" 18 #include "flang/Semantics/unparse-with-symbols.h" 19 20 using namespace Fortran::frontend; 21 22 bool PrescanAction::BeginSourceFileAction(CompilerInstance &c1) { 23 CompilerInstance &ci = this->instance(); 24 25 std::string currentInputPath{GetCurrentFileOrBufferName()}; 26 27 Fortran::parser::Options parserOptions = ci.invocation().fortranOpts(); 28 29 if (ci.invocation().frontendOpts().fortranForm_ == FortranForm::Unknown) { 30 // Switch between fixed and free form format based on the input file 31 // extension. 32 // 33 // Ideally we should have all Fortran options set before entering this 34 // method (i.e. before processing any specific input files). However, we 35 // can't decide between fixed and free form based on the file extension 36 // earlier than this. 37 parserOptions.isFixedForm = currentInput().IsFixedForm(); 38 } 39 40 // Prescan. In case of failure, report and return. 41 ci.parsing().Prescan(currentInputPath, parserOptions); 42 43 if (ci.parsing().messages().AnyFatalError()) { 44 const unsigned diagID = ci.diagnostics().getCustomDiagID( 45 clang::DiagnosticsEngine::Error, "Could not scan %0"); 46 ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName(); 47 ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); 48 49 return false; 50 } 51 52 return true; 53 } 54 55 bool PrescanAndSemaAction::BeginSourceFileAction(CompilerInstance &c1) { 56 CompilerInstance &ci = this->instance(); 57 58 std::string currentInputPath{GetCurrentFileOrBufferName()}; 59 60 Fortran::parser::Options parserOptions = ci.invocation().fortranOpts(); 61 62 if (ci.invocation().frontendOpts().fortranForm_ == FortranForm::Unknown) { 63 // Switch between fixed and free form format based on the input file 64 // extension. 65 // 66 // Ideally we should have all Fortran options set before entering this 67 // method (i.e. before processing any specific input files). However, we 68 // can't decide between fixed and free form based on the file extension 69 // earlier than this. 70 parserOptions.isFixedForm = currentInput().IsFixedForm(); 71 } 72 73 // Prescan. In case of failure, report and return. 74 ci.parsing().Prescan(currentInputPath, parserOptions); 75 76 if (ci.parsing().messages().AnyFatalError()) { 77 const unsigned diagID = ci.diagnostics().getCustomDiagID( 78 clang::DiagnosticsEngine::Error, "Could not scan %0"); 79 ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName(); 80 ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); 81 82 return false; 83 } 84 85 // Parse. In case of failure, report and return. 86 ci.parsing().Parse(llvm::outs()); 87 88 if (ci.parsing().messages().AnyFatalError()) { 89 unsigned diagID = ci.diagnostics().getCustomDiagID( 90 clang::DiagnosticsEngine::Error, "Could not parse %0"); 91 ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName(); 92 93 ci.parsing().messages().Emit( 94 llvm::errs(), this->instance().allCookedSources()); 95 return false; 96 } 97 98 // Report the diagnostics from parsing 99 ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); 100 101 auto &parseTree{*ci.parsing().parseTree()}; 102 103 // Prepare semantics 104 Fortran::semantics::Semantics semantics{ci.invocation().semanticsContext(), 105 parseTree, ci.parsing().cooked().AsCharBlock()}; 106 107 // Run semantic checks 108 semantics.Perform(); 109 110 // Report the diagnostics from the semantic checks 111 semantics.EmitMessages(ci.semaOutputStream()); 112 113 if (semantics.AnyFatalError()) { 114 unsigned DiagID = ci.diagnostics().getCustomDiagID( 115 clang::DiagnosticsEngine::Error, "Semantic errors in %0"); 116 ci.diagnostics().Report(DiagID) << GetCurrentFileOrBufferName(); 117 118 return false; 119 } 120 121 return true; 122 } 123 124 void InputOutputTestAction::ExecuteAction() { 125 CompilerInstance &ci = instance(); 126 127 // Create a stream for errors 128 std::string buf; 129 llvm::raw_string_ostream error_stream{buf}; 130 131 // Read the input file 132 Fortran::parser::AllSources &allSources{ci.allSources()}; 133 std::string path{GetCurrentFileOrBufferName()}; 134 const Fortran::parser::SourceFile *sf; 135 if (path == "-") 136 sf = allSources.ReadStandardInput(error_stream); 137 else 138 sf = allSources.Open(path, error_stream, std::optional<std::string>{"."s}); 139 llvm::ArrayRef<char> fileContent = sf->content(); 140 141 // Output file descriptor to receive the contents of the input file. 142 std::unique_ptr<llvm::raw_ostream> os; 143 144 // Copy the contents from the input file to the output file 145 if (!ci.IsOutputStreamNull()) { 146 // An output stream (outputStream_) was set earlier 147 ci.WriteOutputStream(fileContent.data()); 148 } else { 149 // No pre-set output stream - create an output file 150 os = ci.CreateDefaultOutputFile( 151 /*binary=*/true, GetCurrentFileOrBufferName(), "txt"); 152 if (!os) 153 return; 154 (*os) << fileContent.data(); 155 } 156 } 157 158 void PrintPreprocessedAction::ExecuteAction() { 159 std::string buf; 160 llvm::raw_string_ostream outForPP{buf}; 161 162 // Run the preprocessor 163 CompilerInstance &ci = this->instance(); 164 ci.parsing().DumpCookedChars(outForPP); 165 166 // If a pre-defined output stream exists, dump the preprocessed content there 167 if (!ci.IsOutputStreamNull()) { 168 // Send the output to the pre-defined output buffer. 169 ci.WriteOutputStream(outForPP.str()); 170 return; 171 } 172 173 // Print diagnostics from the preprocessor 174 ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); 175 176 // Create a file and save the preprocessed output there 177 if (auto os{ci.CreateDefaultOutputFile( 178 /*Binary=*/true, /*InFile=*/GetCurrentFileOrBufferName())}) { 179 (*os) << outForPP.str(); 180 } else { 181 llvm::errs() << "Unable to create the output file\n"; 182 return; 183 } 184 } 185 186 void ParseSyntaxOnlyAction::ExecuteAction() {} 187 188 void DebugUnparseAction::ExecuteAction() { 189 auto &parseTree{instance().parsing().parseTree()}; 190 Fortran::parser::AnalyzedObjectsAsFortran asFortran = 191 Fortran::frontend::getBasicAsFortran(); 192 193 // TODO: Options should come from CompilerInvocation 194 Unparse(llvm::outs(), *parseTree, 195 /*encoding=*/Fortran::parser::Encoding::UTF_8, 196 /*capitalizeKeywords=*/true, /*backslashEscapes=*/false, 197 /*preStatement=*/nullptr, &asFortran); 198 } 199 200 void DebugUnparseWithSymbolsAction::ExecuteAction() { 201 auto &parseTree{*instance().parsing().parseTree()}; 202 203 Fortran::semantics::UnparseWithSymbols( 204 llvm::outs(), parseTree, /*encoding=*/Fortran::parser::Encoding::UTF_8); 205 } 206 207 void EmitObjAction::ExecuteAction() { 208 CompilerInstance &ci = this->instance(); 209 unsigned DiagID = ci.diagnostics().getCustomDiagID( 210 clang::DiagnosticsEngine::Error, "code-generation is not available yet"); 211 ci.diagnostics().Report(DiagID); 212 } 213