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 "llvm/Support/ErrorHandling.h"
25 #include <clang/Basic/Diagnostic.h>
26 #include <memory>
27 
28 using namespace Fortran::frontend;
29 
30 /// Report fatal semantic errors if present.
31 ///
32 /// \param semantics The semantics instance
33 /// \param diags The diagnostics engine instance
34 /// \param bufferName The file or buffer name
35 ///
36 /// \return True if fatal semantic errors are present, false if not
37 bool reportFatalSemanticErrors(const Fortran::semantics::Semantics &semantics,
38     clang::DiagnosticsEngine &diags, const llvm::StringRef &bufferName) {
39   if (semantics.AnyFatalError()) {
40     unsigned DiagID = diags.getCustomDiagID(
41         clang::DiagnosticsEngine::Error, "Semantic errors in %0");
42     diags.Report(DiagID) << bufferName;
43     return true;
44   }
45   return false;
46 }
47 
48 template <unsigned N>
49 static bool reportFatalErrors(
50     const FrontendAction *act, const char (&message)[N]) {
51   CompilerInstance &ci = act->instance();
52   if (!ci.parsing().messages().empty() &&
53       (ci.invocation().warnAsErr() ||
54           ci.parsing().messages().AnyFatalError())) {
55     const unsigned diagID = ci.diagnostics().getCustomDiagID(
56         clang::DiagnosticsEngine::Error, message);
57     ci.diagnostics().Report(diagID) << act->GetCurrentFileOrBufferName();
58     ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
59     return true;
60   }
61   return false;
62 }
63 
64 inline bool reportFatalScanningErrors(const FrontendAction *act) {
65   return reportFatalErrors(act, "Could not scan %0");
66 }
67 
68 inline bool reportFatalParsingErrors(const FrontendAction *act) {
69   return reportFatalErrors(act, "Could not parse %0");
70 }
71 
72 bool PrescanAction::BeginSourceFileAction(CompilerInstance &c1) {
73   CompilerInstance &ci = this->instance();
74   std::string currentInputPath{GetCurrentFileOrBufferName()};
75   Fortran::parser::Options parserOptions = ci.invocation().fortranOpts();
76 
77   // Prescan. In case of failure, report and return.
78   ci.parsing().Prescan(currentInputPath, parserOptions);
79 
80   return !reportFatalScanningErrors(this);
81 }
82 
83 bool PrescanAndParseAction::BeginSourceFileAction(CompilerInstance &c1) {
84   CompilerInstance &ci = this->instance();
85 
86   std::string currentInputPath{GetCurrentFileOrBufferName()};
87 
88   Fortran::parser::Options parserOptions = ci.invocation().fortranOpts();
89 
90   if (ci.invocation().frontendOpts().fortranForm == FortranForm::Unknown) {
91     // Switch between fixed and free form format based on the input file
92     // extension.
93     //
94     // Ideally we should have all Fortran options set before entering this
95     // method (i.e. before processing any specific input files). However, we
96     // can't decide between fixed and free form based on the file extension
97     // earlier than this.
98     parserOptions.isFixedForm = currentInput().IsFixedForm();
99   }
100 
101   // Prescan. In case of failure, report and return.
102   ci.parsing().Prescan(currentInputPath, parserOptions);
103 
104   if (reportFatalScanningErrors(this))
105     return false;
106 
107   // Parse. In case of failure, report and return.
108   ci.parsing().Parse(llvm::outs());
109 
110   if (reportFatalParsingErrors(this))
111     return false;
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 (reportFatalScanningErrors(this))
128     return false;
129 
130   // Parse. In case of failure, report and return.
131   ci.parsing().Parse(llvm::outs());
132 
133   if (reportFatalParsingErrors(this))
134     return false;
135 
136   // Report the diagnostics from parsing
137   ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
138 
139   auto &parseTree{*ci.parsing().parseTree()};
140 
141   // Prepare semantics
142   setSemantics(std::make_unique<Fortran::semantics::Semantics>(
143       ci.invocation().semanticsContext(), parseTree,
144       ci.invocation().debugModuleDir()));
145   auto &semantics = this->semantics();
146 
147   // Run semantic checks
148   semantics.Perform();
149 
150   // Report the diagnostics from the semantic checks
151   semantics.EmitMessages(ci.semaOutputStream());
152 
153   return true;
154 }
155 
156 void InputOutputTestAction::ExecuteAction() {
157   CompilerInstance &ci = instance();
158 
159   // Create a stream for errors
160   std::string buf;
161   llvm::raw_string_ostream error_stream{buf};
162 
163   // Read the input file
164   Fortran::parser::AllSources &allSources{ci.allSources()};
165   std::string path{GetCurrentFileOrBufferName()};
166   const Fortran::parser::SourceFile *sf;
167   if (path == "-")
168     sf = allSources.ReadStandardInput(error_stream);
169   else
170     sf = allSources.Open(path, error_stream, std::optional<std::string>{"."s});
171   llvm::ArrayRef<char> fileContent = sf->content();
172 
173   // Output file descriptor to receive the contents of the input file.
174   std::unique_ptr<llvm::raw_ostream> os;
175 
176   // Copy the contents from the input file to the output file
177   if (!ci.IsOutputStreamNull()) {
178     // An output stream (outputStream_) was set earlier
179     ci.WriteOutputStream(fileContent.data());
180   } else {
181     // No pre-set output stream - create an output file
182     os = ci.CreateDefaultOutputFile(
183         /*binary=*/true, GetCurrentFileOrBufferName(), "txt");
184     if (!os)
185       return;
186     (*os) << fileContent.data();
187   }
188 }
189 
190 void PrintPreprocessedAction::ExecuteAction() {
191   std::string buf;
192   llvm::raw_string_ostream outForPP{buf};
193 
194   // Format or dump the prescanner's output
195   CompilerInstance &ci = this->instance();
196   if (ci.invocation().preprocessorOpts().noReformat) {
197     ci.parsing().DumpCookedChars(outForPP);
198   } else {
199     ci.parsing().EmitPreprocessedSource(
200         outForPP, !ci.invocation().preprocessorOpts().noLineDirectives);
201   }
202 
203   // If a pre-defined output stream exists, dump the preprocessed content there
204   if (!ci.IsOutputStreamNull()) {
205     // Send the output to the pre-defined output buffer.
206     ci.WriteOutputStream(outForPP.str());
207     return;
208   }
209 
210   // Print diagnostics from the prescanner
211   ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
212 
213   // Create a file and save the preprocessed output there
214   if (auto os{ci.CreateDefaultOutputFile(
215           /*Binary=*/true, /*InFile=*/GetCurrentFileOrBufferName())}) {
216     (*os) << outForPP.str();
217   } else {
218     llvm::errs() << "Unable to create the output file\n";
219   }
220 }
221 
222 void DebugDumpProvenanceAction::ExecuteAction() {
223   this->instance().parsing().DumpProvenance(llvm::outs());
224 }
225 
226 void ParseSyntaxOnlyAction::ExecuteAction() {
227   reportFatalSemanticErrors(semantics(), this->instance().diagnostics(),
228       GetCurrentFileOrBufferName());
229 }
230 
231 void DebugUnparseNoSemaAction::ExecuteAction() {
232   auto &invoc = this->instance().invocation();
233   auto &parseTree{instance().parsing().parseTree()};
234 
235   // TODO: Options should come from CompilerInvocation
236   Unparse(llvm::outs(), *parseTree,
237       /*encoding=*/Fortran::parser::Encoding::UTF_8,
238       /*capitalizeKeywords=*/true, /*backslashEscapes=*/false,
239       /*preStatement=*/nullptr,
240       invoc.useAnalyzedObjectsForUnparse() ? &invoc.asFortran() : nullptr);
241 }
242 
243 void DebugUnparseAction::ExecuteAction() {
244   auto &invoc = this->instance().invocation();
245   auto &parseTree{instance().parsing().parseTree()};
246 
247   CompilerInstance &ci = this->instance();
248   auto os{ci.CreateDefaultOutputFile(
249       /*Binary=*/false, /*InFile=*/GetCurrentFileOrBufferName())};
250 
251   // TODO: Options should come from CompilerInvocation
252   Unparse(*os, *parseTree,
253       /*encoding=*/Fortran::parser::Encoding::UTF_8,
254       /*capitalizeKeywords=*/true, /*backslashEscapes=*/false,
255       /*preStatement=*/nullptr,
256       invoc.useAnalyzedObjectsForUnparse() ? &invoc.asFortran() : nullptr);
257 
258   // Report fatal semantic errors
259   reportFatalSemanticErrors(semantics(), this->instance().diagnostics(),
260       GetCurrentFileOrBufferName());
261 }
262 
263 void DebugUnparseWithSymbolsAction::ExecuteAction() {
264   auto &parseTree{*instance().parsing().parseTree()};
265 
266   Fortran::semantics::UnparseWithSymbols(
267       llvm::outs(), parseTree, /*encoding=*/Fortran::parser::Encoding::UTF_8);
268 
269   // Report fatal semantic errors
270   reportFatalSemanticErrors(semantics(), this->instance().diagnostics(),
271       GetCurrentFileOrBufferName());
272 }
273 
274 void DebugDumpSymbolsAction::ExecuteAction() {
275   CompilerInstance &ci = this->instance();
276   auto &semantics = this->semantics();
277 
278   auto tables{Fortran::semantics::BuildRuntimeDerivedTypeTables(
279       instance().invocation().semanticsContext())};
280   // The runtime derived type information table builder may find and report
281   // semantic errors. So it is important that we report them _after_
282   // BuildRuntimeDerivedTypeTables is run.
283   reportFatalSemanticErrors(
284       semantics, this->instance().diagnostics(), GetCurrentFileOrBufferName());
285 
286   if (!tables.schemata) {
287     unsigned DiagID =
288         ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error,
289             "could not find module file for __fortran_type_info");
290     ci.diagnostics().Report(DiagID);
291     llvm::errs() << "\n";
292   }
293 
294   // Dump symbols
295   semantics.DumpSymbols(llvm::outs());
296 }
297 
298 void DebugDumpAllAction::ExecuteAction() {
299   CompilerInstance &ci = this->instance();
300 
301   // Dump parse tree
302   auto &parseTree{instance().parsing().parseTree()};
303   llvm::outs() << "========================";
304   llvm::outs() << " Flang: parse tree dump ";
305   llvm::outs() << "========================\n";
306   Fortran::parser::DumpTree(
307       llvm::outs(), parseTree, &ci.invocation().asFortran());
308 
309   auto &semantics = this->semantics();
310   auto tables{Fortran::semantics::BuildRuntimeDerivedTypeTables(
311       instance().invocation().semanticsContext())};
312   // The runtime derived type information table builder may find and report
313   // semantic errors. So it is important that we report them _after_
314   // BuildRuntimeDerivedTypeTables is run.
315   reportFatalSemanticErrors(
316       semantics, this->instance().diagnostics(), GetCurrentFileOrBufferName());
317 
318   if (!tables.schemata) {
319     unsigned DiagID =
320         ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error,
321             "could not find module file for __fortran_type_info");
322     ci.diagnostics().Report(DiagID);
323     llvm::errs() << "\n";
324   }
325 
326   // Dump symbols
327   llvm::outs() << "=====================";
328   llvm::outs() << " Flang: symbols dump ";
329   llvm::outs() << "=====================\n";
330   semantics.DumpSymbols(llvm::outs());
331 }
332 
333 void DebugDumpParseTreeNoSemaAction::ExecuteAction() {
334   auto &parseTree{instance().parsing().parseTree()};
335 
336   // Dump parse tree
337   Fortran::parser::DumpTree(
338       llvm::outs(), parseTree, &this->instance().invocation().asFortran());
339 }
340 
341 void DebugDumpParseTreeAction::ExecuteAction() {
342   auto &parseTree{instance().parsing().parseTree()};
343 
344   // Dump parse tree
345   Fortran::parser::DumpTree(
346       llvm::outs(), parseTree, &this->instance().invocation().asFortran());
347 
348   // Report fatal semantic errors
349   reportFatalSemanticErrors(semantics(), this->instance().diagnostics(),
350       GetCurrentFileOrBufferName());
351 }
352 
353 void DebugMeasureParseTreeAction::ExecuteAction() {
354   CompilerInstance &ci = this->instance();
355 
356   // Parse. In case of failure, report and return.
357   ci.parsing().Parse(llvm::outs());
358 
359   if (!ci.parsing().messages().empty() &&
360       (ci.invocation().warnAsErr() ||
361           ci.parsing().messages().AnyFatalError())) {
362     unsigned diagID = ci.diagnostics().getCustomDiagID(
363         clang::DiagnosticsEngine::Error, "Could not parse %0");
364     ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName();
365 
366     ci.parsing().messages().Emit(
367         llvm::errs(), this->instance().allCookedSources());
368     return;
369   }
370 
371   // Report the diagnostics from parsing
372   ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
373 
374   auto &parseTree{*ci.parsing().parseTree()};
375 
376   // Measure the parse tree
377   MeasurementVisitor visitor;
378   Fortran::parser::Walk(parseTree, visitor);
379   llvm::outs() << "Parse tree comprises " << visitor.objects
380                << " objects and occupies " << visitor.bytes
381                << " total bytes.\n";
382 }
383 
384 void DebugPreFIRTreeAction::ExecuteAction() {
385   CompilerInstance &ci = this->instance();
386   // Report and exit if fatal semantic errors are present
387   if (reportFatalSemanticErrors(
388           semantics(), ci.diagnostics(), GetCurrentFileOrBufferName())) {
389     return;
390   }
391 
392   auto &parseTree{*ci.parsing().parseTree()};
393 
394   // Dump pre-FIR tree
395   if (auto ast{Fortran::lower::createPFT(
396           parseTree, ci.invocation().semanticsContext())}) {
397     Fortran::lower::dumpPFT(llvm::outs(), *ast);
398   } else {
399     unsigned diagID = ci.diagnostics().getCustomDiagID(
400         clang::DiagnosticsEngine::Error, "Pre FIR Tree is NULL.");
401     ci.diagnostics().Report(diagID);
402   }
403 }
404 
405 void DebugDumpParsingLogAction::ExecuteAction() {
406   CompilerInstance &ci = this->instance();
407 
408   ci.parsing().Parse(llvm::errs());
409   ci.parsing().DumpParsingLog(llvm::outs());
410 }
411 
412 void GetDefinitionAction::ExecuteAction() {
413   // Report and exit if fatal semantic errors are present
414   if (reportFatalSemanticErrors(semantics(), this->instance().diagnostics(),
415           GetCurrentFileOrBufferName()))
416     return;
417 
418   CompilerInstance &ci = this->instance();
419   parser::AllCookedSources &cs = ci.allCookedSources();
420   unsigned diagID = ci.diagnostics().getCustomDiagID(
421       clang::DiagnosticsEngine::Error, "Symbol not found");
422 
423   auto gdv = ci.invocation().frontendOpts().getDefVals;
424   auto charBlock{cs.GetCharBlockFromLineAndColumns(
425       gdv.line, gdv.startColumn, gdv.endColumn)};
426   if (!charBlock) {
427     ci.diagnostics().Report(diagID);
428     return;
429   }
430 
431   llvm::outs() << "String range: >" << charBlock->ToString() << "<\n";
432 
433   auto *symbol{ci.invocation()
434                    .semanticsContext()
435                    .FindScope(*charBlock)
436                    .FindSymbol(*charBlock)};
437   if (!symbol) {
438     ci.diagnostics().Report(diagID);
439     return;
440   }
441 
442   llvm::outs() << "Found symbol name: " << symbol->name().ToString() << "\n";
443 
444   auto sourceInfo{cs.GetSourcePositionRange(symbol->name())};
445   if (!sourceInfo) {
446     llvm_unreachable(
447         "Failed to obtain SourcePosition."
448         "TODO: Please, write a test and replace this with a diagnostic!");
449     return;
450   }
451 
452   llvm::outs() << "Found symbol name: " << symbol->name().ToString() << "\n";
453   llvm::outs() << symbol->name().ToString() << ": "
454                << sourceInfo->first.file.path() << ", "
455                << sourceInfo->first.line << ", " << sourceInfo->first.column
456                << "-" << sourceInfo->second.column << "\n";
457 }
458 
459 void GetSymbolsSourcesAction::ExecuteAction() {
460   // Report and exit if fatal semantic errors are present
461   if (reportFatalSemanticErrors(semantics(), this->instance().diagnostics(),
462           GetCurrentFileOrBufferName()))
463     return;
464 
465   semantics().DumpSymbolsSources(llvm::outs());
466 }
467 
468 void EmitObjAction::ExecuteAction() {
469   CompilerInstance &ci = this->instance();
470   unsigned DiagID = ci.diagnostics().getCustomDiagID(
471       clang::DiagnosticsEngine::Error, "code-generation is not available yet");
472   ci.diagnostics().Report(DiagID);
473 }
474 
475 void InitOnlyAction::ExecuteAction() {
476   CompilerInstance &ci = this->instance();
477   unsigned DiagID =
478       ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Warning,
479           "Use `-init-only` for testing purposes only");
480   ci.diagnostics().Report(DiagID);
481 }
482