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/Parser/parsing.h"
14 #include "flang/Parser/provenance.h"
15 #include "flang/Parser/source.h"
16 #include "flang/Parser/unparse.h"
17 #include "flang/Semantics/semantics.h"
18 #include "flang/Semantics/unparse-with-symbols.h"
19 
20 using namespace Fortran::frontend;
21 
22 bool PrescanAction::BeginSourceFileAction(CompilerInstance &c1) {
23   CompilerInstance &ci = this->instance();
24 
25   std::string currentInputPath{GetCurrentFileOrBufferName()};
26 
27   Fortran::parser::Options parserOptions = ci.invocation().fortranOpts();
28 
29   if (ci.invocation().frontendOpts().fortranForm_ == FortranForm::Unknown) {
30     // Switch between fixed and free form format based on the input file
31     // extension.
32     //
33     // Ideally we should have all Fortran options set before entering this
34     // method (i.e. before processing any specific input files). However, we
35     // can't decide between fixed and free form based on the file extension
36     // earlier than this.
37     parserOptions.isFixedForm = currentInput().IsFixedForm();
38   }
39 
40   // Prescan. In case of failure, report and return.
41   ci.parsing().Prescan(currentInputPath, parserOptions);
42 
43   if (ci.parsing().messages().AnyFatalError()) {
44     const unsigned diagID = ci.diagnostics().getCustomDiagID(
45         clang::DiagnosticsEngine::Error, "Could not scan %0");
46     ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName();
47     ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
48 
49     return false;
50   }
51 
52   return true;
53 }
54 
55 bool PrescanAndSemaAction::BeginSourceFileAction(CompilerInstance &c1) {
56   CompilerInstance &ci = this->instance();
57 
58   std::string currentInputPath{GetCurrentFileOrBufferName()};
59 
60   Fortran::parser::Options parserOptions = ci.invocation().fortranOpts();
61 
62   if (ci.invocation().frontendOpts().fortranForm_ == FortranForm::Unknown) {
63     // Switch between fixed and free form format based on the input file
64     // extension.
65     //
66     // Ideally we should have all Fortran options set before entering this
67     // method (i.e. before processing any specific input files). However, we
68     // can't decide between fixed and free form based on the file extension
69     // earlier than this.
70     parserOptions.isFixedForm = currentInput().IsFixedForm();
71   }
72 
73   // Prescan. In case of failure, report and return.
74   ci.parsing().Prescan(currentInputPath, parserOptions);
75 
76   if (ci.parsing().messages().AnyFatalError()) {
77     const unsigned diagID = ci.diagnostics().getCustomDiagID(
78         clang::DiagnosticsEngine::Error, "Could not scan %0");
79     ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName();
80     ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
81 
82     return false;
83   }
84 
85   // Parse. In case of failure, report and return.
86   ci.parsing().Parse(llvm::outs());
87 
88   if (ci.parsing().messages().AnyFatalError()) {
89     unsigned diagID = ci.diagnostics().getCustomDiagID(
90         clang::DiagnosticsEngine::Error, "Could not parse %0");
91     ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName();
92 
93     ci.parsing().messages().Emit(
94         llvm::errs(), this->instance().allCookedSources());
95     return false;
96   }
97 
98   // Report the diagnostics from parsing
99   ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
100 
101   auto &parseTree{*ci.parsing().parseTree()};
102 
103   // Prepare semantics
104   Fortran::semantics::Semantics semantics{ci.invocation().semanticsContext(),
105       parseTree, ci.parsing().cooked().AsCharBlock()};
106 
107   // Run semantic checks
108   semantics.Perform();
109 
110   // Report the diagnostics from the semantic checks
111   semantics.EmitMessages(ci.semaOutputStream());
112 
113   if (semantics.AnyFatalError()) {
114     unsigned DiagID = ci.diagnostics().getCustomDiagID(
115         clang::DiagnosticsEngine::Error, "Semantic errors in %0");
116     ci.diagnostics().Report(DiagID) << GetCurrentFileOrBufferName();
117 
118     return false;
119   }
120 
121   return true;
122 }
123 
124 void InputOutputTestAction::ExecuteAction() {
125   CompilerInstance &ci = instance();
126 
127   // Create a stream for errors
128   std::string buf;
129   llvm::raw_string_ostream error_stream{buf};
130 
131   // Read the input file
132   Fortran::parser::AllSources &allSources{ci.allSources()};
133   std::string path{GetCurrentFileOrBufferName()};
134   const Fortran::parser::SourceFile *sf;
135   if (path == "-")
136     sf = allSources.ReadStandardInput(error_stream);
137   else
138     sf = allSources.Open(path, error_stream, std::optional<std::string>{"."s});
139   llvm::ArrayRef<char> fileContent = sf->content();
140 
141   // Output file descriptor to receive the contents of the input file.
142   std::unique_ptr<llvm::raw_ostream> os;
143 
144   // Copy the contents from the input file to the output file
145   if (!ci.IsOutputStreamNull()) {
146     // An output stream (outputStream_) was set earlier
147     ci.WriteOutputStream(fileContent.data());
148   } else {
149     // No pre-set output stream - create an output file
150     os = ci.CreateDefaultOutputFile(
151         /*binary=*/true, GetCurrentFileOrBufferName(), "txt");
152     if (!os)
153       return;
154     (*os) << fileContent.data();
155   }
156 }
157 
158 void PrintPreprocessedAction::ExecuteAction() {
159   std::string buf;
160   llvm::raw_string_ostream outForPP{buf};
161 
162   // Run the preprocessor
163   CompilerInstance &ci = this->instance();
164   ci.parsing().DumpCookedChars(outForPP);
165 
166   // If a pre-defined output stream exists, dump the preprocessed content there
167   if (!ci.IsOutputStreamNull()) {
168     // Send the output to the pre-defined output buffer.
169     ci.WriteOutputStream(outForPP.str());
170     return;
171   }
172 
173   // Print diagnostics from the preprocessor
174   ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
175 
176   // Create a file and save the preprocessed output there
177   if (auto os{ci.CreateDefaultOutputFile(
178           /*Binary=*/true, /*InFile=*/GetCurrentFileOrBufferName())}) {
179     (*os) << outForPP.str();
180   } else {
181     llvm::errs() << "Unable to create the output file\n";
182     return;
183   }
184 }
185 
186 void ParseSyntaxOnlyAction::ExecuteAction() {}
187 
188 void DebugUnparseAction::ExecuteAction() {
189   auto &parseTree{instance().parsing().parseTree()};
190   Fortran::parser::AnalyzedObjectsAsFortran asFortran =
191       Fortran::frontend::getBasicAsFortran();
192 
193   // TODO: Options should come from CompilerInvocation
194   Unparse(llvm::outs(), *parseTree,
195       /*encoding=*/Fortran::parser::Encoding::UTF_8,
196       /*capitalizeKeywords=*/true, /*backslashEscapes=*/false,
197       /*preStatement=*/nullptr, &asFortran);
198 }
199 
200 void DebugUnparseWithSymbolsAction::ExecuteAction() {
201   auto &parseTree{*instance().parsing().parseTree()};
202 
203   Fortran::semantics::UnparseWithSymbols(
204       llvm::outs(), parseTree, /*encoding=*/Fortran::parser::Encoding::UTF_8);
205 }
206 
207 void EmitObjAction::ExecuteAction() {
208   CompilerInstance &ci = this->instance();
209   unsigned DiagID = ci.diagnostics().getCustomDiagID(
210       clang::DiagnosticsEngine::Error, "code-generation is not available yet");
211   ci.diagnostics().Report(DiagID);
212 }
213