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