xref: /llvm-project/flang/unittests/Frontend/FrontendActionTest.cpp (revision 5d15f606da4cb4346465956d935a2842f8e86200)
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 
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     // 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 
TearDown()79   void TearDown() override {
80     // Clear the input file.
81     llvm::sys::fs::remove(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 
TEST_F(FrontendActionTest,TestInputOutput)93 TEST_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 
TEST_F(FrontendActionTest,PrintPreprocessedInput)118 TEST_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 
TEST_F(FrontendActionTest,ParseSyntaxOnly)149 TEST_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 
TEST_F(FrontendActionTest,EmitLLVM)176 TEST_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 
TEST_F(FrontendActionTest,EmitAsm)205 TEST_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