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 //===----------------------------------------------------------------------===//
31 // Custom BeginSourceFileAction
32 //===----------------------------------------------------------------------===//
33 bool PrescanAction::BeginSourceFileAction() { return RunPrescan(); }
34 
35 bool PrescanAndParseAction::BeginSourceFileAction() {
36   return RunPrescan() && RunParse();
37 }
38 
39 bool PrescanAndSemaAction::BeginSourceFileAction() {
40   return RunPrescan() & RunParse() && RunSemanticChecks();
41 }
42 
43 //===----------------------------------------------------------------------===//
44 // Custom ExecuteAction
45 //===----------------------------------------------------------------------===//
46 void InputOutputTestAction::ExecuteAction() {
47   CompilerInstance &ci = instance();
48 
49   // Create a stream for errors
50   std::string buf;
51   llvm::raw_string_ostream error_stream{buf};
52 
53   // Read the input file
54   Fortran::parser::AllSources &allSources{ci.allSources()};
55   std::string path{GetCurrentFileOrBufferName()};
56   const Fortran::parser::SourceFile *sf;
57   if (path == "-")
58     sf = allSources.ReadStandardInput(error_stream);
59   else
60     sf = allSources.Open(path, error_stream, std::optional<std::string>{"."s});
61   llvm::ArrayRef<char> fileContent = sf->content();
62 
63   // Output file descriptor to receive the contents of the input file.
64   std::unique_ptr<llvm::raw_ostream> os;
65 
66   // Copy the contents from the input file to the output file
67   if (!ci.IsOutputStreamNull()) {
68     // An output stream (outputStream_) was set earlier
69     ci.WriteOutputStream(fileContent.data());
70   } else {
71     // No pre-set output stream - create an output file
72     os = ci.CreateDefaultOutputFile(
73         /*binary=*/true, GetCurrentFileOrBufferName(), "txt");
74     if (!os)
75       return;
76     (*os) << fileContent.data();
77   }
78 }
79 
80 void PrintPreprocessedAction::ExecuteAction() {
81   std::string buf;
82   llvm::raw_string_ostream outForPP{buf};
83 
84   // Format or dump the prescanner's output
85   CompilerInstance &ci = this->instance();
86   if (ci.invocation().preprocessorOpts().noReformat) {
87     ci.parsing().DumpCookedChars(outForPP);
88   } else {
89     ci.parsing().EmitPreprocessedSource(
90         outForPP, !ci.invocation().preprocessorOpts().noLineDirectives);
91   }
92 
93   // Print diagnostics from the prescanner
94   ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
95 
96   // If a pre-defined output stream exists, dump the preprocessed content there
97   if (!ci.IsOutputStreamNull()) {
98     // Send the output to the pre-defined output buffer.
99     ci.WriteOutputStream(outForPP.str());
100     return;
101   }
102 
103   // Create a file and save the preprocessed output there
104   std::unique_ptr<llvm::raw_pwrite_stream> os{ci.CreateDefaultOutputFile(
105       /*Binary=*/true, /*InFile=*/GetCurrentFileOrBufferName())};
106   if (!os) {
107     return;
108   }
109 
110   (*os) << outForPP.str();
111 }
112 
113 void DebugDumpProvenanceAction::ExecuteAction() {
114   this->instance().parsing().DumpProvenance(llvm::outs());
115 }
116 
117 void ParseSyntaxOnlyAction::ExecuteAction() {
118 }
119 
120 void DebugUnparseNoSemaAction::ExecuteAction() {
121   auto &invoc = this->instance().invocation();
122   auto &parseTree{instance().parsing().parseTree()};
123 
124   // TODO: Options should come from CompilerInvocation
125   Unparse(llvm::outs(), *parseTree,
126       /*encoding=*/Fortran::parser::Encoding::UTF_8,
127       /*capitalizeKeywords=*/true, /*backslashEscapes=*/false,
128       /*preStatement=*/nullptr,
129       invoc.useAnalyzedObjectsForUnparse() ? &invoc.asFortran() : nullptr);
130 }
131 
132 void DebugUnparseAction::ExecuteAction() {
133   auto &invoc = this->instance().invocation();
134   auto &parseTree{instance().parsing().parseTree()};
135 
136   CompilerInstance &ci = this->instance();
137   auto os{ci.CreateDefaultOutputFile(
138       /*Binary=*/false, /*InFile=*/GetCurrentFileOrBufferName())};
139 
140   // TODO: Options should come from CompilerInvocation
141   Unparse(*os, *parseTree,
142       /*encoding=*/Fortran::parser::Encoding::UTF_8,
143       /*capitalizeKeywords=*/true, /*backslashEscapes=*/false,
144       /*preStatement=*/nullptr,
145       invoc.useAnalyzedObjectsForUnparse() ? &invoc.asFortran() : nullptr);
146 
147   // Report fatal semantic errors
148   reportFatalSemanticErrors();
149 }
150 
151 void DebugUnparseWithSymbolsAction::ExecuteAction() {
152   auto &parseTree{*instance().parsing().parseTree()};
153 
154   Fortran::semantics::UnparseWithSymbols(
155       llvm::outs(), parseTree, /*encoding=*/Fortran::parser::Encoding::UTF_8);
156 
157   // Report fatal semantic errors
158   reportFatalSemanticErrors();
159 }
160 
161 void DebugDumpSymbolsAction::ExecuteAction() {
162   CompilerInstance &ci = this->instance();
163   auto &semantics = ci.semantics();
164 
165   auto tables{Fortran::semantics::BuildRuntimeDerivedTypeTables(
166       instance().invocation().semanticsContext())};
167   // The runtime derived type information table builder may find and report
168   // semantic errors. So it is important that we report them _after_
169   // BuildRuntimeDerivedTypeTables is run.
170   reportFatalSemanticErrors();
171 
172   if (!tables.schemata) {
173     unsigned DiagID =
174         ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error,
175             "could not find module file for __fortran_type_info");
176     ci.diagnostics().Report(DiagID);
177     llvm::errs() << "\n";
178   }
179 
180   // Dump symbols
181   semantics.DumpSymbols(llvm::outs());
182 }
183 
184 void DebugDumpAllAction::ExecuteAction() {
185   CompilerInstance &ci = this->instance();
186 
187   // Dump parse tree
188   auto &parseTree{instance().parsing().parseTree()};
189   llvm::outs() << "========================";
190   llvm::outs() << " Flang: parse tree dump ";
191   llvm::outs() << "========================\n";
192   Fortran::parser::DumpTree(
193       llvm::outs(), parseTree, &ci.invocation().asFortran());
194 
195   auto &semantics = ci.semantics();
196   auto tables{Fortran::semantics::BuildRuntimeDerivedTypeTables(
197       instance().invocation().semanticsContext())};
198   // The runtime derived type information table builder may find and report
199   // semantic errors. So it is important that we report them _after_
200   // BuildRuntimeDerivedTypeTables is run.
201   reportFatalSemanticErrors();
202 
203   if (!tables.schemata) {
204     unsigned DiagID =
205         ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error,
206             "could not find module file for __fortran_type_info");
207     ci.diagnostics().Report(DiagID);
208     llvm::errs() << "\n";
209   }
210 
211   // Dump symbols
212   llvm::outs() << "=====================";
213   llvm::outs() << " Flang: symbols dump ";
214   llvm::outs() << "=====================\n";
215   semantics.DumpSymbols(llvm::outs());
216 }
217 
218 void DebugDumpParseTreeNoSemaAction::ExecuteAction() {
219   auto &parseTree{instance().parsing().parseTree()};
220 
221   // Dump parse tree
222   Fortran::parser::DumpTree(
223       llvm::outs(), parseTree, &this->instance().invocation().asFortran());
224 }
225 
226 void DebugDumpParseTreeAction::ExecuteAction() {
227   auto &parseTree{instance().parsing().parseTree()};
228 
229   // Dump parse tree
230   Fortran::parser::DumpTree(
231       llvm::outs(), parseTree, &this->instance().invocation().asFortran());
232 
233   // Report fatal semantic errors
234   reportFatalSemanticErrors();
235 }
236 
237 void DebugMeasureParseTreeAction::ExecuteAction() {
238   CompilerInstance &ci = this->instance();
239 
240   // Parse. In case of failure, report and return.
241   ci.parsing().Parse(llvm::outs());
242 
243   if (!ci.parsing().messages().empty() &&
244       (ci.invocation().warnAsErr() ||
245           ci.parsing().messages().AnyFatalError())) {
246     unsigned diagID = ci.diagnostics().getCustomDiagID(
247         clang::DiagnosticsEngine::Error, "Could not parse %0");
248     ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName();
249 
250     ci.parsing().messages().Emit(
251         llvm::errs(), this->instance().allCookedSources());
252     return;
253   }
254 
255   // Report the diagnostics from parsing
256   ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
257 
258   auto &parseTree{*ci.parsing().parseTree()};
259 
260   // Measure the parse tree
261   MeasurementVisitor visitor;
262   Fortran::parser::Walk(parseTree, visitor);
263   llvm::outs() << "Parse tree comprises " << visitor.objects
264                << " objects and occupies " << visitor.bytes
265                << " total bytes.\n";
266 }
267 
268 void DebugPreFIRTreeAction::ExecuteAction() {
269   CompilerInstance &ci = this->instance();
270   // Report and exit if fatal semantic errors are present
271   if (reportFatalSemanticErrors()) {
272     return;
273   }
274 
275   auto &parseTree{*ci.parsing().parseTree()};
276 
277   // Dump pre-FIR tree
278   if (auto ast{Fortran::lower::createPFT(
279           parseTree, ci.invocation().semanticsContext())}) {
280     Fortran::lower::dumpPFT(llvm::outs(), *ast);
281   } else {
282     unsigned diagID = ci.diagnostics().getCustomDiagID(
283         clang::DiagnosticsEngine::Error, "Pre FIR Tree is NULL.");
284     ci.diagnostics().Report(diagID);
285   }
286 }
287 
288 void DebugDumpParsingLogAction::ExecuteAction() {
289   CompilerInstance &ci = this->instance();
290 
291   ci.parsing().Parse(llvm::errs());
292   ci.parsing().DumpParsingLog(llvm::outs());
293 }
294 
295 void GetDefinitionAction::ExecuteAction() {
296   CompilerInstance &ci = this->instance();
297 
298   // Report and exit if fatal semantic errors are present
299   if (reportFatalSemanticErrors()) {
300     return;
301   }
302 
303   parser::AllCookedSources &cs = ci.allCookedSources();
304   unsigned diagID = ci.diagnostics().getCustomDiagID(
305       clang::DiagnosticsEngine::Error, "Symbol not found");
306 
307   auto gdv = ci.invocation().frontendOpts().getDefVals;
308   auto charBlock{cs.GetCharBlockFromLineAndColumns(
309       gdv.line, gdv.startColumn, gdv.endColumn)};
310   if (!charBlock) {
311     ci.diagnostics().Report(diagID);
312     return;
313   }
314 
315   llvm::outs() << "String range: >" << charBlock->ToString() << "<\n";
316 
317   auto *symbol{ci.invocation()
318                    .semanticsContext()
319                    .FindScope(*charBlock)
320                    .FindSymbol(*charBlock)};
321   if (!symbol) {
322     ci.diagnostics().Report(diagID);
323     return;
324   }
325 
326   llvm::outs() << "Found symbol name: " << symbol->name().ToString() << "\n";
327 
328   auto sourceInfo{cs.GetSourcePositionRange(symbol->name())};
329   if (!sourceInfo) {
330     llvm_unreachable(
331         "Failed to obtain SourcePosition."
332         "TODO: Please, write a test and replace this with a diagnostic!");
333     return;
334   }
335 
336   llvm::outs() << "Found symbol name: " << symbol->name().ToString() << "\n";
337   llvm::outs() << symbol->name().ToString() << ": "
338                << sourceInfo->first.file.path() << ", "
339                << sourceInfo->first.line << ", " << sourceInfo->first.column
340                << "-" << sourceInfo->second.column << "\n";
341 }
342 
343 void GetSymbolsSourcesAction::ExecuteAction() {
344   CompilerInstance &ci = this->instance();
345 
346   // Report and exit if fatal semantic errors are present
347   if (reportFatalSemanticErrors()) {
348     return;
349   }
350 
351   ci.semantics().DumpSymbolsSources(llvm::outs());
352 }
353 
354 void EmitObjAction::ExecuteAction() {
355   CompilerInstance &ci = this->instance();
356   unsigned DiagID = ci.diagnostics().getCustomDiagID(
357       clang::DiagnosticsEngine::Error, "code-generation is not available yet");
358   ci.diagnostics().Report(DiagID);
359 }
360 
361 void InitOnlyAction::ExecuteAction() {
362   CompilerInstance &ci = this->instance();
363   unsigned DiagID =
364       ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Warning,
365           "Use `-init-only` for testing purposes only");
366   ci.diagnostics().Report(DiagID);
367 }
368 
369 void PluginParseTreeAction::ExecuteAction() {}
370