1 //===--- FrontendActions.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 #include "flang/Frontend/FrontendActions.h" 10 #include "flang/Common/default-kinds.h" 11 #include "flang/Frontend/CompilerInstance.h" 12 #include "flang/Parser/parsing.h" 13 #include "flang/Parser/provenance.h" 14 #include "flang/Parser/source.h" 15 #include "flang/Semantics/semantics.h" 16 17 using namespace Fortran::frontend; 18 19 bool PrescanAction::BeginSourceFileAction(CompilerInstance &c1) { 20 CompilerInstance &ci = this->instance(); 21 22 std::string currentInputPath{GetCurrentFileOrBufferName()}; 23 24 Fortran::parser::Options parserOptions = ci.invocation().fortranOpts(); 25 26 if (ci.invocation().frontendOpts().fortranForm_ == FortranForm::Unknown) { 27 // Switch between fixed and free form format based on the input file 28 // extension. 29 // 30 // Ideally we should have all Fortran options set before entering this 31 // method (i.e. before processing any specific input files). However, we 32 // can't decide between fixed and free form based on the file extension 33 // earlier than this. 34 parserOptions.isFixedForm = currentInput().IsFixedForm(); 35 } 36 37 // Prescan. In case of failure, report and return. 38 ci.parsing().Prescan(currentInputPath, parserOptions); 39 40 if (ci.parsing().messages().AnyFatalError()) { 41 const unsigned diagID = ci.diagnostics().getCustomDiagID( 42 clang::DiagnosticsEngine::Error, "Could not scan %0"); 43 ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName(); 44 ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); 45 46 return false; 47 } 48 49 return true; 50 } 51 52 void InputOutputTestAction::ExecuteAction() { 53 CompilerInstance &ci = instance(); 54 55 // Create a stream for errors 56 std::string buf; 57 llvm::raw_string_ostream error_stream{buf}; 58 59 // Read the input file 60 Fortran::parser::AllSources &allSources{ci.allSources()}; 61 std::string path{GetCurrentFileOrBufferName()}; 62 const Fortran::parser::SourceFile *sf; 63 if (path == "-") 64 sf = allSources.ReadStandardInput(error_stream); 65 else 66 sf = allSources.Open(path, error_stream, std::optional<std::string>{"."s}); 67 llvm::ArrayRef<char> fileContent = sf->content(); 68 69 // Output file descriptor to receive the contents of the input file. 70 std::unique_ptr<llvm::raw_ostream> os; 71 72 // Copy the contents from the input file to the output file 73 if (!ci.IsOutputStreamNull()) { 74 // An output stream (outputStream_) was set earlier 75 ci.WriteOutputStream(fileContent.data()); 76 } else { 77 // No pre-set output stream - create an output file 78 os = ci.CreateDefaultOutputFile( 79 /*binary=*/true, GetCurrentFileOrBufferName(), "txt"); 80 if (!os) 81 return; 82 (*os) << fileContent.data(); 83 } 84 } 85 86 void PrintPreprocessedAction::ExecuteAction() { 87 std::string buf; 88 llvm::raw_string_ostream outForPP{buf}; 89 90 // Run the preprocessor 91 CompilerInstance &ci = this->instance(); 92 ci.parsing().DumpCookedChars(outForPP); 93 94 // If a pre-defined output stream exists, dump the preprocessed content there 95 if (!ci.IsOutputStreamNull()) { 96 // Send the output to the pre-defined output buffer. 97 ci.WriteOutputStream(outForPP.str()); 98 return; 99 } 100 101 // Print diagnostics from the preprocessor 102 ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); 103 104 // Create a file and save the preprocessed output there 105 if (auto os{ci.CreateDefaultOutputFile( 106 /*Binary=*/true, /*InFile=*/GetCurrentFileOrBufferName())}) { 107 (*os) << outForPP.str(); 108 } else { 109 llvm::errs() << "Unable to create the output file\n"; 110 return; 111 } 112 } 113 114 void ParseSyntaxOnlyAction::ExecuteAction() { 115 CompilerInstance &ci = this->instance(); 116 117 // Parse. In case of failure, report and return. 118 ci.parsing().Parse(llvm::outs()); 119 120 if (ci.parsing().messages().AnyFatalError()) { 121 unsigned diagID = ci.diagnostics().getCustomDiagID( 122 clang::DiagnosticsEngine::Error, "Could not parse %0"); 123 ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName(); 124 125 ci.parsing().messages().Emit( 126 llvm::errs(), this->instance().allCookedSources()); 127 return; 128 } 129 130 // Report the diagnostics from parsing 131 ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); 132 133 auto &parseTree{*ci.parsing().parseTree()}; 134 135 // Prepare semantics 136 Fortran::semantics::Semantics semantics{ci.invocation().semanticsContext(), 137 parseTree, ci.parsing().cooked().AsCharBlock()}; 138 139 // Run semantic checks 140 semantics.Perform(); 141 142 // Report the diagnostics from the semantic checks 143 semantics.EmitMessages(ci.semaOutputStream()); 144 145 if (semantics.AnyFatalError()) { 146 unsigned DiagID = ci.diagnostics().getCustomDiagID( 147 clang::DiagnosticsEngine::Error, "Semantic errors in %0"); 148 ci.diagnostics().Report(DiagID) << GetCurrentFileOrBufferName(); 149 } 150 } 151 152 void EmitObjAction::ExecuteAction() { 153 CompilerInstance &ci = this->instance(); 154 unsigned DiagID = ci.diagnostics().getCustomDiagID( 155 clang::DiagnosticsEngine::Error, "code-generation is not available yet"); 156 ci.diagnostics().Report(DiagID); 157 } 158