xref: /llvm-project/flang/unittests/Frontend/FrontendActionTest.cpp (revision 5d15f606da4cb4346465956d935a2842f8e86200)
13338ef93Speter klausler //===- unittests/Frontend/FrontendActionTest.cpp  FrontendAction tests-----===//
2db2195bcSAndrzej Warzynski //
3db2195bcSAndrzej Warzynski // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4db2195bcSAndrzej Warzynski // See https://llvm.org/LICENSE.txt for license information.
5db2195bcSAndrzej Warzynski // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6db2195bcSAndrzej Warzynski //
7db2195bcSAndrzej Warzynski //===----------------------------------------------------------------------===//
8db2195bcSAndrzej Warzynski 
9db2195bcSAndrzej Warzynski #include "flang/Frontend/CompilerInstance.h"
1044e74c75SAndrzej Warzynski #include "flang/Frontend/CompilerInvocation.h"
11db2195bcSAndrzej Warzynski #include "flang/Frontend/FrontendOptions.h"
12db2195bcSAndrzej Warzynski #include "flang/FrontendTool/Utils.h"
13db2195bcSAndrzej Warzynski #include "llvm/Support/FileSystem.h"
1438101b4eSAndrzej Warzynski #include "llvm/Support/TargetSelect.h"
15db2195bcSAndrzej Warzynski #include "llvm/Support/raw_ostream.h"
16d768bf99SArchibald Elliott #include "llvm/TargetParser/Host.h"
1762c7f035SArchibald Elliott #include "llvm/TargetParser/Triple.h"
18db2195bcSAndrzej Warzynski 
19b564b12bSAndrzej Warzynski #include "gtest/gtest.h"
20b564b12bSAndrzej Warzynski 
21db2195bcSAndrzej Warzynski using namespace Fortran::frontend;
22db2195bcSAndrzej Warzynski 
23db2195bcSAndrzej Warzynski namespace {
24db2195bcSAndrzej Warzynski 
2544e74c75SAndrzej Warzynski class FrontendActionTest : public ::testing::Test {
2644e74c75SAndrzej Warzynski protected:
2744e74c75SAndrzej Warzynski   // AllSources (which is used to manage files inside every compiler
2844e74c75SAndrzej Warzynski   // instance), works with paths. So we need a filename and a path for the
2944e74c75SAndrzej Warzynski   // input file.
301e462fafSAndrzej Warzynski   // TODO: We could use `-` for inputFilePath, but then we'd need a way to
3144e74c75SAndrzej Warzynski   // write to stdin that's then read by AllSources. Ideally, AllSources should
3244e74c75SAndrzej Warzynski   // be capable of reading from any stream.
331e462fafSAndrzej Warzynski   std::string inputFileName;
341e462fafSAndrzej Warzynski   std::string inputFilePath;
3544e74c75SAndrzej Warzynski   // The output stream for the input file. Use this to populate the input.
361e462fafSAndrzej Warzynski   std::unique_ptr<llvm::raw_fd_ostream> inputFileOs;
37db2195bcSAndrzej Warzynski 
381e462fafSAndrzej Warzynski   std::error_code ec;
39db2195bcSAndrzej Warzynski 
401e462fafSAndrzej Warzynski   CompilerInstance compInst;
411e462fafSAndrzej Warzynski   std::shared_ptr<CompilerInvocation> invoc;
4244e74c75SAndrzej Warzynski 
SetUp()4344e74c75SAndrzej Warzynski   void SetUp() override {
4444e74c75SAndrzej Warzynski     // Generate a unique test file name.
451e462fafSAndrzej Warzynski     const testing::TestInfo *const testInfo =
4644e74c75SAndrzej Warzynski         testing::UnitTest::GetInstance()->current_test_info();
471e462fafSAndrzej Warzynski     inputFileName = std::string(testInfo->name()) + "_test-file.f90";
4844e74c75SAndrzej Warzynski 
4944e74c75SAndrzej Warzynski     // Create the input file stream. Note that this stream is populated
5044e74c75SAndrzej Warzynski     // separately in every test (i.e. the input is test specific).
511e462fafSAndrzej Warzynski     inputFileOs = std::make_unique<llvm::raw_fd_ostream>(
521e462fafSAndrzej Warzynski         inputFileName, ec, llvm::sys::fs::OF_None);
531e462fafSAndrzej Warzynski     if (ec)
5444e74c75SAndrzej Warzynski       FAIL() << "Failed to create the input file";
5544e74c75SAndrzej Warzynski 
5644e74c75SAndrzej Warzynski     // Get the path of the input file.
5744e74c75SAndrzej Warzynski     llvm::SmallString<256> cwd;
581e462fafSAndrzej Warzynski     if (std::error_code ec = llvm::sys::fs::current_path(cwd))
5944e74c75SAndrzej Warzynski       FAIL() << "Failed to obtain the current working directory";
601e462fafSAndrzej Warzynski     inputFilePath = cwd.c_str();
611e462fafSAndrzej Warzynski     inputFilePath += "/" + inputFileName;
6244e74c75SAndrzej Warzynski 
6344e74c75SAndrzej Warzynski     // Prepare the compiler (CompilerInvocation + CompilerInstance)
641e462fafSAndrzej Warzynski     compInst.createDiagnostics();
651e462fafSAndrzej Warzynski     invoc = std::make_shared<CompilerInvocation>();
6644e74c75SAndrzej Warzynski 
67e59e8488SjeanPerier     // Set-up default target triple and initialize LLVM Targets so that the
68e59e8488SjeanPerier     // target data layout can be passed to the frontend.
69e59e8488SjeanPerier     invoc->getTargetOpts().triple =
70e59e8488SjeanPerier         llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple());
71e59e8488SjeanPerier     llvm::InitializeAllTargets();
72e59e8488SjeanPerier     llvm::InitializeAllTargetMCs();
73e59e8488SjeanPerier 
741e462fafSAndrzej Warzynski     compInst.setInvocation(std::move(invoc));
751e462fafSAndrzej Warzynski     compInst.getFrontendOpts().inputs.push_back(
761e462fafSAndrzej Warzynski         FrontendInputFile(inputFilePath, Language::Fortran));
7744e74c75SAndrzej Warzynski   }
7844e74c75SAndrzej Warzynski 
TearDown()7944e74c75SAndrzej Warzynski   void TearDown() override {
8044e74c75SAndrzej Warzynski     // Clear the input file.
811e462fafSAndrzej Warzynski     llvm::sys::fs::remove(inputFileName);
8244e74c75SAndrzej Warzynski 
8344e74c75SAndrzej Warzynski     // Clear the output files.
8444e74c75SAndrzej Warzynski     // Note that these tests use an output buffer (as opposed to an output
8544e74c75SAndrzej Warzynski     // file), hence there are no physical output files to delete and
8644e74c75SAndrzej Warzynski     // `EraseFiles` is set to `false`. Also, some actions (e.g.
8744e74c75SAndrzej Warzynski     // `ParseSyntaxOnly`) don't generated output. In such cases there's no
8844e74c75SAndrzej Warzynski     // output to clear and `ClearOutputFile` returns immediately.
891e462fafSAndrzej Warzynski     compInst.clearOutputFiles(/*EraseFiles=*/false);
9044e74c75SAndrzej Warzynski   }
9144e74c75SAndrzej Warzynski };
9244e74c75SAndrzej Warzynski 
TEST_F(FrontendActionTest,TestInputOutput)93b564b12bSAndrzej Warzynski TEST_F(FrontendActionTest, TestInputOutput) {
94b564b12bSAndrzej Warzynski   // Populate the input file with the pre-defined input and flush it.
951e462fafSAndrzej Warzynski   *(inputFileOs) << "End Program arithmetic";
961e462fafSAndrzej Warzynski   inputFileOs.reset();
97b564b12bSAndrzej Warzynski 
98b564b12bSAndrzej Warzynski   // Set-up the action kind.
991e462fafSAndrzej Warzynski   compInst.getInvocation().getFrontendOpts().programAction = InputOutputTest;
100b564b12bSAndrzej Warzynski 
101b564b12bSAndrzej Warzynski   // Set-up the output stream. Using output buffer wrapped as an output
102b564b12bSAndrzej Warzynski   // stream, as opposed to an actual file (or a file descriptor).
103b564b12bSAndrzej Warzynski   llvm::SmallVector<char, 256> outputFileBuffer;
104b564b12bSAndrzej Warzynski   std::unique_ptr<llvm::raw_pwrite_stream> outputFileStream(
105b564b12bSAndrzej Warzynski       new llvm::raw_svector_ostream(outputFileBuffer));
1061e462fafSAndrzej Warzynski   compInst.setOutputStream(std::move(outputFileStream));
107b564b12bSAndrzej Warzynski 
108b564b12bSAndrzej Warzynski   // Execute the action.
1091e462fafSAndrzej Warzynski   bool success = executeCompilerInvocation(&compInst);
110b564b12bSAndrzej Warzynski 
111b564b12bSAndrzej Warzynski   // Validate the expected output.
112b564b12bSAndrzej Warzynski   EXPECT_TRUE(success);
113b564b12bSAndrzej Warzynski   EXPECT_TRUE(!outputFileBuffer.empty());
114b564b12bSAndrzej Warzynski   EXPECT_TRUE(llvm::StringRef(outputFileBuffer.data())
11511efcceaSKazu Hirata                   .starts_with("End Program arithmetic"));
116b564b12bSAndrzej Warzynski }
117b564b12bSAndrzej Warzynski 
TEST_F(FrontendActionTest,PrintPreprocessedInput)11844e74c75SAndrzej Warzynski TEST_F(FrontendActionTest, PrintPreprocessedInput) {
119db2195bcSAndrzej Warzynski   // Populate the input file with the pre-defined input and flush it.
1201e462fafSAndrzej Warzynski   *(inputFileOs) << "#ifdef NEW\n"
121db2195bcSAndrzej Warzynski                  << "  Program A \n"
122db2195bcSAndrzej Warzynski                  << "#else\n"
123db2195bcSAndrzej Warzynski                  << "  Program B\n"
124db2195bcSAndrzej Warzynski                  << "#endif";
1251e462fafSAndrzej Warzynski   inputFileOs.reset();
126db2195bcSAndrzej Warzynski 
12744e74c75SAndrzej Warzynski   // Set-up the action kind.
1281e462fafSAndrzej Warzynski   compInst.getInvocation().getFrontendOpts().programAction =
1291e462fafSAndrzej Warzynski       PrintPreprocessedInput;
1301e462fafSAndrzej Warzynski   compInst.getInvocation().getPreprocessorOpts().noReformat = true;
131db2195bcSAndrzej Warzynski 
13244e74c75SAndrzej Warzynski   // Set-up the output stream. We are using output buffer wrapped as an output
133db2195bcSAndrzej Warzynski   // stream, as opposed to an actual file (or a file descriptor).
134db2195bcSAndrzej Warzynski   llvm::SmallVector<char, 256> outputFileBuffer;
135db2195bcSAndrzej Warzynski   std::unique_ptr<llvm::raw_pwrite_stream> outputFileStream(
136db2195bcSAndrzej Warzynski       new llvm::raw_svector_ostream(outputFileBuffer));
1371e462fafSAndrzej Warzynski   compInst.setOutputStream(std::move(outputFileStream));
138db2195bcSAndrzej Warzynski 
13944e74c75SAndrzej Warzynski   // Execute the action.
1401e462fafSAndrzej Warzynski   bool success = executeCompilerInvocation(&compInst);
141db2195bcSAndrzej Warzynski 
14244e74c75SAndrzej Warzynski   // Validate the expected output.
143db2195bcSAndrzej Warzynski   EXPECT_TRUE(success);
144db2195bcSAndrzej Warzynski   EXPECT_TRUE(!outputFileBuffer.empty());
145db2195bcSAndrzej Warzynski   EXPECT_TRUE(
14611efcceaSKazu Hirata       llvm::StringRef(outputFileBuffer.data()).starts_with(" program b\n"));
147db2195bcSAndrzej Warzynski }
148db2195bcSAndrzej Warzynski 
TEST_F(FrontendActionTest,ParseSyntaxOnly)14944e74c75SAndrzej Warzynski TEST_F(FrontendActionTest, ParseSyntaxOnly) {
150db2195bcSAndrzej Warzynski   // Populate the input file with the pre-defined input and flush it.
1511e462fafSAndrzej Warzynski   *(inputFileOs) << "IF (A > 0.0) IF (B < 0.0) A = LOG (A)\n"
152db2195bcSAndrzej Warzynski                  << "END";
1531e462fafSAndrzej Warzynski   inputFileOs.reset();
154db2195bcSAndrzej Warzynski 
15544e74c75SAndrzej Warzynski   // Set-up the action kind.
1561e462fafSAndrzej Warzynski   compInst.getInvocation().getFrontendOpts().programAction = ParseSyntaxOnly;
157db2195bcSAndrzej Warzynski 
15844e74c75SAndrzej Warzynski   // Set-up the output stream for the semantic diagnostics.
159db2195bcSAndrzej Warzynski   llvm::SmallVector<char, 256> outputDiagBuffer;
160db2195bcSAndrzej Warzynski   std::unique_ptr<llvm::raw_pwrite_stream> outputStream(
161db2195bcSAndrzej Warzynski       new llvm::raw_svector_ostream(outputDiagBuffer));
1621e462fafSAndrzej Warzynski   compInst.setSemaOutputStream(std::move(outputStream));
163db2195bcSAndrzej Warzynski 
16444e74c75SAndrzej Warzynski   // Execute the action.
1651e462fafSAndrzej Warzynski   bool success = executeCompilerInvocation(&compInst);
166db2195bcSAndrzej Warzynski 
16744e74c75SAndrzej Warzynski   // Validate the expected output.
168db2195bcSAndrzej Warzynski   EXPECT_FALSE(success);
169db2195bcSAndrzej Warzynski   EXPECT_TRUE(!outputDiagBuffer.empty());
170db2195bcSAndrzej Warzynski   EXPECT_TRUE(
171db2195bcSAndrzej Warzynski       llvm::StringRef(outputDiagBuffer.data())
1726110e771Speter klausler           .contains(
17344e74c75SAndrzej Warzynski               ":1:14: error: IF statement is not allowed in IF statement\n"));
174db2195bcSAndrzej Warzynski }
175e993b20cSAndrzej Warzynski 
TEST_F(FrontendActionTest,EmitLLVM)176e993b20cSAndrzej Warzynski TEST_F(FrontendActionTest, EmitLLVM) {
177e993b20cSAndrzej Warzynski   // Populate the input file with the pre-defined input and flush it.
1781e462fafSAndrzej Warzynski   *(inputFileOs) << "end program";
1791e462fafSAndrzej Warzynski   inputFileOs.reset();
180e993b20cSAndrzej Warzynski 
181e993b20cSAndrzej Warzynski   // Set-up the action kind.
1821e462fafSAndrzej Warzynski   compInst.getInvocation().getFrontendOpts().programAction = EmitLLVM;
18302fb5b77SAndrzej Warzynski 
18481181089SMats Petersson   // Initialise LLVM backend
18581181089SMats Petersson   llvm::InitializeAllAsmPrinters();
18681181089SMats Petersson 
187e993b20cSAndrzej Warzynski   // Set-up the output stream. We are using output buffer wrapped as an output
188e993b20cSAndrzej Warzynski   // stream, as opposed to an actual file (or a file descriptor).
189e993b20cSAndrzej Warzynski   llvm::SmallVector<char> outputFileBuffer;
190e993b20cSAndrzej Warzynski   std::unique_ptr<llvm::raw_pwrite_stream> outputFileStream(
191e993b20cSAndrzej Warzynski       new llvm::raw_svector_ostream(outputFileBuffer));
1921e462fafSAndrzej Warzynski   compInst.setOutputStream(std::move(outputFileStream));
193e993b20cSAndrzej Warzynski 
194e993b20cSAndrzej Warzynski   // Execute the action.
1951e462fafSAndrzej Warzynski   bool success = executeCompilerInvocation(&compInst);
196e993b20cSAndrzej Warzynski 
197e993b20cSAndrzej Warzynski   // Validate the expected output.
198e993b20cSAndrzej Warzynski   EXPECT_TRUE(success);
199e993b20cSAndrzej Warzynski   EXPECT_TRUE(!outputFileBuffer.empty());
200e993b20cSAndrzej Warzynski 
201*aa26faf0SKrzysztof Parzyszek   EXPECT_TRUE(llvm::StringRef(outputFileBuffer.begin(), outputFileBuffer.size())
202e993b20cSAndrzej Warzynski                   .contains("define void @_QQmain()"));
203e993b20cSAndrzej Warzynski }
20438101b4eSAndrzej Warzynski 
TEST_F(FrontendActionTest,EmitAsm)20538101b4eSAndrzej Warzynski TEST_F(FrontendActionTest, EmitAsm) {
20638101b4eSAndrzej Warzynski   // Populate the input file with the pre-defined input and flush it.
2071e462fafSAndrzej Warzynski   *(inputFileOs) << "end program";
2081e462fafSAndrzej Warzynski   inputFileOs.reset();
20938101b4eSAndrzej Warzynski 
21038101b4eSAndrzej Warzynski   // Set-up the action kind.
2111e462fafSAndrzej Warzynski   compInst.getInvocation().getFrontendOpts().programAction = EmitAssembly;
21202fb5b77SAndrzej Warzynski 
21338101b4eSAndrzej Warzynski   // Initialise LLVM backend
21438101b4eSAndrzej Warzynski   llvm::InitializeAllAsmPrinters();
21538101b4eSAndrzej Warzynski 
21638101b4eSAndrzej Warzynski   // Set-up the output stream. We are using output buffer wrapped as an output
21738101b4eSAndrzej Warzynski   // stream, as opposed to an actual file (or a file descriptor).
21838101b4eSAndrzej Warzynski   llvm::SmallVector<char, 256> outputFileBuffer;
21938101b4eSAndrzej Warzynski   std::unique_ptr<llvm::raw_pwrite_stream> outputFileStream(
22038101b4eSAndrzej Warzynski       new llvm::raw_svector_ostream(outputFileBuffer));
2211e462fafSAndrzej Warzynski   compInst.setOutputStream(std::move(outputFileStream));
22238101b4eSAndrzej Warzynski 
22338101b4eSAndrzej Warzynski   // Execute the action.
2241e462fafSAndrzej Warzynski   bool success = executeCompilerInvocation(&compInst);
22538101b4eSAndrzej Warzynski 
22638101b4eSAndrzej Warzynski   // Validate the expected output.
22738101b4eSAndrzej Warzynski   EXPECT_TRUE(success);
22838101b4eSAndrzej Warzynski   EXPECT_TRUE(!outputFileBuffer.empty());
22938101b4eSAndrzej Warzynski 
230*aa26faf0SKrzysztof Parzyszek   EXPECT_TRUE(llvm::StringRef(outputFileBuffer.begin(), outputFileBuffer.size())
231*aa26faf0SKrzysztof Parzyszek                   .contains("_QQmain"));
23238101b4eSAndrzej Warzynski }
233db2195bcSAndrzej Warzynski } // namespace
234