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 bool PrescanAction::BeginSourceFileAction(CompilerInstance &c1) {
49   CompilerInstance &ci = this->instance();
50   std::string currentInputPath{GetCurrentFileOrBufferName()};
51   Fortran::parser::Options parserOptions = ci.invocation().fortranOpts();
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.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 &invoc = this->instance().invocation();
246   auto &parseTree{instance().parsing().parseTree()};
247 
248   // TODO: Options should come from CompilerInvocation
249   Unparse(llvm::outs(), *parseTree,
250       /*encoding=*/Fortran::parser::Encoding::UTF_8,
251       /*capitalizeKeywords=*/true, /*backslashEscapes=*/false,
252       /*preStatement=*/nullptr,
253       invoc.useAnalyzedObjectsForUnparse() ? &invoc.asFortran() : nullptr);
254 }
255 
256 void DebugUnparseAction::ExecuteAction() {
257   auto &invoc = this->instance().invocation();
258   auto &parseTree{instance().parsing().parseTree()};
259 
260   // TODO: Options should come from CompilerInvocation
261   Unparse(llvm::outs(), *parseTree,
262       /*encoding=*/Fortran::parser::Encoding::UTF_8,
263       /*capitalizeKeywords=*/true, /*backslashEscapes=*/false,
264       /*preStatement=*/nullptr,
265       invoc.useAnalyzedObjectsForUnparse() ? &invoc.asFortran() : nullptr);
266 
267   // Report fatal semantic errors
268   reportFatalSemanticErrors(semantics(), this->instance().diagnostics(),
269       GetCurrentFileOrBufferName());
270 }
271 
272 void DebugUnparseWithSymbolsAction::ExecuteAction() {
273   auto &parseTree{*instance().parsing().parseTree()};
274 
275   Fortran::semantics::UnparseWithSymbols(
276       llvm::outs(), parseTree, /*encoding=*/Fortran::parser::Encoding::UTF_8);
277 
278   // Report fatal semantic errors
279   reportFatalSemanticErrors(semantics(), this->instance().diagnostics(),
280       GetCurrentFileOrBufferName());
281 }
282 
283 void DebugDumpSymbolsAction::ExecuteAction() {
284   CompilerInstance &ci = this->instance();
285   auto &semantics = this->semantics();
286 
287   auto tables{Fortran::semantics::BuildRuntimeDerivedTypeTables(
288       instance().invocation().semanticsContext())};
289   // The runtime derived type information table builder may find and report
290   // semantic errors. So it is important that we report them _after_
291   // BuildRuntimeDerivedTypeTables is run.
292   reportFatalSemanticErrors(
293       semantics, this->instance().diagnostics(), GetCurrentFileOrBufferName());
294 
295   if (!tables.schemata) {
296     unsigned DiagID =
297         ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error,
298             "could not find module file for __fortran_type_info");
299     ci.diagnostics().Report(DiagID);
300     llvm::errs() << "\n";
301   }
302 
303   // Dump symbols
304   semantics.DumpSymbols(llvm::outs());
305 }
306 
307 void DebugDumpAllAction::ExecuteAction() {
308   CompilerInstance &ci = this->instance();
309 
310   // Dump parse tree
311   auto &parseTree{instance().parsing().parseTree()};
312   llvm::outs() << "========================";
313   llvm::outs() << " Flang: parse tree dump ";
314   llvm::outs() << "========================\n";
315   Fortran::parser::DumpTree(
316       llvm::outs(), parseTree, &ci.invocation().asFortran());
317 
318   auto &semantics = this->semantics();
319   auto tables{Fortran::semantics::BuildRuntimeDerivedTypeTables(
320       instance().invocation().semanticsContext())};
321   // The runtime derived type information table builder may find and report
322   // semantic errors. So it is important that we report them _after_
323   // BuildRuntimeDerivedTypeTables is run.
324   reportFatalSemanticErrors(
325       semantics, this->instance().diagnostics(), GetCurrentFileOrBufferName());
326 
327   if (!tables.schemata) {
328     unsigned DiagID =
329         ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error,
330             "could not find module file for __fortran_type_info");
331     ci.diagnostics().Report(DiagID);
332     llvm::errs() << "\n";
333   }
334 
335   // Dump symbols
336   llvm::outs() << "=====================";
337   llvm::outs() << " Flang: symbols dump ";
338   llvm::outs() << "=====================\n";
339   semantics.DumpSymbols(llvm::outs());
340 }
341 
342 void DebugDumpParseTreeNoSemaAction::ExecuteAction() {
343   auto &parseTree{instance().parsing().parseTree()};
344 
345   // Dump parse tree
346   Fortran::parser::DumpTree(
347       llvm::outs(), parseTree, &this->instance().invocation().asFortran());
348 }
349 
350 void DebugDumpParseTreeAction::ExecuteAction() {
351   auto &parseTree{instance().parsing().parseTree()};
352 
353   // Dump parse tree
354   Fortran::parser::DumpTree(
355       llvm::outs(), parseTree, &this->instance().invocation().asFortran());
356 
357   // Report fatal semantic errors
358   reportFatalSemanticErrors(semantics(), this->instance().diagnostics(),
359       GetCurrentFileOrBufferName());
360 }
361 
362 void DebugMeasureParseTreeAction::ExecuteAction() {
363   CompilerInstance &ci = this->instance();
364 
365   // Parse. In case of failure, report and return.
366   ci.parsing().Parse(llvm::outs());
367 
368   if (!ci.parsing().messages().empty() &&
369       (ci.invocation().warnAsErr() ||
370           ci.parsing().messages().AnyFatalError())) {
371     unsigned diagID = ci.diagnostics().getCustomDiagID(
372         clang::DiagnosticsEngine::Error, "Could not parse %0");
373     ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName();
374 
375     ci.parsing().messages().Emit(
376         llvm::errs(), this->instance().allCookedSources());
377     return;
378   }
379 
380   // Report the diagnostics from parsing
381   ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
382 
383   auto &parseTree{*ci.parsing().parseTree()};
384 
385   // Measure the parse tree
386   MeasurementVisitor visitor;
387   Fortran::parser::Walk(parseTree, visitor);
388   llvm::outs() << "Parse tree comprises " << visitor.objects
389                << " objects and occupies " << visitor.bytes
390                << " total bytes.\n";
391 }
392 
393 void DebugPreFIRTreeAction::ExecuteAction() {
394   CompilerInstance &ci = this->instance();
395   // Report and exit if fatal semantic errors are present
396   if (reportFatalSemanticErrors(
397           semantics(), ci.diagnostics(), GetCurrentFileOrBufferName())) {
398     return;
399   }
400 
401   auto &parseTree{*ci.parsing().parseTree()};
402 
403   // Dump pre-FIR tree
404   if (auto ast{Fortran::lower::createPFT(
405           parseTree, ci.invocation().semanticsContext())}) {
406     Fortran::lower::dumpPFT(llvm::outs(), *ast);
407   } else {
408     unsigned diagID = ci.diagnostics().getCustomDiagID(
409         clang::DiagnosticsEngine::Error, "Pre FIR Tree is NULL.");
410     ci.diagnostics().Report(diagID);
411   }
412 }
413 
414 void DebugDumpParsingLogAction::ExecuteAction() {
415   CompilerInstance &ci = this->instance();
416 
417   ci.parsing().Parse(llvm::errs());
418   ci.parsing().DumpParsingLog(llvm::outs());
419 }
420 
421 void GetDefinitionAction::ExecuteAction() {
422   // Report and exit if fatal semantic errors are present
423   if (reportFatalSemanticErrors(semantics(), this->instance().diagnostics(),
424           GetCurrentFileOrBufferName()))
425     return;
426 
427   CompilerInstance &ci = this->instance();
428   parser::AllCookedSources &cs = ci.allCookedSources();
429   unsigned diagID = ci.diagnostics().getCustomDiagID(
430       clang::DiagnosticsEngine::Error, "Symbol not found");
431 
432   auto gdv = ci.invocation().frontendOpts().getDefVals_;
433   auto charBlock{cs.GetCharBlockFromLineAndColumns(
434       gdv.line, gdv.startColumn, gdv.endColumn)};
435   if (!charBlock) {
436     ci.diagnostics().Report(diagID);
437     return;
438   }
439 
440   llvm::outs() << "String range: >" << charBlock->ToString() << "<\n";
441 
442   auto *symbol{ci.invocation()
443                    .semanticsContext()
444                    .FindScope(*charBlock)
445                    .FindSymbol(*charBlock)};
446   if (!symbol) {
447     ci.diagnostics().Report(diagID);
448     return;
449   }
450 
451   llvm::outs() << "Found symbol name: " << symbol->name().ToString() << "\n";
452 
453   auto sourceInfo{cs.GetSourcePositionRange(symbol->name())};
454   if (!sourceInfo) {
455     llvm_unreachable(
456         "Failed to obtain SourcePosition."
457         "TODO: Please, write a test and replace this with a diagnostic!");
458     return;
459   }
460 
461   llvm::outs() << "Found symbol name: " << symbol->name().ToString() << "\n";
462   llvm::outs() << symbol->name().ToString() << ": "
463                << sourceInfo->first.file.path() << ", "
464                << sourceInfo->first.line << ", " << sourceInfo->first.column
465                << "-" << sourceInfo->second.column << "\n";
466 }
467 
468 void GetSymbolsSourcesAction::ExecuteAction() {
469   // Report and exit if fatal semantic errors are present
470   if (reportFatalSemanticErrors(semantics(), this->instance().diagnostics(),
471           GetCurrentFileOrBufferName()))
472     return;
473 
474   semantics().DumpSymbolsSources(llvm::outs());
475 }
476 
477 void EmitObjAction::ExecuteAction() {
478   CompilerInstance &ci = this->instance();
479   unsigned DiagID = ci.diagnostics().getCustomDiagID(
480       clang::DiagnosticsEngine::Error, "code-generation is not available yet");
481   ci.diagnostics().Report(DiagID);
482 }
483 
484 void InitOnlyAction::ExecuteAction() {
485   CompilerInstance &ci = this->instance();
486   unsigned DiagID =
487       ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Warning,
488           "Use `-init-only` for testing purposes only");
489   ci.diagnostics().Report(DiagID);
490 }
491