275 lines · cpp
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-exception6//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 descriptors33static 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. Note45 // 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 report49 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 return61 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`, or81 // * 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 already93 // enabled.94 invoc.getFortranOpts().features.Enable(95 Fortran::common::LanguageFeature::CUDA,96 getCurrentInput().getIsCUDAFortran());97 }98 99 // -fpreprocess-include-lines100 invoc.getFortranOpts().expandIncludeLinesInPreprocessedOutput =101 invoc.getPreprocessorOpts().preprocessIncludeLines;102 103 // Decide between fixed and free form (if the user didn't express any104 // 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 the131 // 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 file146 // extension.147 //148 // Ideally we should have all Fortran options set before entering this149 // method (i.e. before processing any specific input files). However, we150 // can't decide between fixed and free form based on the file extension151 // 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 than173 // combining them with messages from semantics.174 const common::LanguageFeatureControl &features{175 ci.getInvocation().getFortranOpts().features};176 // Default maxErrors here because none are fatal.177 ci.getParsing().messages().Emit(llvm::errs(), ci.getAllCookedSources(),178 /*echoSourceLine=*/true, &features);179 }180 return true;181}182 183bool FrontendAction::runSemanticChecks() {184 CompilerInstance &ci = this->getInstance();185 std::optional<parser::Program> &parseTree{ci.getParsing().parseTree()};186 assert(parseTree && "Cannot run semantic checks without a parse tree!");187 188 // Transfer any pending non-fatal messages from parsing to semantics189 // so that they are merged and all printed in order.190 auto &semanticsCtx{ci.createNewSemanticsContext()};191 semanticsCtx.messages().Annex(std::move(ci.getParsing().messages()));192 semanticsCtx.set_debugModuleWriter(ci.getInvocation().getDebugModuleDir());193 194 // Prepare semantics195 ci.setSemantics(std::make_unique<Fortran::semantics::Semantics>(semanticsCtx,196 *parseTree));197 auto &semantics = ci.getSemantics();198 semantics.set_hermeticModuleFileOutput(199 ci.getInvocation().getHermeticModuleFileOutput());200 201 // Run semantic checks202 semantics.Perform();203 204 if (reportFatalSemanticErrors()) {205 return false;206 }207 208 // Report the diagnostics from parsing and the semantic checks209 semantics.EmitMessages(ci.getSemaOutputStream());210 211 return true;212}213 214bool FrontendAction::generateRtTypeTables() {215 getInstance().setRtTyTables(216 std::make_unique<Fortran::semantics::RuntimeDerivedTypeTables>(217 BuildRuntimeDerivedTypeTables(getInstance().getSemanticsContext())));218 219 // The runtime derived type information table builder may find additional220 // semantic errors. Report them.221 if (reportFatalSemanticErrors()) {222 return false;223 }224 225 return true;226}227 228template <unsigned N>229bool FrontendAction::reportFatalErrors(const char (&message)[N]) {230 const common::LanguageFeatureControl &features{231 instance->getInvocation().getFortranOpts().features};232 const size_t maxErrors{instance->getInvocation().getMaxErrors()};233 const bool warningsAreErrors{instance->getInvocation().getWarnAsErr()};234 if (instance->getParsing().messages().AnyFatalError(warningsAreErrors)) {235 const unsigned diagID = instance->getDiagnostics().getCustomDiagID(236 clang::DiagnosticsEngine::Error, message);237 instance->getDiagnostics().Report(diagID) << getCurrentFileOrBufferName();238 instance->getParsing().messages().Emit(239 llvm::errs(), instance->getAllCookedSources(),240 /*echoSourceLines=*/true, &features, maxErrors, warningsAreErrors);241 return true;242 }243 if (instance->getParsing().parseTree().has_value() &&244 !instance->getParsing().consumedWholeFile()) {245 // Parsing failed without error.246 const unsigned diagID = instance->getDiagnostics().getCustomDiagID(247 clang::DiagnosticsEngine::Error, message);248 instance->getDiagnostics().Report(diagID) << getCurrentFileOrBufferName();249 instance->getParsing().messages().Emit(250 llvm::errs(), instance->getAllCookedSources(),251 /*echoSourceLine=*/true, &features, maxErrors, warningsAreErrors);252 instance->getParsing().EmitMessage(253 llvm::errs(), instance->getParsing().finalRestingPlace(),254 "parser FAIL (final position)", "error: ", llvm::raw_ostream::RED);255 return true;256 }257 return false;258}259 260bool FrontendAction::reportFatalSemanticErrors() {261 auto &diags = instance->getDiagnostics();262 auto &sema = instance->getSemantics();263 264 if (instance->getSemantics().AnyFatalError()) {265 unsigned diagID = diags.getCustomDiagID(clang::DiagnosticsEngine::Error,266 "Semantic errors in %0");267 diags.Report(diagID) << getCurrentFileOrBufferName();268 sema.EmitMessages(instance->getSemaOutputStream());269 270 return true;271 }272 273 return false;274}275