mirror of
https://github.com/eclipse-openvehicle-api/openvehicle-api.git
synced 2026-02-05 15:18:45 +00:00
778 lines
28 KiB
C++
778 lines
28 KiB
C++
#include "includes.h"
|
|
#include "parser_test.h"
|
|
#include "../../../sdv_executables/sdv_idl_compiler/preproc.cpp"
|
|
#include "../../../sdv_executables/sdv_idl_compiler/macro.cpp"
|
|
#include "../../../sdv_executables/sdv_idl_compiler/parser.h"
|
|
#include "../../../sdv_executables/sdv_idl_compiler/exception.h"
|
|
#include <fstream>
|
|
|
|
using CPreprocTest = CParserTest;
|
|
|
|
TEST_F(CPreprocTest, PreprocessorUnknownDirective)
|
|
{
|
|
// Unknown preproc error
|
|
EXPECT_THROW(CParser(nullptr).LexicalCheck(), CCompileException);
|
|
EXPECT_THROW(CParser("#abc").LexicalCheck(), CCompileException);
|
|
}
|
|
|
|
TEST_F(CPreprocTest, PreprocessorWhitespace)
|
|
{
|
|
// Parse preprocessor with additional whitespace before
|
|
|
|
// Single preprocessing directive
|
|
EXPECT_TRUE(CParser(" #define TEST").LexicalCheck().GetEnvironment().Defined("TEST"));
|
|
EXPECT_TRUE(CParser(" # define TEST").LexicalCheck().GetEnvironment().Defined("TEST"));
|
|
EXPECT_TRUE(CParser("\t#\tdefine TEST").LexicalCheck().GetEnvironment().Defined("TEST"));
|
|
EXPECT_TRUE(CParser(" \n#define TEST").LexicalCheck().GetEnvironment().Defined("TEST"));
|
|
|
|
// Multiple preprocessing directives
|
|
EXPECT_TRUE(CParser(R"code(
|
|
# if defined HELLO
|
|
# define NO_TEST
|
|
# else
|
|
# define TEST
|
|
# endif)code").LexicalCheck().GetEnvironment().Defined("TEST"));
|
|
|
|
// Tests bug fix: #381248
|
|
// Verbatim block test
|
|
EXPECT_NO_THROW(CParser(R"code(#verbatim_begin
|
|
// ...
|
|
#verbatim_end)code").Parse());
|
|
EXPECT_NO_THROW(CParser(R"code( # verbatim_begin
|
|
// ...
|
|
# not valid text...
|
|
# verbatim_end)code").Parse());
|
|
}
|
|
|
|
TEST_F(CPreprocTest, PreprocessorMacroDefined)
|
|
{
|
|
// Parse preprocessor
|
|
EXPECT_TRUE(CParser("#define TEST").LexicalCheck().GetEnvironment().Defined("TEST"));
|
|
EXPECT_TRUE(CParser("#define TEST()").LexicalCheck().GetEnvironment().Defined("TEST"));
|
|
EXPECT_TRUE(CParser("#define TEST(a)").LexicalCheck().GetEnvironment().Defined("TEST"));
|
|
EXPECT_TRUE(CParser("#define TEST(a, b,c)").LexicalCheck().GetEnvironment().Defined("TEST"));
|
|
EXPECT_TRUE(CParser("#define TEST value").LexicalCheck().GetEnvironment().Defined("TEST"));
|
|
EXPECT_TRUE(CParser("#define TEST(a) value a").LexicalCheck().GetEnvironment().Defined("TEST"));
|
|
EXPECT_TRUE(CParser("#define TEST(a, b,c) value a*b*c").LexicalCheck().GetEnvironment().Defined("TEST"));
|
|
}
|
|
|
|
TEST_F(CPreprocTest, PreprocessorDefineRedefine)
|
|
{
|
|
// Redefinition
|
|
EXPECT_NO_THROW(CParser("#define TEST(a, b,c) value a*b*c\n#define TEST(a, b,c) value a*b*c").LexicalCheck());
|
|
EXPECT_THROW(CParser("#define TEST(a, b,c) value a*b*c\n#define TEST").LexicalCheck(), CCompileException);
|
|
}
|
|
|
|
TEST_F(CPreprocTest, PreprocessorDefineAndSingleExpand)
|
|
{
|
|
// Parse preprocessor
|
|
CParser parser(
|
|
"#define TEST\n"
|
|
"#define ABBA 2\n"
|
|
"#define ABBABA 3\n"
|
|
"#define AB(c) AB##c\n"
|
|
"#define BA(c) AB ## c\n"
|
|
"#define TEXT(c) # c"
|
|
);
|
|
parser.LexicalCheck();
|
|
|
|
// Code will be prepended with an empty tring
|
|
CCodePos codeTest("");
|
|
EXPECT_TRUE(parser.GetEnvironment().TestAndExpand("TEST", codeTest));
|
|
EXPECT_TRUE(static_cast<std::string>(codeTest.GetLocation()).empty());
|
|
|
|
// Code will be prepended with the value "2"
|
|
CCodePos codeAbba("");
|
|
EXPECT_TRUE(parser.GetEnvironment().TestAndExpand("ABBA", codeAbba));
|
|
EXPECT_EQ(static_cast<std::string>(codeAbba.GetLocation()), "2");
|
|
|
|
// Code will be prepended with "ABBA" and expanded to "2"
|
|
CCodePos codeAbba2("(BA)");
|
|
EXPECT_TRUE(parser.GetEnvironment().TestAndExpand("AB", codeAbba2));
|
|
EXPECT_EQ(static_cast<std::string>(codeAbba2.GetLocation()), "2");
|
|
|
|
// Code will be prepended with "ABBA" and expanded to "2"
|
|
CCodePos codeAbba3("(BA)");
|
|
EXPECT_TRUE(parser.GetEnvironment().TestAndExpand("BA", codeAbba3));
|
|
EXPECT_EQ(static_cast<std::string>(codeAbba3.GetLocation()), "2");
|
|
|
|
// Code will be prepende with "\"hello\""
|
|
CCodePos codeText("(hello)");
|
|
EXPECT_TRUE(parser.GetEnvironment().TestAndExpand("TEXT", codeText));
|
|
EXPECT_EQ(static_cast<std::string>(codeText.GetLocation()), "\"hello\"");
|
|
|
|
// Code will be prepended with "ABBA"
|
|
CCodePos codeAbba4("(B)A");
|
|
EXPECT_TRUE(parser.GetEnvironment().TestAndExpand("AB", codeAbba4));
|
|
CToken tokenAbba4 = codeAbba4.GetLocation();
|
|
++codeAbba4; // Skip prepended 'A'
|
|
++codeAbba4; // Skip prepended 'B'
|
|
++codeAbba4; // Skip prepended 'C'
|
|
++codeAbba4; // Skip 'A'
|
|
codeAbba4.UpdateLocation(tokenAbba4);
|
|
EXPECT_EQ(static_cast<std::string>(tokenAbba4), "ABBA");
|
|
}
|
|
|
|
TEST_F(CPreprocTest, PreprocessorDefineAndExpand)
|
|
{
|
|
// Parse preprocessor
|
|
CParser parser(
|
|
"#define TEST\n"
|
|
"#define ABBA 2\n"
|
|
"#define ABBABA 3\n"
|
|
"#define AB(c) AB##c\n"
|
|
"#define BA(c) AB ## c\n"
|
|
"#define DE AB(BA) ## BA\n"
|
|
"#define EF ABBA ## BA\n"
|
|
"#define CIRCULAR CIRCULAR\n"
|
|
"#define MULTI(a, b) a ## b\n"
|
|
"#define MULTI_SPACE(a, b) a \\\n \\\r\n\t b\n"
|
|
"#define MULTI_COMMENT1(a, b) a // ## b\n"
|
|
"#define MULTI_COMMENT2(a, b) a /* ## */ b\n"
|
|
"#define TEXT(c) # c\n"
|
|
"#define Fx abc\n"
|
|
"#define Bx def\n"
|
|
"#define FB(arg) #arg\n"
|
|
"#define FB1(arg) FB(arg)\n"
|
|
"#define DOx(x) x\n"
|
|
"#define CIRCULAR2 DOx(CIRCULAR2)\n"
|
|
"#define CIRCULAR3 CIRCULAR"
|
|
);
|
|
parser.LexicalCheck();
|
|
|
|
// Simple parsing function
|
|
auto fnGetLastProcessedToken = [&](CCodePos& rCode) -> CToken
|
|
{
|
|
CToken token;
|
|
while (!rCode.HasEOF())
|
|
{
|
|
// If the current position is not part of the macro expansion, reset the set of macros used in a expansion.
|
|
bool bInMacroExpansion = rCode.CurrentPositionInMacroExpansion();
|
|
|
|
// Get a token
|
|
token = parser.GetLexer().GetToken(rCode, nullptr);
|
|
|
|
// Check whether the token is an identifier, if so, check for any macro
|
|
if (token.GetType() == ETokenType::token_identifier)
|
|
{
|
|
if (parser.GetEnvironment().TestAndExpand(static_cast<std::string>(token).c_str(), rCode, bInMacroExpansion))
|
|
continue; // macro was replaced, get a token again.
|
|
}
|
|
}
|
|
return token;
|
|
};
|
|
|
|
// Multiple arguments.
|
|
CCodePos codeMulti("FB1(MULTI(abc, def))");
|
|
EXPECT_EQ(static_cast<std::string>(fnGetLastProcessedToken(codeMulti)), "\"abcdef\"");
|
|
|
|
// Comments are ignored in expansion.
|
|
CCodePos codeComment1("FB1(MULTI_COMMENT1(abc, def))");
|
|
EXPECT_EQ(static_cast<std::string>(fnGetLastProcessedToken(codeComment1)), "\"abc\"");
|
|
CCodePos codeComment2("FB1(MULTI_COMMENT2(abc, def))");
|
|
EXPECT_EQ(static_cast<std::string>(fnGetLastProcessedToken(codeComment2)), "\"abc def\"");
|
|
CCodePos codeComment3("FB1(MULTI(abc /*the first*/, /*the second*/def))");
|
|
EXPECT_EQ(static_cast<std::string>(fnGetLastProcessedToken(codeComment3)), "\"abcdef\"");
|
|
|
|
// Macro is formed using another macro and expanded itself to "2".
|
|
CCodePos codeAbba5("AB(B)A");
|
|
EXPECT_EQ(static_cast<std::string>(fnGetLastProcessedToken(codeAbba5)), "2");
|
|
|
|
// Strings from arguments - argument expansion only when not stringificating.
|
|
// Version 1 stringificates the argument without expansion
|
|
// Version 2 expands the argument first and then stringificates
|
|
CCodePos codeFB0("FB(Fx Bx)");
|
|
EXPECT_EQ(static_cast<std::string>(fnGetLastProcessedToken(codeFB0)), "\"Fx Bx\"");
|
|
CCodePos codeFB1("FB1(Fx Bx)");
|
|
EXPECT_EQ(static_cast<std::string>(fnGetLastProcessedToken(codeFB1)), "\"abc def\"");
|
|
|
|
// Reduce large spaces to one space
|
|
// Version 1 has space in the arguments, which will be reduced to one space
|
|
// Version 2 has space in the definition, which will be reduced to one space
|
|
CCodePos codeSpace1("FB1(Fx \t\\\n \t\t\\\r\n \tBx)");
|
|
EXPECT_EQ(static_cast<std::string>(fnGetLastProcessedToken(codeSpace1)), "\"abc def\"");
|
|
CCodePos codeSpace2("FB1(MULTI_SPACE(abc, def))");
|
|
EXPECT_EQ(static_cast<std::string>(fnGetLastProcessedToken(codeSpace2)), "\"abc def\"");
|
|
|
|
// Expand results before added to the code
|
|
// Version 1 expands ABBA into 2 and then adds A
|
|
// VERSION 2 glues ABBA to BA and expands ABABA into 3
|
|
CCodePos codeRes1("FB1(DE)");
|
|
EXPECT_EQ(static_cast<std::string>(fnGetLastProcessedToken(codeRes1)), "\"2BA\"");
|
|
CCodePos codeRes2("FB1(EF)");
|
|
EXPECT_EQ(static_cast<std::string>(fnGetLastProcessedToken(codeRes2)), "\"3\"");
|
|
|
|
// Circular referencing
|
|
// Version 1 calls itself
|
|
// Version 2 calls another macro calling itsel
|
|
// Version 3 has multiple circular macros in one parameter
|
|
CCodePos codeCircular1("FB1(CIRCULAR)");
|
|
EXPECT_EQ(static_cast<std::string>(fnGetLastProcessedToken(codeCircular1)), "\"CIRCULAR\"");
|
|
CCodePos codeCircular2("FB1(CIRCULAR2)");
|
|
EXPECT_EQ(static_cast<std::string>(fnGetLastProcessedToken(codeCircular2)), "\"CIRCULAR2\"");
|
|
CCodePos codeCircular3("FB1(CIRCULAR3 CIRCULAR CIRCULAR3)");
|
|
EXPECT_EQ(static_cast<std::string>(fnGetLastProcessedToken(codeCircular3)), "\"CIRCULAR CIRCULAR CIRCULAR\"");
|
|
}
|
|
|
|
TEST_F(CPreprocTest, PreprocessorUndef)
|
|
{
|
|
// Parse preprocessor
|
|
CParser parser(
|
|
"#define TEST\n"
|
|
"#define ABBA 2\n"
|
|
"#define ABBABA 3\n"
|
|
"#define AB(c) AB##c\n"
|
|
"#define BA(c) AB ## c\n"
|
|
"#undef TEST\n"
|
|
"#undef ABRACADABRA\n"
|
|
"#undef ABBABA with dummy text... is ignored\n"
|
|
"#undef BA // with comments"
|
|
);
|
|
parser.LexicalCheck();
|
|
|
|
EXPECT_FALSE(parser.GetEnvironment().Defined("TEST"));
|
|
EXPECT_TRUE(parser.GetEnvironment().Defined("ABBA"));
|
|
EXPECT_FALSE(parser.GetEnvironment().Defined("ABBABA"));
|
|
EXPECT_TRUE(parser.GetEnvironment().Defined("AB"));
|
|
EXPECT_FALSE(parser.GetEnvironment().Defined("BA"));
|
|
}
|
|
|
|
TEST_F(CPreprocTest, PreprocessorIncludeLocal)
|
|
{
|
|
// Create a dummy files
|
|
std::ofstream fstreamNormal("dummy_source_test_preprocessor_include.tmp", std::ios_base::out | std::ios_base::trunc);
|
|
ASSERT_TRUE(fstreamNormal.is_open());
|
|
fstreamNormal << "#define HELLO2";
|
|
fstreamNormal.close();
|
|
std::ofstream fstreamCircular("dummy_source_test_preprocessor_include_circular.tmp", std::ios_base::out | std::ios_base::trunc);
|
|
ASSERT_TRUE(fstreamCircular.is_open());
|
|
fstreamCircular << "#include \"dummy_source_test_preprocessor_include_circular.tmp\"";
|
|
fstreamCircular.close();
|
|
std::filesystem::create_directory("dummy1");
|
|
std::filesystem::path pathDummy1File = "dummy1/dummy1_subdir_source_test_preprocessor_include.tmp";
|
|
std::ofstream fstreamDummy1Subdir(pathDummy1File.string(), std::ios_base::out | std::ios_base::trunc);
|
|
ASSERT_TRUE(fstreamDummy1Subdir.is_open());
|
|
fstreamDummy1Subdir << "#define DUMMY1";
|
|
fstreamDummy1Subdir.close();
|
|
std::filesystem::create_directory("dummy2");
|
|
std::filesystem::path pathDummy2File = "dummy2/dummy2_subdir_source_test_preprocessor_include.tmp";
|
|
std::ofstream fstreamDummy2Subdir(pathDummy2File.string(), std::ios_base::out | std::ios_base::trunc);
|
|
ASSERT_TRUE(fstreamDummy2Subdir.is_open());
|
|
fstreamDummy2Subdir << "#include \"../dummy3/dummy3_subdir_source_test_preprocessor_include.tmp\"";
|
|
fstreamDummy2Subdir.close();
|
|
std::filesystem::create_directory("dummy3");
|
|
std::filesystem::path pathDummy3File = "dummy3/dummy3_subdir_source_test_preprocessor_include.tmp";
|
|
std::ofstream fstreamDummy3Subdir(pathDummy3File.string(), std::ios_base::out | std::ios_base::trunc);
|
|
ASSERT_TRUE(fstreamDummy3Subdir.is_open());
|
|
fstreamDummy3Subdir << "#define DUMMY3\n#include \"dummy4_subdir_source_test_preprocessor_include.tmp\"";
|
|
fstreamDummy3Subdir.close();
|
|
std::filesystem::path pathDummy4File = "dummy3/dummy4_subdir_source_test_preprocessor_include.tmp";
|
|
std::ofstream fstreamDummy4Subdir(pathDummy4File.string(), std::ios_base::out | std::ios_base::trunc);
|
|
ASSERT_TRUE(fstreamDummy4Subdir.is_open());
|
|
fstreamDummy4Subdir << "#define DUMMY4";
|
|
fstreamDummy4Subdir.close();
|
|
|
|
// Make include dirs (1st argument is the executable file path).
|
|
std::vector<std::string> rvecArgs = {"idl_compiler_test", "-Idummy1", "-Idummy2"};
|
|
CIdlCompilerEnvironment environment(rvecArgs);
|
|
|
|
// Check non-existing
|
|
EXPECT_THROW(CParser("#include \"non_existing_file.tmp\"", environment).LexicalCheck(), CCompileException);
|
|
|
|
// Check local
|
|
EXPECT_TRUE(CParser("#include \"dummy_source_test_preprocessor_include.tmp\"", environment).LexicalCheck().
|
|
GetEnvironment().Defined("HELLO2"));
|
|
|
|
// Check local with relative path
|
|
EXPECT_TRUE(CParser("#include \"dummy1/dummy1_subdir_source_test_preprocessor_include.tmp\"").LexicalCheck().
|
|
GetEnvironment().Defined("DUMMY1"));
|
|
|
|
// Check circular (re-inclusion is prevented)
|
|
EXPECT_NO_THROW(CParser("#include \"dummy_source_test_preprocessor_include_circular.tmp\"", environment).LexicalCheck());
|
|
|
|
// Check through include path
|
|
EXPECT_TRUE(CParser("#include \"dummy1_subdir_source_test_preprocessor_include.tmp\"", environment).LexicalCheck().
|
|
GetEnvironment().Defined("DUMMY1"));
|
|
|
|
// Check local includes from an included file not in the search path
|
|
EXPECT_TRUE(CParser("#include \"dummy2_subdir_source_test_preprocessor_include.tmp\"", environment).LexicalCheck().
|
|
GetEnvironment().Defined("DUMMY4"));
|
|
|
|
try
|
|
{
|
|
std::filesystem::remove("dummy_source_test_preprocessor_include.tmp");
|
|
std::filesystem::remove("dummy_source_test_preprocessor_include_circular.tmp");
|
|
std::filesystem::remove_all("dummy1");
|
|
std::filesystem::remove_all("dummy2");
|
|
std::filesystem::remove_all("dummy3");
|
|
} catch (const std::filesystem::filesystem_error&)
|
|
{}
|
|
}
|
|
|
|
TEST_F(CPreprocTest, PreprocessorIncludeLocalAbsolute)
|
|
{
|
|
// Create a dummy files
|
|
std::filesystem::path pathCurrent = std::filesystem::current_path();
|
|
std::ofstream fstreamNormal("dummy_source_test_preprocessor_include.tmp", std::ios_base::out | std::ios_base::trunc);
|
|
ASSERT_TRUE(fstreamNormal.is_open());
|
|
fstreamNormal << "#include \"" << (pathCurrent / "dummy" / "dummy_subdir_source_test_preprocessor_include.tmp").generic_u8string() << "\"";
|
|
fstreamNormal.close();
|
|
std::filesystem::create_directory("dummy");
|
|
std::filesystem::path pathDummy1File = "dummy/dummy_subdir_source_test_preprocessor_include.tmp";
|
|
std::ofstream fstreamDummySubdir(pathDummy1File.string(), std::ios_base::out | std::ios_base::trunc);
|
|
ASSERT_TRUE(fstreamDummySubdir.is_open());
|
|
fstreamDummySubdir << "#define HELLO";
|
|
fstreamDummySubdir.close();
|
|
|
|
// Check access through path (should not be possible)
|
|
EXPECT_THROW(CParser("#include \"dummy_subdir_source_test_preprocessor_include.tmp\"").LexicalCheck(), CCompileException);
|
|
|
|
// Check relative path
|
|
EXPECT_TRUE(CParser("#include \"dummy/dummy_subdir_source_test_preprocessor_include.tmp\"").LexicalCheck().
|
|
GetEnvironment().Defined("HELLO"));
|
|
|
|
// Check direct absolute path
|
|
std::string ssCode = "#include \"";
|
|
ssCode += (pathCurrent / "dummy" / "dummy_subdir_source_test_preprocessor_include.tmp").generic_u8string();
|
|
ssCode += "\"";
|
|
EXPECT_TRUE(CParser(ssCode.c_str()).LexicalCheck().
|
|
GetEnvironment().Defined("HELLO"));
|
|
|
|
// Check indirect absolute path
|
|
EXPECT_TRUE(CParser("#include \"dummy_source_test_preprocessor_include.tmp\"").LexicalCheck().
|
|
GetEnvironment().Defined("HELLO"));
|
|
|
|
try
|
|
{
|
|
std::filesystem::remove("dummy_source_test_preprocessor_include.tmp");
|
|
std::filesystem::remove_all("dummy");
|
|
} catch (const std::filesystem::filesystem_error&)
|
|
{}
|
|
}
|
|
|
|
TEST_F(CPreprocTest, PreprocessorIncludeGlobal)
|
|
{
|
|
// Create a dummy files
|
|
std::ofstream fstreamNormal("dummy_source_test_preprocessor_include.tmp", std::ios_base::out | std::ios_base::trunc);
|
|
ASSERT_TRUE(fstreamNormal.is_open());
|
|
fstreamNormal << "#define HELLO2";
|
|
fstreamNormal.close();
|
|
std::ofstream fstreamCircular("dummy_source_test_preprocessor_include_circular.tmp", std::ios_base::out | std::ios_base::trunc);
|
|
ASSERT_TRUE(fstreamCircular.is_open());
|
|
fstreamCircular << "#include \"dummy_source_test_preprocessor_include_circular.tmp\"";
|
|
fstreamCircular.close();
|
|
std::filesystem::create_directory("dummy1");
|
|
std::filesystem::path pathDummy1File = "dummy1/dummy1_subdir_source_test_preprocessor_include.tmp";
|
|
std::ofstream fstreamDummy1Subdir(pathDummy1File.string(), std::ios_base::out | std::ios_base::trunc);
|
|
ASSERT_TRUE(fstreamDummy1Subdir.is_open());
|
|
fstreamDummy1Subdir << "#define DUMMY1";
|
|
fstreamDummy1Subdir.close();
|
|
std::filesystem::create_directory("dummy2");
|
|
std::filesystem::path pathDummy2File = "dummy2/dummy2_subdir_source_test_preprocessor_include.tmp";
|
|
std::ofstream fstreamDummy2Subdir(pathDummy2File.string(), std::ios_base::out | std::ios_base::trunc);
|
|
ASSERT_TRUE(fstreamDummy2Subdir.is_open());
|
|
fstreamDummy2Subdir << "#include \"../dummy3/dummy3_subdir_source_test_preprocessor_include.tmp\"";
|
|
fstreamDummy2Subdir.close();
|
|
std::filesystem::create_directory("dummy3");
|
|
std::filesystem::path pathDummy3File = "dummy3/dummy3_subdir_source_test_preprocessor_include.tmp";
|
|
std::ofstream fstreamDummy3Subdir(pathDummy3File.string(), std::ios_base::out | std::ios_base::trunc);
|
|
ASSERT_TRUE(fstreamDummy3Subdir.is_open());
|
|
fstreamDummy3Subdir << "#define DUMMY3\n#include \"dummy4_subdir_source_test_preprocessor_include.tmp\"";
|
|
fstreamDummy3Subdir.close();
|
|
std::filesystem::path pathDummy4File = "dummy3/dummy4_subdir_source_test_preprocessor_include.tmp";
|
|
std::ofstream fstreamDummy4Subdir(pathDummy4File.string(), std::ios_base::out | std::ios_base::trunc);
|
|
ASSERT_TRUE(fstreamDummy4Subdir.is_open());
|
|
fstreamDummy4Subdir << "#define DUMMY4";
|
|
fstreamDummy4Subdir.close();
|
|
|
|
// Make include dirs (1st argument is the executable file path).
|
|
std::vector<std::string> rvecArgs = {"idl_compiler_test.exe", "-Idummy1", "-Idummy2"};
|
|
CIdlCompilerEnvironment environment(rvecArgs);
|
|
|
|
// Check non-existing
|
|
EXPECT_THROW(CParser("#include <non_existing_file.tmp>", environment).LexicalCheck(), CCompileException);
|
|
|
|
// Check local
|
|
EXPECT_THROW(CParser("#include <dummy_source_test_preprocessor_include.tmp>", environment).LexicalCheck(), CCompileException);
|
|
|
|
// Check local with relative path
|
|
EXPECT_THROW(CParser("#include <dummy1/dummy1_subdir_source_test_preprocessor_include.tmp>").LexicalCheck(), CCompileException);
|
|
|
|
// Check through include path
|
|
EXPECT_TRUE(CParser("#include <dummy1_subdir_source_test_preprocessor_include.tmp>", environment).LexicalCheck().
|
|
GetEnvironment().Defined("DUMMY1"));
|
|
|
|
// Check local includes from an included file not in the search path
|
|
EXPECT_TRUE(CParser("#include <dummy2_subdir_source_test_preprocessor_include.tmp>", environment).LexicalCheck().
|
|
GetEnvironment().Defined("DUMMY4"));
|
|
|
|
try
|
|
{
|
|
std::filesystem::remove("dummy_source_test_preprocessor_include.tmp");
|
|
std::filesystem::remove("dummy_source_test_preprocessor_include_circular.tmp");
|
|
std::filesystem::remove_all("dummy1");
|
|
std::filesystem::remove_all("dummy2");
|
|
std::filesystem::remove_all("dummy3");
|
|
} catch (const std::filesystem::filesystem_error&)
|
|
{}
|
|
}
|
|
|
|
TEST_F(CPreprocTest, PreprocessorIf)
|
|
{
|
|
// Direct if with integer
|
|
EXPECT_TRUE(CParser(
|
|
"#if 1\n"
|
|
"#define CORRECT\n"
|
|
"#else\n"
|
|
"#define INCORRECT\n"
|
|
"#endif"
|
|
).LexicalCheck().GetEnvironment().Defined("CORRECT"));
|
|
|
|
// Direct if with boolean
|
|
EXPECT_TRUE(CParser(
|
|
"#if true\n"
|
|
"#define CORRECT\n"
|
|
"#else\n"
|
|
"#define INCORRECT\n"
|
|
"#endif"
|
|
).LexicalCheck().GetEnvironment().Defined("CORRECT"));
|
|
|
|
// Direct if with character
|
|
EXPECT_TRUE(CParser(
|
|
"#if 'a'\n"
|
|
"#define CORRECT\n"
|
|
"#else\n"
|
|
"#define INCORRECT\n"
|
|
"#endif"
|
|
).LexicalCheck().GetEnvironment().Defined("CORRECT"));
|
|
|
|
// Direct if with macro
|
|
EXPECT_TRUE(CParser(
|
|
"#define a 10\n"
|
|
"#if a\n"
|
|
"#define CORRECT\n"
|
|
"#else\n"
|
|
"#define INCORRECT\n"
|
|
"#endif"
|
|
).LexicalCheck().GetEnvironment().Defined("CORRECT"));
|
|
|
|
// Direct if with zerod macro
|
|
EXPECT_TRUE(CParser(
|
|
"#define a 0\n"
|
|
"#if a\n"
|
|
"#define INCORRECT\n"
|
|
"#else\n"
|
|
"#define CORRECT\n"
|
|
"#endif"
|
|
).LexicalCheck().GetEnvironment().Defined("CORRECT"));
|
|
|
|
// Direct if with defined macro without value
|
|
EXPECT_THROW(CParser(
|
|
"#define a\n"
|
|
"#if a\n"
|
|
"#define CORRECT\n"
|
|
"#endif"
|
|
).LexicalCheck(), CCompileException);
|
|
|
|
// Direct if with non-existent macro (equals to 0)
|
|
EXPECT_TRUE(CParser(
|
|
"#if a\n"
|
|
"#define INCORRECT\n"
|
|
"#else\n"
|
|
"#define CORRECT\n"
|
|
"#endif"
|
|
).LexicalCheck().GetEnvironment().Defined("CORRECT"));
|
|
|
|
// Incomplete if statement
|
|
EXPECT_THROW(CParser(
|
|
"#if\n"
|
|
"#define CORRECT\n"
|
|
"#else\n"
|
|
"#define INCORRECT\n"
|
|
"#endif"
|
|
).LexicalCheck(), CCompileException);
|
|
|
|
// Correct if section using macros
|
|
EXPECT_TRUE(CParser(
|
|
"#define a 10\n"
|
|
"#define b(c, d) c*d\n"
|
|
"#define e 10\n"
|
|
"#if a + b(5, 5) + e == 45\n"
|
|
"#define CORRECT\n"
|
|
"#else\n"
|
|
"#define INCORRECT\n"
|
|
"#endif"
|
|
).LexicalCheck().GetEnvironment().Defined("CORRECT"));
|
|
|
|
// Incorrect if section using macros
|
|
EXPECT_FALSE(CParser(
|
|
"#define a 10\n"
|
|
"#define b(c, d) c*d\n"
|
|
"#define e 10\n"
|
|
"#if a + b(5, 5) + e == 45\n"
|
|
"#define CORRECT\n"
|
|
"#else\n"
|
|
"#define INCORRECT\n"
|
|
"#endif"
|
|
).LexicalCheck().GetEnvironment().Defined("INCORRECT"));
|
|
|
|
// Parenthesis while calculating
|
|
EXPECT_TRUE(CParser(
|
|
"#define a 10\n"
|
|
"#define b(c, d) c*d\n"
|
|
"#define e 10\n"
|
|
"#if a * (b(5, 5) + e) == 350\n"
|
|
"#define CORRECT\n"
|
|
"#endif"
|
|
).LexicalCheck().GetEnvironment().Defined("CORRECT"));
|
|
|
|
// Operators + - / * %
|
|
EXPECT_TRUE(CParser(
|
|
"#if 10 + 11 * 35 % 10 - 2 == 13\n"
|
|
"#define CORRECT\n"
|
|
"#endif"
|
|
).LexicalCheck().GetEnvironment().Defined("CORRECT"));
|
|
|
|
// Operators << >> & | ~
|
|
// NOTE: '!=' comes before '&'
|
|
EXPECT_TRUE(CParser(
|
|
"#if (2 << 2 | 7 >> 1 & (~ 120 & 7)) != 11\n"
|
|
"#define INCORRECT\n"
|
|
"#else\n"
|
|
"#define CORRECT\n"
|
|
"#endif"
|
|
).LexicalCheck().GetEnvironment().Defined("CORRECT"));
|
|
|
|
// Operators && || < > <= >= == !=
|
|
EXPECT_TRUE(CParser(
|
|
"#if (20 || 10 && 1) < 2\n"
|
|
"#define CORRECT1 1\n"
|
|
"#endif\n"
|
|
"#if 50 + 7 >= 57\n"
|
|
"#define CORRECT2 1\n"
|
|
"#endif\n"
|
|
"#if 50 + 8 > 57\n"
|
|
"#define CORRECT3 1\n"
|
|
"#endif\n"
|
|
"#if 50 + 7 <= 57\n"
|
|
"#define CORRECT4 1\n"
|
|
"#endif\n"
|
|
"#if 10 != TEST\n"
|
|
"#define CORRECT5 1\n"
|
|
"#endif\n"
|
|
"#if CORRECT1 + CORRECT2 + CORRECT3 + CORRECT4 + CORRECT5 == 5\n"
|
|
"#define CORRECT\n"
|
|
"#endif\n"
|
|
).LexicalCheck().GetEnvironment().Defined("CORRECT"));
|
|
|
|
// Div by zero /
|
|
EXPECT_THROW(CParser(
|
|
"#if 10 / TEST\n"
|
|
"#define CORRECT\n"
|
|
"#endif"
|
|
).LexicalCheck(), CCompileException);
|
|
|
|
// Div by zero %
|
|
EXPECT_THROW(CParser(
|
|
"#if 10 % TEST\n"
|
|
"#define CORRECT\n"
|
|
"#endif"
|
|
).LexicalCheck(), CCompileException);
|
|
|
|
// Incomplete
|
|
EXPECT_THROW(CParser(
|
|
"#define TEST\n"
|
|
"#if 10 % TEST\n"
|
|
"#define CORRECT\n"
|
|
"#endif"
|
|
).LexicalCheck(), CCompileException);
|
|
|
|
// Exclude code
|
|
EXPECT_NO_THROW(CParser(
|
|
"#if 0\n"
|
|
"bla bla\n"
|
|
"#endif"
|
|
).LexicalCheck());
|
|
|
|
// Exclude code incomplete
|
|
EXPECT_THROW(CParser(
|
|
"#if 0\n"
|
|
"bla bla\n"
|
|
).LexicalCheck(), CCompileException);
|
|
}
|
|
|
|
TEST_F(CPreprocTest, PreprocessorDefinedOperator)
|
|
{
|
|
// defined(macro)
|
|
EXPECT_TRUE(CParser(
|
|
"#if !defined(TEST)\n"
|
|
"#define CORRECT\n"
|
|
"#endif"
|
|
).LexicalCheck().GetEnvironment().Defined("CORRECT"));
|
|
EXPECT_TRUE(CParser(
|
|
"#define TEST\n"
|
|
"#if defined(TEST)\n"
|
|
"#define CORRECT\n"
|
|
"#endif"
|
|
).LexicalCheck().GetEnvironment().Defined("CORRECT"));
|
|
EXPECT_THROW(CParser(
|
|
"#if defined()\n"
|
|
"#define CORRECT\n"
|
|
"#endif"
|
|
).LexicalCheck(), CCompileException);
|
|
|
|
// defined macro
|
|
EXPECT_TRUE(CParser(
|
|
"#if !defined TEST\n"
|
|
"#define CORRECT\n"
|
|
"#endif"
|
|
).LexicalCheck().GetEnvironment().Defined("CORRECT"));
|
|
EXPECT_TRUE(CParser(
|
|
"#define TEST\n"
|
|
"#if defined TEST\n"
|
|
"#define CORRECT\n"
|
|
"#endif"
|
|
).LexicalCheck().GetEnvironment().Defined("CORRECT"));
|
|
EXPECT_THROW(CParser(
|
|
"#if defined\n"
|
|
"#define CORRECT\n"
|
|
"#endif"
|
|
).LexicalCheck(), CCompileException);
|
|
}
|
|
|
|
TEST_F(CPreprocTest, PreprocessorElif)
|
|
{
|
|
// Parse preprocessor
|
|
CParser parser(
|
|
"#define TEST\n"
|
|
"#define AB(c) AB##c\n"
|
|
"#ifdef TOAST\n"
|
|
"#define XY\n"
|
|
"#elif defined(TEST)\n"
|
|
"#define VW\n"
|
|
"#endif"
|
|
);
|
|
parser.LexicalCheck();
|
|
|
|
EXPECT_TRUE(parser.GetEnvironment().Defined("TEST"));
|
|
EXPECT_FALSE(parser.GetEnvironment().Defined("XY"));
|
|
EXPECT_TRUE(parser.GetEnvironment().Defined("VW"));
|
|
}
|
|
|
|
TEST_F(CPreprocTest, PreprocessorIfDef)
|
|
{
|
|
// Parse preprocessor
|
|
CParser parser(
|
|
"#define TEST\n"
|
|
"#define AB(c) AB##c\n"
|
|
"#ifdef TEST\n"
|
|
"#define XY\n"
|
|
"#ifdef AB\n"
|
|
"#define VW\n"
|
|
"#endif\n"
|
|
"#endif\n"
|
|
"#ifdef TEST2\n"
|
|
"#define MN\n"
|
|
"#endif"
|
|
);
|
|
parser.LexicalCheck();
|
|
|
|
EXPECT_TRUE(parser.GetEnvironment().Defined("TEST"));
|
|
EXPECT_TRUE(parser.GetEnvironment().Defined("AB"));
|
|
EXPECT_TRUE(parser.GetEnvironment().Defined("XY"));
|
|
EXPECT_TRUE(parser.GetEnvironment().Defined("VW"));
|
|
EXPECT_FALSE(parser.GetEnvironment().Defined("MD"));
|
|
}
|
|
|
|
TEST_F(CPreprocTest, PreprocessorIfnDef)
|
|
{
|
|
// Parse preprocessor
|
|
CParser parser(
|
|
"#define TEST\n"
|
|
"#define AB(c) AB##c\n"
|
|
"#ifndef TEST\n"
|
|
"#define XY\n"
|
|
"#ifndef AB\n"
|
|
"#define VW\n"
|
|
"#endif\n"
|
|
"#endif\n"
|
|
"#ifndef TEST2\n"
|
|
"#define MN\n"
|
|
"#endif"
|
|
);
|
|
parser.LexicalCheck();
|
|
|
|
EXPECT_TRUE(parser.GetEnvironment().Defined("TEST"));
|
|
EXPECT_TRUE(parser.GetEnvironment().Defined("AB"));
|
|
EXPECT_FALSE(parser.GetEnvironment().Defined("XY"));
|
|
EXPECT_FALSE(parser.GetEnvironment().Defined("VW"));
|
|
EXPECT_TRUE(parser.GetEnvironment().Defined("MN"));
|
|
}
|
|
|
|
TEST_F(CPreprocTest, PreprocessorElse)
|
|
{
|
|
// Parse preprocessor
|
|
CParser parser(
|
|
"#define TEST\n"
|
|
"#ifdef TEST\n"
|
|
"#define XY\n"
|
|
"#else\n"
|
|
"#define VW\n"
|
|
"#endif\n"
|
|
"#ifdef TEST2\n"
|
|
"#define ZA\n"
|
|
"#else\n"
|
|
"#define TR\n"
|
|
"#endif"
|
|
);
|
|
parser.LexicalCheck();
|
|
|
|
EXPECT_TRUE(parser.GetEnvironment().Defined("TEST"));
|
|
EXPECT_TRUE(parser.GetEnvironment().Defined("XY"));
|
|
EXPECT_FALSE(parser.GetEnvironment().Defined("VW"));
|
|
EXPECT_FALSE(parser.GetEnvironment().Defined("ZA"));
|
|
EXPECT_TRUE(parser.GetEnvironment().Defined("TR"));
|
|
}
|
|
|
|
TEST_F(CPreprocTest, ProcessVerbatim)
|
|
{
|
|
// Parse preprocessor
|
|
CParser parser(
|
|
"#verbatim const int i = 10\n"
|
|
"#verbatim const int j = 10\\n"
|
|
"#verbatim const int k = 10\n"
|
|
"#verbatim #define L\n"
|
|
"#verbatim #include <string>"
|
|
);
|
|
EXPECT_NO_THROW(parser.LexicalCheck());
|
|
}
|
|
|
|
TEST_F(CPreprocTest, ProcessVerbatimBlock)
|
|
{
|
|
// Parse preprocessor
|
|
CParser parser(
|
|
"#verbatim_begin ignored text\n"
|
|
"const int j = 10\\n"
|
|
"const int k = 10\n"
|
|
"#define L\n"
|
|
"#include <string>\n"
|
|
"#verbatim_end ignored text"
|
|
);
|
|
EXPECT_NO_THROW(parser.LexicalCheck());
|
|
|
|
CParser parser2(
|
|
"#verbatim_end ignored text"
|
|
);
|
|
EXPECT_THROW(parser2.LexicalCheck(), CCompileException);
|
|
|
|
CParser parser3(
|
|
"#verbatim_begin ignored text\n"
|
|
"const int j = 10\\n"
|
|
"const int k = 10\n"
|
|
"#define L\n"
|
|
"#include <string>\n"
|
|
);
|
|
EXPECT_THROW(parser3.LexicalCheck(), CCompileException);
|
|
}
|
|
|