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 
54   // Prescan. In case of failure, report and return.
55   ci.parsing().Prescan(currentInputPath, parserOptions);
56 
57   if (!ci.parsing().messages().empty() &&
58       (ci.invocation().warnAsErr() ||
59           ci.parsing().messages().AnyFatalError())) {
60     const unsigned diagID = ci.diagnostics().getCustomDiagID(
61         clang::DiagnosticsEngine::Error, "Could not scan %0");
62     ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName();
63     ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
64 
65     return false;
66   }
67 
68   return true;
69 }
70 
71 bool PrescanAndParseAction::BeginSourceFileAction(CompilerInstance &c1) {
72   CompilerInstance &ci = this->instance();
73 
74   std::string currentInputPath{GetCurrentFileOrBufferName()};
75 
76   Fortran::parser::Options parserOptions = ci.invocation().fortranOpts();
77 
78   if (ci.invocation().frontendOpts().fortranForm_ == FortranForm::Unknown) {
79     // Switch between fixed and free form format based on the input file
80     // extension.
81     //
82     // Ideally we should have all Fortran options set before entering this
83     // method (i.e. before processing any specific input files). However, we
84     // can't decide between fixed and free form based on the file extension
85     // earlier than this.
86     parserOptions.isFixedForm = currentInput().IsFixedForm();
87   }
88 
89   // Prescan. In case of failure, report and return.
90   ci.parsing().Prescan(currentInputPath, parserOptions);
91 
92   if (ci.parsing().messages().AnyFatalError()) {
93     const unsigned diagID = ci.diagnostics().getCustomDiagID(
94         clang::DiagnosticsEngine::Error, "Could not scan %0");
95     ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName();
96     ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
97 
98     return false;
99   }
100 
101   // Parse. In case of failure, report and return.
102   ci.parsing().Parse(llvm::outs());
103 
104   if (ci.parsing().messages().AnyFatalError()) {
105     unsigned diagID = ci.diagnostics().getCustomDiagID(
106         clang::DiagnosticsEngine::Error, "Could not parse %0");
107     ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName();
108 
109     ci.parsing().messages().Emit(
110         llvm::errs(), this->instance().allCookedSources());
111     return false;
112   }
113 
114   // Report the diagnostics from parsing
115   ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
116 
117   return true;
118 }
119 
120 bool PrescanAndSemaAction::BeginSourceFileAction(CompilerInstance &c1) {
121   CompilerInstance &ci = this->instance();
122   std::string currentInputPath{GetCurrentFileOrBufferName()};
123   Fortran::parser::Options parserOptions = ci.invocation().fortranOpts();
124 
125   // Prescan. In case of failure, report and return.
126   ci.parsing().Prescan(currentInputPath, parserOptions);
127 
128   if (!ci.parsing().messages().empty() &&
129       (ci.invocation().warnAsErr() ||
130           ci.parsing().messages().AnyFatalError())) {
131     const unsigned diagID = ci.diagnostics().getCustomDiagID(
132         clang::DiagnosticsEngine::Error, "Could not scan %0");
133     ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName();
134     ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
135 
136     return false;
137   }
138 
139   // Parse. In case of failure, report and return.
140   ci.parsing().Parse(llvm::outs());
141 
142   if (!ci.parsing().messages().empty() &&
143       (ci.invocation().warnAsErr() ||
144           ci.parsing().messages().AnyFatalError())) {
145     unsigned diagID = ci.diagnostics().getCustomDiagID(
146         clang::DiagnosticsEngine::Error, "Could not parse %0");
147     ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName();
148 
149     ci.parsing().messages().Emit(
150         llvm::errs(), this->instance().allCookedSources());
151     return false;
152   }
153 
154   // Report the diagnostics from parsing
155   ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
156 
157   auto &parseTree{*ci.parsing().parseTree()};
158 
159   // Prepare semantics
160   setSemantics(std::make_unique<Fortran::semantics::Semantics>(
161       ci.invocation().semanticsContext(), parseTree,
162       ci.parsing().cooked().AsCharBlock(), ci.invocation().debugModuleDir()));
163   auto &semantics = this->semantics();
164 
165   // Run semantic checks
166   semantics.Perform();
167 
168   // Report the diagnostics from the semantic checks
169   semantics.EmitMessages(ci.semaOutputStream());
170 
171   return true;
172 }
173 
174 void InputOutputTestAction::ExecuteAction() {
175   CompilerInstance &ci = instance();
176 
177   // Create a stream for errors
178   std::string buf;
179   llvm::raw_string_ostream error_stream{buf};
180 
181   // Read the input file
182   Fortran::parser::AllSources &allSources{ci.allSources()};
183   std::string path{GetCurrentFileOrBufferName()};
184   const Fortran::parser::SourceFile *sf;
185   if (path == "-")
186     sf = allSources.ReadStandardInput(error_stream);
187   else
188     sf = allSources.Open(path, error_stream, std::optional<std::string>{"."s});
189   llvm::ArrayRef<char> fileContent = sf->content();
190 
191   // Output file descriptor to receive the contents of the input file.
192   std::unique_ptr<llvm::raw_ostream> os;
193 
194   // Copy the contents from the input file to the output file
195   if (!ci.IsOutputStreamNull()) {
196     // An output stream (outputStream_) was set earlier
197     ci.WriteOutputStream(fileContent.data());
198   } else {
199     // No pre-set output stream - create an output file
200     os = ci.CreateDefaultOutputFile(
201         /*binary=*/true, GetCurrentFileOrBufferName(), "txt");
202     if (!os)
203       return;
204     (*os) << fileContent.data();
205   }
206 }
207 
208 void PrintPreprocessedAction::ExecuteAction() {
209   std::string buf;
210   llvm::raw_string_ostream outForPP{buf};
211 
212   // Run the preprocessor
213   CompilerInstance &ci = this->instance();
214   ci.parsing().DumpCookedChars(outForPP);
215 
216   // If a pre-defined output stream exists, dump the preprocessed content there
217   if (!ci.IsOutputStreamNull()) {
218     // Send the output to the pre-defined output buffer.
219     ci.WriteOutputStream(outForPP.str());
220     return;
221   }
222 
223   // Print diagnostics from the preprocessor
224   ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
225 
226   // Create a file and save the preprocessed output there
227   if (auto os{ci.CreateDefaultOutputFile(
228           /*Binary=*/true, /*InFile=*/GetCurrentFileOrBufferName())}) {
229     (*os) << outForPP.str();
230   } else {
231     llvm::errs() << "Unable to create the output file\n";
232     return;
233   }
234 }
235 
236 void DebugDumpProvenanceAction::ExecuteAction() {
237   this->instance().parsing().DumpProvenance(llvm::outs());
238 }
239 
240 void ParseSyntaxOnlyAction::ExecuteAction() {
241   reportFatalSemanticErrors(semantics(), this->instance().diagnostics(),
242       GetCurrentFileOrBufferName());
243 }
244 
245 void DebugUnparseNoSemaAction::ExecuteAction() {
246   auto &parseTree{instance().parsing().parseTree()};
247 
248   Fortran::parser::AnalyzedObjectsAsFortran asFortran =
249       Fortran::frontend::getBasicAsFortran();
250 
251   // TODO: Options should come from CompilerInvocation
252   Unparse(llvm::outs(), *parseTree,
253       /*encoding=*/Fortran::parser::Encoding::UTF_8,
254       /*capitalizeKeywords=*/true, /*backslashEscapes=*/false,
255       /*preStatement=*/nullptr, &asFortran);
256 }
257 
258 void DebugUnparseAction::ExecuteAction() {
259   auto &parseTree{instance().parsing().parseTree()};
260   Fortran::parser::AnalyzedObjectsAsFortran asFortran =
261       Fortran::frontend::getBasicAsFortran();
262 
263   // TODO: Options should come from CompilerInvocation
264   Unparse(llvm::outs(), *parseTree,
265       /*encoding=*/Fortran::parser::Encoding::UTF_8,
266       /*capitalizeKeywords=*/true, /*backslashEscapes=*/false,
267       /*preStatement=*/nullptr, &asFortran);
268 
269   // Report fatal semantic errors
270   reportFatalSemanticErrors(semantics(), this->instance().diagnostics(),
271       GetCurrentFileOrBufferName());
272 }
273 
274 void DebugUnparseWithSymbolsAction::ExecuteAction() {
275   auto &parseTree{*instance().parsing().parseTree()};
276 
277   Fortran::semantics::UnparseWithSymbols(
278       llvm::outs(), parseTree, /*encoding=*/Fortran::parser::Encoding::UTF_8);
279 
280   // Report fatal semantic errors
281   reportFatalSemanticErrors(semantics(), this->instance().diagnostics(),
282       GetCurrentFileOrBufferName());
283 }
284 
285 void DebugDumpSymbolsAction::ExecuteAction() {
286   auto &semantics = this->semantics();
287 
288   // Dump symbols
289   semantics.DumpSymbols(llvm::outs());
290   // Report fatal semantic errors
291   reportFatalSemanticErrors(
292       semantics, this->instance().diagnostics(), GetCurrentFileOrBufferName());
293 }
294 
295 void DebugDumpParseTreeNoSemaAction::ExecuteAction() {
296   auto &parseTree{instance().parsing().parseTree()};
297   Fortran::parser::AnalyzedObjectsAsFortran asFortran =
298       Fortran::frontend::getBasicAsFortran();
299 
300   // Dump parse tree
301   Fortran::parser::DumpTree(llvm::outs(), parseTree, &asFortran);
302 }
303 
304 void DebugDumpParseTreeAction::ExecuteAction() {
305   auto &parseTree{instance().parsing().parseTree()};
306   Fortran::parser::AnalyzedObjectsAsFortran asFortran =
307       Fortran::frontend::getBasicAsFortran();
308 
309   // Dump parse tree
310   Fortran::parser::DumpTree(llvm::outs(), parseTree, &asFortran);
311   // Report fatal semantic errors
312   reportFatalSemanticErrors(semantics(), this->instance().diagnostics(),
313       GetCurrentFileOrBufferName());
314 }
315 
316 void DebugMeasureParseTreeAction::ExecuteAction() {
317   CompilerInstance &ci = this->instance();
318 
319   // Parse. In case of failure, report and return.
320   ci.parsing().Parse(llvm::outs());
321 
322   if (!ci.parsing().messages().empty() &&
323       (ci.invocation().warnAsErr() ||
324           ci.parsing().messages().AnyFatalError())) {
325     unsigned diagID = ci.diagnostics().getCustomDiagID(
326         clang::DiagnosticsEngine::Error, "Could not parse %0");
327     ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName();
328 
329     ci.parsing().messages().Emit(
330         llvm::errs(), this->instance().allCookedSources());
331     return;
332   }
333 
334   // Report the diagnostics from parsing
335   ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
336 
337   auto &parseTree{*ci.parsing().parseTree()};
338 
339   // Measure the parse tree
340   MeasurementVisitor visitor;
341   Fortran::parser::Walk(parseTree, visitor);
342   llvm::outs() << "Parse tree comprises " << visitor.objects
343                << " objects and occupies " << visitor.bytes
344                << " total bytes.\n";
345 }
346 
347 void DebugPreFIRTreeAction::ExecuteAction() {
348   CompilerInstance &ci = this->instance();
349   // Report and exit if fatal semantic errors are present
350   if (reportFatalSemanticErrors(
351           semantics(), ci.diagnostics(), GetCurrentFileOrBufferName())) {
352     return;
353   }
354 
355   auto &parseTree{*ci.parsing().parseTree()};
356 
357   // Dump pre-FIR tree
358   if (auto ast{Fortran::lower::createPFT(
359           parseTree, ci.invocation().semanticsContext())}) {
360     Fortran::lower::dumpPFT(llvm::outs(), *ast);
361   } else {
362     unsigned diagID = ci.diagnostics().getCustomDiagID(
363         clang::DiagnosticsEngine::Error, "Pre FIR Tree is NULL.");
364     ci.diagnostics().Report(diagID);
365   }
366 }
367 
368 void DebugDumpParsingLogAction::ExecuteAction() {
369   CompilerInstance &ci = this->instance();
370 
371   ci.parsing().Parse(llvm::errs());
372   ci.parsing().DumpParsingLog(llvm::outs());
373 }
374 
375 void GetDefinitionAction::ExecuteAction() {
376   // Report and exit if fatal semantic errors are present
377   if (reportFatalSemanticErrors(semantics(), this->instance().diagnostics(),
378           GetCurrentFileOrBufferName()))
379     return;
380 
381   CompilerInstance &ci = this->instance();
382   parser::AllCookedSources &cs = ci.allCookedSources();
383   unsigned diagID = ci.diagnostics().getCustomDiagID(
384       clang::DiagnosticsEngine::Error, "Symbol not found");
385 
386   auto gdv = ci.invocation().frontendOpts().getDefVals_;
387   auto charBlock{cs.GetCharBlockFromLineAndColumns(
388       gdv.line, gdv.startColumn, gdv.endColumn)};
389   if (!charBlock) {
390     ci.diagnostics().Report(diagID);
391     return;
392   }
393 
394   llvm::outs() << "String range: >" << charBlock->ToString() << "<\n";
395 
396   auto *symbol{ci.invocation()
397                    .semanticsContext()
398                    .FindScope(*charBlock)
399                    .FindSymbol(*charBlock)};
400   if (!symbol) {
401     ci.diagnostics().Report(diagID);
402     return;
403   }
404 
405   llvm::outs() << "Found symbol name: " << symbol->name().ToString() << "\n";
406 
407   auto sourceInfo{cs.GetSourcePositionRange(symbol->name())};
408   if (!sourceInfo) {
409     llvm_unreachable(
410         "Failed to obtain SourcePosition."
411         "TODO: Please, write a test and replace this with a diagnostic!");
412     return;
413   }
414 
415   llvm::outs() << "Found symbol name: " << symbol->name().ToString() << "\n";
416   llvm::outs() << symbol->name().ToString() << ": "
417                << sourceInfo->first.file.path() << ", "
418                << sourceInfo->first.line << ", " << sourceInfo->first.column
419                << "-" << sourceInfo->second.column << "\n";
420 }
421 
422 void GetSymbolsSourcesAction::ExecuteAction() {
423   // Report and exit if fatal semantic errors are present
424   if (reportFatalSemanticErrors(semantics(), this->instance().diagnostics(),
425           GetCurrentFileOrBufferName()))
426     return;
427 
428   semantics().DumpSymbolsSources(llvm::outs());
429 }
430 
431 void EmitObjAction::ExecuteAction() {
432   CompilerInstance &ci = this->instance();
433   unsigned DiagID = ci.diagnostics().getCustomDiagID(
434       clang::DiagnosticsEngine::Error, "code-generation is not available yet");
435   ci.diagnostics().Report(DiagID);
436 }
437