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