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 &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 DebugDumpAllAction::ExecuteAction() {
309   CompilerInstance &ci = this->instance();
310 
311   // Dump parse tree
312   auto &parseTree{instance().parsing().parseTree()};
313   Fortran::parser::AnalyzedObjectsAsFortran asFortran =
314       Fortran::frontend::getBasicAsFortran();
315   llvm::outs() << "========================";
316   llvm::outs() << " Flang: parse tree dump ";
317   llvm::outs() << "========================\n";
318   Fortran::parser::DumpTree(llvm::outs(), parseTree, &asFortran);
319 
320   auto &semantics = this->semantics();
321   auto tables{Fortran::semantics::BuildRuntimeDerivedTypeTables(
322       instance().invocation().semanticsContext())};
323   // The runtime derived type information table builder may find and report
324   // semantic errors. So it is important that we report them _after_
325   // BuildRuntimeDerivedTypeTables is run.
326   reportFatalSemanticErrors(
327       semantics, this->instance().diagnostics(), GetCurrentFileOrBufferName());
328 
329   if (!tables.schemata) {
330     unsigned DiagID =
331         ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error,
332             "could not find module file for __fortran_type_info");
333     ci.diagnostics().Report(DiagID);
334     llvm::errs() << "\n";
335   }
336 
337   // Dump symbols
338   llvm::outs() << "=====================";
339   llvm::outs() << " Flang: symbols dump ";
340   llvm::outs() << "=====================\n";
341   semantics.DumpSymbols(llvm::outs());
342 }
343 
344 void DebugDumpParseTreeNoSemaAction::ExecuteAction() {
345   auto &parseTree{instance().parsing().parseTree()};
346   Fortran::parser::AnalyzedObjectsAsFortran asFortran =
347       Fortran::frontend::getBasicAsFortran();
348 
349   // Dump parse tree
350   Fortran::parser::DumpTree(llvm::outs(), parseTree, &asFortran);
351 }
352 
353 void DebugDumpParseTreeAction::ExecuteAction() {
354   auto &parseTree{instance().parsing().parseTree()};
355   Fortran::parser::AnalyzedObjectsAsFortran asFortran =
356       Fortran::frontend::getBasicAsFortran();
357 
358   // Dump parse tree
359   Fortran::parser::DumpTree(llvm::outs(), parseTree, &asFortran);
360   // Report fatal semantic errors
361   reportFatalSemanticErrors(semantics(), this->instance().diagnostics(),
362       GetCurrentFileOrBufferName());
363 }
364 
365 void DebugMeasureParseTreeAction::ExecuteAction() {
366   CompilerInstance &ci = this->instance();
367 
368   // Parse. In case of failure, report and return.
369   ci.parsing().Parse(llvm::outs());
370 
371   if (!ci.parsing().messages().empty() &&
372       (ci.invocation().warnAsErr() ||
373           ci.parsing().messages().AnyFatalError())) {
374     unsigned diagID = ci.diagnostics().getCustomDiagID(
375         clang::DiagnosticsEngine::Error, "Could not parse %0");
376     ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName();
377 
378     ci.parsing().messages().Emit(
379         llvm::errs(), this->instance().allCookedSources());
380     return;
381   }
382 
383   // Report the diagnostics from parsing
384   ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
385 
386   auto &parseTree{*ci.parsing().parseTree()};
387 
388   // Measure the parse tree
389   MeasurementVisitor visitor;
390   Fortran::parser::Walk(parseTree, visitor);
391   llvm::outs() << "Parse tree comprises " << visitor.objects
392                << " objects and occupies " << visitor.bytes
393                << " total bytes.\n";
394 }
395 
396 void DebugPreFIRTreeAction::ExecuteAction() {
397   CompilerInstance &ci = this->instance();
398   // Report and exit if fatal semantic errors are present
399   if (reportFatalSemanticErrors(
400           semantics(), ci.diagnostics(), GetCurrentFileOrBufferName())) {
401     return;
402   }
403 
404   auto &parseTree{*ci.parsing().parseTree()};
405 
406   // Dump pre-FIR tree
407   if (auto ast{Fortran::lower::createPFT(
408           parseTree, ci.invocation().semanticsContext())}) {
409     Fortran::lower::dumpPFT(llvm::outs(), *ast);
410   } else {
411     unsigned diagID = ci.diagnostics().getCustomDiagID(
412         clang::DiagnosticsEngine::Error, "Pre FIR Tree is NULL.");
413     ci.diagnostics().Report(diagID);
414   }
415 }
416 
417 void DebugDumpParsingLogAction::ExecuteAction() {
418   CompilerInstance &ci = this->instance();
419 
420   ci.parsing().Parse(llvm::errs());
421   ci.parsing().DumpParsingLog(llvm::outs());
422 }
423 
424 void GetDefinitionAction::ExecuteAction() {
425   // Report and exit if fatal semantic errors are present
426   if (reportFatalSemanticErrors(semantics(), this->instance().diagnostics(),
427           GetCurrentFileOrBufferName()))
428     return;
429 
430   CompilerInstance &ci = this->instance();
431   parser::AllCookedSources &cs = ci.allCookedSources();
432   unsigned diagID = ci.diagnostics().getCustomDiagID(
433       clang::DiagnosticsEngine::Error, "Symbol not found");
434 
435   auto gdv = ci.invocation().frontendOpts().getDefVals_;
436   auto charBlock{cs.GetCharBlockFromLineAndColumns(
437       gdv.line, gdv.startColumn, gdv.endColumn)};
438   if (!charBlock) {
439     ci.diagnostics().Report(diagID);
440     return;
441   }
442 
443   llvm::outs() << "String range: >" << charBlock->ToString() << "<\n";
444 
445   auto *symbol{ci.invocation()
446                    .semanticsContext()
447                    .FindScope(*charBlock)
448                    .FindSymbol(*charBlock)};
449   if (!symbol) {
450     ci.diagnostics().Report(diagID);
451     return;
452   }
453 
454   llvm::outs() << "Found symbol name: " << symbol->name().ToString() << "\n";
455 
456   auto sourceInfo{cs.GetSourcePositionRange(symbol->name())};
457   if (!sourceInfo) {
458     llvm_unreachable(
459         "Failed to obtain SourcePosition."
460         "TODO: Please, write a test and replace this with a diagnostic!");
461     return;
462   }
463 
464   llvm::outs() << "Found symbol name: " << symbol->name().ToString() << "\n";
465   llvm::outs() << symbol->name().ToString() << ": "
466                << sourceInfo->first.file.path() << ", "
467                << sourceInfo->first.line << ", " << sourceInfo->first.column
468                << "-" << sourceInfo->second.column << "\n";
469 }
470 
471 void GetSymbolsSourcesAction::ExecuteAction() {
472   // Report and exit if fatal semantic errors are present
473   if (reportFatalSemanticErrors(semantics(), this->instance().diagnostics(),
474           GetCurrentFileOrBufferName()))
475     return;
476 
477   semantics().DumpSymbolsSources(llvm::outs());
478 }
479 
480 void EmitObjAction::ExecuteAction() {
481   CompilerInstance &ci = this->instance();
482   unsigned DiagID = ci.diagnostics().getCustomDiagID(
483       clang::DiagnosticsEngine::Error, "code-generation is not available yet");
484   ci.diagnostics().Report(DiagID);
485 }
486 
487 void InitOnlyAction::ExecuteAction() {
488   CompilerInstance &ci = this->instance();
489   unsigned DiagID =
490       ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Warning,
491           "Use `-init-only` for testing purposes only");
492   ci.diagnostics().Report(DiagID);
493 }
494