blob: c9d59611f4868aaa5bcdeb1c1cbcc4143b72f8aa [file] [log] [blame]
import os
import sys
import unittest
# Run from the root dir
sys.path.insert(0, '.')
from pycparser import c_parser, c_generator, c_ast, parse_file
from tests.test_util import cpp_supported, cpp_path, cpp_args
_c_parser = c_parser.CParser(
lex_optimize=False,
yacc_debug=True,
yacc_optimize=False,
yacctab='yacctab')
def compare_asts(ast1, ast2):
"""Compares two ASTs recursively just enough for the purpose of testing.
Since this function is recursive it also accepts non-ast parameters, in
which case it compares them literally (with ==) or recursively (for tuples
or lists).
"""
# After the initial `if`, all the `elif` clauses assume that the types of
# ast1 and ast2 are the same.
if type(ast1) != type(ast2):
return False
elif isinstance(ast1, (list, tuple)):
if len(ast1) != len(ast2):
return False
for i in range(len(ast1)):
if not compare_asts(ast1[i], ast2[i]):
return False
return True
elif isinstance(ast1, c_ast.Node):
for attr in ast1.attr_names:
attr1 = getattr(ast1, attr)
attr2 = getattr(ast2, attr)
if not compare_asts(attr1, attr2):
return False
children1 = ast1.children()
children2 = ast2.children()
if len(children1) != len(children2):
return False
for i in range(len(children1)):
if not compare_asts(children1[i], children2[i]):
return False
return True
else:
return ast1 == ast2
def parse_to_ast(src):
return _c_parser.parse(src)
class TestFunctionDeclGeneration(unittest.TestCase):
class _FuncDeclVisitor(c_ast.NodeVisitor):
def __init__(self):
self.stubs = []
def visit_FuncDecl(self, node):
gen = c_generator.CGenerator()
self.stubs.append(gen.visit(node))
def test_partial_funcdecl_generation(self):
src = r'''
void noop(void);
void *something(void *thing);
int add(int x, int y);'''
ast = parse_to_ast(src)
v = TestFunctionDeclGeneration._FuncDeclVisitor()
v.visit(ast)
self.assertEqual(len(v.stubs), 3)
self.assertTrue(r'void noop(void)' in v.stubs)
self.assertTrue(r'void *something(void *thing)' in v.stubs)
self.assertTrue(r'int add(int x, int y)' in v.stubs)
class TestCtoC(unittest.TestCase):
def _run_c_to_c(self, src, *args, **kwargs):
ast = parse_to_ast(src)
generator = c_generator.CGenerator(*args, **kwargs)
return generator.visit(ast)
def _assert_ctoc_correct(self, src, *args, **kwargs):
""" Checks that the c2c translation was correct by parsing the code
generated by c2c for src and comparing the AST with the original
AST.
Additional arguments are passed to CGenerator.__init__.
"""
src2 = self._run_c_to_c(src, *args, **kwargs)
self.assertTrue(compare_asts(parse_to_ast(src), parse_to_ast(src2)),
"{!r} != {!r}".format(src, src2))
return src2
def test_trivial_decls(self):
self._assert_ctoc_correct('int a;')
self._assert_ctoc_correct('int b, a;')
self._assert_ctoc_correct('int c, b, a;')
self._assert_ctoc_correct('auto int a;')
self._assert_ctoc_correct('register int a;')
self._assert_ctoc_correct('_Thread_local int a;')
def test_complex_decls(self):
self._assert_ctoc_correct('int** (*a)(void);')
self._assert_ctoc_correct('int** (*a)(void*, int);')
self._assert_ctoc_correct('int (*b)(char * restrict k, float);')
self._assert_ctoc_correct('int (*b)(char * _Atomic k, float);')
self._assert_ctoc_correct('int (*b)(char * _Atomic volatile k, float);')
self._assert_ctoc_correct('int test(const char* const* arg);')
self._assert_ctoc_correct('int test(const char** const arg);')
def test_alignment(self):
self._assert_ctoc_correct('_Alignas(32) int b;')
self._assert_ctoc_correct('int _Alignas(32) a;')
self._assert_ctoc_correct('_Alignas(32) _Atomic(int) b;')
self._assert_ctoc_correct('_Atomic(int) _Alignas(32) b;')
self._assert_ctoc_correct('_Alignas(long long) int a;')
self._assert_ctoc_correct('int _Alignas(long long) a;')
self._assert_ctoc_correct(r'''
typedef struct node_t {
_Alignas(64) void* next;
int data;
} node;
''')
self._assert_ctoc_correct(r'''
typedef struct node_t {
void _Alignas(64) * next;
int data;
} node;
''')
def test_ternary(self):
self._assert_ctoc_correct('''
int main(void)
{
int a, b;
(a == 0) ? (b = 1) : (b = 2);
}''')
def test_casts(self):
self._assert_ctoc_correct(r'''
int main() {
int b = (int) f;
int c = (int*) f;
}''')
self._assert_ctoc_correct(r'''
int main() {
int a = (int) b + 8;
int t = (int) c;
}
''')
def test_initlist(self):
self._assert_ctoc_correct('int arr[] = {1, 2, 3};')
def test_exprs(self):
self._assert_ctoc_correct('''
int main(void)
{
int a;
int b = a++;
int c = ++a;
int d = a--;
int e = --a;
}''')
def test_statements(self):
# note two minuses here
self._assert_ctoc_correct(r'''
int main() {
int a;
a = 5;
;
b = - - a;
return a;
}''')
def test_struct_decl(self):
self._assert_ctoc_correct(r'''
typedef struct node_t {
struct node_t* next;
int data;
} node;
''')
def test_krstyle(self):
self._assert_ctoc_correct(r'''
int main(argc, argv)
int argc;
char** argv;
{
return 0;
}
''')
def test_switchcase(self):
self._assert_ctoc_correct(r'''
int main() {
switch (myvar) {
case 10:
{
k = 10;
p = k + 1;
break;
}
case 20:
case 30:
return 20;
default:
break;
}
}
''')
def test_nest_initializer_list(self):
self._assert_ctoc_correct(r'''
int main()
{
int i[1][1] = { { 1 } };
}''')
def test_nest_named_initializer(self):
self._assert_ctoc_correct(r'''struct test
{
int i;
struct test_i_t
{
int k;
} test_i;
int j;
};
struct test test_var = {.i = 0, .test_i = {.k = 1}, .j = 2};
''')
def test_expr_list_in_initializer_list(self):
self._assert_ctoc_correct(r'''
int main()
{
int i[1] = { (1, 2) };
}''')
def test_issue36(self):
self._assert_ctoc_correct(r'''
int main() {
}''')
def test_issue37(self):
self._assert_ctoc_correct(r'''
int main(void)
{
unsigned size;
size = sizeof(size);
return 0;
}''')
def test_issue66(self):
# A non-existing body must not be generated
# (previous valid behavior, still working)
self._assert_ctoc_correct(r'''
struct foo;
''')
# An empty body must be generated
# (added behavior)
self._assert_ctoc_correct(r'''
struct foo {};
''')
def test_issue83(self):
self._assert_ctoc_correct(r'''
void x(void) {
int i = (9, k);
}
''')
def test_issue84(self):
self._assert_ctoc_correct(r'''
void x(void) {
for (int i = 0;;)
i;
}
''')
def test_issue246(self):
self._assert_ctoc_correct(r'''
int array[3] = {[0] = 0, [1] = 1, [1+1] = 2};
''')
def test_noreturn(self):
self._assert_ctoc_correct(r'''
_Noreturn int x(void) {
abort();
}
''')
def test_exprlist_with_semi(self):
self._assert_ctoc_correct(r'''
void x() {
if (i < j)
tmp = C[i], C[i] = C[j], C[j] = tmp;
if (i <= j)
i++, j--;
}
''')
def test_exprlist_with_subexprlist(self):
self._assert_ctoc_correct(r'''
void x() {
(a = b, (b = c, c = a));
}
''')
def test_comma_operator_funcarg(self):
self._assert_ctoc_correct(r'''
void f(int x) { return x; }
int main(void) { f((1, 2)); return 0; }
''')
def test_comma_op_in_ternary(self):
self._assert_ctoc_correct(r'''
void f() {
(0, 0) ? (0, 0) : (0, 0);
}
''')
def test_comma_op_assignment(self):
self._assert_ctoc_correct(r'''
void f() {
i = (a, b, c);
}
''')
def test_pragma(self):
self._assert_ctoc_correct(r'''
#pragma foo
void f() {
#pragma bar
i = (a, b, c);
if (d)
#pragma qux
j = e;
if (d)
#pragma qux
#pragma quux
j = e;
}
typedef struct s {
#pragma baz
} s;
''')
def test_compound_literal(self):
self._assert_ctoc_correct('char **foo = (char *[]){ "x", "y", "z" };')
self._assert_ctoc_correct('int i = ++(int){ 1 };')
self._assert_ctoc_correct('struct foo_s foo = (struct foo_s){ 1, 2 };')
def test_enum(self):
self._assert_ctoc_correct(r'''
enum e
{
a,
b = 2,
c = 3
};
''')
self._assert_ctoc_correct(r'''
enum f
{
g = 4,
h,
i
};
''')
def test_enum_typedef(self):
self._assert_ctoc_correct('typedef enum EnumName EnumTypedefName;')
def test_generate_struct_union_enum_exception(self):
generator = c_generator.CGenerator()
self.assertRaises(
AssertionError,
generator._generate_struct_union_enum,
n=c_ast.Struct(
name='TestStruct',
decls=[],
),
name='',
)
def test_array_decl(self):
self._assert_ctoc_correct('int g(const int a[const 20]){}')
ast = parse_to_ast('const int a[const 20];')
generator = c_generator.CGenerator()
self.assertEqual(generator.visit(ast.ext[0].type),
'const int [const 20]')
self.assertEqual(generator.visit(ast.ext[0].type.type),
'const int')
def test_ptr_decl(self):
src = 'const int ** const x;'
self._assert_ctoc_correct(src)
ast = parse_to_ast(src)
generator = c_generator.CGenerator()
self.assertEqual(generator.visit(ast.ext[0].type),
'const int ** const')
self.assertEqual(generator.visit(ast.ext[0].type.type),
'const int *')
self.assertEqual(generator.visit(ast.ext[0].type.type.type),
'const int')
def test_atomic_qual(self):
self._assert_ctoc_correct('_Atomic int x;')
self._assert_ctoc_correct('_Atomic int* x;')
self._assert_ctoc_correct('int* _Atomic x;')
# _Atomic specifier gets turned into qualifier.
s1 = '_Atomic(int) x;'
c1 = self._run_c_to_c(s1)
self.assertEqual(c1, '_Atomic int x;\n')
self._assert_ctoc_correct(s1)
s2 = '_Atomic(int*) x;'
c2 = self._run_c_to_c(s2)
self.assertEqual(c2, 'int * _Atomic x;\n')
self._assert_ctoc_correct(s2)
s3 = '_Atomic(_Atomic(int)*) x;'
c3 = self._run_c_to_c(s3)
self.assertEqual(c3, '_Atomic int * _Atomic x;\n')
self._assert_ctoc_correct(s3)
# TODO: Regeneration with multiple qualifiers is not fully supported.
# REF: https://github.com/eliben/pycparser/issues/433
# self._assert_ctoc_correct('auto const _Atomic(int *) a;')
s4 = 'typedef _Atomic(int) atomic_int;'
c4 = self._run_c_to_c(s4)
self.assertEqual(c4, 'typedef _Atomic int atomic_int;\n')
self._assert_ctoc_correct(s4)
s5 = 'typedef _Atomic(_Atomic(_Atomic(int (*)(void)) *) *) t;'
c5 = self._run_c_to_c(s5)
self.assertEqual(c5, 'typedef int (* _Atomic * _Atomic * _Atomic t)(void);\n')
self._assert_ctoc_correct(s5)
self._assert_ctoc_correct(r'''
typedef struct node_t {
_Atomic(void*) a;
_Atomic(void) *b;
_Atomic void *c;
} node;
''')
def test_nested_sizeof(self):
src = '1'
for _ in range(30):
src = 'sizeof(' + src + ')'
src = 'int x = ' + src + ';'
self._assert_ctoc_correct(src)
def test_static_assert(self):
self._assert_ctoc_correct('_Static_assert(sizeof(int) == sizeof(int), "123");')
self._assert_ctoc_correct('int main() { _Static_assert(sizeof(int) == sizeof(int), "123"); } ')
self._assert_ctoc_correct('_Static_assert(sizeof(int) == sizeof(int));')
def test_reduce_parentheses_binaryops(self):
c1 = 'int x = a + b + c + d;';
self.assertEqual(self._run_c_to_c(c1), 'int x = ((a + b) + c) + d;\n')
self.assertEqual(
self._run_c_to_c(c1, reduce_parentheses=True),
'int x = a + b + c + d;\n')
# codes with minimum number of (necessary) parenthesis:
test_snippets = [
'int x = a*b*c*d;',
'int x = a+b*c*d;',
'int x = a*b+c*d;',
'int x = a*b*c+d;',
'int x = (a+b)*c*d;',
'int x = (a+b)*(c+d);',
'int x = (a+b)/(c-d);',
'int x = a+b-c-d;',
'int x = a+(b-c)-d;'
]
for src in test_snippets:
src2 = self._assert_ctoc_correct(src, reduce_parentheses=True)
self.assertTrue(
src2.count('(') == src.count('('),
msg="{!r} did not have minimum number of parenthesis, should be like {!r}.".format(
src2, src))
class TestCasttoC(unittest.TestCase):
def _find_file(self, name):
test_dir = os.path.dirname(__file__)
name = os.path.join(test_dir, 'c_files', name)
assert os.path.exists(name)
return name
def test_to_type(self):
src = 'int *x;'
generator = c_generator.CGenerator()
test_fun = c_ast.FuncCall(c_ast.ID('test_fun'), c_ast.ExprList([]))
ast1 = parse_to_ast(src)
int_ptr_type = ast1.ext[0].type
int_type = int_ptr_type.type
self.assertEqual(generator.visit(c_ast.Cast(int_ptr_type, test_fun)),
'(int *) test_fun()')
self.assertEqual(generator.visit(c_ast.Cast(int_type, test_fun)),
'(int) test_fun()')
@unittest.skipUnless(cpp_supported(), 'cpp only works on Unix')
def test_to_type_with_cpp(self):
generator = c_generator.CGenerator()
test_fun = c_ast.FuncCall(c_ast.ID('test_fun'), c_ast.ExprList([]))
memmgr_path = self._find_file('memmgr.h')
ast2 = parse_file(memmgr_path, use_cpp=True,
cpp_path=cpp_path(), cpp_args=cpp_args())
void_ptr_type = ast2.ext[-3].type.type
void_type = void_ptr_type.type
self.assertEqual(generator.visit(c_ast.Cast(void_ptr_type, test_fun)),
'(void *) test_fun()')
self.assertEqual(generator.visit(c_ast.Cast(void_type, test_fun)),
'(void) test_fun()')
def test_nested_else_if_line_breaks(self):
generator = c_generator.CGenerator()
test_ast1 = c_ast.If(None, None, None)
test_ast2 = c_ast.If(None, None, c_ast.If(None, None, None))
test_ast3 = c_ast.If(None, None, c_ast.If(None, None, c_ast.If(None, None, None)))
test_ast4 = c_ast.If(None, c_ast.Compound([]), c_ast.If(None, c_ast.Compound([]), c_ast.If(None, c_ast.Compound([]), None)))
self.assertEqual(generator.visit(test_ast1),
'if ()\n \n')
self.assertEqual(generator.visit(test_ast2),
'if ()\n \nelse\n if ()\n \n')
self.assertEqual(generator.visit(test_ast3),
'if ()\n \nelse\n if ()\n \nelse\n if ()\n \n')
self.assertEqual(generator.visit(test_ast4),
'if ()\n{\n}\nelse\n if ()\n{\n}\nelse\n if ()\n{\n}\n')
if __name__ == "__main__":
unittest.main()