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