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