#include "flang/Frontend/CompilerInstance.h"
#include "flang/Frontend/CompilerInvocation.h"
#include "flang/Frontend/FrontendOptions.h"
#include "flang/FrontendTool/Utils.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TargetParser/Host.h"
#include "llvm/TargetParser/Triple.h"
#include "gtest/gtest.h"
using namespace Fortran::frontend;
namespace {
class FrontendActionTest : public ::testing::Test {
protected:
std::string inputFileName;
std::string inputFilePath;
std::unique_ptr<llvm::raw_fd_ostream> inputFileOs;
std::error_code ec;
CompilerInstance compInst;
std::shared_ptr<CompilerInvocation> invoc;
void SetUp() override {
const testing::TestInfo *const testInfo =
testing::UnitTest::GetInstance()->current_test_info();
inputFileName = std::string(testInfo->name()) + "_test-file.f90";
inputFileOs = std::make_unique<llvm::raw_fd_ostream>(
inputFileName, ec, llvm::sys::fs::OF_None);
if (ec)
FAIL() << "Failed to create the input file";
llvm::SmallString<256> cwd;
if (std::error_code ec = llvm::sys::fs::current_path(cwd))
FAIL() << "Failed to obtain the current working directory";
inputFilePath = cwd.c_str();
inputFilePath += "/" + inputFileName;
compInst.createDiagnostics();
invoc = std::make_shared<CompilerInvocation>();
invoc->getTargetOpts().triple =
llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple());
llvm::InitializeAllTargets();
llvm::InitializeAllTargetMCs();
compInst.setInvocation(std::move(invoc));
compInst.getFrontendOpts().inputs.push_back(
FrontendInputFile(inputFilePath, Language::Fortran));
}
void TearDown() override {
llvm::sys::fs::remove(inputFileName);
compInst.clearOutputFiles(false);
}
};
TEST_F(FrontendActionTest, TestInputOutput) {
*(inputFileOs) << "End Program arithmetic";
inputFileOs.reset();
compInst.getInvocation().getFrontendOpts().programAction = InputOutputTest;
llvm::SmallVector<char, 256> outputFileBuffer;
std::unique_ptr<llvm::raw_pwrite_stream> outputFileStream(
new llvm::raw_svector_ostream(outputFileBuffer));
compInst.setOutputStream(std::move(outputFileStream));
bool success = executeCompilerInvocation(&compInst);
EXPECT_TRUE(success);
EXPECT_TRUE(!outputFileBuffer.empty());
EXPECT_TRUE(llvm::StringRef(outputFileBuffer.data())
.starts_with("End Program arithmetic"));
}
TEST_F(FrontendActionTest, PrintPreprocessedInput) {
*(inputFileOs) << "#ifdef NEW\n"
<< " Program A \n"
<< "#else\n"
<< " Program B\n"
<< "#endif";
inputFileOs.reset();
compInst.getInvocation().getFrontendOpts().programAction =
PrintPreprocessedInput;
compInst.getInvocation().getPreprocessorOpts().noReformat = true;
llvm::SmallVector<char, 256> outputFileBuffer;
std::unique_ptr<llvm::raw_pwrite_stream> outputFileStream(
new llvm::raw_svector_ostream(outputFileBuffer));
compInst.setOutputStream(std::move(outputFileStream));
bool success = executeCompilerInvocation(&compInst);
EXPECT_TRUE(success);
EXPECT_TRUE(!outputFileBuffer.empty());
EXPECT_TRUE(
llvm::StringRef(outputFileBuffer.data()).starts_with(" program b\n"));
}
TEST_F(FrontendActionTest, ParseSyntaxOnly) {
*(inputFileOs) << "IF (A > 0.0) IF (B < 0.0) A = LOG (A)\n"
<< "END";
inputFileOs.reset();
compInst.getInvocation().getFrontendOpts().programAction = ParseSyntaxOnly;
llvm::SmallVector<char, 256> outputDiagBuffer;
std::unique_ptr<llvm::raw_pwrite_stream> outputStream(
new llvm::raw_svector_ostream(outputDiagBuffer));
compInst.setSemaOutputStream(std::move(outputStream));
bool success = executeCompilerInvocation(&compInst);
EXPECT_FALSE(success);
EXPECT_TRUE(!outputDiagBuffer.empty());
EXPECT_TRUE(
llvm::StringRef(outputDiagBuffer.data())
.contains(
":1:14: error: IF statement is not allowed in IF statement\n"));
}
TEST_F(FrontendActionTest, EmitLLVM) {
*(inputFileOs) << "end program";
inputFileOs.reset();
compInst.getInvocation().getFrontendOpts().programAction = EmitLLVM;
llvm::InitializeAllAsmPrinters();
llvm::SmallVector<char> outputFileBuffer;
std::unique_ptr<llvm::raw_pwrite_stream> outputFileStream(
new llvm::raw_svector_ostream(outputFileBuffer));
compInst.setOutputStream(std::move(outputFileStream));
bool success = executeCompilerInvocation(&compInst);
EXPECT_TRUE(success);
EXPECT_TRUE(!outputFileBuffer.empty());
EXPECT_TRUE(llvm::StringRef(outputFileBuffer.begin(), outputFileBuffer.size())
.contains("define void @_QQmain()"));
}
TEST_F(FrontendActionTest, EmitAsm) {
*(inputFileOs) << "end program";
inputFileOs.reset();
compInst.getInvocation().getFrontendOpts().programAction = EmitAssembly;
llvm::InitializeAllAsmPrinters();
llvm::SmallVector<char, 256> outputFileBuffer;
std::unique_ptr<llvm::raw_pwrite_stream> outputFileStream(
new llvm::raw_svector_ostream(outputFileBuffer));
compInst.setOutputStream(std::move(outputFileStream));
bool success = executeCompilerInvocation(&compInst);
EXPECT_TRUE(success);
EXPECT_TRUE(!outputFileBuffer.empty());
EXPECT_TRUE(llvm::StringRef(outputFileBuffer.begin(), outputFileBuffer.size())
.contains("_QQmain"));
}
}