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