1 //===- unittests/Frontend/FrontendActionTest.cpp FrontendAction tests-----===// 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/CompilerInstance.h" 10 #include "flang/Frontend/CompilerInvocation.h" 11 #include "flang/Frontend/FrontendOptions.h" 12 #include "flang/FrontendTool/Utils.h" 13 #include "llvm/Support/FileSystem.h" 14 #include "llvm/Support/TargetSelect.h" 15 #include "llvm/Support/raw_ostream.h" 16 17 #include "gtest/gtest.h" 18 19 using namespace Fortran::frontend; 20 21 namespace { 22 23 class FrontendActionTest : public ::testing::Test { 24 protected: 25 // AllSources (which is used to manage files inside every compiler 26 // instance), works with paths. So we need a filename and a path for the 27 // input file. 28 // TODO: We could use `-` for inputFilePath_, but then we'd need a way to 29 // write to stdin that's then read by AllSources. Ideally, AllSources should 30 // be capable of reading from any stream. 31 std::string inputFileName_; 32 std::string inputFilePath_; 33 // The output stream for the input file. Use this to populate the input. 34 std::unique_ptr<llvm::raw_fd_ostream> inputFileOs_; 35 36 std::error_code ec_; 37 38 CompilerInstance compInst_; 39 std::shared_ptr<CompilerInvocation> invocation_; 40 41 void SetUp() override { 42 // Generate a unique test file name. 43 const testing::TestInfo *const test_info = 44 testing::UnitTest::GetInstance()->current_test_info(); 45 inputFileName_ = std::string(test_info->name()) + "_test-file.f90"; 46 47 // Create the input file stream. Note that this stream is populated 48 // separately in every test (i.e. the input is test specific). 49 inputFileOs_ = std::make_unique<llvm::raw_fd_ostream>( 50 inputFileName_, ec_, llvm::sys::fs::OF_None); 51 if (ec_) 52 FAIL() << "Failed to create the input file"; 53 54 // Get the path of the input file. 55 llvm::SmallString<256> cwd; 56 if (std::error_code ec_ = llvm::sys::fs::current_path(cwd)) 57 FAIL() << "Failed to obtain the current working directory"; 58 inputFilePath_ = cwd.c_str(); 59 inputFilePath_ += "/" + inputFileName_; 60 61 // Prepare the compiler (CompilerInvocation + CompilerInstance) 62 compInst_.CreateDiagnostics(); 63 invocation_ = std::make_shared<CompilerInvocation>(); 64 65 compInst_.set_invocation(std::move(invocation_)); 66 compInst_.frontendOpts().inputs.push_back( 67 FrontendInputFile(inputFilePath_, Language::Fortran)); 68 } 69 70 void TearDown() override { 71 // Clear the input file. 72 llvm::sys::fs::remove(inputFileName_); 73 74 // Clear the output files. 75 // Note that these tests use an output buffer (as opposed to an output 76 // file), hence there are no physical output files to delete and 77 // `EraseFiles` is set to `false`. Also, some actions (e.g. 78 // `ParseSyntaxOnly`) don't generated output. In such cases there's no 79 // output to clear and `ClearOutputFile` returns immediately. 80 compInst_.ClearOutputFiles(/*EraseFiles=*/false); 81 } 82 }; 83 84 TEST_F(FrontendActionTest, TestInputOutput) { 85 // Populate the input file with the pre-defined input and flush it. 86 *(inputFileOs_) << "End Program arithmetic"; 87 inputFileOs_.reset(); 88 89 // Set-up the action kind. 90 compInst_.invocation().frontendOpts().programAction = InputOutputTest; 91 92 // Set-up the output stream. Using output buffer wrapped as an output 93 // stream, as opposed to an actual file (or a file descriptor). 94 llvm::SmallVector<char, 256> outputFileBuffer; 95 std::unique_ptr<llvm::raw_pwrite_stream> outputFileStream( 96 new llvm::raw_svector_ostream(outputFileBuffer)); 97 compInst_.set_outputStream(std::move(outputFileStream)); 98 99 // Execute the action. 100 bool success = ExecuteCompilerInvocation(&compInst_); 101 102 // Validate the expected output. 103 EXPECT_TRUE(success); 104 EXPECT_TRUE(!outputFileBuffer.empty()); 105 EXPECT_TRUE(llvm::StringRef(outputFileBuffer.data()) 106 .startswith("End Program arithmetic")); 107 } 108 109 TEST_F(FrontendActionTest, PrintPreprocessedInput) { 110 // Populate the input file with the pre-defined input and flush it. 111 *(inputFileOs_) << "#ifdef NEW\n" 112 << " Program A \n" 113 << "#else\n" 114 << " Program B\n" 115 << "#endif"; 116 inputFileOs_.reset(); 117 118 // Set-up the action kind. 119 compInst_.invocation().frontendOpts().programAction = PrintPreprocessedInput; 120 compInst_.invocation().preprocessorOpts().noReformat = true; 121 122 // Set-up the output stream. We are using output buffer wrapped as an output 123 // stream, as opposed to an actual file (or a file descriptor). 124 llvm::SmallVector<char, 256> outputFileBuffer; 125 std::unique_ptr<llvm::raw_pwrite_stream> outputFileStream( 126 new llvm::raw_svector_ostream(outputFileBuffer)); 127 compInst_.set_outputStream(std::move(outputFileStream)); 128 129 // Execute the action. 130 bool success = ExecuteCompilerInvocation(&compInst_); 131 132 // Validate the expected output. 133 EXPECT_TRUE(success); 134 EXPECT_TRUE(!outputFileBuffer.empty()); 135 EXPECT_TRUE( 136 llvm::StringRef(outputFileBuffer.data()).startswith("program b\n")); 137 } 138 139 TEST_F(FrontendActionTest, ParseSyntaxOnly) { 140 // Populate the input file with the pre-defined input and flush it. 141 *(inputFileOs_) << "IF (A > 0.0) IF (B < 0.0) A = LOG (A)\n" 142 << "END"; 143 inputFileOs_.reset(); 144 145 // Set-up the action kind. 146 compInst_.invocation().frontendOpts().programAction = ParseSyntaxOnly; 147 148 // Set-up the output stream for the semantic diagnostics. 149 llvm::SmallVector<char, 256> outputDiagBuffer; 150 std::unique_ptr<llvm::raw_pwrite_stream> outputStream( 151 new llvm::raw_svector_ostream(outputDiagBuffer)); 152 compInst_.set_semaOutputStream(std::move(outputStream)); 153 154 // Execute the action. 155 bool success = ExecuteCompilerInvocation(&compInst_); 156 157 // Validate the expected output. 158 EXPECT_FALSE(success); 159 EXPECT_TRUE(!outputDiagBuffer.empty()); 160 EXPECT_TRUE( 161 llvm::StringRef(outputDiagBuffer.data()) 162 .contains( 163 ":1:14: error: IF statement is not allowed in IF statement\n")); 164 } 165 166 TEST_F(FrontendActionTest, EmitLLVM) { 167 // Populate the input file with the pre-defined input and flush it. 168 *(inputFileOs_) << "end program"; 169 inputFileOs_.reset(); 170 171 // Set-up the action kind. 172 compInst_.invocation().frontendOpts().programAction = EmitLLVM; 173 compInst_.invocation().preprocessorOpts().noReformat = true; 174 175 // Set-up the output stream. We are using output buffer wrapped as an output 176 // stream, as opposed to an actual file (or a file descriptor). 177 llvm::SmallVector<char> outputFileBuffer; 178 std::unique_ptr<llvm::raw_pwrite_stream> outputFileStream( 179 new llvm::raw_svector_ostream(outputFileBuffer)); 180 compInst_.set_outputStream(std::move(outputFileStream)); 181 182 // Execute the action. 183 bool success = ExecuteCompilerInvocation(&compInst_); 184 185 // Validate the expected output. 186 EXPECT_TRUE(success); 187 EXPECT_TRUE(!outputFileBuffer.empty()); 188 189 EXPECT_TRUE(llvm::StringRef(outputFileBuffer.data()) 190 .contains("define void @_QQmain()")); 191 } 192 193 TEST_F(FrontendActionTest, EmitAsm) { 194 // Populate the input file with the pre-defined input and flush it. 195 *(inputFileOs_) << "end program"; 196 inputFileOs_.reset(); 197 198 // Set-up the action kind. 199 compInst_.invocation().frontendOpts().programAction = EmitAssembly; 200 compInst_.invocation().preprocessorOpts().noReformat = true; 201 202 // Initialise LLVM backend 203 llvm::InitializeAllTargets(); 204 llvm::InitializeAllTargetMCs(); 205 llvm::InitializeAllAsmPrinters(); 206 207 // Set-up the output stream. We are using output buffer wrapped as an output 208 // stream, as opposed to an actual file (or a file descriptor). 209 llvm::SmallVector<char, 256> outputFileBuffer; 210 std::unique_ptr<llvm::raw_pwrite_stream> outputFileStream( 211 new llvm::raw_svector_ostream(outputFileBuffer)); 212 compInst_.set_outputStream(std::move(outputFileStream)); 213 214 // Execute the action. 215 bool success = ExecuteCompilerInvocation(&compInst_); 216 217 // Validate the expected output. 218 EXPECT_TRUE(success); 219 EXPECT_TRUE(!outputFileBuffer.empty()); 220 221 EXPECT_TRUE(llvm::StringRef(outputFileBuffer.data()).contains("_QQmain")); 222 } 223 } // namespace 224