| //===--- 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); |
| } |