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