/*
* simplecpp - A simple and high-fidelity C/C++ preprocessor library
* Copyright (C) 2016-2023 simplecpp team
*/
#include "simplecpp.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifndef SIMPLECPP_TEST_SOURCE_DIR
#error "SIMPLECPP_TEST_SOURCE_DIR is not defined."
#endif
#define STRINGIZE_(x) #x
#define STRINGIZE(x) STRINGIZE_(x)
static const std::string testSourceDir = SIMPLECPP_TEST_SOURCE_DIR;
static int numberOfFailedAssertions = 0;
#define ASSERT_EQUALS(expected, actual) (assertEquals((expected), (actual), __LINE__))
#define ASSERT_THROW_EQUALS(stmt, e, expected) do { try { stmt; assertThrowFailed(__LINE__); } catch (const e& ex) { assertEquals((expected), (ex.what()), __LINE__); } } while (false)
static std::string pprint(const std::string &in)
{
std::string ret;
for (std::string::size_type i = 0; i < in.size(); ++i) {
if (in[i] == '\n')
ret += "\\n";
ret += in[i];
}
return ret;
}
static int assertEquals(const std::string &expected, const std::string &actual, int line)
{
if (expected != actual) {
numberOfFailedAssertions++;
std::cerr << "------ assertion failed ---------" << std::endl;
std::cerr << "line test.cpp:" << line << std::endl;
std::cerr << "expected:" << pprint(expected) << std::endl;
std::cerr << "actual:" << pprint(actual) << std::endl;
}
return (expected == actual);
}
static int assertEquals(const long long &expected, const long long &actual, int line)
{
return assertEquals(std::to_string(expected), std::to_string(actual), line);
}
static void assertThrowFailed(int line)
{
numberOfFailedAssertions++;
std::cerr << "------ assertion failed ---------" << std::endl;
std::cerr << "line " << line << std::endl;
std::cerr << "exception not thrown" << std::endl;
}
static void testcase(const std::string &name, void (*f)(), int argc, char * const *argv)
{
if (argc == 1)
f();
else {
for (int i = 1; i < argc; i++) {
if (name == argv[i])
f();
}
}
}
#define TEST_CASE(F) (testcase(#F, F, argc, argv))
static simplecpp::TokenList makeTokenList(const char code[], std::size_t size, std::vector<:string> &filenames, const std::string &filename=std::string(), simplecpp::OutputList *outputList=nullptr)
{
std::istringstream istr(std::string(code, size));
return simplecpp::TokenList(istr,filenames,filename,outputList);
}
static simplecpp::TokenList makeTokenList(const char code[], std::vector<:string> &filenames, const std::string &filename=std::string(), simplecpp::OutputList *outputList=nullptr)
{
return makeTokenList(code, strlen(code), filenames, filename, outputList);
}
static std::string readfile(const char code[], simplecpp::OutputList *outputList=nullptr)
{
std::vector<:string> files;
return makeTokenList(code,files,std::string(),outputList).stringify();
}
static std::string readfile(const char code[], std::size_t size, simplecpp::OutputList *outputList=nullptr)
{
std::vector<:string> files;
return makeTokenList(code,size,files,std::string(),outputList).stringify();
}
static std::string preprocess(const char code[], const simplecpp::DUI &dui, simplecpp::OutputList *outputList)
{
std::vector<:string> files;
simplecpp::FileDataCache cache;
simplecpp::TokenList tokens = makeTokenList(code,files);
if (dui.removeComments)
tokens.removeComments();
simplecpp::TokenList tokens2(files);
simplecpp::preprocess(tokens2, tokens, files, cache, dui, outputList);
simplecpp::cleanup(cache);
return tokens2.stringify();
}
static std::string preprocess(const char code[])
{
return preprocess(code, simplecpp::DUI(), nullptr);
}
static std::string preprocess(const char code[], const simplecpp::DUI &dui)
{
return preprocess(code, dui, nullptr);
}
static std::string preprocess(const char code[], simplecpp::OutputList *outputList)
{
return preprocess(code, simplecpp::DUI(), outputList);
}
static std::string toString(const simplecpp::OutputList &outputList)
{
std::ostringstream ostr;
for (const simplecpp::Output &output : outputList) {
ostr << "file" << output.location.fileIndex << ',' << output.location.line << ',';
switch (output.type) {
case simplecpp::Output::Type::ERROR:
ostr << "#error,";
break;
case simplecpp::Output::Type::WARNING:
ostr << "#warning,";
break;
case simplecpp::Output::Type::MISSING_HEADER:
ostr << "missing_header,";
break;
case simplecpp::Output::Type::INCLUDE_NESTED_TOO_DEEPLY:
ostr << "include_nested_too_deeply,";
break;
case simplecpp::Output::Type::SYNTAX_ERROR:
ostr << "syntax_error,";
break;
case simplecpp::Output::Type::PORTABILITY_BACKSLASH:
ostr << "portability_backslash,";
break;
case simplecpp::Output::Type::UNHANDLED_CHAR_ERROR:
ostr << "unhandled_char_error,";
break;
case simplecpp::Output::Type::EXPLICIT_INCLUDE_NOT_FOUND:
ostr << "explicit_include_not_found,";
break;
case simplecpp::Output::Type::FILE_NOT_FOUND:
ostr << "file_not_found,";
break;
case simplecpp::Output::Type::DUI_ERROR:
ostr << "dui_error,";
break;
}
ostr << output.msg << '\n';
}
return ostr.str();
}
static void backslash()
{
// preprocessed differently
simplecpp::OutputList outputList;
readfile("//123 \\\n456", &outputList);
ASSERT_EQUALS("", toString(outputList));
readfile("//123 \\ \n456", &outputList);
ASSERT_EQUALS("file0,1,portability_backslash,Combination 'backslash space newline' is not portable.\n", toString(outputList));
outputList.clear();
readfile("#define A \\\n123", &outputList);
ASSERT_EQUALS("", toString(outputList));
readfile("#define A \\ \n123", &outputList);
ASSERT_EQUALS("file0,1,portability_backslash,Combination 'backslash space newline' is not portable.\n", toString(outputList));
}
static void builtin()
{
ASSERT_EQUALS("\"\" 1 0", preprocess("__FILE__ __LINE__ __COUNTER__"));
ASSERT_EQUALS("\n\n3", preprocess("\n\n__LINE__"));
ASSERT_EQUALS("\n\n0", preprocess("\n\n__COUNTER__"));
ASSERT_EQUALS("\n\n0 1", preprocess("\n\n__COUNTER__ __COUNTER__"));
ASSERT_EQUALS("\n0 + 0", preprocess("#define A(c) c+c\n"
"A(__COUNTER__)\n"));
ASSERT_EQUALS("\n0 + 0 + 1", preprocess("#define A(c) c+c+__COUNTER__\n"
"A(__COUNTER__)\n"));
}
static std::string testConstFold(const char code[])
{
try {
std::vector<:string> files;
simplecpp::TokenList expr = makeTokenList(code, files);
expr.constFold();
return expr.stringify();
} catch (std::exception &) {
return "exception";
}
}
static void characterLiteral()
{
ASSERT_EQUALS('A', simplecpp::characterLiteralToLL("'A'"));
ASSERT_EQUALS('\'', simplecpp::characterLiteralToLL("'\\''"));
ASSERT_EQUALS('\"', simplecpp::characterLiteralToLL("'\\\"'"));
ASSERT_EQUALS('\?', simplecpp::characterLiteralToLL("'\\?'"));
ASSERT_EQUALS('\\', simplecpp::characterLiteralToLL("'\\\\'"));
ASSERT_EQUALS('\a', simplecpp::characterLiteralToLL("'\\a'"));
ASSERT_EQUALS('\b', simplecpp::characterLiteralToLL("'\\b'"));
ASSERT_EQUALS('\f', simplecpp::characterLiteralToLL("'\\f'"));
ASSERT_EQUALS('\n', simplecpp::characterLiteralToLL("'\\n'"));
ASSERT_EQUALS('\r', simplecpp::characterLiteralToLL("'\\r'"));
ASSERT_EQUALS('\t', simplecpp::characterLiteralToLL("'\\t'"));
ASSERT_EQUALS('\v', simplecpp::characterLiteralToLL("'\\v'"));
// GCC extension for ESC character
ASSERT_EQUALS(0x1b, simplecpp::characterLiteralToLL("'\\e'"));
ASSERT_EQUALS(0x1b, simplecpp::characterLiteralToLL("'\\E'"));
// more obscure GCC extensions
ASSERT_EQUALS('(', simplecpp::characterLiteralToLL("'\\('"));
ASSERT_EQUALS('[', simplecpp::characterLiteralToLL("'\\['"));
ASSERT_EQUALS('{', simplecpp::characterLiteralToLL("'\\{'"));
ASSERT_EQUALS('%', simplecpp::characterLiteralToLL("'\\%'"));
ASSERT_EQUALS('\0', simplecpp::characterLiteralToLL("'\\0'"));
ASSERT_EQUALS('\1', simplecpp::characterLiteralToLL("'\\1'"));
ASSERT_EQUALS('\10', simplecpp::characterLiteralToLL("'\\10'"));
ASSERT_EQUALS('\010', simplecpp::characterLiteralToLL("'\\010'"));
ASSERT_EQUALS('\377', simplecpp::characterLiteralToLL("'\\377'"));
ASSERT_EQUALS('\x0', simplecpp::characterLiteralToLL("'\\x0'"));
ASSERT_EQUALS('\x10', simplecpp::characterLiteralToLL("'\\x10'"));
ASSERT_EQUALS('\xff', simplecpp::characterLiteralToLL("'\\xff'"));
ASSERT_EQUALS('\u0012', simplecpp::characterLiteralToLL("'\\u0012'"));
ASSERT_EQUALS('\U00000012', simplecpp::characterLiteralToLL("'\\U00000012'"));
ASSERT_EQUALS((static_cast(static_cast('b')) << 8) | static_cast('c'), simplecpp::characterLiteralToLL("'bc'"));
ASSERT_EQUALS((static_cast(static_cast('\x23')) << 8) | static_cast('\x45'), simplecpp::characterLiteralToLL("'\\x23\\x45'"));
ASSERT_EQUALS((static_cast(static_cast('\11')) << 8) | static_cast('\222'), simplecpp::characterLiteralToLL("'\\11\\222'"));
ASSERT_EQUALS((static_cast(static_cast('\a')) << 8) | static_cast('\b'), simplecpp::characterLiteralToLL("'\\a\\b'"));
if (sizeof(int) <= 4)
ASSERT_EQUALS(-1, simplecpp::characterLiteralToLL("'\\xff\\xff\\xff\\xff'"));
else
ASSERT_EQUALS(0xffffffff, simplecpp::characterLiteralToLL("'\\xff\\xff\\xff\\xff'"));
ASSERT_EQUALS('A', simplecpp::characterLiteralToLL("u8'A'"));
ASSERT_EQUALS('A', simplecpp::characterLiteralToLL("u'A'"));
ASSERT_EQUALS('A', simplecpp::characterLiteralToLL("L'A'"));
ASSERT_EQUALS('A', simplecpp::characterLiteralToLL("U'A'"));
ASSERT_EQUALS(0xff, simplecpp::characterLiteralToLL("u8'\\xff'"));
ASSERT_EQUALS(0xff, simplecpp::characterLiteralToLL("u'\\xff'"));
ASSERT_EQUALS(0xff, simplecpp::characterLiteralToLL("L'\\xff'"));
ASSERT_EQUALS(0xff, simplecpp::characterLiteralToLL("U'\\xff'"));
ASSERT_EQUALS(0xfedc, simplecpp::characterLiteralToLL("u'\\xfedc'"));
ASSERT_EQUALS(0xfedcba98, simplecpp::characterLiteralToLL("L'\\xfedcba98'"));
ASSERT_EQUALS(0xfedcba98, simplecpp::characterLiteralToLL("U'\\xfedcba98'"));
ASSERT_EQUALS(0x12, simplecpp::characterLiteralToLL("u8'\\u0012'"));
ASSERT_EQUALS(0x1234, simplecpp::characterLiteralToLL("u'\\u1234'"));
ASSERT_EQUALS(0x00012345, simplecpp::characterLiteralToLL("L'\\U00012345'"));
ASSERT_EQUALS(0x00012345, simplecpp::characterLiteralToLL("U'\\U00012345'"));
#ifdef __GNUC__
// BEGIN Implementation-specific results
ASSERT_EQUALS('AB', simplecpp::characterLiteralToLL("'AB'"));
ASSERT_EQUALS('ABC', simplecpp::characterLiteralToLL("'ABC'"));
ASSERT_EQUALS('ABCD', simplecpp::characterLiteralToLL("'ABCD'"));
ASSERT_EQUALS('\134t', simplecpp::characterLiteralToLL("'\\134t'")); // cppcheck ticket #7452
// END Implementation-specific results
#endif
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("'\\9'"), std::runtime_error, "invalid escape sequence");
// Input is manually encoded to (escaped) UTF-8 byte sequences
// to avoid dependence on source encoding used for this file
ASSERT_EQUALS(0xb5, simplecpp::characterLiteralToLL("U'\302\265'"));
ASSERT_EQUALS(0x157, simplecpp::characterLiteralToLL("U'\305\227'"));
ASSERT_EQUALS(0xff0f, simplecpp::characterLiteralToLL("U'\357\274\217'"));
ASSERT_EQUALS(0x3042, simplecpp::characterLiteralToLL("U'\343\201\202'"));
ASSERT_EQUALS(0x13000, simplecpp::characterLiteralToLL("U'\360\223\200\200'"));
ASSERT_EQUALS(0xb5, simplecpp::characterLiteralToLL("L'\302\265'"));
ASSERT_EQUALS(0x157, simplecpp::characterLiteralToLL("L'\305\227'"));
ASSERT_EQUALS(0xff0f, simplecpp::characterLiteralToLL("L'\357\274\217'"));
ASSERT_EQUALS(0x3042, simplecpp::characterLiteralToLL("L'\343\201\202'"));
ASSERT_EQUALS(0x13000, simplecpp::characterLiteralToLL("L'\360\223\200\200'"));
ASSERT_EQUALS(0xb5, simplecpp::characterLiteralToLL("u'\302\265'"));
ASSERT_EQUALS(0x157, simplecpp::characterLiteralToLL("u'\305\227'"));
ASSERT_EQUALS(0xff0f, simplecpp::characterLiteralToLL("u'\357\274\217'"));
ASSERT_EQUALS(0x3042, simplecpp::characterLiteralToLL("u'\343\201\202'"));
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("u'\360\223\200\200'"), std::runtime_error, "code point too large");
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("u8'\302\265'"), std::runtime_error, "code point too large");
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("u8'\305\227'"), std::runtime_error, "code point too large");
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("u8'\357\274\217'"), std::runtime_error, "code point too large");
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("u8'\343\201\202'"), std::runtime_error, "code point too large");
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("u8'\360\223\200\200'"), std::runtime_error, "code point too large");
ASSERT_EQUALS('\x89', simplecpp::characterLiteralToLL("'\x89'"));
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("U'\x89'"), std::runtime_error, "assumed UTF-8 encoded source, but sequence is invalid");
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("U'\xf4\x90\x80\x80'"), std::runtime_error, "code point too large");
// following examples based on https://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
ASSERT_EQUALS(0x80, simplecpp::characterLiteralToLL("U'\xc2\x80'"));
ASSERT_EQUALS(0x800, simplecpp::characterLiteralToLL("U'\xe0\xa0\x80'"));
ASSERT_EQUALS(0x10000, simplecpp::characterLiteralToLL("U'\xf0\x90\x80\x80'"));
ASSERT_EQUALS(0x7f, simplecpp::characterLiteralToLL("U'\x7f'"));
ASSERT_EQUALS(0x7ff, simplecpp::characterLiteralToLL("U'\xdf\xbf'"));
ASSERT_EQUALS(0xffff, simplecpp::characterLiteralToLL("U'\xef\xbf\xbf'"));
ASSERT_EQUALS(0xd7ff, simplecpp::characterLiteralToLL("U'\xed\x9f\xbf'"));
ASSERT_EQUALS(0xe000, simplecpp::characterLiteralToLL("U'\xee\x80\x80'"));
ASSERT_EQUALS(0xfffd, simplecpp::characterLiteralToLL("U'\xef\xbf\xbd'"));
ASSERT_EQUALS(0x10ffff, simplecpp::characterLiteralToLL("U'\xf4\x8f\xbf\xbf'"));
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("U'\x80'"), std::runtime_error, "assumed UTF-8 encoded source, but sequence is invalid");
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("U'\x80\x8f'"), std::runtime_error, "assumed UTF-8 encoded source, but sequence is invalid");
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("U'\x80\x8f\x8f'"), std::runtime_error, "assumed UTF-8 encoded source, but sequence is invalid");
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("U'\x80\x8f\x8f\x8f'"), std::runtime_error, "assumed UTF-8 encoded source, but sequence is invalid");
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("U'\xbf'"), std::runtime_error, "assumed UTF-8 encoded source, but sequence is invalid");
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("U'\xbf\x8f'"), std::runtime_error, "assumed UTF-8 encoded source, but sequence is invalid");
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("U'\xbf\x8f\x8f'"), std::runtime_error, "assumed UTF-8 encoded source, but sequence is invalid");
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("U'\xbf\x8f\x8f\x8f'"), std::runtime_error, "assumed UTF-8 encoded source, but sequence is invalid");
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("U'\xc0'"), std::runtime_error, "assumed UTF-8 encoded source, but sequence is invalid");
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("U'\xc0 '"), std::runtime_error, "assumed UTF-8 encoded source, but sequence is invalid");
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("U'\xe0\x8f'"), std::runtime_error, "assumed UTF-8 encoded source, but sequence is invalid");
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("U'\xe0\x8f '"), std::runtime_error, "assumed UTF-8 encoded source, but sequence is invalid");
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("U'\xf0\x8f\x8f'"), std::runtime_error, "assumed UTF-8 encoded source, but sequence is invalid");
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("U'\xf0\x8f\x8f '"), std::runtime_error, "assumed UTF-8 encoded source, but sequence is invalid");
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("U'\xf8'"), std::runtime_error, "assumed UTF-8 encoded source, but sequence is invalid");
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("U'\xff'"), std::runtime_error, "assumed UTF-8 encoded source, but sequence is invalid");
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("U'\xc0\xaf'"), std::runtime_error, "assumed UTF-8 encoded source, but sequence is invalid");
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("U'\xe0\x80\xaf'"), std::runtime_error, "assumed UTF-8 encoded source, but sequence is invalid");
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("U'\xf0\x80\x80\xaf'"), std::runtime_error, "assumed UTF-8 encoded source, but sequence is invalid");
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("U'\xc1\xbf'"), std::runtime_error, "assumed UTF-8 encoded source, but sequence is invalid");
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("U'\xe0\x9f\xbf'"), std::runtime_error, "assumed UTF-8 encoded source, but sequence is invalid");
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("U'\xf0\x8f\xbf\xbf'"), std::runtime_error, "assumed UTF-8 encoded source, but sequence is invalid");
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("U'\xc0\x80'"), std::runtime_error, "assumed UTF-8 encoded source, but sequence is invalid");
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("U'\xe0\x80\x80'"), std::runtime_error, "assumed UTF-8 encoded source, but sequence is invalid");
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("U'\xf0\x80\x80\x80'"), std::runtime_error, "assumed UTF-8 encoded source, but sequence is invalid");
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("U'\xed\xa0\x80'"), std::runtime_error, "assumed UTF-8 encoded source, but sequence is invalid");
ASSERT_THROW_EQUALS(simplecpp::characterLiteralToLL("U'\xed\xbf\xbf'"), std::runtime_error, "assumed UTF-8 encoded source, but sequence is invalid");
}
static void combineOperators_floatliteral()
{
ASSERT_EQUALS("1.", preprocess("1."));
ASSERT_EQUALS("1.f", preprocess("1.f"));
ASSERT_EQUALS(".1", preprocess(".1"));
ASSERT_EQUALS(".1f", preprocess(".1f"));
ASSERT_EQUALS("3.1", preprocess("3.1"));
ASSERT_EQUALS("1E7", preprocess("1E7"));
ASSERT_EQUALS("1E-7", preprocess("1E-7"));
ASSERT_EQUALS("1E+7", preprocess("1E+7"));
ASSERT_EQUALS("1.e+7", preprocess("1.e+7"));
ASSERT_EQUALS("0x1E + 7", preprocess("0x1E+7"));
ASSERT_EQUALS("0x1ffp10", preprocess("0x1ffp10"));
ASSERT_EQUALS("0x0p-1", preprocess("0x0p-1"));
ASSERT_EQUALS("0x1.p0", preprocess("0x1.p0"));
ASSERT_EQUALS("0xf.p-1", preprocess("0xf.p-1"));
ASSERT_EQUALS("0x1.2p3", preprocess("0x1.2p3"));
ASSERT_EQUALS("0x1.ap3", preprocess("0x1.ap3"));
ASSERT_EQUALS("0x1.2ap3", preprocess("0x1.2ap3"));
ASSERT_EQUALS("0x1p+3", preprocess("0x1p+3"));
ASSERT_EQUALS("0x1p+3f", preprocess("0x1p+3f"));
ASSERT_EQUALS("0x1p+3L", preprocess("0x1p+3L"));
ASSERT_EQUALS("1p + 3", preprocess("1p+3"));
ASSERT_EQUALS("1.0_a . b", preprocess("1.0_a.b"));
ASSERT_EQUALS("1_a . b", preprocess("1_a.b"));
}
static void combineOperators_increment()
{
ASSERT_EQUALS("; ++ x ;", preprocess(";++x;"));
ASSERT_EQUALS("; x ++ ;", preprocess(";x++;"));
ASSERT_EQUALS("1 + + 2", preprocess("1++2"));
}
static void combineOperators_coloncolon()
{
ASSERT_EQUALS("x ? y : :: z", preprocess("x ? y : ::z"));
}
static void combineOperators_andequal()
{
ASSERT_EQUALS("x &= 2 ;", preprocess("x &= 2;"));
ASSERT_EQUALS("void f ( x & = 2 ) ;", preprocess("void f(x &= 2);"));
ASSERT_EQUALS("f ( x &= 2 ) ;", preprocess("f(x &= 2);"));
}
static void combineOperators_ellipsis()
{
ASSERT_EQUALS("void f ( int , ... ) ;", preprocess("void f(int, ...);"));
ASSERT_EQUALS("void f ( ) { switch ( x ) { case 1 ... 4 : } }", preprocess("void f() { switch(x) { case 1 ... 4: } }"));
}
static void comment()
{
ASSERT_EQUALS("// abc", readfile("// abc"));
ASSERT_EQUALS("", preprocess("// abc"));
ASSERT_EQUALS("/*\n\n*/abc", readfile("/*\n\n*/abc"));
ASSERT_EQUALS("\n\nabc", preprocess("/*\n\n*/abc"));
ASSERT_EQUALS("* p = a / * b / * c ;", readfile("*p=a/ *b/ *c;"));
ASSERT_EQUALS("* p = a / * b / * c ;", preprocess("*p=a/ *b/ *c;"));
}
static void comment_multiline()
{
simplecpp::DUI dui;
dui.removeComments = true;
const char code[] = "#define ABC {// \\\n"
"}\n"
"void f() ABC\n";
ASSERT_EQUALS("\n\nvoid f ( ) {", preprocess(code, dui));
const char code1[] = "#define ABC {// \\\r\n"
"}\n"
"void f() ABC\n";
ASSERT_EQUALS("\n\nvoid f ( ) {", preprocess(code1, dui));
const char code2[] = "#define A 1// \\\r"
"\r"
"2\r"
"A\r";
ASSERT_EQUALS("\n\n2\n1", preprocess(code2, dui));
const char code3[] = "void f() {// \\ \n}\n";
ASSERT_EQUALS("void f ( ) {", preprocess(code3, dui));
const char code4[] = "void f() {// \\\\\\\t\t\n}\n";
ASSERT_EQUALS("void f ( ) {", preprocess(code4, dui));
const char code5[] = "void f() {// \\\\\\a\n}\n";
ASSERT_EQUALS("void f ( ) {\n}", preprocess(code5, dui));
const char code6[] = "void f() {// \\\n\n\n}\n";
ASSERT_EQUALS("void f ( ) {\n\n\n}", preprocess(code6, dui));
// #471 ensure there is newline in comment so that line-splicing can be detected by tools
ASSERT_EQUALS("// abc\ndef", readfile("// abc\\\ndef"));
}
static void constFold()
{
ASSERT_EQUALS("7", testConstFold("1+2*3"));
ASSERT_EQUALS("15", testConstFold("1+2*(3+4)"));
ASSERT_EQUALS("123", testConstFold("+123"));
ASSERT_EQUALS("1", testConstFold("-123<1"));
ASSERT_EQUALS("6", testConstFold("14 & 7"));
ASSERT_EQUALS("29", testConstFold("13 ^ 16"));
ASSERT_EQUALS("25", testConstFold("24 | 1"));
ASSERT_EQUALS("2", testConstFold("1?2:3"));
ASSERT_EQUALS("24", testConstFold("010+020"));
ASSERT_EQUALS("1", testConstFold("010==8"));
ASSERT_EQUALS("exception", testConstFold("!1 ? 2 :"));
ASSERT_EQUALS("exception", testConstFold("?2:3"));
}
#ifdef __CYGWIN__
static void convertCygwinPath()
{
// absolute paths
ASSERT_EQUALS("X:\\", simplecpp::convertCygwinToWindowsPath("/cygdrive/x")); // initial backslash
ASSERT_EQUALS("X:\\", simplecpp::convertCygwinToWindowsPath("/cygdrive/x/"));
ASSERT_EQUALS("X:\\dir", simplecpp::convertCygwinToWindowsPath("/cygdrive/x/dir"));
ASSERT_EQUALS("X:\\dir\\file", simplecpp::convertCygwinToWindowsPath("/cygdrive/x/dir/file"));
// relative paths
ASSERT_EQUALS("file", simplecpp::convertCygwinToWindowsPath("file"));
ASSERT_EQUALS("dir\\file", simplecpp::convertCygwinToWindowsPath("dir/file"));
ASSERT_EQUALS("..\\dir\\file", simplecpp::convertCygwinToWindowsPath("../dir/file"));
// incorrect Cygwin paths
ASSERT_EQUALS("\\cygdrive", simplecpp::convertCygwinToWindowsPath("/cygdrive"));
ASSERT_EQUALS("\\cygdrive\\", simplecpp::convertCygwinToWindowsPath("/cygdrive/"));
}
#endif
static void define1()
{
const char code[] = "#define A 1+2\n"
"a=A+3;";
ASSERT_EQUALS("# define A 1 + 2\n"
"a = A + 3 ;",
readfile(code));
ASSERT_EQUALS("\na = 1 + 2 + 3 ;",
preprocess(code));
}
static void define2()
{
const char code[] = "#define ADD(A,B) A+B\n"
"ADD(1+2,3);";
ASSERT_EQUALS("# define ADD ( A , B ) A + B\n"
"ADD ( 1 + 2 , 3 ) ;",
readfile(code));
ASSERT_EQUALS("\n1 + 2 + 3 ;",
preprocess(code));
}
static void define3()
{
const char code[] = "#define A 123\n"
"#define B A\n"
"A B";
ASSERT_EQUALS("# define A 123\n"
"# define B A\n"
"A B",
readfile(code));
ASSERT_EQUALS("\n\n123 123",
preprocess(code));
}
static void define4()
{
const char code[] = "#define A 123\n"
"#define B(C) A\n"
"A B(1)";
ASSERT_EQUALS("# define A 123\n"
"# define B ( C ) A\n"
"A B ( 1 )",
readfile(code));
ASSERT_EQUALS("\n\n123 123",
preprocess(code));
}
static void define5()
{
const char code[] = "#define add(x,y) x+y\n"
"add(add(1,2),3)";
ASSERT_EQUALS("\n1 + 2 + 3", preprocess(code));
}
static void define6()
{
const char code[] = "#define A() 1\n"
"A()";
ASSERT_EQUALS("\n1", preprocess(code));
}
static void define7()
{
simplecpp::DUI dui;
dui.removeComments = true;
const char code[] = "#define A(X) X+1\n"
"A(1 /*23*/)";
ASSERT_EQUALS("\n1 + 1", preprocess(code, dui));
}
static void define8() // 6.10.3.10
{
const char code[] = "#define A(X) \n"
"int A[10];";
ASSERT_EQUALS("\nint A [ 10 ] ;", preprocess(code));
}
static void define9()
{
const char code[] = "#define AB ab.AB\n"
"AB.CD\n";
ASSERT_EQUALS("\nab . AB . CD", preprocess(code));
}
static void define10() // don't combine prefix with space in macro
{
const char code[] = "#define A u8 \"a b\"\n"
"A;";
ASSERT_EQUALS("\nu8 \"a b\" ;", preprocess(code));
}
static void define11() // location of expanded argument
{
const char code[] = "#line 4 \"version.h\"\n"
"#define A(x) B(x)\n"
"#define B(x) x\n"
"#define VER A(1)\n"
"\n"
"#line 10 \"cppcheck.cpp\"\n"
"VER;";
ASSERT_EQUALS("\n#line 10 \"cppcheck.cpp\"\n1 ;", preprocess(code));
}
static void define12()
{
const char code[] = "struct foo x = {\n"
" #define V 0\n"
" .x = V,\n"
"};\n";
ASSERT_EQUALS("struct foo x = {\n"
"# define V 0\n"
". x = V ,\n"
"} ;", readfile(code));
ASSERT_EQUALS("struct foo x = {\n"
"\n"
". x = 0 ,\n"
"} ;", preprocess(code));
}
static void define13()
{
const char code[] = "#define M 180.\n"
"extern void g();\n"
"void f(double d) {\n"
" if (d > M) {}\n"
"}\n";
ASSERT_EQUALS("\nextern void g ( ) ;\n"
"void f ( double d ) {\n"
"if ( d > 180. ) { }\n"
"}", preprocess(code));
}
static void define_invalid_1()
{
const char code[] = "#define A(\nB\n";
simplecpp::OutputList outputList;
ASSERT_EQUALS("", preprocess(code, &outputList));
ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define\n", toString(outputList));
}
static void define_invalid_2()
{
const char code[] = "#define\nhas#";
simplecpp::OutputList outputList;
ASSERT_EQUALS("", preprocess(code, &outputList));
ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define\n", toString(outputList));
}
static void define_define_1()
{
const char code[] = "#define A(x) (x+1)\n"
"#define B A(\n"
"B(i))";
ASSERT_EQUALS("\n\n( ( i ) + 1 )", preprocess(code));
}
static void define_define_2()
{
const char code[] = "#define A(m) n=m\n"
"#define B(x) A(x)\n"
"B(0)";
ASSERT_EQUALS("\n\nn = 0", preprocess(code));
}
static void define_define_3()
{
const char code[] = "#define ABC 123\n"
"#define A(B) A##B\n"
"A(BC)";
ASSERT_EQUALS("\n\n123", preprocess(code));
}
static void define_define_4()
{
const char code[] = "#define FOO1()\n"
"#define TEST(FOO) FOO FOO()\n"
"TEST(FOO1)";
ASSERT_EQUALS("\n\nFOO1", preprocess(code));
}
static void define_define_5()
{
const char code[] = "#define X() Y\n"
"#define Y() X\n"
"A: X()()()\n";
// mcpp outputs "A: X()" and gcc/clang/vc outputs "A: Y"
ASSERT_EQUALS("\n\nA : Y", preprocess(code)); // <- match the output from gcc/clang/vc
}
static void define_define_6()
{
const char code1[] = "#define f(a) a*g\n"
"#define g f\n"
"a: f(2)(9)\n";
ASSERT_EQUALS("\n\na : 2 * f ( 9 )", preprocess(code1));
const char code2[] = "#define f(a) a*g\n"
"#define g(a) f(a)\n"
"a: f(2)(9)\n";
ASSERT_EQUALS("\n\na : 2 * 9 * g", preprocess(code2));
}
static void define_define_7()
{
const char code[] = "#define f(x) g(x\n"
"#define g(x) x()\n"
"f(f))\n";
ASSERT_EQUALS("\n\nf ( )", preprocess(code));
}
static void define_define_8() // line break in nested macro call
{
const char code[] = "#define A(X,Y) ((X)*(Y))\n"
"#define B(X,Y) ((X)+(Y))\n"
"B(0,A(255,x+\n"
"y))\n";
ASSERT_EQUALS("\n\n( ( 0 ) + ( ( ( 255 ) * ( x + y ) ) ) )", preprocess(code));
}
static void define_define_9() // line break in nested macro call
{
const char code[] = "#define A(X) X\n"
"#define B(X) X\n"
"A(\nB(dostuff(1,\n2)))\n";
ASSERT_EQUALS("\n\ndostuff ( 1 , 2 )", preprocess(code));
}
static void define_define_10()
{
const char code[] = "#define glue(a, b) a ## b\n"
"#define xglue(a, b) glue(a, b)\n"
"#define AB 1\n"
"#define B B 2\n"
"xglue(A, B)\n";
ASSERT_EQUALS("\n\n\n\n1 2", preprocess(code));
}
static void define_define_11()
{
const char code[] = "#define XY(x, y) x ## y\n"
"#define XY2(x, y) XY(x, y)\n"
"#define PORT XY2(P, 2)\n"
"#define ABC XY2(PORT, DIR)\n"
"ABC;\n";
ASSERT_EQUALS("\n\n\n\nP2DIR ;", preprocess(code));
}
static void define_define_11a()
{
const char code[] = "#define A_B_C 0x1\n"
"#define A_ADDRESS 0x00001000U\n"
"#define A ((uint32_t ) A_ADDRESS)\n"
"#define CONCAT(x, y, z) x ## _ ## y ## _ ## z\n"
"#define TEST_MACRO CONCAT(A, B, C)\n"
"TEST_MACRO\n";
ASSERT_EQUALS("\n\n\n\n\n0x1", preprocess(code));
const char code2[] = "#define ADDER_S(a, b) a + b\n" // #374
"#define ADDER(x) ADDER_S(x)\n"
"#define ARGUMENTS 1, 2\n"
"#define RUN ADDER(ARGUMENTS)\n"
"void f() { RUN; }\n";
ASSERT_EQUALS("\n\n\n\nvoid f ( ) { 1 + 2 ; }", preprocess(code2));
}
static void define_define_12()
{
const char code[] = "#define XY(Z) Z\n"
"#define X(ID) X##ID(0)\n"
"X(Y)\n";
ASSERT_EQUALS("\n\n0", preprocess(code));
}
static void define_define_13() // issue #49 - empty macro
{
const char code[] = "#define f()\n"
"#define t(a) a\n"
"(t(f))\n";
ASSERT_EQUALS("\n\n( f )", preprocess(code));
}
static void define_define_14() // issue #58 - endless recursion
{
const char code[] = "#define z f(w\n"
"#define f()\n"
"#define w f(z\n"
"w\n";
ASSERT_EQUALS("\n\n\nf ( f ( w", preprocess(code)); // Don't crash
}
static void define_define_15() // issue #72 without __VA_ARGS__
{
const char code[] = "#define a f\n"
"#define foo(x,y) a(x,y)\n"
"#define f(x, y) x y\n"
"foo(1,2)";
ASSERT_EQUALS("\n\n\n1 2", preprocess(code));
}
static void define_define_16() // issue #72 with __VA_ARGS__
{
const char code[] = "#define ab(a, b) a##b\n"
"#define foo(...) ab(f, 2) (__VA_ARGS__)\n"
"#define f2(x, y) x y\n"
"foo(1,2)";
ASSERT_EQUALS("\n\n\n1 2", preprocess(code));
}
static void define_define_17()
{
const char code[] = "#define Bar(x) x\n"
"#define Foo Bar(1)\n"
"Bar( Foo ) ;";
ASSERT_EQUALS("\n\n1 ;", preprocess(code));
}
static void define_define_18()
{
const char code[] = "#define FOO(v) BAR(v, 0)\n"
"#define BAR(v, x) (v)\n"
"#define var (p->var)\n"
"FOO(var);";
ASSERT_EQUALS("\n\n\n( ( p -> var ) ) ;", preprocess(code));
}
static void define_define_19() // #292
{
const char code[] = "#define X 1,2,3\n"
"#define Foo(A, B) A\n"
"#define Bar Foo(X, 0)\n"
"Bar\n";
ASSERT_EQUALS("\n\n\n1 , 2 , 3", preprocess(code));
}
static void define_define_20() // #384 arg contains comma
{
const char code[] = "#define Z_IS_ENABLED1(config_macro) Z_IS_ENABLED2(_XXXX##config_macro)\n"
"#define _XXXX1 _YYYY,\n"
"#define Z_IS_ENABLED2(one_or_two_args) Z_IS_ENABLED3(one_or_two_args 1, 0)\n"
"#define Z_IS_ENABLED3(ignore_this, val, ...) val\n"
"#define IS_ENABLED(config_macro) Z_IS_ENABLED1(config_macro)\n"
"#define FEATURE 1\n"
"a = IS_ENABLED(FEATURE)\n";
ASSERT_EQUALS("\n\n\n\n\n\na = 1", preprocess(code));
}
static void define_define_21() // #397 DEBRACKET macro
{
const char code1[] = "#define A(val) B val\n"
"#define B(val) val\n"
"A((2))\n";
ASSERT_EQUALS("\n\n2", preprocess(code1));
const char code2[] = "#define x (2)\n"
"#define A B x\n"
"#define B(val) val\n"
"A\n";
ASSERT_EQUALS("\n\n\nB ( 2 )", preprocess(code2));
const char code3[] = "#define __GET_ARG2_DEBRACKET(ignore_this, val, ...) __DEBRACKET val\n"
"#define __DEBRACKET(...) __VA_ARGS__\n"
"#5 \"a.c\"\n"
"__GET_ARG2_DEBRACKET(432 (33), (B))\n";
ASSERT_EQUALS("\n#line 5 \"a.c\"\nB", preprocess(code3));
}
static void define_define_22() // #400 inner macro not expanded after hash hash
{
const char code[] = "#define FOO(a) CAT(DO, STUFF)(1,2)\n"
"#define DOSTUFF(a, b) CAT(3, 4)\n"
"#define CAT(a, b) a##b\n"
"FOO(1)\n";
ASSERT_EQUALS("\n\n\n34", preprocess(code));
}
static void define_define_23() // #403 crash (infinite recursion)
{
const char code[] = "#define C_(x, y) x ## y\n"
"#define C(x, y) C_(x, y)\n"
"#define X(func) C(Y, C(func, Z))\n"
"#define die X(die)\n"
"die(void);\n";
ASSERT_EQUALS("\n\n\n\nYdieZ ( void ) ;", preprocess(code));
}
static void define_va_args_1()
{
const char code[] = "#define A(fmt...) dostuff(fmt)\n"
"A(1,2);";
ASSERT_EQUALS("\ndostuff ( 1 , 2 ) ;", preprocess(code));
}
static void define_va_args_2()
{
const char code[] = "#define A(X,...) X(#__VA_ARGS__)\n"
"A(f,123);";
ASSERT_EQUALS("\nf ( \"123\" ) ;", preprocess(code));
}
static void define_va_args_3() // min number of arguments
{
const char code[] = "#define A(x, y, z...) 1\n"
"A(1, 2)\n";
ASSERT_EQUALS("\n1", preprocess(code));
}
static void define_va_args_4() // cppcheck trac #9754
{
const char code[] = "#define A(x, y, ...) printf(x, y, __VA_ARGS__)\n"
"A(1, 2)\n";
ASSERT_EQUALS("\nprintf ( 1 , 2 )", preprocess(code));
}
static void define_va_opt_1()
{
const char code[] = "#define p1(fmt, args...) printf(fmt __VA_OPT__(,) args)\n"
"p1(\"hello\");\n"
"p1(\"%s\", \"hello\");\n";
ASSERT_EQUALS("\nprintf ( \"hello\" ) ;\n"
"printf ( \"%s\" , \"hello\" ) ;",
preprocess(code));
}
static void define_va_opt_2()
{
const char code[] = "#define err(...)\\\n"
"__VA_OPT__(\\\n"
"printf(__VA_ARGS__);\\\n"
")\n"
"#define err2(something, ...) __VA_OPT__(err(__VA_ARGS__))\n"
"err2(test)\n"
"err2(test, \"%d\", 2)\n";
ASSERT_EQUALS("\n\n\n\n\n\nprintf ( \"%d\" , 2 ) ;", preprocess(code));
}
static void define_va_opt_3()
{
// non-escaped newline without closing parenthesis
const char code1[] = "#define err(...) __VA_OPT__(printf( __VA_ARGS__);\n"
")\n"
"err()";
simplecpp::OutputList outputList;
ASSERT_EQUALS("", preprocess(code1, &outputList));
ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define, In definition of 'err': Missing closing parenthesis for __VA_OPT__\n",
toString(outputList));
outputList.clear();
// non-escaped newline without open parenthesis
const char code2[] = "#define err(...) __VA_OPT__\n"
"(something)\n"
"err()";
ASSERT_EQUALS("", preprocess(code2, &outputList));
ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define, In definition of 'err': Missing opening parenthesis for __VA_OPT__\n",
toString(outputList));
}
static void define_va_opt_4()
{
// missing parenthesis
const char code1[] = "#define err(...) __VA_OPT__ something\n"
"err()";
simplecpp::OutputList outputList;
ASSERT_EQUALS("", preprocess(code1, &outputList));
ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define, In definition of 'err': Missing opening parenthesis for __VA_OPT__\n",
toString(outputList));
outputList.clear();
// missing open parenthesis
const char code2[] = "#define err(...) __VA_OPT__ something)\n"
"err()";
ASSERT_EQUALS("", preprocess(code2, &outputList));
ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define, In definition of 'err': Missing opening parenthesis for __VA_OPT__\n",
toString(outputList));
}
static void define_va_opt_5()
{
// parenthesis not directly proceeding __VA_OPT__
const char code[] = "#define err(...) __VA_OPT__ something (something)\n"
"err()";
simplecpp::OutputList outputList;
ASSERT_EQUALS("", preprocess(code, &outputList));
ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define, In definition of 'err': Missing opening parenthesis for __VA_OPT__\n",
toString(outputList));
}
static void define_va_opt_6()
{
// nested __VA_OPT__
const char code[] = "#define err(...) __VA_OPT__(__VA_OPT__(something))\n"
"err()";
simplecpp::OutputList outputList;
ASSERT_EQUALS("", preprocess(code, &outputList));
ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define, In definition of 'err': __VA_OPT__ cannot be nested\n",
toString(outputList));
}
static void define_va_opt_7()
{
// eof in __VA_OPT__
const char code1[] = "#define err(...) __VA_OPT__";
simplecpp::OutputList outputList;
ASSERT_EQUALS("", preprocess(code1, &outputList));
ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define, In definition of 'err': Missing opening parenthesis for __VA_OPT__\n",
toString(outputList));
outputList.clear();
const char code2[] = "#define err(...) __VA_OPT__(";
ASSERT_EQUALS("", preprocess(code2, &outputList));
ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define, In definition of 'err': Missing closing parenthesis for __VA_OPT__\n",
toString(outputList));
outputList.clear();
const char code3[] = "#define err(...) __VA_OPT__(x";
ASSERT_EQUALS("", preprocess(code3, &outputList));
ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define, In definition of 'err': Missing closing parenthesis for __VA_OPT__\n",
toString(outputList));
}
static void define_va_opt_8()
{
const char code[] = "#define f(...) #__VA_OPT__(x)\n"
"const char* v1 = f();";
simplecpp::OutputList outputList;
ASSERT_EQUALS("\nconst char * v1 = \"\" ;", preprocess(code, &outputList));
ASSERT_EQUALS("", toString(outputList));
}
static void define_ifdef()
{
const char code[] = "#define A(X) X\n"
"A(1\n"
"#ifdef CFG\n"
"#endif\n"
")\n";
simplecpp::OutputList outputList;
ASSERT_EQUALS("", preprocess(code, &outputList));
ASSERT_EQUALS("file0,3,syntax_error,failed to expand 'A', it is invalid to use a preprocessor directive as macro parameter\n", toString(outputList));
}
static void pragma_backslash()
{
const char code[] = "#pragma comment (longstring, \\\n"
"\"HEADER\\\n"
"This is a very long string that is\\\n"
"a multi-line string.\\\n"
"How much more do I have to say?\\\n"
"Well, be prepared, because the\\\n"
"story is just beginning. This is a test\\\n"
"string for demonstration purposes. \")\n";
simplecpp::OutputList outputList;
ASSERT_EQUALS("", preprocess(code, &outputList));
}
static void dollar()
{
ASSERT_EQUALS("$ab", readfile("$ab"));
ASSERT_EQUALS("a$b", readfile("a$b"));
}
static void error1()
{
const char code[] = "#error hello world!\n";
simplecpp::OutputList outputList;
ASSERT_EQUALS("", preprocess(code, &outputList));
ASSERT_EQUALS("file0,1,#error,#error hello world!\n", toString(outputList));
}
static void error2()
{
const char code[] = "#error it's an error\n";
simplecpp::OutputList outputList;
ASSERT_EQUALS("", preprocess(code, &outputList));
ASSERT_EQUALS("file0,1,#error,#error it's an error\n", toString(outputList));
}
static void error3()
{
const char code[] = "#error \"bla bla\\\n"
" bla bla.\"\n";
std::vector<:string> files;
simplecpp::OutputList outputList;
const simplecpp::TokenList rawtokens = makeTokenList(code, files, "test.c", &outputList);
ASSERT_EQUALS("", toString(outputList));
}
static void error4()
{
// "#error x\n1"
const char code[] = "\xFE\xFF\x00\x23\x00\x65\x00\x72\x00\x72\x00\x6f\x00\x72\x00\x20\x00\x78\x00\x0a\x00\x31";
std::vector<:string> files;
simplecpp::FileDataCache cache;
simplecpp::OutputList outputList;
simplecpp::TokenList tokens2(files);
const simplecpp::TokenList rawtoken = makeTokenList(code, sizeof(code),files,"test.c");
simplecpp::preprocess(tokens2, rawtoken, files, cache, simplecpp::DUI(), &outputList);
ASSERT_EQUALS("file0,1,#error,#error x\n", toString(outputList));
}
static void error5()
{
// "#error x\n1"
const char code[] = "\xFF\xFE\x23\x00\x65\x00\x72\x00\x72\x00\x6f\x00\x72\x00\x20\x00\x78\x00\x0a\x00\x78\x00\x31\x00";
std::vector<:string> files;
simplecpp::FileDataCache cache;
simplecpp::OutputList outputList;
simplecpp::TokenList tokens2(files);
const simplecpp::TokenList rawtokens = makeTokenList(code, sizeof(code),files,"test.c");
simplecpp::preprocess(tokens2, rawtokens, files, cache, simplecpp::DUI(), &outputList);
ASSERT_EQUALS("file0,1,#error,#error x\n", toString(outputList));
}
static void garbage()
{
simplecpp::OutputList outputList;
outputList.clear();
ASSERT_EQUALS("", preprocess("#ifdef\n", &outputList));
ASSERT_EQUALS("file0,1,syntax_error,Syntax error in #ifdef\n", toString(outputList));
outputList.clear();
ASSERT_EQUALS("", preprocess("#define TEST2() A ##\nTEST2()\n", &outputList));
ASSERT_EQUALS("file0,1,syntax_error,failed to expand 'TEST2', Invalid ## usage when expanding 'TEST2': Unexpected newline\n", toString(outputList));
outputList.clear();
ASSERT_EQUALS("", preprocess("#define CON(a,b) a##b##\nCON(1,2)\n", &outputList));
ASSERT_EQUALS("file0,1,syntax_error,failed to expand 'CON', Invalid ## usage when expanding 'CON': Unexpected newline\n", toString(outputList));
}
static void garbage_endif()
{
simplecpp::OutputList outputList;
outputList.clear();
ASSERT_EQUALS("", preprocess("#elif A<0\n", &outputList));
ASSERT_EQUALS("file0,1,syntax_error,#elif without #if\n", toString(outputList));
outputList.clear();
ASSERT_EQUALS("", preprocess("#else\n", &outputList));
ASSERT_EQUALS("file0,1,syntax_error,#else without #if\n", toString(outputList));
outputList.clear();
ASSERT_EQUALS("", preprocess("#endif\n", &outputList));
ASSERT_EQUALS("file0,1,syntax_error,#endif without #if\n", toString(outputList));
}
static void hash()
{
ASSERT_EQUALS("x = \"1\"", preprocess("x=#__LINE__"));
const char code[] = "#define a(x) #x\n"
"a(1)\n"
"a(2+3)";
ASSERT_EQUALS("\n"
"\"1\"\n"
"\"2+3\"", preprocess(code));
ASSERT_EQUALS("\n\"\\\"abc\\\\0\\\"\"", preprocess("#define str(x) #x\nstr(\"abc\\0\")\n"));
ASSERT_EQUALS("\n\n( \"123\" )",
preprocess("#define A(x) (x)\n"
"#define B(x) A(#x)\n"
"B(123)"));
ASSERT_EQUALS("\n\nprintf ( \"bar(3)\" \"\\n\" ) ;",
preprocess("#define bar(x) x % 2\n"
"#define foo(x) printf(#x \"\\n\")\n"
"foo(bar(3));"));
ASSERT_EQUALS("\n\n\n\"Y Y\"",
preprocess("#define X(x,y) x y\n"
"#define STR_(x) #x\n"
"#define STR(x) STR_(x)\n"
"STR(X(Y,Y))"));
}
static void hashhash1() // #4703
{
const char code[] = "#define MACRO( A, B, C ) class A##B##C##Creator {};\n"
"MACRO( B\t, U , G )";
ASSERT_EQUALS("\nclass BUGCreator { } ;", preprocess(code));
}
static void hashhash2()
{
const char code[] = "#define A(x) a##x\n"
"#define B 0\n"
"A(B)";
ASSERT_EQUALS("\n\naB", preprocess(code));
}
static void hashhash3()
{
const char code[] = "#define A(B) A##B\n"
"#define a(B) A(B)\n"
"a(A(B))";
ASSERT_EQUALS("\n\nAAB", preprocess(code));
}
static void hashhash4() // nonstandard gcc/clang extension for empty varargs
{
const char *code;
code = "#define A(x,y...) a(x,##y)\n"
"A(1)\n";
ASSERT_EQUALS("\na ( 1 )", preprocess(code));
code = "#define A(x, ...) a(x, ## __VA_ARGS__)\n"
"#define B(x, ...) A(x, ## __VA_ARGS__)\n"
"B(1);";
ASSERT_EQUALS("\n\na ( 1 ) ;", preprocess(code));
}
static void hashhash4a()
{
const char code[] = "#define GETMYID(a) ((a))+1\n"
"#define FIGHT_FOO(c, ...) foo(c, ##__VA_ARGS__)\n"
"#define FIGHT_BAR(c, args...) bar(c, ##args)\n"
"FIGHT_FOO(1, GETMYID(a));\n"
"FIGHT_BAR(1, GETMYID(b));";
ASSERT_EQUALS("\n\n\nfoo ( 1 , ( ( a ) ) + 1 ) ;\nbar ( 1 , ( ( b ) ) + 1 ) ;", preprocess(code));
}
static void hashhash5()
{
ASSERT_EQUALS("x1", preprocess("x##__LINE__"));
}
static void hashhash6()
{
const char *code;
code = "#define A(X, ...) LOG(X, ##__VA_ARGS__)\n"
"A(1,(int)2)";
ASSERT_EQUALS("\nLOG ( 1 , ( int ) 2 )", preprocess(code));
code = "#define A(X, ...) LOG(X, ##__VA_ARGS__)\n"
"#define B(X, ...) A(X, ##__VA_ARGS__)\n"
"#define C(X, ...) B(X, ##__VA_ARGS__)\n"
"C(1,(int)2)";
ASSERT_EQUALS("\n\n\nLOG ( 1 , ( int ) 2 )", preprocess(code));
}
static void hashhash7() // # ## # (C standard; 6.10.3.3.p4)
{
const char *code;
code = "#define hash_hash # ## #\n"
"x hash_hash y";
ASSERT_EQUALS("\nx ## y", preprocess(code));
}
static void hashhash8()
{
const char code[] = "#define a(xy) x##y = xy\n"
"a(123);";
ASSERT_EQUALS("\nxy = 123 ;", preprocess(code));
}
static void hashhash9()
{
const char * code = "#define ADD_OPERATOR(OP) void operator OP ## = (void) { x = x OP 1; }\n"
"ADD_OPERATOR(+);\n"
"ADD_OPERATOR(-);\n"
"ADD_OPERATOR(*);\n"
"ADD_OPERATOR(/);\n"
"ADD_OPERATOR(%);\n"
"ADD_OPERATOR(&);\n"
"ADD_OPERATOR(|);\n"
"ADD_OPERATOR(^);\n"
"ADD_OPERATOR(<<);\n"
"ADD_OPERATOR(>>);\n";
const char expected[] = "\n"
"void operator += ( void ) { x = x + 1 ; } ;\n"
"void operator -= ( void ) { x = x - 1 ; } ;\n"
"void operator *= ( void ) { x = x * 1 ; } ;\n"
"void operator /= ( void ) { x = x / 1 ; } ;\n"
"void operator %= ( void ) { x = x % 1 ; } ;\n"
"void operator &= ( void ) { x = x & 1 ; } ;\n"
"void operator |= ( void ) { x = x | 1 ; } ;\n"
"void operator ^= ( void ) { x = x ^ 1 ; } ;\n"
"void operator <<= ( void ) { x = x << 1 ; } ;\n"
"void operator >>= ( void ) { x = x >> 1 ; } ;";
ASSERT_EQUALS(expected, preprocess(code));
simplecpp::OutputList outputList;
code = "#define A +##x\n"
"A";
outputList.clear();
ASSERT_EQUALS("", preprocess(code, &outputList));
ASSERT_EQUALS("file0,1,syntax_error,failed to expand 'A', Invalid ## usage when expanding 'A': Combining '+' and 'x' yields an invalid token.\n", toString(outputList));
code = "#define A 2##=\n"
"A";
outputList.clear();
ASSERT_EQUALS("", preprocess(code, &outputList));
ASSERT_EQUALS("file0,1,syntax_error,failed to expand 'A', Invalid ## usage when expanding 'A': Combining '2' and '=' yields an invalid token.\n", toString(outputList));
code = "#define A <<##x\n"
"A";
outputList.clear();
ASSERT_EQUALS("", preprocess(code, &outputList));
ASSERT_EQUALS("file0,1,syntax_error,failed to expand 'A', Invalid ## usage when expanding 'A': Combining '<<' and 'x' yields an invalid token.\n", toString(outputList));
}
static void hashhash10()
{
const char code[] = "#define x # #\n"
"x";
ASSERT_EQUALS("# #", preprocess(code));
}
static void hashhash11()
{
const char code[] = "#define x # # #\n"
"x";
ASSERT_EQUALS("# # #", preprocess(code));
}
static void hashhash12()
{
{
const char code[] = "#define MAX_FOO 1\n"
"#define MAX_FOO_AA 2\n"
"\n"
"#define M(UpperCaseName, b) "
" do { "
" int MaxValue = MAX_##UpperCaseName; "
" if (b) { "
" MaxValue = MAX_##UpperCaseName##_AA; "
" } "
" } while (0)"
"\n"
"static void f(bool b) { M(FOO, b); }\n";
ASSERT_EQUALS("\n\n\n\nstatic void f ( bool b ) { do { int MaxValue = 1 ; if ( b ) { MaxValue = 2 ; } } while ( 0 ) ; }", preprocess(code));
}
{
const char code[] = "#define MAX_FOO (1 * 1)\n"
"#define MAX_FOO_AA (2 * 1)\n"
"\n"
"#define M(UpperCaseName, b) "
" do { "
" int MaxValue = MAX_##UpperCaseName; "
" if (b) { "
" MaxValue = MAX_##UpperCaseName##_AA; "
" } "
" } while (0)"
"\n"
"static void f(bool b) { M(FOO, b); }\n";
ASSERT_EQUALS("\n\n\n\nstatic void f ( bool b ) { do { int MaxValue = ( 1 * 1 ) ; if ( b ) { MaxValue = ( 2 * 1 ) ; } } while ( 0 ) ; }", preprocess(code));
}
}
static void hashhash13()
{
const char code[] = "#define X(x) x##U\n"
"X((1<<1)-1)";
ASSERT_EQUALS("\n( 1 << 1 ) - 1U", preprocess(code));
const char code2[] = "#define CONCAT(x, y) x##y\n"
"CONCAT(&a, b)";
ASSERT_EQUALS("\n& ab", preprocess(code2));
}
static void hashhash_string_literal()
{
const char code[] =
"#define UL(x) x##_ul\n"
"\"ABC\"_ul;\n"
"UL(\"ABC\");";
ASSERT_EQUALS("\n\"ABC\" _ul ;\n\"ABC\" _ul ;", preprocess(code));
}
static void hashhash_string_wrapped()
{
const char code[] =
"#define CONCAT(a,b) a##b\n"
"#define STR(x) CONCAT(x,s)\n"
"STR(\"ABC\");";
ASSERT_EQUALS("\n\n\"ABC\" s ;", preprocess(code));
}
static void hashhash_char_literal()
{
const char code[] =
"#define CH(x) x##_ch\n"
"CH('a');";
ASSERT_EQUALS("\n'a' _ch ;", preprocess(code));
}
static void hashhash_multichar_literal()
{
const char code[] =
"#define CH(x) x##_ch\n"
"CH('abcd');";
ASSERT_EQUALS("\n'abcd' _ch ;", preprocess(code));
}
static void hashhash_char_escaped()
{
const char code[] =
"#define CH(x) x##_ch\n"
"CH('\\'');";
ASSERT_EQUALS("\n'\\'' _ch ;", preprocess(code));
}
static void hashhash_string_nothing()
{
const char code[] =
"#define CONCAT(a,b) a##b\n"
"CONCAT(\"ABC\",);";
ASSERT_EQUALS("\n\"ABC\" ;", preprocess(code));
}
static void hashhash_string_char()
{
const char code[] =
"#define CONCAT(a,b) a##b\n"
"CONCAT(\"ABC\", 'c');";
// This works, but maybe shouldn't since the result isn't useful.
ASSERT_EQUALS("\n\"ABC\" 'c' ;", preprocess(code));
}
static void hashhash_string_name()
{
const char code[] =
"#define CONCAT(a,b) a##b\n"
"#define LIT _literal\n"
"CONCAT(\"string\", LIT);";
// TODO is this correct? clang fails because that's not really a valid thing but gcc seems to accept it
// see https://gist.github.com/patrickdowling/877a25294f069bf059f3b07f9b5b7039
ASSERT_EQUALS("\n\n\"string\" LIT ;", preprocess(code));
}
static void hashhashhash_int_literal()
{
const char code[] =
"#define CONCAT(a,b,c) a##b##c\n"
"#define PASTER(a,b,c) CONCAT(a,b,c)\n"
"PASTER(\"123\",_i,ul);";
ASSERT_EQUALS("\n\n\"123\" _iul ;", preprocess(code));
}
static void hashhash_int_literal()
{
const char code[] =
"#define PASTE(a,b) a##b\n"
"PASTE(123,_i);\n"
"1234_i;\n";
ASSERT_EQUALS("\n123_i ;\n1234_i ;", preprocess(code));
}
static void hashhash_invalid_1()
{
const char code[] = "#define f(a) (##x)\nf(1)";
simplecpp::OutputList outputList;
ASSERT_EQUALS("", preprocess(code, &outputList));
ASSERT_EQUALS("file0,1,syntax_error,failed to expand 'f', Invalid ## usage when expanding 'f': Unexpected token '('\n", toString(outputList));
}
static void hashhash_invalid_2()
{
const char code[] = "#define f(a) (x##)\nf(1)";
simplecpp::OutputList outputList;
ASSERT_EQUALS("", preprocess(code, &outputList));
ASSERT_EQUALS("file0,1,syntax_error,failed to expand 'f', Invalid ## usage when expanding 'f': Unexpected token ')'\n", toString(outputList));
}
static void hashhash_invalid_string_number()
{
const char code[] =
"#define BAD(x) x##12345\nBAD(\"ABC\")";
simplecpp::OutputList outputList;
preprocess(code, simplecpp::DUI(), &outputList);
ASSERT_EQUALS("file0,1,syntax_error,failed to expand 'BAD', Invalid ## usage when expanding 'BAD': Combining '\"ABC\"' and '12345' yields an invalid token.\n", toString(outputList));
}
static void hashhash_invalid_missing_args()
{
const char code[] =
"#define BAD(x) ##x\nBAD()";
simplecpp::OutputList outputList;
preprocess(code, simplecpp::DUI(), &outputList);
ASSERT_EQUALS("file0,1,syntax_error,failed to expand 'BAD', Invalid ## usage when expanding 'BAD': Missing first argument\n", toString(outputList));
}
static void hashhash_null_stmt()
{
const char code[] =
"# define B(x) C ## x\n"
"#\n"
"# define C0 1\n"
"\n"
"B(0);\n";
simplecpp::OutputList outputList;
ASSERT_EQUALS("\n\n\n\n1 ;", preprocess(code, &outputList));
}
static void hashhash_empty_va_args()
{
// #395 hash hash with an empty __VA_ARGS__ in a macro
const char code[] =
"#define CAT(a, ...) a##__VA_ARGS__\n"
"#define X(a, ...) CAT(a)\n"
"#define LEVEL_2 (2)\n"
"X(LEVEL_2)\n";
ASSERT_EQUALS("\n\n\n( 2 )", preprocess(code));
}
static void hashhash_universal_character()
{
const char code[] =
"#define A(x,y) x##y\nint A(\\u01,04);";
simplecpp::OutputList outputList;
preprocess(code, simplecpp::DUI(), &outputList);
ASSERT_EQUALS("file0,1,syntax_error,failed to expand 'A', Invalid ## usage when expanding 'A': Combining '\\u01' and '04' yields universal character '\\u0104'. This is undefined behavior according to C standard chapter 5.1.1.2, paragraph 4.\n", toString(outputList));
}
static void has_include_1()
{
const char code[] = "#ifdef __has_include\n"
" #if __has_include(\"simplecpp.h\")\n"
" A\n"
" #else\n"
" B\n"
" #endif\n"
"#endif";
simplecpp::DUI dui;
dui.includePaths.push_back(testSourceDir);
ASSERT_EQUALS("\n\nA", preprocess(code, dui)); // we default to latest standard internally
dui.std = "c++14";
ASSERT_EQUALS("", preprocess(code, dui));
dui.std = "c++17";
ASSERT_EQUALS("\n\nA", preprocess(code, dui));
dui.std = "c++20";
ASSERT_EQUALS("\n\nA", preprocess(code, dui));
}
static void has_include_2()
{
const char code[] = "#if defined( __has_include)\n"
" #if /*comment*/ __has_include /*comment*/(\"simplecpp.h\") // comment\n"
" A\n"
" #else\n"
" B\n"
" #endif\n"
"#endif";
simplecpp::DUI dui;
dui.removeComments = true; // TODO: remove this
dui.includePaths.push_back(testSourceDir);
ASSERT_EQUALS("\n\nA", preprocess(code, dui)); // we default to latest standard internally
dui.std = "c++14";
ASSERT_EQUALS("", preprocess(code, dui));
dui.std = "c++17";
ASSERT_EQUALS("\n\nA", preprocess(code, dui));
dui.std = "c++20";
ASSERT_EQUALS("\n\nA", preprocess(code, dui));
}
static void has_include_3()
{
const char code[] = "#ifdef __has_include\n"
" #if __has_include()\n"
" A\n"
" #else\n"
" B\n"
" #endif\n"
"#endif";
simplecpp::DUI dui;
// Test file not found...
ASSERT_EQUALS("\n\n\n\nB", preprocess(code, dui)); // we default to latest standard internally
dui.std = "c++14";
ASSERT_EQUALS("", preprocess(code, dui));
dui.std = "c++17";
ASSERT_EQUALS("\n\n\n\nB", preprocess(code, dui));
// Unless -I is set (preferably, we should differentiate -I and -isystem...)
dui.includePaths.push_back(testSourceDir + "/testsuite");
dui.std = "";
ASSERT_EQUALS("\n\nA", preprocess(code, dui)); // we default to latest standard internally
dui.std = "c++14";
ASSERT_EQUALS("", preprocess(code, dui));
dui.std = "c++17";
ASSERT_EQUALS("\n\nA", preprocess(code, dui));
dui.std = "c++20";
ASSERT_EQUALS("\n\nA", preprocess(code, dui));
}
static void has_include_4()
{
const char code[] = "#ifdef __has_include\n"
" #if __has_include(\"testsuite/realFileName1.cpp\")\n"
" A\n"
" #else\n"
" B\n"
" #endif\n"
"#endif";
simplecpp::DUI dui;
dui.includePaths.push_back(testSourceDir); // we default to latest standard internally
ASSERT_EQUALS("\n\nA", preprocess(code, dui));
dui.std = "c++14";
ASSERT_EQUALS("", preprocess(code, dui));
dui.std = "c++17";
ASSERT_EQUALS("\n\nA", preprocess(code, dui));
dui.std = "c++20";
ASSERT_EQUALS("\n\nA", preprocess(code, dui));
}
static void has_include_5()
{
const char code[] = "#if defined( __has_include)\n"
" #if !__has_include()\n"
" A\n"
" #else\n"
" B\n"
" #endif\n"
"#endif";
simplecpp::DUI dui;
ASSERT_EQUALS("\n\nA", preprocess(code, dui)); // we default to latest standard internally
dui.includePaths.push_back(testSourceDir);
dui.std = "c++14";
ASSERT_EQUALS("", preprocess(code, dui));
dui.std = "c++17";
ASSERT_EQUALS("\n\nA", preprocess(code, dui));
dui.std = "c++20";
ASSERT_EQUALS("\n\nA", preprocess(code, dui));
}
static void has_include_6()
{
const char code[] = "#if defined( __has_include)\n"
" #if !__has_include()\n"
" A\n"
" #else\n"
" B\n"
" #endif\n"
"#endif";
simplecpp::DUI dui;
dui.includePaths.push_back(testSourceDir);
ASSERT_EQUALS("\n\nA", preprocess(code, dui)); // we default to latest standard internally
dui.std = "c++99";
ASSERT_EQUALS("", preprocess(code, dui));
dui.std = "gnu99";
ASSERT_EQUALS("\n\nA", preprocess(code, dui));
}
static void strict_ansi_1()
{
const char code[] = "#if __STRICT_ANSI__\n"
" A\n"
"#endif";
simplecpp::DUI dui;
dui.std = "gnu99";
ASSERT_EQUALS("", preprocess(code, dui));
}
static void strict_ansi_2()
{
const char code[] = "#if __STRICT_ANSI__\n"
" A\n"
"#endif";
simplecpp::DUI dui;
dui.std = "c99";
ASSERT_EQUALS("\nA", preprocess(code, dui));
}
static void strict_ansi_3()
{
const char code[] = "#if __STRICT_ANSI__\n"
" A\n"
"#endif";
simplecpp::DUI dui;
dui.std = "c99";
dui.undefined.insert("__STRICT_ANSI__");
ASSERT_EQUALS("", preprocess(code, dui));
}
static void strict_ansi_4()
{
const char code[] = "#if __STRICT_ANSI__\n"
" A\n"
"#endif";
simplecpp::DUI dui;
dui.std = "gnu99";
dui.defines.emplace_back("__STRICT_ANSI__");
ASSERT_EQUALS("\nA", preprocess(code, dui));
}
static void ifdef1()
{
const char code[] = "#ifdef A\n"
"1\n"
"#else\n"
"2\n"
"#endif";
ASSERT_EQUALS("\n\n\n2", preprocess(code));
}
static void ifdef2()
{
const char code[] = "#define A\n"
"#ifdef A\n"
"1\n"
"#else\n"
"2\n"
"#endif";
ASSERT_EQUALS("\n\n1", preprocess(code));
}
static void ifndef()
{
const char code1[] = "#define A\n"
"#ifndef A\n"
"1\n"
"#endif";
ASSERT_EQUALS("", preprocess(code1));
const char code2[] = "#ifndef A\n"
"1\n"
"#endif";
ASSERT_EQUALS("\n1", preprocess(code2));
}
static void ifA()
{
const char code[] = "#if A==1\n"
"X\n"
"#endif";
ASSERT_EQUALS("", preprocess(code));
simplecpp::DUI dui;
dui.defines.emplace_back("A=1");
ASSERT_EQUALS("\nX", preprocess(code, dui));
}
static void ifCharLiteral()
{
const char code[] = "#if ('A'==0x41)\n"
"123\n"
"#endif";
ASSERT_EQUALS("\n123", preprocess(code));
}
static void ifDefined()
{
const char code[] = "#if defined(A)\n"
"X\n"
"#endif";
simplecpp::DUI dui;
ASSERT_EQUALS("", preprocess(code, dui));
dui.defines.emplace_back("A=1");
ASSERT_EQUALS("\nX", preprocess(code, dui));
}
static void ifDefinedNoPar()
{
const char code[] = "#if defined A\n"
"X\n"
"#endif";
simplecpp::DUI dui;
ASSERT_EQUALS("", preprocess(code, dui));
dui.defines.emplace_back("A=1");
ASSERT_EQUALS("\nX", preprocess(code, dui));
}
static void ifDefinedNested()
{
const char code[] = "#define FOODEF defined(FOO)\n"
"#if FOODEF\n"
"X\n"
"#endif";
simplecpp::DUI dui;
ASSERT_EQUALS("", preprocess(code, dui));
dui.defines.emplace_back("FOO=1");
ASSERT_EQUALS("\n\nX", preprocess(code, dui));
}
static void ifDefinedNestedNoPar()
{
const char code[] = "#define FOODEF defined FOO\n"
"#if FOODEF\n"
"X\n"
"#endif";
simplecpp::DUI dui;
ASSERT_EQUALS("", preprocess(code, dui));
dui.defines.emplace_back("FOO=1");
ASSERT_EQUALS("\n\nX", preprocess(code, dui));
}
static void ifDefinedInvalid1() // #50 - invalid unterminated defined
{
const char code[] = "#if defined(A";
simplecpp::OutputList outputList;
ASSERT_EQUALS("", preprocess(code, &outputList));
ASSERT_EQUALS("file0,1,syntax_error,failed to evaluate #if condition\n", toString(outputList));
}
static void ifDefinedInvalid2()
{
const char code[] = "#if defined";
simplecpp::OutputList outputList;
ASSERT_EQUALS("", preprocess(code, &outputList));
ASSERT_EQUALS("file0,1,syntax_error,failed to evaluate #if condition\n", toString(outputList));
}
static void ifDefinedHashHash()
{
const char code[] = "#define ENABLE(FEATURE) defined ENABLE_##FEATURE\n"
"#define ENABLE_FOO 1\n"
"#if ENABLE(FOO)\n"
"#error FOO is enabled\n" // <-- expected result
"#else\n"
"#error FOO is not enabled\n"
"#endif\n";
simplecpp::OutputList outputList;
ASSERT_EQUALS("", preprocess(code, &outputList));
ASSERT_EQUALS("file0,4,#error,#error FOO is enabled\n", toString(outputList));
}
static void ifDefinedHashHash2()
{
// #409
// do not crash when expanding P() (as ## rhs is "null")
// note: gcc outputs "defined E"
const char code[] = "#define P(p)defined E##p\n"
"P()\n";
simplecpp::OutputList outputList;
ASSERT_EQUALS("\n0", preprocess(code, &outputList));
}
static void ifLogical()
{
const char code[] = "#if defined(A) || defined(B)\n"
"X\n"
"#endif";
simplecpp::DUI dui;
ASSERT_EQUALS("", preprocess(code, dui));
dui.defines.clear();
dui.defines.emplace_back("A=1");
ASSERT_EQUALS("\nX", preprocess(code, dui));
dui.defines.clear();
dui.defines.emplace_back("B=1");
ASSERT_EQUALS("\nX", preprocess(code, dui));
}
static void ifSizeof()
{
const char code[] = "#if sizeof(unsigned short)==2\n"
"X\n"
"#else\n"
"Y\n"
"#endif";
ASSERT_EQUALS("\nX", preprocess(code));
}
static void elif()
{
const char code1[] = "#ifndef X\n"
"1\n"
"#elif 1<2\n"
"2\n"
"#else\n"
"3\n"
"#endif";
ASSERT_EQUALS("\n1", preprocess(code1));
const char code2[] = "#ifdef X\n"
"1\n"
"#elif 1<2\n"
"2\n"
"#else\n"
"3\n"
"#endif";
ASSERT_EQUALS("\n\n\n2", preprocess(code2));
const char code3[] = "#ifdef X\n"
"1\n"
"#elif 1>2\n"
"2\n"
"#else\n"
"3\n"
"#endif";
ASSERT_EQUALS("\n\n\n\n\n3", preprocess(code3));
}
static void ifif()
{
// source code from LLVM
const char code[] = "#if defined(__has_include)\n"
"#if __has_include()\n"
"#endif\n"
"#endif\n";
ASSERT_EQUALS("", preprocess(code));
}
static void ifoverflow()
{
// source code from CLANG
const char code[] = "#if 0x7FFFFFFFFFFFFFFF*2\n"
"#endif\n"
"#if 0xFFFFFFFFFFFFFFFF*2\n"
"#endif\n"
"#if 0x7FFFFFFFFFFFFFFF+1\n"
"#endif\n"
"#if 0xFFFFFFFFFFFFFFFF+1\n"
"#endif\n"
"#if 0x7FFFFFFFFFFFFFFF--1\n"
"#endif\n"
"#if 0xFFFFFFFFFFFFFFFF--1\n"
"#endif\n"
"123";
(void)preprocess(code);
}
static void ifdiv0()
{
const char code[] = "#if 1000/0\n"
"#endif\n"
"123";
ASSERT_EQUALS("", preprocess(code));
}
static void ifalt() // using "and", "or", etc
{
const char *code;
code = "#if 1 and 1\n"
"1\n"
"#else\n"
"2\n"
"#endif\n";
ASSERT_EQUALS("\n1", preprocess(code));
code = "#if 1 or 0\n"
"1\n"
"#else\n"
"2\n"
"#endif\n";
ASSERT_EQUALS("\n1", preprocess(code));
}
static void ifexpr()
{
const char code[] = "#define MACRO() (1)\n"
"#if ~MACRO() & 8\n"
"1\n"
"#endif";
ASSERT_EQUALS("\n\n1", preprocess(code));
}
static void ifUndefFuncStyleMacro()
{
const char code[] = "#if A()\n"
"#endif\n";
simplecpp::OutputList outputList;
ASSERT_EQUALS("", preprocess(code, &outputList));
ASSERT_EQUALS("file0,1,syntax_error,failed to evaluate #if condition, undefined function-like macro invocation: A( ... )\n", toString(outputList));
}
static void location1()
{
const char *code;
code = "# 1 \"main.c\"\n\n\n"
"x";
ASSERT_EQUALS("\n#line 3 \"main.c\"\nx", preprocess(code));
}
static void location2()
{
const char *code;
code = "{ {\n"
"#line 40 \"abc.y\"\n"
"{\n"
"}\n"
"#line 42 \"abc.y\"\n"
"{\n"
"}\n"
"} }";
ASSERT_EQUALS("{ {\n"
"#line 40 \"abc.y\"\n"
"{\n"
"}\n"
"{\n"
"}\n"
"} }", preprocess(code));
}
static void location3()
{
const char *code;
code = "#line 1 \"x\" \n"
"a\n"
"#line 1 \"x\" \n"
"b\n";
ASSERT_EQUALS("\n#line 1 \"x\"\na b", preprocess(code));
}
static void location4()
{
const char *code;
code = "#line 1 \"abc\\\\def.g\" \n"
"a\n";
ASSERT_EQUALS("\n#line 1 \"abc\\def.g\"\na", preprocess(code));
}
static void location5()
{
// https://sourceforge.net/p/cppcheck/discussion/general/thread/eccf020a13/
const char *code;
code = "#line 10 \"/a/Attribute/parser/FilterParser.y\" // lalr1.cc:377\n"
"int x;\n";
ASSERT_EQUALS("\n#line 10 \"/a/Attribute/parser/FilterParser.y\"\n"
"int x ;", preprocess(code));
}
static void location6()
{
const char code[] =
"#line 3\n"
"__LINE__ __FILE__\n";
ASSERT_EQUALS("\n"
"\n"
"3 \"\"",
preprocess(code));
}
static void location7()
{
const char code[] =
"#line 3 \"file.c\"\n"
"__LINE__ __FILE__\n";
ASSERT_EQUALS("\n"
"#line 3 \"file.c\"\n"
"3 \"file.c\"",
preprocess(code));
}
static void location8()
{
const char code[] =
"# 3\n"
"__LINE__ __FILE__\n";
ASSERT_EQUALS("\n"
"2 \"\"", // TODO: should say 3
preprocess(code));
}
static void location9()
{
const char code[] =
"# 3 \"file.c\"\n"
"__LINE__ __FILE__\n";
ASSERT_EQUALS("\n"
"#line 3 \"file.c\"\n"
"3 \"file.c\"",
preprocess(code));
}
static void location10()
{
const char code[] =
"#line 3\n"
"__LINE__ __FILE__\n";
ASSERT_EQUALS("\n"
"\n" // TODO: should this have the #line marker?
"3 \"\"",
preprocess(code));
}
static void location11()
{
const char code[] =
"#line 3 \"file.c\"\n"
"__LINE__ __FILE__\n"
"#line 33 \"file2.c\"\n"
"__LINE__ __FILE__\n";
ASSERT_EQUALS("\n"
"#line 3 \"file.c\"\n"
"3 \"file.c\"\n"
"#line 33 \"file2.c\"\n"
"33 \"file2.c\"",
preprocess(code));
}
// TODO: test #file/#endfile
static void missingHeader1()
{
const char code[] = "#include \"notexist.h\"\n";
simplecpp::OutputList outputList;
ASSERT_EQUALS("", preprocess(code, &outputList));
ASSERT_EQUALS("file0,1,missing_header,Header not found: \"notexist.h\"\n", toString(outputList));
}
static void missingHeader2()
{
const char code[] = "#include \"foo.h\"\n"; // this file exists
std::vector<:string> files;
simplecpp::FileDataCache cache;
cache.insert({"foo.h", simplecpp::TokenList(files)});
simplecpp::OutputList outputList;
simplecpp::TokenList tokens2(files);
const simplecpp::TokenList rawtokens = makeTokenList(code,files);
simplecpp::DUI dui;
dui.includePaths.emplace_back(".");
simplecpp::preprocess(tokens2, rawtokens, files, cache, dui, &outputList);
ASSERT_EQUALS("", toString(outputList));
}
static void missingHeader3()
{
const char code[] = "#ifdef UNDEFINED\n#include \"notexist.h\"\n#endif\n"; // this file is not included
simplecpp::OutputList outputList;
ASSERT_EQUALS("", preprocess(code, &outputList));
ASSERT_EQUALS("", toString(outputList));
}
static void missingHeader4()
{
const char code[] = "#/**/include <>\n";
simplecpp::OutputList outputList;
simplecpp::DUI dui;
dui.removeComments = true; // TODO: remove this
ASSERT_EQUALS("", preprocess(code, dui, &outputList));
ASSERT_EQUALS("file0,1,syntax_error,No header in #include\n", toString(outputList));
}
static void nestedInclude()
{
const char code[] = "#include \"test.h\"\n";
std::vector<:string> files;
const simplecpp::TokenList rawtokens = makeTokenList(code,files,"test.h");
simplecpp::FileDataCache cache;
cache.insert({"test.h", rawtokens});
simplecpp::OutputList outputList;
simplecpp::TokenList tokens2(files);
simplecpp::DUI dui;
dui.includePaths.emplace_back(".");
simplecpp::preprocess(tokens2, rawtokens, files, cache, dui, &outputList);
ASSERT_EQUALS("file0,1,include_nested_too_deeply,#include nested too deeply\n", toString(outputList));
}
static void systemInclude()
{
const char code[] = "#include \n";
std::vector<:string> files;
const simplecpp::TokenList rawtokens = makeTokenList(code,files,"local/limits.h");
simplecpp::FileDataCache cache;
cache.insert({"include/limits.h", simplecpp::TokenList(files)});
cache.insert({"local/limits.h", rawtokens});
simplecpp::OutputList outputList;
simplecpp::TokenList tokens2(files);
simplecpp::DUI dui;
dui.includePaths.emplace_back("include");
simplecpp::preprocess(tokens2, rawtokens, files, cache, dui, &outputList);
ASSERT_EQUALS("", toString(outputList));
}
static void circularInclude()
{
std::vector<:string> files;
simplecpp::FileDataCache cache;
{
const char *const path = "test.h";
const char code[] =
"#ifndef TEST_H\n"
"#define TEST_H\n"
"#include \"a/a.h\"\n"
"#endif\n"
;
cache.insert({path, makeTokenList(code, files, path)});
}
{
const char *const path = "a/a.h";
const char code[] =
"#ifndef A_H\n"
"#define A_H\n"
"#include \"../test.h\"\n"
"#endif\n"
;
cache.insert({path, makeTokenList(code, files, path)});
}
simplecpp::OutputList outputList;
simplecpp::TokenList tokens2(files);
{
std::vector<:string> filenames;
const simplecpp::DUI dui;
const char code[] = "#include \"test.h\"\n";
const simplecpp::TokenList rawtokens = makeTokenList(code, files, "test.cpp");
cache = simplecpp::load(rawtokens, filenames, dui, &outputList, std::move(cache));
simplecpp::preprocess(tokens2, rawtokens, files, cache, dui, &outputList);
}
ASSERT_EQUALS("", toString(outputList));
}
static void multiline1()
{
const char code[] = "#define A \\\n"
"1\n"
"A";
ASSERT_EQUALS("\n\n1", preprocess(code));
}
static void multiline2()
{
const char code[] = "#define A /*\\\n"
"*/1\n"
"A";
std::vector<:string> files;
simplecpp::TokenList rawtokens = makeTokenList(code,files);
ASSERT_EQUALS("# define A /**/ 1\n\nA", rawtokens.stringify());
rawtokens.removeComments();
simplecpp::FileDataCache cache;
simplecpp::TokenList tokens2(files);
simplecpp::preprocess(tokens2, rawtokens, files, cache, simplecpp::DUI());
ASSERT_EQUALS("\n\n1", tokens2.stringify());
}
static void multiline3() // #28 - macro with multiline comment
{
const char code[] = "#define A /*\\\n"
" */ 1\n"
"A";
std::vector<:string> files;
simplecpp::TokenList rawtokens = makeTokenList(code,files);
ASSERT_EQUALS("# define A /* */ 1\n\nA", rawtokens.stringify());
rawtokens.removeComments();
simplecpp::FileDataCache cache;
simplecpp::TokenList tokens2(files);
simplecpp::preprocess(tokens2, rawtokens, files, cache, simplecpp::DUI());
ASSERT_EQUALS("\n\n1", tokens2.stringify());
}
static void multiline4() // #28 - macro with multiline comment
{
const char code[] = "#define A \\\n"
" /*\\\n"
" */ 1\n"
"A";
std::vector<:string> files;
simplecpp::TokenList rawtokens = makeTokenList(code,files);
ASSERT_EQUALS("# define A /* */ 1\n\n\nA", rawtokens.stringify());
rawtokens.removeComments();
simplecpp::FileDataCache cache;
simplecpp::TokenList tokens2(files);
simplecpp::preprocess(tokens2, rawtokens, files, cache, simplecpp::DUI());
ASSERT_EQUALS("\n\n\n1", tokens2.stringify());
}
static void multiline5() // column
{
const char code[] = "#define A\\\n"
"(";
std::vector<:string> files;
const simplecpp::TokenList rawtokens = makeTokenList(code, files);
ASSERT_EQUALS("# define A (", rawtokens.stringify());
ASSERT_EQUALS(11, rawtokens.cback()->location.col);
}
static void multiline6() // multiline string in macro
{
const char code[] = "#define string (\"\\\n"
"x\")\n"
"string\n";
std::vector<:string> files;
const simplecpp::TokenList rawtokens = makeTokenList(code, files);
ASSERT_EQUALS("# define string ( \"x\" )\n"
"\n"
"string", rawtokens.stringify());
}
static void multiline7() // multiline string in macro
{
const char code[] = "#define A(X) aaa { f(\"\\\n"
"a\"); }\n"
"A(1)";
std::vector<:string> files;
const simplecpp::TokenList rawtokens = makeTokenList(code, files);
ASSERT_EQUALS("# define A ( X ) aaa { f ( \"a\" ) ; }\n"
"\n"
"A ( 1 )", rawtokens.stringify());
}
static void multiline8() // multiline prefix string in macro
{
const char code[] = "#define A L\"a\\\n"
" b\"\n"
"A;";
ASSERT_EQUALS("\n\nL\"a b\" ;", preprocess(code));
}
static void multiline9() // multiline prefix string in macro
{
const char code[] = "#define A u8\"a\\\n"
" b\"\n"
"A;";
ASSERT_EQUALS("\n\nu8\"a b\" ;", preprocess(code));
}
static void multiline10() // multiline string literal
{
const char code[] = "const char *ptr = \"\\\\\n"
"\\n\";";
ASSERT_EQUALS("const char * ptr = \"\\\\n\"\n;", preprocess(code));
}
static void nullDirective1()
{
const char code[] = "#\n"
"#if 1\n"
"#define a 1\n"
"#endif\n"
"x = a;\n";
ASSERT_EQUALS("\n\n\n\nx = 1 ;", preprocess(code));
}
static void nullDirective2()
{
const char code[] = "# // comment\n"
"#if 1\n"
"#define a 1\n"
"#endif\n"
"x = a;\n";
ASSERT_EQUALS("\n\n\n\nx = 1 ;", preprocess(code));
}
static void nullDirective3()
{
const char code[] = "#if 1\n"
"#define a 1\n"
"#\n"
"#endif\n"
"x = a;\n";
ASSERT_EQUALS("\n\n\n\nx = 1 ;", preprocess(code));
}
static void include1()
{
const char code[] = "#include \"A.h\"\n";
ASSERT_EQUALS("# include \"A.h\"", readfile(code));
}
static void include2()
{
const char code[] = "#include \n";
ASSERT_EQUALS("# include ", readfile(code));
}
static void include3() // #16 - crash when expanding macro from header
{
const char code_c[] = "#include \"A.h\"\n"
"glue(1,2,3,4)\n";
const char code_h[] = "#define glue(a,b,c,d) a##b##c##d\n";
std::vector<:string> files;
const simplecpp::TokenList rawtokens_c = makeTokenList(code_c, files, "A.c");
const simplecpp::TokenList rawtokens_h = makeTokenList(code_h, files, "A.h");
ASSERT_EQUALS(2U, files.size());
ASSERT_EQUALS("A.c", files[0]);
ASSERT_EQUALS("A.h", files[1]);
simplecpp::FileDataCache cache;
cache.insert({"A.c", rawtokens_c});
cache.insert({"A.h", rawtokens_h});
simplecpp::TokenList out(files);
simplecpp::DUI dui;
dui.includePaths.emplace_back(".");
simplecpp::preprocess(out, rawtokens_c, files, cache, dui);
ASSERT_EQUALS("\n1234", out.stringify());
}
static void include4() // #27 - -include
{
const char code_c[] = "X\n";
const char code_h[] = "#define X 123\n";
std::vector<:string> files;
const simplecpp::TokenList rawtokens_c = makeTokenList(code_c, files, "27.c");
const simplecpp::TokenList rawtokens_h = makeTokenList(code_h, files, "27.h");
ASSERT_EQUALS(2U, files.size());
ASSERT_EQUALS("27.c", files[0]);
ASSERT_EQUALS("27.h", files[1]);
simplecpp::FileDataCache cache;
cache.insert({"27.c", rawtokens_c});
cache.insert({"27.h", rawtokens_h});
simplecpp::TokenList out(files);
simplecpp::DUI dui;
dui.includePaths.emplace_back(".");
dui.includes.emplace_back("27.h");
simplecpp::preprocess(out, rawtokens_c, files, cache, dui);
ASSERT_EQUALS("123", out.stringify());
}
static void include5() // #3 - handle #include MACRO
{
const char code_c[] = "#define A \"3.h\"\n#include A\n";
const char code_h[] = "123\n";
std::vector<:string> files;
const simplecpp::TokenList rawtokens_c = makeTokenList(code_c, files, "3.c");
const simplecpp::TokenList rawtokens_h = makeTokenList(code_h, files, "3.h");
ASSERT_EQUALS(2U, files.size());
ASSERT_EQUALS("3.c", files[0]);
ASSERT_EQUALS("3.h", files[1]);
simplecpp::FileDataCache cache;
cache.insert({"3.c", rawtokens_c});
cache.insert({"3.h", rawtokens_h});
simplecpp::TokenList out(files);
simplecpp::DUI dui;
dui.includePaths.emplace_back(".");
simplecpp::preprocess(out, rawtokens_c, files, cache, dui);
ASSERT_EQUALS("\n#line 1 \"3.h\"\n123", out.stringify());
}
static void include6() // #57 - incomplete macro #include MACRO(,)
{
const char code[] = "#define MACRO(X,Y) X##Y\n#include MACRO(,)\n";
std::vector<:string> files;
const simplecpp::TokenList rawtokens = makeTokenList(code, files, "57.c");
ASSERT_EQUALS(1U, files.size());
ASSERT_EQUALS("57.c", files[0]);
simplecpp::FileDataCache cache;
cache.insert({"57.c", rawtokens});
simplecpp::TokenList out(files);
simplecpp::preprocess(out, rawtokens, files, cache, simplecpp::DUI());
}
static void include7() // #include MACRO
{
const char code_c[] = "#define HDR <3.h>\n"
"#include HDR\n";
const char code_h[] = "123\n";
std::vector<:string> files;
const simplecpp::TokenList rawtokens_c = makeTokenList(code_c, files, "3.c");
const simplecpp::TokenList rawtokens_h = makeTokenList(code_h, files, "3.h");
ASSERT_EQUALS(2U, files.size());
ASSERT_EQUALS("3.c", files[0]);
ASSERT_EQUALS("3.h", files[1]);
simplecpp::FileDataCache cache;
cache.insert({"3.c", rawtokens_c});
cache.insert({"3.h", rawtokens_h});
simplecpp::TokenList out(files);
simplecpp::DUI dui;
dui.includePaths.emplace_back(".");
simplecpp::preprocess(out, rawtokens_c, files, cache, dui);
ASSERT_EQUALS("\n#line 1 \"3.h\"\n123", out.stringify());
}
static void include8() // #include MACRO(X)
{
const char code[] = "#define INCLUDE_LOCATION ../somewhere\n"
"#define INCLUDE_FILE(F) \n"
"#include INCLUDE_FILE(header)\n";
simplecpp::OutputList outputList;
ASSERT_EQUALS("", preprocess(code, &outputList));
ASSERT_EQUALS("file0,3,missing_header,Header not found: <../somewhere/header.h>\n", toString(outputList));
}
static void include9()
{
const char code_c[] = "#define HDR \"1.h\"\n"
"#include HDR\n";
const char code_h[] = "/**/ #define X 1\n" // <- comment before hash should be ignored
"x=X;";
std::vector<:string> files;
const simplecpp::TokenList rawtokens_c = makeTokenList(code_c, files, "1.c");
const simplecpp::TokenList rawtokens_h = makeTokenList(code_h, files, "1.h");
ASSERT_EQUALS(2U, files.size());
ASSERT_EQUALS("1.c", files[0]);
ASSERT_EQUALS("1.h", files[1]);
simplecpp::FileDataCache cache;
cache.insert({"1.c", rawtokens_c});
cache.insert({"1.h", rawtokens_h});
simplecpp::TokenList out(files);
simplecpp::DUI dui;
dui.includePaths.emplace_back(".");
simplecpp::preprocess(out, rawtokens_c, files, cache, dui);
ASSERT_EQUALS("\n#line 2 \"1.h\"\nx = 1 ;", out.stringify());
}
static void readfile_nullbyte()
{
const char code[] = "ab\0cd";
simplecpp::OutputList outputList;
ASSERT_EQUALS("ab cd", readfile(code,sizeof(code), &outputList));
ASSERT_EQUALS(true, outputList.empty()); // should warning be written?
}
static void readfile_char()
{
ASSERT_EQUALS("'c'", readfile("'c'"));
ASSERT_EQUALS("L'c'", readfile("L'c'"));
ASSERT_EQUALS("L 'c'", readfile("L 'c'"));
ASSERT_EQUALS("A = 'c'", readfile("A = 'c'"));
ASSERT_EQUALS("A = L'c'", readfile("A = L'c'"));
ASSERT_EQUALS("A = u'c'", readfile("A = u'c'"));
ASSERT_EQUALS("A = U'c'", readfile("A = U'c'"));
ASSERT_EQUALS("A = u8'c'", readfile("A = u8'c'"));
ASSERT_EQUALS("A = u8 'c'", readfile("A = u8 'c'"));
// The character \'
ASSERT_EQUALS("'\\''", readfile("'\\\''"));
ASSERT_EQUALS("L'\\''", readfile("L'\\\''"));
ASSERT_EQUALS("u'\\''", readfile("u'\\\''"));
ASSERT_EQUALS("U'\\''", readfile("U'\\\''"));
ASSERT_EQUALS("u8'\\''", readfile("u8'\\\''"));
}
static void readfile_char_error()
{
simplecpp::OutputList outputList;
readfile("A = L's", &outputList);
ASSERT_EQUALS("file0,1,syntax_error,No pair for character (\'). Can't process file. File is either invalid or unicode, which is currently not supported.\n", toString(outputList));
outputList.clear();
readfile("A = 's\n'", &outputList);
ASSERT_EQUALS("file0,1,syntax_error,No pair for character (\'). Can't process file. File is either invalid or unicode, which is currently not supported.\n", toString(outputList));
}
static void readfile_string()
{
ASSERT_EQUALS("\"\"", readfile("\"\""));
ASSERT_EQUALS("\"a\"", readfile("\"a\""));
ASSERT_EQUALS("u\"a\"", readfile("u\"a\""));
ASSERT_EQUALS("u \"a\"", readfile("u \"a\""));
ASSERT_EQUALS("A = \"\"", readfile("A = \"\""));
ASSERT_EQUALS("A = \"abc\'def\"", readfile("A = \"abc\'def\""));
ASSERT_EQUALS("( \"\\\\\\\\\" )", readfile("(\"\\\\\\\\\")"));
ASSERT_EQUALS("x = \"a b\"\n;", readfile("x=\"a\\\n b\";"));
ASSERT_EQUALS("x = \"a b\"\n;", readfile("x=\"a\\\r\n b\";"));
ASSERT_EQUALS("A = u8\"a\"", readfile("A = u8\"a\""));
ASSERT_EQUALS("A = u\"a\"", readfile("A = u\"a\""));
ASSERT_EQUALS("A = U\"a\"", readfile("A = U\"a\""));
ASSERT_EQUALS("A = L\"a\"", readfile("A = L\"a\""));
ASSERT_EQUALS("A = L \"a\"", readfile("A = L \"a\""));
ASSERT_EQUALS("A = \"abc\\\\\\\\def\"", readfile("A = R\"(abc\\\\def)\""));
ASSERT_EQUALS("A = \"abc\\\\\\\\def\"", readfile("A = R\"x(abc\\\\def)x\""));
ASSERT_EQUALS("A = \"\"", readfile("A = R\"()\""));
ASSERT_EQUALS("A = \"\\\\\"", readfile("A = R\"(\\)\""));
ASSERT_EQUALS("A = \"\\\"\"", readfile("A = R\"(\")\""));
ASSERT_EQUALS("A = \"abc\"", readfile("A = R\"\"\"(abc)\"\"\""));
ASSERT_EQUALS("A = \"a\nb\nc\";", readfile("A = R\"foo(a\nb\nc)foo\";"));
ASSERT_EQUALS("A = L\"abc\"", readfile("A = LR\"(abc)\""));
ASSERT_EQUALS("A = u\"abc\"", readfile("A = uR\"(abc)\""));
ASSERT_EQUALS("A = U\"abc\"", readfile("A = UR\"(abc)\""));
ASSERT_EQUALS("A = u8\"abc\"", readfile("A = u8R\"(abc)\""));
// Strings containing \"
ASSERT_EQUALS("\"\\\"\"", readfile("\"\\\"\""));
ASSERT_EQUALS("L\"\\\"\"", readfile("L\"\\\"\""));
ASSERT_EQUALS("u\"\\\"\"", readfile("u\"\\\"\""));
ASSERT_EQUALS("U\"\\\"\"", readfile("U\"\\\"\""));
ASSERT_EQUALS("u8\"\\\"\"", readfile("u8\"\\\"\""));
ASSERT_EQUALS("\"\\\"a\\\"\"", readfile("\"\\\"a\\\"\""));
ASSERT_EQUALS("L\"a\\\"\\\"\"", readfile("L\"a\\\"\\\"\""));
ASSERT_EQUALS("u\"a\\\"\\\"\"", readfile("u\"a\\\"\\\"\""));
ASSERT_EQUALS("U\"a\\\"\\\"\"", readfile("U\"a\\\"\\\"\""));
ASSERT_EQUALS("u8\"a\\\"\\\"\"", readfile("u8\"a\\\"\\\"\""));
// Do not concatenate when prefix is not directly adjacent to "
ASSERT_EQUALS("u8 \"a b\"", readfile("u8 \"a b\""));
ASSERT_EQUALS("u8\n\"a b\"", readfile("u8\n \"a b\""));
}
static void readfile_string_error()
{
simplecpp::OutputList outputList;
readfile("A = \"abs", &outputList);
ASSERT_EQUALS("file0,1,syntax_error,No pair for character (\"). Can't process file. File is either invalid or unicode, which is currently not supported.\n", toString(outputList));
outputList.clear();
readfile("A = u8\"abs\n\"", &outputList);
ASSERT_EQUALS("file0,1,syntax_error,No pair for character (\"). Can't process file. File is either invalid or unicode, which is currently not supported.\n", toString(outputList));
outputList.clear();
readfile("A = R\"as\n(abc)as\"", &outputList);
ASSERT_EQUALS("file0,1,syntax_error,Invalid newline in raw string delimiter.\n", toString(outputList));
outputList.clear();
readfile("A = u8R\"as\n(abc)as\"", &outputList);
ASSERT_EQUALS("file0,1,syntax_error,Invalid newline in raw string delimiter.\n", toString(outputList));
outputList.clear();
readfile("A = R\"as(abc)a\"", &outputList);
ASSERT_EQUALS("file0,1,syntax_error,Raw string missing terminating delimiter.\n", toString(outputList));
outputList.clear();
readfile("A = LR\"as(abc)a\"", &outputList);
ASSERT_EQUALS("file0,1,syntax_error,Raw string missing terminating delimiter.\n", toString(outputList));
outputList.clear();
readfile("#define A \"abs", &outputList);
ASSERT_EQUALS("file0,1,syntax_error,No pair for character (\"). Can't process file. File is either invalid or unicode, which is currently not supported.\n", toString(outputList));
outputList.clear();
// Don't warn for a multiline define
readfile("#define A \"abs\\\n\"", &outputList);
ASSERT_EQUALS("", toString(outputList));
}
static void readfile_cpp14_number()
{
ASSERT_EQUALS("A = 12345 ;", readfile("A = 12\'345;"));
}
static void readfile_unhandled_chars()
{
simplecpp::OutputList outputList;
readfile("// ä½ å¥½ä¸ç", &outputList);
ASSERT_EQUALS("", toString(outputList));
readfile("s=\"ä½ å¥½ä¸ç\"", &outputList);
ASSERT_EQUALS("", toString(outputList));
readfile("int ä½ å¥½ä¸ç=0;", &outputList);
ASSERT_EQUALS("file0,1,unhandled_char_error,The code contains unhandled character(s) (character code=228). Neither unicode nor extended ascii is supported.\n", toString(outputList));
}
static void readfile_error()
{
ASSERT_EQUALS("# if ! A\n"
"# error\n"
"# endif\n"
"X",readfile("#if !A\n#error\n#endif\nX\n"));
}
static void readfile_file_not_found()
{
simplecpp::OutputList outputList;
std::vector<:string> files;
(void)simplecpp::TokenList("NotAFile", files, &outputList);
ASSERT_EQUALS("file0,1,file_not_found,File is missing: NotAFile\n", toString(outputList));
}
static void stringify1()
{
const char code_c[] = "#include \"A.h\"\n"
"#include \"A.h\"\n";
const char code_h[] = "1\n2";
std::vector<:string> files;
const simplecpp::TokenList rawtokens_c = makeTokenList(code_c, files, "A.c");
const simplecpp::TokenList rawtokens_h = makeTokenList(code_h, files, "A.h");
ASSERT_EQUALS(2U, files.size());
ASSERT_EQUALS("A.c", files[0]);
ASSERT_EQUALS("A.h", files[1]);
simplecpp::FileDataCache cache;
cache.insert({"A.c", rawtokens_c});
cache.insert({"A.h", rawtokens_h});
simplecpp::TokenList out(files);
simplecpp::DUI dui;
dui.includePaths.emplace_back(".");
simplecpp::preprocess(out, rawtokens_c, files, cache, dui);
ASSERT_EQUALS("\n#line 1 \"A.h\"\n1\n2\n#line 1 \"A.h\"\n1\n2", out.stringify());
}
static void tokenMacro1()
{
const char code[] = "#define A 123\n"
"A";
std::vector<:string> files;
simplecpp::FileDataCache cache;
simplecpp::TokenList tokenList(files);
const simplecpp::TokenList rawtokens = makeTokenList(code,files);
simplecpp::preprocess(tokenList, rawtokens, files, cache, simplecpp::DUI());
ASSERT_EQUALS("A", tokenList.cback()->macro);
}
static void tokenMacro2()
{
const char code[] = "#define ADD(X,Y) X+Y\n"
"ADD(1,2)";
std::vector<:string> files;
simplecpp::FileDataCache cache;
simplecpp::TokenList tokenList(files);
const simplecpp::TokenList rawtokens = makeTokenList(code,files);
simplecpp::preprocess(tokenList, rawtokens, files, cache, simplecpp::DUI());
const simplecpp::Token *tok = tokenList.cfront();
ASSERT_EQUALS("1", tok->str());
ASSERT_EQUALS("", tok->macro);
tok = tok->next;
ASSERT_EQUALS("+", tok->str());
ASSERT_EQUALS("ADD", tok->macro);
tok = tok->next;
ASSERT_EQUALS("2", tok->str());
ASSERT_EQUALS("", tok->macro);
}
static void tokenMacro3()
{
const char code[] = "#define ADD(X,Y) X+Y\n"
"#define FRED 1\n"
"ADD(FRED,2)";
std::vector<:string> files;
simplecpp::FileDataCache cache;
simplecpp::TokenList tokenList(files);
const simplecpp::TokenList rawtokens = makeTokenList(code,files);
simplecpp::preprocess(tokenList, rawtokens, files, cache, simplecpp::DUI());
const simplecpp::Token *tok = tokenList.cfront();
ASSERT_EQUALS("1", tok->str());
ASSERT_EQUALS("FRED", tok->macro);
tok = tok->next;
ASSERT_EQUALS("+", tok->str());
ASSERT_EQUALS("ADD", tok->macro);
tok = tok->next;
ASSERT_EQUALS("2", tok->str());
ASSERT_EQUALS("", tok->macro);
}
static void tokenMacro4()
{
const char code[] = "#define A B\n"
"#define B 1\n"
"A";
std::vector<:string> files;
simplecpp::FileDataCache cache;
simplecpp::TokenList tokenList(files);
const simplecpp::TokenList rawtokens = makeTokenList(code,files);
simplecpp::preprocess(tokenList, rawtokens, files, cache, simplecpp::DUI());
const simplecpp::Token * const tok = tokenList.cfront();
ASSERT_EQUALS("1", tok->str());
ASSERT_EQUALS("A", tok->macro);
}
static void tokenMacro5()
{
const char code[] = "#define SET_BPF(code) (code)\n"
"#define SET_BPF_JUMP(code) SET_BPF(D | code)\n"
"SET_BPF_JUMP(A | B | C);";
std::vector<:string> files;
simplecpp::FileDataCache cache;
simplecpp::TokenList tokenList(files);
const simplecpp::TokenList rawtokens = makeTokenList(code,files);
simplecpp::preprocess(tokenList, rawtokens, files, cache, simplecpp::DUI());
const simplecpp::Token * const tok = tokenList.cfront()->next;
ASSERT_EQUALS("D", tok->str());
ASSERT_EQUALS("SET_BPF_JUMP", tok->macro);
}
static void undef()
{
const char code[] = "#define A\n"
"#undef A\n"
"#ifdef A\n"
"123\n"
"#endif";
ASSERT_EQUALS("", preprocess(code));
}
static void userdef()
{
const char code[] = "#ifdef A\n123\n#endif\n";
simplecpp::DUI dui;
dui.defines.emplace_back("A=1");
ASSERT_EQUALS("\n123", preprocess(code, dui));
}
static void utf8()
{
ASSERT_EQUALS("123", readfile("\xEF\xBB\xBF 123"));
}
static void utf8_invalid()
{
ASSERT_EQUALS("", readfile("\xEF 123"));
ASSERT_EQUALS("", readfile("\xEF\xBB 123"));
}
static void unicode()
{
{
const char code[] = "\xFE\xFF\x00\x31\x00\x32";
ASSERT_EQUALS("12", readfile(code, sizeof(code)));
}
{
const char code[] = "\xFF\xFE\x31\x00\x32\x00";
ASSERT_EQUALS("12", readfile(code, sizeof(code)));
}
{
const char code[] = "\xFE\xFF\x00\x2f\x00\x2f\x00\x0a\x00\x31";
ASSERT_EQUALS("//\n1", readfile(code, sizeof(code)));
}
{
const char code[] = "\xFF\xFE\x2f\x00\x2f\x00\x0a\x00\x31\x00";
ASSERT_EQUALS("//\n1", readfile(code, sizeof(code)));
}
{
const char code[] = "\xFE\xFF\x00\x22\x00\x61\x00\x22";
ASSERT_EQUALS("\"a\"", readfile(code, sizeof(code)));
}
{
const char code[] = "\xFF\xFE\x22\x00\x61\x00\x22\x00";
ASSERT_EQUALS("\"a\"", readfile(code, sizeof(code)));
}
{
const char code[] = "\xff\xfe\x0d\x00\x0a\x00\x2f\x00\x2f\x00\x31\x00\x0d\x00\x0a\x00";
ASSERT_EQUALS("\n//1", readfile(code, sizeof(code)));
}
}
static void unicode_invalid()
{
{
const char code[] = "\xFF";
ASSERT_EQUALS("", readfile(code, sizeof(code)));
}
{
const char code[] = "\xFE";
ASSERT_EQUALS("", readfile(code, sizeof(code)));
}
{
const char code[] = "\xFE\xFF\x31";
ASSERT_EQUALS("", readfile(code, sizeof(code)));
}
{
const char code[] = "\xFF\xFE\x31";
ASSERT_EQUALS("1", readfile(code, sizeof(code)));
}
{
const char code[] = "\xFE\xFF\x31\x32";
ASSERT_EQUALS("", readfile(code, sizeof(code)));
}
{
const char code[] = "\xFF\xFE\x31\x32";
ASSERT_EQUALS("", readfile(code, sizeof(code)));
}
{
const char code[] = "\xFE\xFF\x00\x31\x00\x32\x33";
ASSERT_EQUALS("", readfile(code, sizeof(code)));
}
{
const char code[] = "\xFF\xFE\x31\x00\x32\x00\x33";
ASSERT_EQUALS("123", readfile(code, sizeof(code)));
}
}
static void warning()
{
const char code[] = "#warning MSG\n1";
simplecpp::OutputList outputList;
ASSERT_EQUALS("\n1", preprocess(code, &outputList));
ASSERT_EQUALS("file0,1,#warning,#warning MSG\n", toString(outputList));
}
static void simplifyPath()
{
ASSERT_EQUALS("1.c", simplecpp::simplifyPath("./1.c"));
ASSERT_EQUALS("1.c", simplecpp::simplifyPath("././1.c"));
ASSERT_EQUALS("/1.c", simplecpp::simplifyPath("/./1.c"));
ASSERT_EQUALS("/1.c", simplecpp::simplifyPath("/././1.c"));
ASSERT_EQUALS("trailing_dot./1.c", simplecpp::simplifyPath("trailing_dot./1.c"));
ASSERT_EQUALS("1.c", simplecpp::simplifyPath("a/../1.c"));
ASSERT_EQUALS("1.c", simplecpp::simplifyPath("a/b/../../1.c"));
ASSERT_EQUALS("a/1.c", simplecpp::simplifyPath("a/b/../1.c"));
ASSERT_EQUALS("/a/1.c", simplecpp::simplifyPath("/a/b/../1.c"));
ASSERT_EQUALS("/a/1.c", simplecpp::simplifyPath("/a/b/c/../../1.c"));
ASSERT_EQUALS("/a/1.c", simplecpp::simplifyPath("/a/b/c/../.././1.c"));
ASSERT_EQUALS("../1.c", simplecpp::simplifyPath("../1.c"));
ASSERT_EQUALS("../1.c", simplecpp::simplifyPath("../a/../1.c"));
ASSERT_EQUALS("/../1.c", simplecpp::simplifyPath("/../1.c"));
ASSERT_EQUALS("/../1.c", simplecpp::simplifyPath("/../a/../1.c"));
ASSERT_EQUALS("a/..b/1.c", simplecpp::simplifyPath("a/..b/1.c"));
ASSERT_EQUALS("../../1.c", simplecpp::simplifyPath("../../1.c"));
ASSERT_EQUALS("../../../1.c", simplecpp::simplifyPath("../../../1.c"));
ASSERT_EQUALS("../../../1.c", simplecpp::simplifyPath("../../../a/../1.c"));
ASSERT_EQUALS("../../1.c", simplecpp::simplifyPath("a/../../../1.c"));
}
// tests transferred from cppcheck
// https://github.com/danmar/cppcheck/blob/d3e79b71b5ec6e641ca3e516cfced623b27988af/test/testpath.cpp#L43
static void simplifyPath_cppcheck()
{
ASSERT_EQUALS("index.h", simplecpp::simplifyPath("index.h"));
ASSERT_EQUALS("index.h", simplecpp::simplifyPath("./index.h"));
ASSERT_EQUALS("index.h", simplecpp::simplifyPath(".//index.h"));
ASSERT_EQUALS("index.h", simplecpp::simplifyPath(".///index.h"));
ASSERT_EQUALS("/index.h", simplecpp::simplifyPath("/index.h"));
ASSERT_EQUALS("/path/", simplecpp::simplifyPath("/path/"));
ASSERT_EQUALS("/", simplecpp::simplifyPath("/"));
ASSERT_EQUALS("/", simplecpp::simplifyPath("/."));
ASSERT_EQUALS("/", simplecpp::simplifyPath("/./"));
ASSERT_EQUALS("/index.h", simplecpp::simplifyPath("/./index.h"));
ASSERT_EQUALS("/", simplecpp::simplifyPath("/.//"));
ASSERT_EQUALS("/index.h", simplecpp::simplifyPath("/.//index.h"));
ASSERT_EQUALS("../index.h", simplecpp::simplifyPath("../index.h"));
ASSERT_EQUALS("/index.h", simplecpp::simplifyPath("/path/../index.h"));
ASSERT_EQUALS("index.h", simplecpp::simplifyPath("./path/../index.h"));
ASSERT_EQUALS("index.h", simplecpp::simplifyPath("path/../index.h"));
ASSERT_EQUALS("/index.h", simplecpp::simplifyPath("/path//../index.h"));
ASSERT_EQUALS("index.h", simplecpp::simplifyPath("./path//../index.h"));
ASSERT_EQUALS("index.h", simplecpp::simplifyPath("path//../index.h"));
ASSERT_EQUALS("/index.h", simplecpp::simplifyPath("/path/..//index.h"));
ASSERT_EQUALS("index.h", simplecpp::simplifyPath("./path/..//index.h"));
ASSERT_EQUALS("index.h", simplecpp::simplifyPath("path/..//index.h"));
ASSERT_EQUALS("/index.h", simplecpp::simplifyPath("/path//..//index.h"));
ASSERT_EQUALS("index.h", simplecpp::simplifyPath("./path//..//index.h"));
ASSERT_EQUALS("index.h", simplecpp::simplifyPath("path//..//index.h"));
ASSERT_EQUALS("/index.h", simplecpp::simplifyPath("/path/../other/../index.h"));
ASSERT_EQUALS("/index.h", simplecpp::simplifyPath("/path/../other///././../index.h"));
ASSERT_EQUALS("/index.h", simplecpp::simplifyPath("/path/../other/././..///index.h"));
ASSERT_EQUALS("/index.h", simplecpp::simplifyPath("/path/../other///././..///index.h"));
ASSERT_EQUALS("../path/index.h", simplecpp::simplifyPath("../path/other/../index.h"));
ASSERT_EQUALS("a/index.h", simplecpp::simplifyPath("a/../a/index.h"));
ASSERT_EQUALS(".", simplecpp::simplifyPath("a/.."));
ASSERT_EQUALS(".", simplecpp::simplifyPath("./a/.."));
ASSERT_EQUALS("../../src/test.cpp", simplecpp::simplifyPath("../../src/test.cpp"));
ASSERT_EQUALS("../../../src/test.cpp", simplecpp::simplifyPath("../../../src/test.cpp"));
ASSERT_EQUALS("src/test.cpp", simplecpp::simplifyPath(".//src/test.cpp"));
ASSERT_EQUALS("src/test.cpp", simplecpp::simplifyPath(".///src/test.cpp"));
ASSERT_EQUALS("test.cpp", simplecpp::simplifyPath("./././././test.cpp"));
ASSERT_EQUALS("src/", simplecpp::simplifyPath("src/abc/.."));
ASSERT_EQUALS("src/", simplecpp::simplifyPath("src/abc/../"));
// Handling of UNC paths on Windows
ASSERT_EQUALS("//" STRINGIZE(UNCHOST) "/test.cpp", simplecpp::simplifyPath("//" STRINGIZE(UNCHOST) "/test.cpp"));
ASSERT_EQUALS("//" STRINGIZE(UNCHOST) "/test.cpp", simplecpp::simplifyPath("///" STRINGIZE(UNCHOST) "/test.cpp"));
}
static void simplifyPath_New()
{
ASSERT_EQUALS("", simplecpp::simplifyPath(""));
ASSERT_EQUALS("/", simplecpp::simplifyPath("/"));
ASSERT_EQUALS("//", simplecpp::simplifyPath("//"));
ASSERT_EQUALS("//", simplecpp::simplifyPath("///"));
ASSERT_EQUALS("/", simplecpp::simplifyPath("\\"));
}
static void preprocessSizeOf()
{
simplecpp::OutputList outputList;
ASSERT_EQUALS("", preprocess("#if 3 > sizeof", &outputList));
ASSERT_EQUALS("file0,1,syntax_error,failed to evaluate #if condition, missing sizeof argument\n", toString(outputList));
outputList.clear();
ASSERT_EQUALS("", preprocess("#if 3 > sizeof A", &outputList));
ASSERT_EQUALS("file0,1,syntax_error,failed to evaluate #if condition, missing sizeof argument\n", toString(outputList));
outputList.clear();
ASSERT_EQUALS("", preprocess("#if 3 > sizeof(int", &outputList));
ASSERT_EQUALS("file0,1,syntax_error,failed to evaluate #if condition, invalid sizeof expression\n", toString(outputList));
}
static void timeDefine()
{
const char code[] = "__TIME__";
const std::string t = preprocess(code);
// "19:09:53"
ASSERT_EQUALS(10, t.size());
// TODO: split string and check proper ranges instead
ASSERT_EQUALS('"', t[0]);
ASSERT_EQUALS(true, isdigit(t[1]) != 0);
ASSERT_EQUALS(true, isdigit(t[2]) != 0);
ASSERT_EQUALS(':', t[3]);
ASSERT_EQUALS(true, isdigit(t[4]) != 0);
ASSERT_EQUALS(true, isdigit(t[5]) != 0);
ASSERT_EQUALS(':', t[6]);
ASSERT_EQUALS(true, isdigit(t[7]) != 0);
ASSERT_EQUALS(true, isdigit(t[8]) != 0);
ASSERT_EQUALS('"', t[9]);
}
static void dateDefine()
{
const char code[] = "__DATE__";
const std::string dt = preprocess(code);
// "\"Mar 11 2022\""
ASSERT_EQUALS(13, dt.size());
// TODO: split string and check proper ranges instead
ASSERT_EQUALS('"', dt[0]);
ASSERT_EQUALS(true, dt[1] >= 'A' && dt[1] <= 'Z'); // uppercase letter
ASSERT_EQUALS(true, dt[2] >= 'a' && dt[2] <= 'z'); // lowercase letter
ASSERT_EQUALS(true, dt[3] >= 'a' && dt[3] <= 'z'); // lowercase letter
ASSERT_EQUALS(' ', dt[4]);
ASSERT_EQUALS(true, isdigit(dt[5]) != 0);
ASSERT_EQUALS(true, isdigit(dt[6]) != 0);
ASSERT_EQUALS(' ', dt[7]);
ASSERT_EQUALS(true, isdigit(dt[8]) != 0);
ASSERT_EQUALS(true, isdigit(dt[9]) != 0);
ASSERT_EQUALS(true, isdigit(dt[10]) != 0);
ASSERT_EQUALS(true, isdigit(dt[11]) != 0);
ASSERT_EQUALS('"', dt[12]);
}
static void stdcVersionDefine()
{
const char code[] = "#if defined(__STDC_VERSION__)\n"
" __STDC_VERSION__\n"
"#endif\n";
simplecpp::DUI dui;
ASSERT_EQUALS("", preprocess(code, dui));
dui.std = "c11";
ASSERT_EQUALS("\n201112L", preprocess(code, dui));
}
static void cpluscplusDefine()
{
const char code[] = "#if defined(__cplusplus)\n"
" __cplusplus\n"
"#endif\n";
simplecpp::DUI dui;
ASSERT_EQUALS("", preprocess(code, dui));
dui.std = "c++11";
ASSERT_EQUALS("\n201103L", preprocess(code, dui));
}
static void invalidStd()
{
const char code[] = "";
simplecpp::DUI dui;
simplecpp::OutputList outputList;
dui.std = "c88";
ASSERT_EQUALS("", preprocess(code, dui, &outputList));
ASSERT_EQUALS(1, outputList.size());
ASSERT_EQUALS(simplecpp::Output::Type::DUI_ERROR, outputList.cbegin()->type);
ASSERT_EQUALS("unknown standard specified: 'c88'", outputList.cbegin()->msg);
outputList.clear();
dui.std = "gnu88";
ASSERT_EQUALS("", preprocess(code, dui, &outputList));
ASSERT_EQUALS(1, outputList.size());
ASSERT_EQUALS(simplecpp::Output::Type::DUI_ERROR, outputList.cbegin()->type);
ASSERT_EQUALS("unknown standard specified: 'gnu88'", outputList.cbegin()->msg);
outputList.clear();
dui.std = "d99";
ASSERT_EQUALS("", preprocess(code, dui, &outputList));
ASSERT_EQUALS(1, outputList.size());
ASSERT_EQUALS(simplecpp::Output::Type::DUI_ERROR, outputList.cbegin()->type);
ASSERT_EQUALS("unknown standard specified: 'd99'", outputList.cbegin()->msg);
outputList.clear();
dui.std = "c++77";
ASSERT_EQUALS("", preprocess(code, dui, &outputList));
ASSERT_EQUALS(1, outputList.size());
ASSERT_EQUALS(simplecpp::Output::Type::DUI_ERROR, outputList.cbegin()->type);
ASSERT_EQUALS("unknown standard specified: 'c++77'", outputList.cbegin()->msg);
outputList.clear();
dui.std = "gnu++33";
ASSERT_EQUALS("", preprocess(code, dui, &outputList));
ASSERT_EQUALS(1, outputList.size());
ASSERT_EQUALS(simplecpp::Output::Type::DUI_ERROR, outputList.cbegin()->type);
ASSERT_EQUALS("unknown standard specified: 'gnu++33'", outputList.cbegin()->msg);
outputList.clear();
}
static void stdEnum()
{
ASSERT_EQUALS(simplecpp::cstd_t::C89, simplecpp::getCStd("c89"));
ASSERT_EQUALS(simplecpp::cstd_t::C89, simplecpp::getCStd("c90"));
ASSERT_EQUALS(simplecpp::cstd_t::C11, simplecpp::getCStd("iso9899:2011"));
ASSERT_EQUALS(simplecpp::cstd_t::C23, simplecpp::getCStd("gnu23"));
ASSERT_EQUALS(simplecpp::cstd_t::CUnknown, simplecpp::getCStd("gnu77"));
ASSERT_EQUALS(simplecpp::cstd_t::CUnknown, simplecpp::getCStd("c++11"));
ASSERT_EQUALS(simplecpp::cppstd_t::CPP03, simplecpp::getCppStd("c++03"));
ASSERT_EQUALS(simplecpp::cppstd_t::CPP03, simplecpp::getCppStd("c++98"));
ASSERT_EQUALS(simplecpp::cppstd_t::CPP17, simplecpp::getCppStd("c++1z"));
ASSERT_EQUALS(simplecpp::cppstd_t::CPP26, simplecpp::getCppStd("gnu++26"));
ASSERT_EQUALS(simplecpp::cppstd_t::CPPUnknown, simplecpp::getCppStd("gnu++77"));
ASSERT_EQUALS(simplecpp::cppstd_t::CPPUnknown, simplecpp::getCppStd("c11"));
}
static void stdValid()
{
const char code[] = "";
simplecpp::DUI dui;
simplecpp::OutputList outputList;
dui.std = "c89";
ASSERT_EQUALS("", preprocess(code, dui, &outputList));
ASSERT_EQUALS(0, outputList.size());
outputList.clear();
dui.std = "gnu23";
ASSERT_EQUALS("", preprocess(code, dui, &outputList));
ASSERT_EQUALS(0, outputList.size());
outputList.clear();
dui.std = "c++03";
ASSERT_EQUALS("", preprocess(code, dui, &outputList));
ASSERT_EQUALS(0, outputList.size());
outputList.clear();
dui.std = "gnu++26";
ASSERT_EQUALS("", preprocess(code, dui, &outputList));
ASSERT_EQUALS(0, outputList.size());
outputList.clear();
}
static void assertToken(const std::string& s, bool name, bool number, bool comment, char op, int line)
{
const std::vector<:string> f;
const simplecpp::Location l(f);
const simplecpp::Token t(s, l);
assertEquals(name, t.name, line);
assertEquals(number, t.number, line);
assertEquals(comment, t.comment, line);
assertEquals(op, t.op, line);
}
#define ASSERT_TOKEN(s, na, nu, c) assertToken(s, na, nu, c, '\0', __LINE__)
#define ASSERT_TOKEN_OP(s, na, nu, c, o) assertToken(s, na, nu, c, o, __LINE__)
static void token()
{
// name
ASSERT_TOKEN("n", true, false, false);
ASSERT_TOKEN("name", true, false, false);
ASSERT_TOKEN("name_1", true, false, false);
ASSERT_TOKEN("name2", true, false, false);
ASSERT_TOKEN("name$", true, false, false);
// character literal
ASSERT_TOKEN("'n'", false, false, false);
ASSERT_TOKEN("'\\''", false, false, false);
ASSERT_TOKEN("'\\u0012'", false, false, false);
ASSERT_TOKEN("'\\xff'", false, false, false);
ASSERT_TOKEN("u8'\\u0012'", false, false, false);
ASSERT_TOKEN("u'\\u0012'", false, false, false);
ASSERT_TOKEN("L'\\u0012'", false, false, false);
ASSERT_TOKEN("U'\\u0012'", false, false, false);
// include
ASSERT_TOKEN("", false, false, false);
// comment
ASSERT_TOKEN("/*comment*/", false, false, true);
ASSERT_TOKEN("// TODO", false, false, true);
// string literal
ASSERT_TOKEN("\"literal\"", false, false, false);
// op
ASSERT_TOKEN_OP("<", false, false, false, '<');
ASSERT_TOKEN_OP(">", false, false, false, '>');
ASSERT_TOKEN_OP("(", false, false, false, '(');
ASSERT_TOKEN_OP(")", false, false, false, ')');
// number
ASSERT_TOKEN("2", false, true, false);
ASSERT_TOKEN("22", false, true, false);
ASSERT_TOKEN("-2", false, true, false);
ASSERT_TOKEN("-22", false, true, false);
ASSERT_TOKEN("+2", false, true, false);
ASSERT_TOKEN("+22", false, true, false);
}
static void preprocess_files()
{
{
const char code[] = "#define A";
std::vector<:string> files;
const simplecpp::TokenList tokens = makeTokenList(code, files);
ASSERT_EQUALS(1, files.size());
ASSERT_EQUALS("", *files.cbegin());
simplecpp::TokenList tokens2(files);
ASSERT_EQUALS(1, files.size());
ASSERT_EQUALS("", *files.cbegin());
simplecpp::FileDataCache cache;
simplecpp::preprocess(tokens2, tokens, files, cache, simplecpp::DUI(), nullptr);
ASSERT_EQUALS(1, files.size());
ASSERT_EQUALS("", *files.cbegin());
}
{
const char code[] = "#define A";
std::vector<:string> files;
const simplecpp::TokenList tokens = makeTokenList(code, files, "test.cpp");
ASSERT_EQUALS(1, files.size());
ASSERT_EQUALS("test.cpp", *files.cbegin());
simplecpp::TokenList tokens2(files);
ASSERT_EQUALS(1, files.size());
ASSERT_EQUALS("test.cpp", *files.cbegin());
simplecpp::FileDataCache cache;
simplecpp::preprocess(tokens2, tokens, files, cache, simplecpp::DUI(), nullptr);
ASSERT_EQUALS(1, files.size());
ASSERT_EQUALS("test.cpp", *files.cbegin());
}
}
static void safe_api()
{
// this test is to make sure the safe APIs are compiling
#if defined(__cpp_lib_string_view) || defined(__cpp_lib_span)
std::vector<:string> filenames;
# if defined(__cpp_lib_string_view)
{
const char input[] = "code";
const std::string_view sv = input;
// std::string_view can be implicitly converted into a std::span
simplecpp::TokenList(sv,filenames,"");
}
# endif
# ifdef __cpp_lib_span
{
char input[] = "code";
const std::span sp = input;
simplecpp::TokenList(sp,filenames,"");
}
{
const char input[] = "code";
const std::span sp = input;
simplecpp::TokenList(sp,filenames,"");
}
{
unsigned char input[] = "code";
const std::span sp = input;
simplecpp::TokenList(sp,filenames,"");
}
{
const unsigned char input[] = "code";
const std::span sp = input;
simplecpp::TokenList(sp,filenames,"");
}
# endif
#endif
}
static void isAbsolutePath() {
#ifdef _WIN32
ASSERT_EQUALS(true, simplecpp::isAbsolutePath("C:\\foo\\bar"));
ASSERT_EQUALS(true, simplecpp::isAbsolutePath("C:/foo/bar"));
ASSERT_EQUALS(true, simplecpp::isAbsolutePath("\\\\foo\\bar"));
ASSERT_EQUALS(false, simplecpp::isAbsolutePath("foo\\bar"));
ASSERT_EQUALS(false, simplecpp::isAbsolutePath("foo/bar"));
ASSERT_EQUALS(false, simplecpp::isAbsolutePath("foo.cpp"));
ASSERT_EQUALS(false, simplecpp::isAbsolutePath("C:foo.cpp"));
ASSERT_EQUALS(false, simplecpp::isAbsolutePath("C:foo\\bar.cpp"));
ASSERT_EQUALS(false, simplecpp::isAbsolutePath("bar.cpp"));
//ASSERT_EQUALS(true, simplecpp::isAbsolutePath("\\")); // TODO
ASSERT_EQUALS(false, simplecpp::isAbsolutePath("0:\\foo\\bar"));
ASSERT_EQUALS(false, simplecpp::isAbsolutePath("0:/foo/bar"));
ASSERT_EQUALS(false, simplecpp::isAbsolutePath("\\foo\\bar"));
//ASSERT_EQUALS(false, simplecpp::isAbsolutePath("\\\\")); // TODO
//ASSERT_EQUALS(false, simplecpp::isAbsolutePath("//")); // TODO
ASSERT_EQUALS(false, simplecpp::isAbsolutePath("/foo/bar"));
ASSERT_EQUALS(false, simplecpp::isAbsolutePath("/"));
#else
ASSERT_EQUALS(true, simplecpp::isAbsolutePath("/foo/bar"));
ASSERT_EQUALS(true, simplecpp::isAbsolutePath("/"));
ASSERT_EQUALS(true, simplecpp::isAbsolutePath("//host/foo/bar"));
ASSERT_EQUALS(false, simplecpp::isAbsolutePath("foo/bar"));
ASSERT_EQUALS(false, simplecpp::isAbsolutePath("foo.cpp"));
ASSERT_EQUALS(false, simplecpp::isAbsolutePath("C:\\foo\\bar"));
ASSERT_EQUALS(false, simplecpp::isAbsolutePath("C:/foo/bar"));
ASSERT_EQUALS(false, simplecpp::isAbsolutePath("\\\\foo\\bar"));
#endif
}
// crashes detected by fuzzer
static void fuzz_crash()
{
{
const char code[] = "#define n __VA_OPT__(u\n"
"n\n";
(void)preprocess(code, simplecpp::DUI()); // do not crash
}
{ // #346
const char code[] = "#define foo(intp)f##oo(intp\n"
"foo(f##oo(intp))\n";
(void)preprocess(code, simplecpp::DUI()); // do not crash
}
{ // #546
const char code[] = "#if __has_include<\n";
simplecpp::OutputList outputList;
ASSERT_EQUALS("", preprocess(code, &outputList)); // do not crash
ASSERT_EQUALS("file0,1,syntax_error,failed to evaluate #if condition\n", toString(outputList));
}
}
// memory leaks detected by LSAN/valgrind
static void leak()
{
{ // #498
const char code[] = "#define e(...)__VA_OPT__()\n"
"#define e\n";
(void)preprocess(code, simplecpp::DUI());
}
}
int main(int argc, char **argv)
{
TEST_CASE(backslash);
TEST_CASE(builtin);
TEST_CASE(characterLiteral);
TEST_CASE(combineOperators_floatliteral);
TEST_CASE(combineOperators_increment);
TEST_CASE(combineOperators_coloncolon);
TEST_CASE(combineOperators_andequal);
TEST_CASE(combineOperators_ellipsis);
TEST_CASE(comment);
TEST_CASE(comment_multiline);
TEST_CASE(constFold);
#ifdef __CYGWIN__
TEST_CASE(convertCygwinPath);
#endif
TEST_CASE(define1);
TEST_CASE(define2);
TEST_CASE(define3);
TEST_CASE(define4);
TEST_CASE(define5);
TEST_CASE(define6);
TEST_CASE(define7);
TEST_CASE(define8);
TEST_CASE(define9);
TEST_CASE(define10);
TEST_CASE(define11);
TEST_CASE(define12);
TEST_CASE(define13);
TEST_CASE(define_invalid_1);
TEST_CASE(define_invalid_2);
TEST_CASE(define_define_1);
TEST_CASE(define_define_2);
TEST_CASE(define_define_3);
TEST_CASE(define_define_4);
TEST_CASE(define_define_5);
TEST_CASE(define_define_6);
TEST_CASE(define_define_7);
TEST_CASE(define_define_8); // line break in nested macro call
TEST_CASE(define_define_9); // line break in nested macro call
TEST_CASE(define_define_10);
TEST_CASE(define_define_11);
TEST_CASE(define_define_11a);
TEST_CASE(define_define_12); // expand result of ##
TEST_CASE(define_define_13);
TEST_CASE(define_define_14);
TEST_CASE(define_define_15);
TEST_CASE(define_define_16);
TEST_CASE(define_define_17);
TEST_CASE(define_define_18);
TEST_CASE(define_define_19);
TEST_CASE(define_define_20); // 384 arg contains comma
TEST_CASE(define_define_21);
TEST_CASE(define_define_22); // #400
TEST_CASE(define_define_23); // #403 - crash, infinite recursion
TEST_CASE(define_va_args_1);
TEST_CASE(define_va_args_2);
TEST_CASE(define_va_args_3);
TEST_CASE(define_va_args_4);
TEST_CASE(define_va_opt_1);
TEST_CASE(define_va_opt_2);
TEST_CASE(define_va_opt_3);
TEST_CASE(define_va_opt_4);
TEST_CASE(define_va_opt_5);
TEST_CASE(define_va_opt_6);
TEST_CASE(define_va_opt_7);
TEST_CASE(define_va_opt_8);
TEST_CASE(pragma_backslash); // multiline pragma directive
// UB: #ifdef as macro parameter
TEST_CASE(define_ifdef);
TEST_CASE(dollar);
TEST_CASE(error1);
TEST_CASE(error2);
TEST_CASE(error3);
TEST_CASE(error4);
TEST_CASE(error5);
TEST_CASE(garbage);
TEST_CASE(garbage_endif);
TEST_CASE(hash);
TEST_CASE(hashhash1);
TEST_CASE(hashhash2);
TEST_CASE(hashhash3);
TEST_CASE(hashhash4);
TEST_CASE(hashhash4a); // #66, #130
TEST_CASE(hashhash5);
TEST_CASE(hashhash6);
TEST_CASE(hashhash7); // # ## # (C standard; 6.10.3.3.p4)
TEST_CASE(hashhash8);
TEST_CASE(hashhash9);
TEST_CASE(hashhash10); // #108 : #define x # #
TEST_CASE(hashhash11); // #60: #define x # # #
TEST_CASE(hashhash12);
TEST_CASE(hashhash13);
TEST_CASE(hashhash_string_literal);
TEST_CASE(hashhash_string_wrapped);
TEST_CASE(hashhash_char_literal);
TEST_CASE(hashhash_multichar_literal);
TEST_CASE(hashhash_char_escaped);
TEST_CASE(hashhash_string_nothing);
TEST_CASE(hashhash_string_char);
TEST_CASE(hashhash_string_name);
TEST_CASE(hashhashhash_int_literal);
TEST_CASE(hashhash_int_literal);
TEST_CASE(hashhash_invalid_1);
TEST_CASE(hashhash_invalid_2);
TEST_CASE(hashhash_invalid_string_number);
TEST_CASE(hashhash_invalid_missing_args);
TEST_CASE(hashhash_null_stmt);
TEST_CASE(hashhash_empty_va_args);
// C standard, 5.1.1.2, paragraph 4:
// If a character sequence that matches the syntax of a universal
// character name is produced by token concatenation (6.10.3.3),
// the behavior is undefined."
TEST_CASE(hashhash_universal_character);
// c++17 __has_include
TEST_CASE(has_include_1);
TEST_CASE(has_include_2);
TEST_CASE(has_include_3);
TEST_CASE(has_include_4);
TEST_CASE(has_include_5);
TEST_CASE(has_include_6);
TEST_CASE(strict_ansi_1);
TEST_CASE(strict_ansi_2);
TEST_CASE(strict_ansi_3);
TEST_CASE(strict_ansi_4);
TEST_CASE(ifdef1);
TEST_CASE(ifdef2);
TEST_CASE(ifndef);
TEST_CASE(ifA);
TEST_CASE(ifCharLiteral);
TEST_CASE(ifDefined);
TEST_CASE(ifDefinedNoPar);
TEST_CASE(ifDefinedNested);
TEST_CASE(ifDefinedNestedNoPar);
TEST_CASE(ifDefinedInvalid1);
TEST_CASE(ifDefinedInvalid2);
TEST_CASE(ifDefinedHashHash);
TEST_CASE(ifDefinedHashHash2);
TEST_CASE(ifLogical);
TEST_CASE(ifSizeof);
TEST_CASE(elif);
TEST_CASE(ifif);
TEST_CASE(ifoverflow);
TEST_CASE(ifdiv0);
TEST_CASE(ifalt); // using "and", "or", etc
TEST_CASE(ifexpr);
TEST_CASE(ifUndefFuncStyleMacro);
TEST_CASE(location1);
TEST_CASE(location2);
TEST_CASE(location3);
TEST_CASE(location4);
TEST_CASE(location5);
TEST_CASE(location6);
TEST_CASE(location7);
TEST_CASE(location8);
TEST_CASE(location9);
TEST_CASE(location10);
TEST_CASE(location11);
TEST_CASE(missingHeader1);
TEST_CASE(missingHeader2);
TEST_CASE(missingHeader3);
TEST_CASE(missingHeader4);
TEST_CASE(nestedInclude);
TEST_CASE(systemInclude);
TEST_CASE(circularInclude);
TEST_CASE(nullDirective1);
TEST_CASE(nullDirective2);
TEST_CASE(nullDirective3);
TEST_CASE(include1);
TEST_CASE(include2);
TEST_CASE(include3);
TEST_CASE(include4); // -include
TEST_CASE(include5); // #include MACRO
TEST_CASE(include6); // invalid code: #include MACRO(,)
TEST_CASE(include7); // #include MACRO
TEST_CASE(include8); // #include MACRO(X)
TEST_CASE(include9); // #include MACRO
TEST_CASE(multiline1);
TEST_CASE(multiline2);
TEST_CASE(multiline3);
TEST_CASE(multiline4);
TEST_CASE(multiline5); // column
TEST_CASE(multiline6); // multiline string in macro
TEST_CASE(multiline7); // multiline string in macro
TEST_CASE(multiline8); // multiline prefix string in macro
TEST_CASE(multiline9); // multiline prefix string in macro
TEST_CASE(multiline10);
TEST_CASE(readfile_nullbyte);
TEST_CASE(readfile_char);
TEST_CASE(readfile_char_error);
TEST_CASE(readfile_string);
TEST_CASE(readfile_string_error);
TEST_CASE(readfile_cpp14_number);
TEST_CASE(readfile_unhandled_chars);
TEST_CASE(readfile_error);
TEST_CASE(readfile_file_not_found);
TEST_CASE(stringify1);
TEST_CASE(tokenMacro1);
TEST_CASE(tokenMacro2);
TEST_CASE(tokenMacro3);
TEST_CASE(tokenMacro4);
TEST_CASE(tokenMacro5);
TEST_CASE(undef);
TEST_CASE(userdef);
// utf/unicode
TEST_CASE(utf8);
TEST_CASE(utf8_invalid);
TEST_CASE(unicode);
TEST_CASE(unicode_invalid);
TEST_CASE(warning);
// utility functions.
TEST_CASE(simplifyPath);
TEST_CASE(simplifyPath_cppcheck);
TEST_CASE(simplifyPath_New);
TEST_CASE(preprocessSizeOf);
TEST_CASE(timeDefine);
TEST_CASE(dateDefine);
TEST_CASE(stdcVersionDefine);
TEST_CASE(cpluscplusDefine);
TEST_CASE(invalidStd);
TEST_CASE(stdEnum);
TEST_CASE(stdValid);
TEST_CASE(token);
TEST_CASE(preprocess_files);
TEST_CASE(safe_api);
TEST_CASE(isAbsolutePath);
TEST_CASE(fuzz_crash);
TEST_CASE(leak);
return numberOfFailedAssertions > 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}