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#include "llvm/TargetParser/Host.h"
17#include "llvm/TargetParser/Triple.h"
18
19#include "gtest/gtest.h"
20
21using namespace Fortran::frontend;
22
23namespace {
24
25class FrontendActionTest : public ::testing::Test {
26protected:
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
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 args&: inputFileName, args&: ec, args: 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(result&: 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 // Set-up default target triple and initialize LLVM Targets so that the
68 // target data layout can be passed to the frontend.
69 invoc->getTargetOpts().triple =
70 llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple());
71 llvm::InitializeAllTargets();
72 llvm::InitializeAllTargetMCs();
73
74 compInst.setInvocation(std::move(invoc));
75 compInst.getFrontendOpts().inputs.push_back(
76 FrontendInputFile(inputFilePath, Language::Fortran));
77 }
78
79 void TearDown() override {
80 // Clear the input file.
81 llvm::sys::fs::remove(path: inputFileName);
82
83 // Clear the output files.
84 // Note that these tests use an output buffer (as opposed to an output
85 // file), hence there are no physical output files to delete and
86 // `EraseFiles` is set to `false`. Also, some actions (e.g.
87 // `ParseSyntaxOnly`) don't generated output. In such cases there's no
88 // output to clear and `ClearOutputFile` returns immediately.
89 compInst.clearOutputFiles(/*EraseFiles=*/false);
90 }
91};
92
93TEST_F(FrontendActionTest, TestInputOutput) {
94 // Populate the input file with the pre-defined input and flush it.
95 *(inputFileOs) << "End Program arithmetic";
96 inputFileOs.reset();
97
98 // Set-up the action kind.
99 compInst.getInvocation().getFrontendOpts().programAction = InputOutputTest;
100
101 // Set-up the output stream. Using output buffer wrapped as an output
102 // stream, as opposed to an actual file (or a file descriptor).
103 llvm::SmallVector<char, 256> outputFileBuffer;
104 std::unique_ptr<llvm::raw_pwrite_stream> outputFileStream(
105 new llvm::raw_svector_ostream(outputFileBuffer));
106 compInst.setOutputStream(std::move(outputFileStream));
107
108 // Execute the action.
109 bool success = executeCompilerInvocation(&compInst);
110
111 // Validate the expected output.
112 EXPECT_TRUE(success);
113 EXPECT_TRUE(!outputFileBuffer.empty());
114 EXPECT_TRUE(llvm::StringRef(outputFileBuffer.data())
115 .starts_with("End Program arithmetic"));
116}
117
118TEST_F(FrontendActionTest, PrintPreprocessedInput) {
119 // Populate the input file with the pre-defined input and flush it.
120 *(inputFileOs) << "#ifdef NEW\n"
121 << " Program A \n"
122 << "#else\n"
123 << " Program B\n"
124 << "#endif";
125 inputFileOs.reset();
126
127 // Set-up the action kind.
128 compInst.getInvocation().getFrontendOpts().programAction =
129 PrintPreprocessedInput;
130 compInst.getInvocation().getPreprocessorOpts().noReformat = true;
131
132 // Set-up the output stream. We are using output buffer wrapped as an output
133 // stream, as opposed to an actual file (or a file descriptor).
134 llvm::SmallVector<char, 256> outputFileBuffer;
135 std::unique_ptr<llvm::raw_pwrite_stream> outputFileStream(
136 new llvm::raw_svector_ostream(outputFileBuffer));
137 compInst.setOutputStream(std::move(outputFileStream));
138
139 // Execute the action.
140 bool success = executeCompilerInvocation(&compInst);
141
142 // Validate the expected output.
143 EXPECT_TRUE(success);
144 EXPECT_TRUE(!outputFileBuffer.empty());
145 EXPECT_TRUE(
146 llvm::StringRef(outputFileBuffer.data()).starts_with("program b\n"));
147}
148
149TEST_F(FrontendActionTest, ParseSyntaxOnly) {
150 // Populate the input file with the pre-defined input and flush it.
151 *(inputFileOs) << "IF (A > 0.0) IF (B < 0.0) A = LOG (A)\n"
152 << "END";
153 inputFileOs.reset();
154
155 // Set-up the action kind.
156 compInst.getInvocation().getFrontendOpts().programAction = ParseSyntaxOnly;
157
158 // Set-up the output stream for the semantic diagnostics.
159 llvm::SmallVector<char, 256> outputDiagBuffer;
160 std::unique_ptr<llvm::raw_pwrite_stream> outputStream(
161 new llvm::raw_svector_ostream(outputDiagBuffer));
162 compInst.setSemaOutputStream(std::move(outputStream));
163
164 // Execute the action.
165 bool success = executeCompilerInvocation(&compInst);
166
167 // Validate the expected output.
168 EXPECT_FALSE(success);
169 EXPECT_TRUE(!outputDiagBuffer.empty());
170 EXPECT_TRUE(
171 llvm::StringRef(outputDiagBuffer.data())
172 .contains(
173 ":1:14: error: IF statement is not allowed in IF statement\n"));
174}
175
176TEST_F(FrontendActionTest, EmitLLVM) {
177 // Populate the input file with the pre-defined input and flush it.
178 *(inputFileOs) << "end program";
179 inputFileOs.reset();
180
181 // Set-up the action kind.
182 compInst.getInvocation().getFrontendOpts().programAction = EmitLLVM;
183
184 // Initialise LLVM backend
185 llvm::InitializeAllAsmPrinters();
186
187 // Set-up the output stream. We are using output buffer wrapped as an output
188 // stream, as opposed to an actual file (or a file descriptor).
189 llvm::SmallVector<char> outputFileBuffer;
190 std::unique_ptr<llvm::raw_pwrite_stream> outputFileStream(
191 new llvm::raw_svector_ostream(outputFileBuffer));
192 compInst.setOutputStream(std::move(outputFileStream));
193
194 // Execute the action.
195 bool success = executeCompilerInvocation(&compInst);
196
197 // Validate the expected output.
198 EXPECT_TRUE(success);
199 EXPECT_TRUE(!outputFileBuffer.empty());
200
201 EXPECT_TRUE(llvm::StringRef(outputFileBuffer.begin(), outputFileBuffer.size())
202 .contains("define void @_QQmain()"));
203}
204
205TEST_F(FrontendActionTest, EmitAsm) {
206 // Populate the input file with the pre-defined input and flush it.
207 *(inputFileOs) << "end program";
208 inputFileOs.reset();
209
210 // Set-up the action kind.
211 compInst.getInvocation().getFrontendOpts().programAction = EmitAssembly;
212
213 // Initialise LLVM backend
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.begin(), outputFileBuffer.size())
231 .contains("_QQmain"));
232}
233} // namespace
234

source code of flang/unittests/Frontend/FrontendActionTest.cpp