llvm-project/flang/lib/Frontend/FrontendActions.cpp
Andrzej Warzynski 2186a4aea0 [flang] Make the plugin API independent of the driver internals
This patch adds a few new member methods in the `PluginParseTreeAction`
frontend action base class. With these new methods, the plugin API
becomes independent of the driver internals. In particular, plugin
writers no longer require the `CompilerInstance.h` header file to access
various driver data structures (instead, they can use newly added
hooks).

This change is desirable as `CompilerInstance.h` includes various
headers from Clang (both explicitly and implicitly). Some of these
header files are generated at build time (through TableGen) and
including them creates a dependency on some of Clang's build targets.
However, plugins in Flang should not depend on Clang build targets.

Note that plugins might still work fine most of the time, even without
this change and without adding Clang build targets as dependency in
plugin's CMake definition. Indeed, these Clang build targets are often
generated early in the build process. However, that's not guaranteed and
we did notice that on occasions plugins would fail to build.

Differential Revision: https://reviews.llvm.org/D120999
2022-04-22 09:18:04 +00:00

665 lines
22 KiB
C++

//===--- 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/Bridge.h"
#include "flang/Lower/PFTBuilder.h"
#include "flang/Lower/Support/Verifier.h"
#include "flang/Optimizer/Support/FIRContext.h"
#include "flang/Optimizer/Support/InitFIR.h"
#include "flang/Optimizer/Support/KindMapping.h"
#include "flang/Optimizer/Support/Utils.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 "mlir/IR/Dialect.h"
#include "mlir/Pass/PassManager.h"
#include "mlir/Target/LLVMIR/ModuleTranslation.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/Bitcode/BitcodeWriterPass.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Target/TargetMachine.h"
#include <clang/Basic/Diagnostic.h>
#include <memory>
using namespace Fortran::frontend;
//===----------------------------------------------------------------------===//
// Custom BeginSourceFileAction
//===----------------------------------------------------------------------===//
bool PrescanAction::BeginSourceFileAction() { return RunPrescan(); }
bool PrescanAndParseAction::BeginSourceFileAction() {
return RunPrescan() && RunParse();
}
bool PrescanAndSemaAction::BeginSourceFileAction() {
return RunPrescan() && RunParse() && RunSemanticChecks() &&
GenerateRtTypeTables();
}
bool PrescanAndSemaDebugAction::BeginSourceFileAction() {
// This is a "debug" action for development purposes. To facilitate this, the
// semantic checks are made to succeed unconditionally to prevent this action
// from exiting early (i.e. in the presence of semantic errors). We should
// never do this in actions intended for end-users or otherwise regular
// compiler workflows!
return RunPrescan() && RunParse() && (RunSemanticChecks() || true) &&
(GenerateRtTypeTables() || true);
}
bool CodeGenAction::BeginSourceFileAction() {
bool res = RunPrescan() && RunParse() && RunSemanticChecks();
if (!res)
return res;
CompilerInstance &ci = this->instance();
// Load the MLIR dialects required by Flang
mlir::DialectRegistry registry;
mlirCtx = std::make_unique<mlir::MLIRContext>(registry);
fir::support::registerNonCodegenDialects(registry);
fir::support::loadNonCodegenDialects(*mlirCtx);
// Create a LoweringBridge
const common::IntrinsicTypeDefaultKinds &defKinds =
ci.invocation().semanticsContext().defaultKinds();
fir::KindMapping kindMap(mlirCtx.get(),
llvm::ArrayRef<fir::KindTy>{fir::fromDefaultKinds(defKinds)});
lower::LoweringBridge lb = Fortran::lower::LoweringBridge::create(*mlirCtx,
defKinds, ci.invocation().semanticsContext().intrinsics(),
ci.parsing().allCooked(), ci.invocation().targetOpts().triple, kindMap);
// Create a parse tree and lower it to FIR
Fortran::parser::Program &parseTree{*ci.parsing().parseTree()};
lb.lower(parseTree, ci.invocation().semanticsContext());
mlirModule = std::make_unique<mlir::ModuleOp>(lb.getModule());
// Run the default passes.
mlir::PassManager pm(mlirCtx.get(), mlir::OpPassManager::Nesting::Implicit);
pm.enableVerifier(/*verifyPasses=*/true);
pm.addPass(std::make_unique<Fortran::lower::VerifierPass>());
if (mlir::failed(pm.run(*mlirModule))) {
unsigned diagID =
ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error,
"verification of lowering to FIR failed");
ci.diagnostics().Report(diagID);
return false;
}
return true;
}
//===----------------------------------------------------------------------===//
// Custom ExecuteAction
//===----------------------------------------------------------------------===//
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};
// Format or dump the prescanner's output
CompilerInstance &ci = this->instance();
if (ci.invocation().preprocessorOpts().noReformat) {
ci.parsing().DumpCookedChars(outForPP);
} else {
ci.parsing().EmitPreprocessedSource(
outForPP, !ci.invocation().preprocessorOpts().noLineDirectives);
}
// Print diagnostics from the prescanner
ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
// 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;
}
// Create a file and save the preprocessed output there
std::unique_ptr<llvm::raw_pwrite_stream> os{ci.CreateDefaultOutputFile(
/*Binary=*/true, /*InFile=*/GetCurrentFileOrBufferName())};
if (!os) {
return;
}
(*os) << outForPP.str();
}
void DebugDumpProvenanceAction::ExecuteAction() {
this->instance().parsing().DumpProvenance(llvm::outs());
}
void ParseSyntaxOnlyAction::ExecuteAction() {
}
void DebugUnparseNoSemaAction::ExecuteAction() {
auto &invoc = this->instance().invocation();
auto &parseTree{instance().parsing().parseTree()};
// TODO: Options should come from CompilerInvocation
Unparse(llvm::outs(), *parseTree,
/*encoding=*/Fortran::parser::Encoding::UTF_8,
/*capitalizeKeywords=*/true, /*backslashEscapes=*/false,
/*preStatement=*/nullptr,
invoc.useAnalyzedObjectsForUnparse() ? &invoc.asFortran() : nullptr);
}
void DebugUnparseAction::ExecuteAction() {
auto &invoc = this->instance().invocation();
auto &parseTree{instance().parsing().parseTree()};
CompilerInstance &ci = this->instance();
auto os{ci.CreateDefaultOutputFile(
/*Binary=*/false, /*InFile=*/GetCurrentFileOrBufferName())};
// TODO: Options should come from CompilerInvocation
Unparse(*os, *parseTree,
/*encoding=*/Fortran::parser::Encoding::UTF_8,
/*capitalizeKeywords=*/true, /*backslashEscapes=*/false,
/*preStatement=*/nullptr,
invoc.useAnalyzedObjectsForUnparse() ? &invoc.asFortran() : nullptr);
// Report fatal semantic errors
reportFatalSemanticErrors();
}
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();
}
void DebugDumpSymbolsAction::ExecuteAction() {
CompilerInstance &ci = this->instance();
if (!ci.getRtTyTables().schemata) {
unsigned DiagID =
ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error,
"could not find module file for __fortran_type_info");
ci.diagnostics().Report(DiagID);
llvm::errs() << "\n";
return;
}
// Dump symbols
ci.semantics().DumpSymbols(llvm::outs());
}
void DebugDumpAllAction::ExecuteAction() {
CompilerInstance &ci = this->instance();
// Dump parse tree
auto &parseTree{instance().parsing().parseTree()};
llvm::outs() << "========================";
llvm::outs() << " Flang: parse tree dump ";
llvm::outs() << "========================\n";
Fortran::parser::DumpTree(
llvm::outs(), parseTree, &ci.invocation().asFortran());
if (!ci.getRtTyTables().schemata) {
unsigned DiagID =
ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error,
"could not find module file for __fortran_type_info");
ci.diagnostics().Report(DiagID);
llvm::errs() << "\n";
return;
}
// Dump symbols
llvm::outs() << "=====================";
llvm::outs() << " Flang: symbols dump ";
llvm::outs() << "=====================\n";
ci.semantics().DumpSymbols(llvm::outs());
}
void DebugDumpParseTreeNoSemaAction::ExecuteAction() {
auto &parseTree{instance().parsing().parseTree()};
// Dump parse tree
Fortran::parser::DumpTree(
llvm::outs(), parseTree, &this->instance().invocation().asFortran());
}
void DebugDumpParseTreeAction::ExecuteAction() {
auto &parseTree{instance().parsing().parseTree()};
// Dump parse tree
Fortran::parser::DumpTree(
llvm::outs(), parseTree, &this->instance().invocation().asFortran());
// Report fatal semantic errors
reportFatalSemanticErrors();
}
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()) {
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 GetDefinitionAction::ExecuteAction() {
CompilerInstance &ci = this->instance();
// Report and exit if fatal semantic errors are present
if (reportFatalSemanticErrors()) {
return;
}
parser::AllCookedSources &cs = ci.allCookedSources();
unsigned diagID = ci.diagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "Symbol not found");
auto gdv = ci.invocation().frontendOpts().getDefVals;
auto charBlock{cs.GetCharBlockFromLineAndColumns(
gdv.line, gdv.startColumn, gdv.endColumn)};
if (!charBlock) {
ci.diagnostics().Report(diagID);
return;
}
llvm::outs() << "String range: >" << charBlock->ToString() << "<\n";
auto *symbol{ci.invocation()
.semanticsContext()
.FindScope(*charBlock)
.FindSymbol(*charBlock)};
if (!symbol) {
ci.diagnostics().Report(diagID);
return;
}
llvm::outs() << "Found symbol name: " << symbol->name().ToString() << "\n";
auto sourceInfo{cs.GetSourcePositionRange(symbol->name())};
if (!sourceInfo) {
llvm_unreachable(
"Failed to obtain SourcePosition."
"TODO: Please, write a test and replace this with a diagnostic!");
return;
}
llvm::outs() << "Found symbol name: " << symbol->name().ToString() << "\n";
llvm::outs() << symbol->name().ToString() << ": "
<< sourceInfo->first.file.path() << ", "
<< sourceInfo->first.line << ", " << sourceInfo->first.column
<< "-" << sourceInfo->second.column << "\n";
}
void GetSymbolsSourcesAction::ExecuteAction() {
CompilerInstance &ci = this->instance();
// Report and exit if fatal semantic errors are present
if (reportFatalSemanticErrors()) {
return;
}
ci.semantics().DumpSymbolsSources(llvm::outs());
}
#include "flang/Tools/CLOptions.inc"
// Lower the previously generated MLIR module into an LLVM IR module
void CodeGenAction::GenerateLLVMIR() {
assert(mlirModule && "The MLIR module has not been generated yet.");
CompilerInstance &ci = this->instance();
fir::support::loadDialects(*mlirCtx);
fir::support::registerLLVMTranslation(*mlirCtx);
// Set-up the MLIR pass manager
mlir::PassManager pm(mlirCtx.get(), mlir::OpPassManager::Nesting::Implicit);
pm.addPass(std::make_unique<Fortran::lower::VerifierPass>());
pm.enableVerifier(/*verifyPasses=*/true);
// Create the pass pipeline
fir::createMLIRToLLVMPassPipeline(pm);
mlir::applyPassManagerCLOptions(pm);
// Run the pass manager
if (!mlir::succeeded(pm.run(*mlirModule))) {
unsigned diagID = ci.diagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "Lowering to LLVM IR failed");
ci.diagnostics().Report(diagID);
}
// Translate to LLVM IR
llvm::Optional<llvm::StringRef> moduleName = mlirModule->getName();
llvmCtx = std::make_unique<llvm::LLVMContext>();
llvmModule = mlir::translateModuleToLLVMIR(
*mlirModule, *llvmCtx, moduleName ? *moduleName : "FIRModule");
if (!llvmModule) {
unsigned diagID = ci.diagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "failed to create the LLVM module");
ci.diagnostics().Report(diagID);
return;
}
}
void EmitLLVMAction::ExecuteAction() {
CompilerInstance &ci = this->instance();
GenerateLLVMIR();
// If set, use the predefined outupt stream to print the generated module.
if (!ci.IsOutputStreamNull()) {
llvmModule->print(
ci.GetOutputStream(), /*AssemblyAnnotationWriter=*/nullptr);
return;
}
// No predefined output stream was set. Create an output file and dump the
// generated module there.
std::unique_ptr<llvm::raw_ostream> os = ci.CreateDefaultOutputFile(
/*Binary=*/false, /*InFile=*/GetCurrentFileOrBufferName(), "ll");
if (!os) {
unsigned diagID = ci.diagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "failed to create the output file");
ci.diagnostics().Report(diagID);
return;
}
llvmModule->print(*os, /*AssemblyAnnotationWriter=*/nullptr);
}
void EmitLLVMBitcodeAction::ExecuteAction() {
CompilerInstance &ci = this->instance();
// Generate an LLVM module if it's not already present (it will already be
// present if the input file is an LLVM IR/BC file).
if (!llvmModule)
GenerateLLVMIR();
// Create and configure `Target`
std::string error;
std::string theTriple = llvmModule->getTargetTriple();
const llvm::Target *theTarget =
llvm::TargetRegistry::lookupTarget(theTriple, error);
assert(theTarget && "Failed to create Target");
// Create and configure `TargetMachine`
std::unique_ptr<llvm::TargetMachine> TM(
theTarget->createTargetMachine(theTriple, /*CPU=*/"",
/*Features=*/"", llvm::TargetOptions(), llvm::None));
assert(TM && "Failed to create TargetMachine");
llvmModule->setDataLayout(TM->createDataLayout());
// Generate an output file
std::unique_ptr<llvm::raw_ostream> os = ci.CreateDefaultOutputFile(
/*Binary=*/true, /*InFile=*/GetCurrentFileOrBufferName(), "bc");
if (!os) {
unsigned diagID = ci.diagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "failed to create the output file");
ci.diagnostics().Report(diagID);
return;
}
// Set-up the pass manager
llvm::ModulePassManager MPM;
llvm::ModuleAnalysisManager MAM;
llvm::PassBuilder PB(TM.get());
PB.registerModuleAnalyses(MAM);
MPM.addPass(llvm::BitcodeWriterPass(*os));
// Run the passes
MPM.run(*llvmModule, MAM);
}
void EmitMLIRAction::ExecuteAction() {
CompilerInstance &ci = this->instance();
// Print the output. If a pre-defined output stream exists, dump the MLIR
// content there.
if (!ci.IsOutputStreamNull()) {
mlirModule->print(ci.GetOutputStream());
return;
}
// ... otherwise, print to a file.
std::unique_ptr<llvm::raw_pwrite_stream> os{ci.CreateDefaultOutputFile(
/*Binary=*/true, /*InFile=*/GetCurrentFileOrBufferName(), "mlir")};
if (!os) {
unsigned diagID = ci.diagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "failed to create the output file");
ci.diagnostics().Report(diagID);
return;
}
mlirModule->print(*os);
}
void BackendAction::ExecuteAction() {
CompilerInstance &ci = this->instance();
// Generate an LLVM module if it's not already present (it will already be
// present if the input file is an LLVM IR/BC file).
if (!llvmModule)
GenerateLLVMIR();
// Create `Target`
std::string error;
const std::string &theTriple = llvmModule->getTargetTriple();
const llvm::Target *theTarget =
llvm::TargetRegistry::lookupTarget(theTriple, error);
// TODO: Make this a diagnostic once `flang-new` can consume LLVM IR files
// (in which users could use unsupported triples)
assert(theTarget && "Failed to create Target");
// Create `TargetMachine`
std::unique_ptr<llvm::TargetMachine> TM(
theTarget->createTargetMachine(theTriple, /*CPU=*/"",
/*Features=*/"", llvm::TargetOptions(), llvm::None));
assert(TM && "Failed to create TargetMachine");
llvmModule->setDataLayout(TM->createDataLayout());
// If the output stream is a file, generate it and define the corresponding
// output stream. If a pre-defined output stream is available, we will use
// that instead.
//
// NOTE: `os` is a smart pointer that will be destroyed at the end of this
// method. However, it won't be written to until `CodeGenPasses` is
// destroyed. By defining `os` before `CodeGenPasses`, we make sure that the
// output stream won't be destroyed before it is written to. This only
// applies when an output file is used (i.e. there is no pre-defined output
// stream).
// TODO: Revisit once the new PM is ready (i.e. when `CodeGenPasses` is
// updated to use it).
std::unique_ptr<llvm::raw_pwrite_stream> os;
if (ci.IsOutputStreamNull()) {
// Get the output buffer/file
switch (action) {
case BackendActionTy::Backend_EmitAssembly:
os = ci.CreateDefaultOutputFile(
/*Binary=*/false, /*InFile=*/GetCurrentFileOrBufferName(), "s");
break;
case BackendActionTy::Backend_EmitObj:
os = ci.CreateDefaultOutputFile(
/*Binary=*/true, /*InFile=*/GetCurrentFileOrBufferName(), "o");
break;
}
if (!os) {
unsigned diagID = ci.diagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "failed to create the output file");
ci.diagnostics().Report(diagID);
return;
}
}
// Create an LLVM code-gen pass pipeline. Currently only the legacy pass
// manager is supported.
// TODO: Switch to the new PM once it's available in the backend.
llvm::legacy::PassManager CodeGenPasses;
CodeGenPasses.add(
createTargetTransformInfoWrapperPass(TM->getTargetIRAnalysis()));
llvm::Triple triple(theTriple);
std::unique_ptr<llvm::TargetLibraryInfoImpl> TLII =
std::make_unique<llvm::TargetLibraryInfoImpl>(triple);
assert(TLII && "Failed to create TargetLibraryInfo");
CodeGenPasses.add(new llvm::TargetLibraryInfoWrapperPass(*TLII));
llvm::CodeGenFileType cgft = (action == BackendActionTy::Backend_EmitAssembly)
? llvm::CodeGenFileType::CGFT_AssemblyFile
: llvm::CodeGenFileType::CGFT_ObjectFile;
if (TM->addPassesToEmitFile(CodeGenPasses,
ci.IsOutputStreamNull() ? *os : ci.GetOutputStream(), nullptr,
cgft)) {
unsigned diagID =
ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error,
"emission of this file type is not supported");
ci.diagnostics().Report(diagID);
return;
}
// Run the code-gen passes
CodeGenPasses.run(*llvmModule);
}
void InitOnlyAction::ExecuteAction() {
CompilerInstance &ci = this->instance();
unsigned DiagID =
ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Warning,
"Use `-init-only` for testing purposes only");
ci.diagnostics().Report(DiagID);
}
void PluginParseTreeAction::ExecuteAction() {}
void DebugDumpPFTAction::ExecuteAction() {
CompilerInstance &ci = this->instance();
if (auto ast = Fortran::lower::createPFT(
*ci.parsing().parseTree(), ci.semantics().context())) {
Fortran::lower::dumpPFT(llvm::outs(), *ast);
return;
}
unsigned DiagID = ci.diagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "Pre FIR Tree is NULL.");
ci.diagnostics().Report(DiagID);
}
Fortran::parser::Parsing &PluginParseTreeAction::getParsing() {
return instance().parsing();
}
std::unique_ptr<llvm::raw_pwrite_stream>
PluginParseTreeAction::createOutputFile(llvm::StringRef extension = "") {
std::unique_ptr<llvm::raw_pwrite_stream> OS{
instance().CreateDefaultOutputFile(
/*Binary=*/false, /*InFile=*/GetCurrentFileOrBufferName(),
extension)};
return OS;
}