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