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