blob: 00b46c90dfd1fa3c94363dab70dd0e4db584edc0 [file] [log] [blame]
//===--- FrontendActions.cpp ----------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "flang/Frontend/FrontendActions.h"
#include "flang/Common/default-kinds.h"
#include "flang/Frontend/CompilerInstance.h"
#include "flang/Frontend/FrontendOptions.h"
#include "flang/Frontend/PreprocessorOptions.h"
#include "flang/Lower/PFTBuilder.h"
#include "flang/Parser/dump-parse-tree.h"
#include "flang/Parser/parsing.h"
#include "flang/Parser/provenance.h"
#include "flang/Parser/source.h"
#include "flang/Parser/unparse.h"
#include "flang/Semantics/runtime-type-info.h"
#include "flang/Semantics/semantics.h"
#include "flang/Semantics/unparse-with-symbols.h"
#include "llvm/ADT/StringRef.h"
#include <clang/Basic/Diagnostic.h>
#include <memory>
using namespace Fortran::frontend;
/// Report fatal semantic errors if present.
///
/// \param semantics The semantics instance
/// \param diags The diagnostics engine instance
/// \param bufferName The file or buffer name
///
/// \return True if fatal semantic errors are present, false if not
bool reportFatalSemanticErrors(const Fortran::semantics::Semantics &semantics,
clang::DiagnosticsEngine &diags, const llvm::StringRef &bufferName) {
if (semantics.AnyFatalError()) {
unsigned DiagID = diags.getCustomDiagID(
clang::DiagnosticsEngine::Error, "Semantic errors in %0");
diags.Report(DiagID) << bufferName;
return true;
}
return false;
}
bool PrescanAction::BeginSourceFileAction(CompilerInstance &c1) {
CompilerInstance &ci = this->instance();
std::string currentInputPath{GetCurrentFileOrBufferName()};
Fortran::parser::Options parserOptions = ci.invocation().fortranOpts();
// Prescan. In case of failure, report and return.
ci.parsing().Prescan(currentInputPath, parserOptions);
if (!ci.parsing().messages().empty() &&
(ci.invocation().warnAsErr() ||
ci.parsing().messages().AnyFatalError())) {
const unsigned diagID = ci.diagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "Could not scan %0");
ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName();
ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
return false;
}
return true;
}
bool PrescanAndParseAction::BeginSourceFileAction(CompilerInstance &c1) {
CompilerInstance &ci = this->instance();
std::string currentInputPath{GetCurrentFileOrBufferName()};
Fortran::parser::Options parserOptions = ci.invocation().fortranOpts();
if (ci.invocation().frontendOpts().fortranForm_ == FortranForm::Unknown) {
// Switch between fixed and free form format based on the input file
// extension.
//
// Ideally we should have all Fortran options set before entering this
// method (i.e. before processing any specific input files). However, we
// can't decide between fixed and free form based on the file extension
// earlier than this.
parserOptions.isFixedForm = currentInput().IsFixedForm();
}
// Prescan. In case of failure, report and return.
ci.parsing().Prescan(currentInputPath, parserOptions);
if (ci.parsing().messages().AnyFatalError()) {
const unsigned diagID = ci.diagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "Could not scan %0");
ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName();
ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
return false;
}
// Parse. In case of failure, report and return.
ci.parsing().Parse(llvm::outs());
if (ci.parsing().messages().AnyFatalError()) {
unsigned diagID = ci.diagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "Could not parse %0");
ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName();
ci.parsing().messages().Emit(
llvm::errs(), this->instance().allCookedSources());
return false;
}
// Report the diagnostics from parsing
ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
return true;
}
bool PrescanAndSemaAction::BeginSourceFileAction(CompilerInstance &c1) {
CompilerInstance &ci = this->instance();
std::string currentInputPath{GetCurrentFileOrBufferName()};
Fortran::parser::Options parserOptions = ci.invocation().fortranOpts();
// Prescan. In case of failure, report and return.
ci.parsing().Prescan(currentInputPath, parserOptions);
if (!ci.parsing().messages().empty() &&
(ci.invocation().warnAsErr() ||
ci.parsing().messages().AnyFatalError())) {
const unsigned diagID = ci.diagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "Could not scan %0");
ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName();
ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
return false;
}
// Parse. In case of failure, report and return.
ci.parsing().Parse(llvm::outs());
if (!ci.parsing().messages().empty() &&
(ci.invocation().warnAsErr() ||
ci.parsing().messages().AnyFatalError())) {
unsigned diagID = ci.diagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "Could not parse %0");
ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName();
ci.parsing().messages().Emit(
llvm::errs(), this->instance().allCookedSources());
return false;
}
// Report the diagnostics from parsing
ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
auto &parseTree{*ci.parsing().parseTree()};
// Prepare semantics
setSemantics(std::make_unique<Fortran::semantics::Semantics>(
ci.invocation().semanticsContext(), parseTree,
ci.parsing().cooked().AsCharBlock(), ci.invocation().debugModuleDir()));
auto &semantics = this->semantics();
// Run semantic checks
semantics.Perform();
// Report the diagnostics from the semantic checks
semantics.EmitMessages(ci.semaOutputStream());
return true;
}
void InputOutputTestAction::ExecuteAction() {
CompilerInstance &ci = instance();
// Create a stream for errors
std::string buf;
llvm::raw_string_ostream error_stream{buf};
// Read the input file
Fortran::parser::AllSources &allSources{ci.allSources()};
std::string path{GetCurrentFileOrBufferName()};
const Fortran::parser::SourceFile *sf;
if (path == "-")
sf = allSources.ReadStandardInput(error_stream);
else
sf = allSources.Open(path, error_stream, std::optional<std::string>{"."s});
llvm::ArrayRef<char> fileContent = sf->content();
// Output file descriptor to receive the contents of the input file.
std::unique_ptr<llvm::raw_ostream> os;
// Copy the contents from the input file to the output file
if (!ci.IsOutputStreamNull()) {
// An output stream (outputStream_) was set earlier
ci.WriteOutputStream(fileContent.data());
} else {
// No pre-set output stream - create an output file
os = ci.CreateDefaultOutputFile(
/*binary=*/true, GetCurrentFileOrBufferName(), "txt");
if (!os)
return;
(*os) << fileContent.data();
}
}
void PrintPreprocessedAction::ExecuteAction() {
std::string buf;
llvm::raw_string_ostream outForPP{buf};
// Run the preprocessor
CompilerInstance &ci = this->instance();
ci.parsing().DumpCookedChars(outForPP);
// If a pre-defined output stream exists, dump the preprocessed content there
if (!ci.IsOutputStreamNull()) {
// Send the output to the pre-defined output buffer.
ci.WriteOutputStream(outForPP.str());
return;
}
// Print diagnostics from the preprocessor
ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
// Create a file and save the preprocessed output there
if (auto os{ci.CreateDefaultOutputFile(
/*Binary=*/true, /*InFile=*/GetCurrentFileOrBufferName())}) {
(*os) << outForPP.str();
} else {
llvm::errs() << "Unable to create the output file\n";
return;
}
}
void DebugDumpProvenanceAction::ExecuteAction() {
this->instance().parsing().DumpProvenance(llvm::outs());
}
void ParseSyntaxOnlyAction::ExecuteAction() {
reportFatalSemanticErrors(semantics(), this->instance().diagnostics(),
GetCurrentFileOrBufferName());
}
void DebugUnparseNoSemaAction::ExecuteAction() {
auto &parseTree{instance().parsing().parseTree()};
Fortran::parser::AnalyzedObjectsAsFortran asFortran =
Fortran::frontend::getBasicAsFortran();
// TODO: Options should come from CompilerInvocation
Unparse(llvm::outs(), *parseTree,
/*encoding=*/Fortran::parser::Encoding::UTF_8,
/*capitalizeKeywords=*/true, /*backslashEscapes=*/false,
/*preStatement=*/nullptr, &asFortran);
}
void DebugUnparseAction::ExecuteAction() {
auto &parseTree{instance().parsing().parseTree()};
Fortran::parser::AnalyzedObjectsAsFortran asFortran =
Fortran::frontend::getBasicAsFortran();
// TODO: Options should come from CompilerInvocation
Unparse(llvm::outs(), *parseTree,
/*encoding=*/Fortran::parser::Encoding::UTF_8,
/*capitalizeKeywords=*/true, /*backslashEscapes=*/false,
/*preStatement=*/nullptr, &asFortran);
// Report fatal semantic errors
reportFatalSemanticErrors(semantics(), this->instance().diagnostics(),
GetCurrentFileOrBufferName());
}
void DebugUnparseWithSymbolsAction::ExecuteAction() {
auto &parseTree{*instance().parsing().parseTree()};
Fortran::semantics::UnparseWithSymbols(
llvm::outs(), parseTree, /*encoding=*/Fortran::parser::Encoding::UTF_8);
// Report fatal semantic errors
reportFatalSemanticErrors(semantics(), this->instance().diagnostics(),
GetCurrentFileOrBufferName());
}
void DebugDumpSymbolsAction::ExecuteAction() {
auto &semantics = this->semantics();
// Dump symbols
semantics.DumpSymbols(llvm::outs());
// Report fatal semantic errors
reportFatalSemanticErrors(
semantics, this->instance().diagnostics(), GetCurrentFileOrBufferName());
}
void DebugDumpParseTreeNoSemaAction::ExecuteAction() {
auto &parseTree{instance().parsing().parseTree()};
Fortran::parser::AnalyzedObjectsAsFortran asFortran =
Fortran::frontend::getBasicAsFortran();
// Dump parse tree
Fortran::parser::DumpTree(llvm::outs(), parseTree, &asFortran);
}
void DebugDumpParseTreeAction::ExecuteAction() {
auto &parseTree{instance().parsing().parseTree()};
Fortran::parser::AnalyzedObjectsAsFortran asFortran =
Fortran::frontend::getBasicAsFortran();
// Dump parse tree
Fortran::parser::DumpTree(llvm::outs(), parseTree, &asFortran);
// Report fatal semantic errors
reportFatalSemanticErrors(semantics(), this->instance().diagnostics(),
GetCurrentFileOrBufferName());
}
void DebugMeasureParseTreeAction::ExecuteAction() {
CompilerInstance &ci = this->instance();
// Parse. In case of failure, report and return.
ci.parsing().Parse(llvm::outs());
if (!ci.parsing().messages().empty() &&
(ci.invocation().warnAsErr() ||
ci.parsing().messages().AnyFatalError())) {
unsigned diagID = ci.diagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "Could not parse %0");
ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName();
ci.parsing().messages().Emit(
llvm::errs(), this->instance().allCookedSources());
return;
}
// Report the diagnostics from parsing
ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
auto &parseTree{*ci.parsing().parseTree()};
// Measure the parse tree
MeasurementVisitor visitor;
Fortran::parser::Walk(parseTree, visitor);
llvm::outs() << "Parse tree comprises " << visitor.objects
<< " objects and occupies " << visitor.bytes
<< " total bytes.\n";
}
void DebugPreFIRTreeAction::ExecuteAction() {
CompilerInstance &ci = this->instance();
// Report and exit if fatal semantic errors are present
if (reportFatalSemanticErrors(
semantics(), ci.diagnostics(), GetCurrentFileOrBufferName())) {
return;
}
auto &parseTree{*ci.parsing().parseTree()};
// Dump pre-FIR tree
if (auto ast{Fortran::lower::createPFT(
parseTree, ci.invocation().semanticsContext())}) {
Fortran::lower::dumpPFT(llvm::outs(), *ast);
} else {
unsigned diagID = ci.diagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "Pre FIR Tree is NULL.");
ci.diagnostics().Report(diagID);
}
}
void DebugDumpParsingLogAction::ExecuteAction() {
CompilerInstance &ci = this->instance();
ci.parsing().Parse(llvm::errs());
ci.parsing().DumpParsingLog(llvm::outs());
}
void GetSymbolsSourcesAction::ExecuteAction() {
// Report and exit if fatal semantic errors are present
if (reportFatalSemanticErrors(semantics(), this->instance().diagnostics(),
GetCurrentFileOrBufferName()))
return;
semantics().DumpSymbolsSources(llvm::outs());
}
void EmitObjAction::ExecuteAction() {
CompilerInstance &ci = this->instance();
unsigned DiagID = ci.diagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "code-generation is not available yet");
ci.diagnostics().Report(DiagID);
}