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