1//===--- FrontendAction.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// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
10//
11//===----------------------------------------------------------------------===//
12
13#include "flang/Frontend/FrontendAction.h"
14#include "flang/Frontend/CompilerInstance.h"
15#include "flang/Frontend/FrontendActions.h"
16#include "flang/Frontend/FrontendOptions.h"
17#include "flang/Frontend/FrontendPluginRegistry.h"
18#include "flang/Parser/parsing.h"
19#include "clang/Basic/DiagnosticFrontend.h"
20#include "llvm/Support/Errc.h"
21#include "llvm/Support/VirtualFileSystem.h"
22
23using namespace Fortran::frontend;
24
25LLVM_INSTANTIATE_REGISTRY(FrontendPluginRegistry)
26
27void FrontendAction::setCurrentInput(const FrontendInputFile &input) {
28 this->currentInput = input;
29}
30
31// Call this method if BeginSourceFile fails.
32// Deallocate compiler instance, input and output descriptors
33static void beginSourceFileCleanUp(FrontendAction &fa, CompilerInstance &ci) {
34 ci.clearOutputFiles(/*EraseFiles=*/true);
35 fa.setCurrentInput(FrontendInputFile());
36 fa.setInstance(nullptr);
37}
38
39bool FrontendAction::beginSourceFile(CompilerInstance &ci,
40 const FrontendInputFile &realInput) {
41
42 FrontendInputFile input(realInput);
43
44 // Return immediately if the input file does not exist or is not a file. Note
45 // that we cannot check this for input from stdin.
46 if (input.getFile() != "-") {
47 if (!llvm::sys::fs::is_regular_file(input.getFile())) {
48 // Create an diagnostic ID to report
49 unsigned diagID;
50 if (llvm::vfs::getRealFileSystem()->exists(input.getFile())) {
51 ci.getDiagnostics().Report(clang::diag::err_fe_error_reading)
52 << input.getFile() << "not a regular file";
53 diagID = ci.getDiagnostics().getCustomDiagID(
54 clang::DiagnosticsEngine::Error, "%0 is not a regular file");
55 } else {
56 diagID = ci.getDiagnostics().getCustomDiagID(
57 clang::DiagnosticsEngine::Error, "%0 does not exist");
58 }
59
60 // Report the diagnostic and return
61 ci.getDiagnostics().Report(diagID) << input.getFile();
62 beginSourceFileCleanUp(*this, ci);
63 return false;
64 }
65 }
66
67 assert(!instance && "Already processing a source file!");
68 assert(!realInput.isEmpty() && "Unexpected empty filename!");
69 setCurrentInput(realInput);
70 setInstance(&ci);
71
72 if (!ci.hasAllSources()) {
73 beginSourceFileCleanUp(*this, ci);
74 return false;
75 }
76
77 auto &invoc = ci.getInvocation();
78
79 // Include command-line and predefined preprocessor macros. Use either:
80 // * `-cpp/-nocpp`, or
81 // * the file extension (if the user didn't express any preference)
82 // to decide whether to include them or not.
83 if ((invoc.getPreprocessorOpts().macrosFlag == PPMacrosFlag::Include) ||
84 (invoc.getPreprocessorOpts().macrosFlag == PPMacrosFlag::Unknown &&
85 getCurrentInput().getMustBePreprocessed())) {
86 invoc.setDefaultPredefinitions();
87 invoc.collectMacroDefinitions();
88 }
89
90 if (!invoc.getFortranOpts().features.IsEnabled(
91 Fortran::common::LanguageFeature::CUDA)) {
92 // Enable CUDA Fortran if source file is *.cuf/*.CUF and not already
93 // enabled.
94 invoc.getFortranOpts().features.Enable(
95 Fortran::common::LanguageFeature::CUDA,
96 getCurrentInput().getIsCUDAFortran());
97 }
98
99 // -fpreprocess-include-lines
100 invoc.getFortranOpts().expandIncludeLinesInPreprocessedOutput =
101 invoc.getPreprocessorOpts().preprocessIncludeLines;
102
103 // Decide between fixed and free form (if the user didn't express any
104 // preference, use the file extension to decide)
105 if (invoc.getFrontendOpts().fortranForm == FortranForm::Unknown) {
106 invoc.getFortranOpts().isFixedForm = getCurrentInput().getIsFixedForm();
107 }
108
109 if (!beginSourceFileAction()) {
110 beginSourceFileCleanUp(*this, ci);
111 return false;
112 }
113
114 return true;
115}
116
117bool FrontendAction::shouldEraseOutputFiles() {
118 return getInstance().getDiagnostics().hasErrorOccurred();
119}
120
121llvm::Error FrontendAction::execute() {
122 executeAction();
123
124 return llvm::Error::success();
125}
126
127void FrontendAction::endSourceFile() {
128 CompilerInstance &ci = getInstance();
129
130 // Cleanup the output streams, and erase the output files if instructed by the
131 // FrontendAction.
132 ci.clearOutputFiles(/*EraseFiles=*/shouldEraseOutputFiles());
133
134 setInstance(nullptr);
135 setCurrentInput(FrontendInputFile());
136}
137
138bool FrontendAction::runPrescan() {
139 CompilerInstance &ci = this->getInstance();
140 std::string currentInputPath{getCurrentFileOrBufferName()};
141 Fortran::parser::Options parserOptions = ci.getInvocation().getFortranOpts();
142
143 if (ci.getInvocation().getFrontendOpts().fortranForm ==
144 FortranForm::Unknown) {
145 // Switch between fixed and free form format based on the input file
146 // extension.
147 //
148 // Ideally we should have all Fortran options set before entering this
149 // method (i.e. before processing any specific input files). However, we
150 // can't decide between fixed and free form based on the file extension
151 // earlier than this.
152 parserOptions.isFixedForm = getCurrentInput().getIsFixedForm();
153 }
154
155 // Prescan. In case of failure, report and return.
156 ci.getParsing().Prescan(currentInputPath, parserOptions);
157
158 return !reportFatalScanningErrors();
159}
160
161bool FrontendAction::runParse(bool emitMessages) {
162 CompilerInstance &ci = this->getInstance();
163
164 // Parse. In case of failure, report and return.
165 ci.getParsing().Parse(llvm::outs());
166
167 if (reportFatalParsingErrors()) {
168 return false;
169 }
170
171 if (emitMessages) {
172 // Report any non-fatal diagnostics from getParsing now rather than
173 // combining them with messages from semantics.
174 ci.getParsing().messages().Emit(llvm::errs(), ci.getAllCookedSources());
175 }
176 return true;
177}
178
179bool FrontendAction::runSemanticChecks() {
180 CompilerInstance &ci = this->getInstance();
181 std::optional<parser::Program> &parseTree{ci.getParsing().parseTree()};
182 assert(parseTree && "Cannot run semantic checks without a parse tree!");
183
184 // Transfer any pending non-fatal messages from parsing to semantics
185 // so that they are merged and all printed in order.
186 auto &semanticsCtx{ci.createNewSemanticsContext()};
187 semanticsCtx.messages().Annex(std::move(ci.getParsing().messages()));
188 semanticsCtx.set_debugModuleWriter(ci.getInvocation().getDebugModuleDir());
189
190 // Prepare semantics
191 ci.setSemantics(std::make_unique<Fortran::semantics::Semantics>(semanticsCtx,
192 *parseTree));
193 auto &semantics = ci.getSemantics();
194 semantics.set_hermeticModuleFileOutput(
195 ci.getInvocation().getHermeticModuleFileOutput());
196
197 // Run semantic checks
198 semantics.Perform();
199
200 if (reportFatalSemanticErrors()) {
201 return false;
202 }
203
204 // Report the diagnostics from parsing and the semantic checks
205 semantics.EmitMessages(ci.getSemaOutputStream());
206
207 return true;
208}
209
210bool FrontendAction::generateRtTypeTables() {
211 getInstance().setRtTyTables(
212 std::make_unique<Fortran::semantics::RuntimeDerivedTypeTables>(
213 BuildRuntimeDerivedTypeTables(getInstance().getSemanticsContext())));
214
215 // The runtime derived type information table builder may find additional
216 // semantic errors. Report them.
217 if (reportFatalSemanticErrors()) {
218 return false;
219 }
220
221 return true;
222}
223
224template <unsigned N>
225bool FrontendAction::reportFatalErrors(const char (&message)[N]) {
226 if (!instance->getParsing().messages().empty() &&
227 (instance->getInvocation().getWarnAsErr() ||
228 instance->getParsing().messages().AnyFatalError())) {
229 const unsigned diagID = instance->getDiagnostics().getCustomDiagID(
230 clang::DiagnosticsEngine::Error, message);
231 instance->getDiagnostics().Report(diagID) << getCurrentFileOrBufferName();
232 instance->getParsing().messages().Emit(llvm::errs(),
233 instance->getAllCookedSources());
234 return true;
235 }
236 if (instance->getParsing().parseTree().has_value() &&
237 !instance->getParsing().consumedWholeFile()) {
238 // Parsing failed without error.
239 const unsigned diagID = instance->getDiagnostics().getCustomDiagID(
240 clang::DiagnosticsEngine::Error, message);
241 instance->getDiagnostics().Report(diagID) << getCurrentFileOrBufferName();
242 instance->getParsing().messages().Emit(llvm::errs(),
243 instance->getAllCookedSources());
244 instance->getParsing().EmitMessage(
245 llvm::errs(), instance->getParsing().finalRestingPlace(),
246 "parser FAIL (final position)", "error: ", llvm::raw_ostream::RED);
247 return true;
248 }
249 return false;
250}
251
252bool FrontendAction::reportFatalSemanticErrors() {
253 auto &diags = instance->getDiagnostics();
254 auto &sema = instance->getSemantics();
255
256 if (instance->getSemantics().AnyFatalError()) {
257 unsigned diagID = diags.getCustomDiagID(clang::DiagnosticsEngine::Error,
258 "Semantic errors in %0");
259 diags.Report(diagID) << getCurrentFileOrBufferName();
260 sema.EmitMessages(instance->getSemaOutputStream());
261
262 return true;
263 }
264
265 return false;
266}
267

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of flang/lib/Frontend/FrontendAction.cpp