You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2244 lines
77 KiB
2244 lines
77 KiB
#!/usr/bin/env python
|
|
|
|
import pprint
|
|
import re
|
|
import os, sys
|
|
import io
|
|
import unittest
|
|
|
|
sys.path[0:0] = ['.', '..']
|
|
|
|
from pycparser import c_parser
|
|
from pycparser.c_ast import *
|
|
from pycparser.c_parser import CParser, Coord, ParseError
|
|
|
|
_c_parser = c_parser.CParser(
|
|
lex_optimize=False,
|
|
yacc_debug=True,
|
|
yacc_optimize=False,
|
|
yacctab='yacctab')
|
|
|
|
|
|
def expand_decl(decl):
|
|
""" Converts the declaration into a nested list.
|
|
"""
|
|
typ = type(decl)
|
|
|
|
if typ == TypeDecl:
|
|
return ['TypeDecl', expand_decl(decl.type)]
|
|
elif typ == IdentifierType:
|
|
return ['IdentifierType', decl.names]
|
|
elif typ == ID:
|
|
return ['ID', decl.name]
|
|
elif typ in [Struct, Union]:
|
|
decls = [expand_decl(d) for d in decl.decls or []]
|
|
return [typ.__name__, decl.name, decls]
|
|
else:
|
|
nested = expand_decl(decl.type)
|
|
|
|
if typ == Decl:
|
|
if decl.quals:
|
|
return ['Decl', decl.quals, decl.name, nested]
|
|
else:
|
|
return ['Decl', decl.name, nested]
|
|
elif typ == Typename: # for function parameters
|
|
if decl.quals:
|
|
return ['Typename', decl.quals, nested]
|
|
else:
|
|
return ['Typename', nested]
|
|
elif typ == ArrayDecl:
|
|
dimval = decl.dim.value if decl.dim else ''
|
|
return ['ArrayDecl', dimval, decl.dim_quals, nested]
|
|
elif typ == PtrDecl:
|
|
if decl.quals:
|
|
return ['PtrDecl', decl.quals, nested]
|
|
else:
|
|
return ['PtrDecl', nested]
|
|
elif typ == Typedef:
|
|
return ['Typedef', decl.name, nested]
|
|
elif typ == FuncDecl:
|
|
if decl.args:
|
|
params = [expand_decl(param) for param in decl.args.params]
|
|
else:
|
|
params = []
|
|
return ['FuncDecl', params, nested]
|
|
|
|
|
|
def expand_init(init):
|
|
""" Converts an initialization into a nested list
|
|
"""
|
|
typ = type(init)
|
|
|
|
if typ == NamedInitializer:
|
|
des = [expand_init(dp) for dp in init.name]
|
|
return (des, expand_init(init.expr))
|
|
elif typ in (InitList, ExprList):
|
|
return [expand_init(expr) for expr in init.exprs]
|
|
elif typ == Constant:
|
|
return ['Constant', init.type, init.value]
|
|
elif typ == ID:
|
|
return ['ID', init.name]
|
|
elif typ == UnaryOp:
|
|
return ['UnaryOp', init.op, expand_decl(init.expr)]
|
|
|
|
|
|
class TestCParser_base(unittest.TestCase):
|
|
def parse(self, txt, filename=''):
|
|
return self.cparser.parse(txt, filename)
|
|
|
|
def setUp(self):
|
|
self.cparser = _c_parser
|
|
|
|
def assert_coord(self, node, line, column=None, file=None):
|
|
self.assertEqual(node.coord.line, line)
|
|
if column is not None:
|
|
self.assertEqual(node.coord.column, column)
|
|
if file:
|
|
self.assertEqual(node.coord.file, file)
|
|
|
|
|
|
|
|
class TestCParser_fundamentals(TestCParser_base):
|
|
def get_decl(self, txt, index=0):
|
|
""" Given a source and an index returns the expanded
|
|
declaration at that index.
|
|
|
|
FileAST holds a list of 'external declarations'.
|
|
index is the offset of the desired declaration in that
|
|
list.
|
|
"""
|
|
t = self.parse(txt).ext[index]
|
|
return expand_decl(t)
|
|
|
|
def get_decl_init(self, txt, index=0):
|
|
""" Returns the expanded initializer of the declaration
|
|
at index.
|
|
"""
|
|
t = self.parse(txt).ext[index]
|
|
return expand_init(t.init)
|
|
|
|
def test_FileAST(self):
|
|
t = self.parse('int a; char c;')
|
|
self.assertIsInstance(t, FileAST)
|
|
self.assertEqual(len(t.ext), 2)
|
|
|
|
# empty file
|
|
t2 = self.parse('')
|
|
self.assertIsInstance(t2, FileAST)
|
|
self.assertEqual(len(t2.ext), 0)
|
|
|
|
def test_empty_toplevel_decl(self):
|
|
code = 'int foo;;'
|
|
t = self.parse(code)
|
|
self.assertIsInstance(t, FileAST)
|
|
self.assertEqual(len(t.ext), 1)
|
|
self.assertEqual(self.get_decl(code),
|
|
['Decl', 'foo',
|
|
['TypeDecl', ['IdentifierType', ['int']]]])
|
|
|
|
def test_initial_semi(self):
|
|
t = self.parse(';')
|
|
self.assertEqual(len(t.ext), 0)
|
|
t = self.parse(';int foo;')
|
|
self.assertEqual(len(t.ext), 1)
|
|
self.assertEqual(expand_decl(t.ext[0]),
|
|
['Decl', 'foo',
|
|
['TypeDecl', ['IdentifierType', ['int']]]])
|
|
|
|
def test_coords(self):
|
|
""" Tests the "coordinates" of parsed elements - file
|
|
name, line and column numbers, with modification
|
|
insterted by #line directives.
|
|
"""
|
|
self.assert_coord(self.parse('int a;').ext[0], 1, 5)
|
|
|
|
t1 = """
|
|
int a;
|
|
int b;\n\n
|
|
int c;
|
|
"""
|
|
f1 = self.parse(t1, filename='test.c')
|
|
self.assert_coord(f1.ext[0], 2, 13, 'test.c')
|
|
self.assert_coord(f1.ext[1], 3, 13, 'test.c')
|
|
self.assert_coord(f1.ext[2], 6, 13, 'test.c')
|
|
|
|
t1_1 = '''
|
|
int main() {
|
|
k = p;
|
|
printf("%d", b);
|
|
return 0;
|
|
}'''
|
|
f1_1 = self.parse(t1_1, filename='test.c')
|
|
self.assert_coord(f1_1.ext[0].body.block_items[0], 3, 13, 'test.c')
|
|
self.assert_coord(f1_1.ext[0].body.block_items[1], 4, 13, 'test.c')
|
|
|
|
t1_2 = '''
|
|
int main () {
|
|
int p = (int) k;
|
|
}'''
|
|
f1_2 = self.parse(t1_2, filename='test.c')
|
|
# make sure that the Cast has a coord (issue 23)
|
|
self.assert_coord(f1_2.ext[0].body.block_items[0].init, 3, 21, file='test.c')
|
|
|
|
t2 = """
|
|
#line 99
|
|
int c;
|
|
"""
|
|
self.assert_coord(self.parse(t2).ext[0], 99, 13)
|
|
|
|
t3 = """
|
|
int dsf;
|
|
char p;
|
|
#line 3000 "in.h"
|
|
char d;
|
|
"""
|
|
f3 = self.parse(t3, filename='test.c')
|
|
self.assert_coord(f3.ext[0], 2, 13, 'test.c')
|
|
self.assert_coord(f3.ext[1], 3, 14, 'test.c')
|
|
self.assert_coord(f3.ext[2], 3000, 14, 'in.h')
|
|
|
|
t4 = """
|
|
#line 20 "restore.h"
|
|
int maydler(char);
|
|
|
|
#line 30 "includes/daween.ph"
|
|
long j, k;
|
|
|
|
#line 50000
|
|
char* ro;
|
|
"""
|
|
f4 = self.parse(t4, filename='myb.c')
|
|
self.assert_coord(f4.ext[0], 20, 13, 'restore.h')
|
|
self.assert_coord(f4.ext[1], 30, 14, 'includes/daween.ph')
|
|
self.assert_coord(f4.ext[2], 30, 17, 'includes/daween.ph')
|
|
self.assert_coord(f4.ext[3], 50000, 13, 'includes/daween.ph')
|
|
|
|
t5 = """
|
|
int
|
|
#line 99
|
|
c;
|
|
"""
|
|
self.assert_coord(self.parse(t5).ext[0], 99, 9)
|
|
|
|
# coord for ellipsis
|
|
t6 = """
|
|
int foo(int j,
|
|
...) {
|
|
}"""
|
|
f6 = self.parse(t6, filename='z.c')
|
|
self.assert_coord(self.parse(t6).ext[0].decl.type.args.params[1], 3, 17)
|
|
|
|
def test_forloop_coord(self):
|
|
t = '''\
|
|
void foo() {
|
|
for(int z=0; z<4;
|
|
z++){}
|
|
}
|
|
'''
|
|
s = self.parse(t, filename='f.c')
|
|
forloop = s.ext[0].body.block_items[0]
|
|
self.assert_coord(forloop.init, 2, 13, 'f.c')
|
|
self.assert_coord(forloop.cond, 2, 26, 'f.c')
|
|
self.assert_coord(forloop.next, 3, 17, 'f.c')
|
|
|
|
def test_simple_decls(self):
|
|
self.assertEqual(self.get_decl('int a;'),
|
|
['Decl', 'a', ['TypeDecl', ['IdentifierType', ['int']]]])
|
|
|
|
self.assertEqual(self.get_decl('unsigned int a;'),
|
|
['Decl', 'a', ['TypeDecl', ['IdentifierType', ['unsigned', 'int']]]])
|
|
|
|
self.assertEqual(self.get_decl('_Bool a;'),
|
|
['Decl', 'a', ['TypeDecl', ['IdentifierType', ['_Bool']]]])
|
|
|
|
self.assertEqual(self.get_decl('float _Complex fcc;'),
|
|
['Decl', 'fcc', ['TypeDecl', ['IdentifierType', ['float', '_Complex']]]])
|
|
|
|
self.assertEqual(self.get_decl('char* string;'),
|
|
['Decl', 'string',
|
|
['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]])
|
|
|
|
self.assertEqual(self.get_decl('long ar[15];'),
|
|
['Decl', 'ar',
|
|
['ArrayDecl', '15', [],
|
|
['TypeDecl', ['IdentifierType', ['long']]]]])
|
|
|
|
self.assertEqual(self.get_decl('long long ar[15];'),
|
|
['Decl', 'ar',
|
|
['ArrayDecl', '15', [],
|
|
['TypeDecl', ['IdentifierType', ['long', 'long']]]]])
|
|
|
|
self.assertEqual(self.get_decl('unsigned ar[];'),
|
|
['Decl', 'ar',
|
|
['ArrayDecl', '', [],
|
|
['TypeDecl', ['IdentifierType', ['unsigned']]]]])
|
|
|
|
self.assertEqual(self.get_decl('int strlen(char* s);'),
|
|
['Decl', 'strlen',
|
|
['FuncDecl',
|
|
[['Decl', 's',
|
|
['PtrDecl',
|
|
['TypeDecl', ['IdentifierType', ['char']]]]]],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]])
|
|
|
|
self.assertEqual(self.get_decl('int strcmp(char* s1, char* s2);'),
|
|
['Decl', 'strcmp',
|
|
['FuncDecl',
|
|
[ ['Decl', 's1',
|
|
['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]],
|
|
['Decl', 's2',
|
|
['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]]
|
|
],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]])
|
|
|
|
# function return values and parameters may not have type information
|
|
self.assertEqual(self.get_decl('extern foobar(foo, bar);'),
|
|
['Decl', 'foobar',
|
|
['FuncDecl',
|
|
[ ['ID', 'foo'],
|
|
['ID', 'bar']
|
|
],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]])
|
|
|
|
def test_int128(self):
|
|
self.assertEqual(self.get_decl('__int128 a;'),
|
|
['Decl', 'a', ['TypeDecl', ['IdentifierType', ['__int128']]]])
|
|
|
|
|
|
def test_nested_decls(self): # the fun begins
|
|
self.assertEqual(self.get_decl('char** ar2D;'),
|
|
['Decl', 'ar2D',
|
|
['PtrDecl', ['PtrDecl',
|
|
['TypeDecl', ['IdentifierType', ['char']]]]]])
|
|
|
|
self.assertEqual(self.get_decl('int (*a)[1][2];'),
|
|
['Decl', 'a',
|
|
['PtrDecl',
|
|
['ArrayDecl', '1', [],
|
|
['ArrayDecl', '2', [],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]]]])
|
|
|
|
self.assertEqual(self.get_decl('int *a[1][2];'),
|
|
['Decl', 'a',
|
|
['ArrayDecl', '1', [],
|
|
['ArrayDecl', '2', [],
|
|
['PtrDecl', ['TypeDecl', ['IdentifierType', ['int']]]]]]])
|
|
|
|
self.assertEqual(self.get_decl('char* const* p;'),
|
|
['Decl', 'p',
|
|
['PtrDecl', ['PtrDecl', ['const'],
|
|
['TypeDecl', ['IdentifierType', ['char']]]]]])
|
|
|
|
self.assertEqual(self.get_decl('char* * const p;'),
|
|
['Decl', 'p',
|
|
['PtrDecl', ['const'], ['PtrDecl',
|
|
['TypeDecl', ['IdentifierType', ['char']]]]]])
|
|
|
|
self.assertEqual(self.get_decl('char ***ar3D[40];'),
|
|
['Decl', 'ar3D',
|
|
['ArrayDecl', '40', [],
|
|
['PtrDecl', ['PtrDecl', ['PtrDecl',
|
|
['TypeDecl', ['IdentifierType', ['char']]]]]]]])
|
|
|
|
self.assertEqual(self.get_decl('char (***ar3D)[40];'),
|
|
['Decl', 'ar3D',
|
|
['PtrDecl', ['PtrDecl', ['PtrDecl',
|
|
['ArrayDecl', '40', [], ['TypeDecl', ['IdentifierType', ['char']]]]]]]])
|
|
|
|
self.assertEqual(self.get_decl('int (*x[4])(char, int);'),
|
|
['Decl', 'x',
|
|
['ArrayDecl', '4', [],
|
|
['PtrDecl',
|
|
['FuncDecl',
|
|
[ ['Typename', ['TypeDecl', ['IdentifierType', ['char']]]],
|
|
['Typename', ['TypeDecl', ['IdentifierType', ['int']]]]],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]]]])
|
|
|
|
self.assertEqual(self.get_decl('char *(*(**foo [][8])())[];'),
|
|
['Decl', 'foo',
|
|
['ArrayDecl', '', [],
|
|
['ArrayDecl', '8', [],
|
|
['PtrDecl', ['PtrDecl',
|
|
['FuncDecl',
|
|
[],
|
|
['PtrDecl',
|
|
['ArrayDecl', '', [],
|
|
['PtrDecl',
|
|
['TypeDecl',
|
|
['IdentifierType', ['char']]]]]]]]]]]])
|
|
|
|
# explore named and unnamed function pointer parameters,
|
|
# with and without qualifiers
|
|
|
|
# unnamed w/o quals
|
|
self.assertEqual(self.get_decl('int (*k)(int);'),
|
|
['Decl', 'k',
|
|
['PtrDecl',
|
|
['FuncDecl',
|
|
[['Typename', ['TypeDecl', ['IdentifierType', ['int']]]]],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]]])
|
|
|
|
# unnamed w/ quals
|
|
self.assertEqual(self.get_decl('int (*k)(const int);'),
|
|
['Decl', 'k',
|
|
['PtrDecl',
|
|
['FuncDecl',
|
|
[['Typename', ['const'], ['TypeDecl', ['IdentifierType', ['int']]]]],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]]])
|
|
|
|
# named w/o quals
|
|
self.assertEqual(self.get_decl('int (*k)(int q);'),
|
|
['Decl', 'k',
|
|
['PtrDecl',
|
|
['FuncDecl',
|
|
[['Decl', 'q', ['TypeDecl', ['IdentifierType', ['int']]]]],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]]])
|
|
|
|
# named w/ quals
|
|
self.assertEqual(self.get_decl('int (*k)(const volatile int q);'),
|
|
['Decl', 'k',
|
|
['PtrDecl',
|
|
['FuncDecl',
|
|
[['Decl', ['const', 'volatile'], 'q',
|
|
['TypeDecl', ['IdentifierType', ['int']]]]],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]]])
|
|
|
|
self.assertEqual(self.get_decl('int (*k)(const volatile int* q);'),
|
|
['Decl', 'k',
|
|
['PtrDecl',
|
|
['FuncDecl',
|
|
[['Decl', ['const', 'volatile'], 'q',
|
|
['PtrDecl', ['TypeDecl', ['IdentifierType', ['int']]]]]],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]]])
|
|
|
|
# restrict qualifier
|
|
self.assertEqual(self.get_decl('int (*k)(restrict int* q);'),
|
|
['Decl', 'k',
|
|
['PtrDecl',
|
|
['FuncDecl',
|
|
[['Decl', ['restrict'], 'q',
|
|
['PtrDecl',
|
|
['TypeDecl', ['IdentifierType', ['int']]]]]],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]]])
|
|
|
|
def test_func_decls_with_array_dim_qualifiers(self):
|
|
# named function parameter
|
|
self.assertEqual(self.get_decl('int zz(int p[static 10]);'),
|
|
['Decl', 'zz',
|
|
['FuncDecl',
|
|
[['Decl', 'p', ['ArrayDecl', '10', ['static'],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]]],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]])
|
|
|
|
self.assertEqual(self.get_decl('int zz(int p[const 10]);'),
|
|
['Decl', 'zz',
|
|
['FuncDecl',
|
|
[['Decl', 'p', ['ArrayDecl', '10', ['const'],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]]],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]])
|
|
|
|
self.assertEqual(self.get_decl('int zz(int p[restrict][5]);'),
|
|
['Decl', 'zz',
|
|
['FuncDecl',
|
|
[['Decl', 'p', ['ArrayDecl', '', ['restrict'],
|
|
['ArrayDecl', '5', [],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]]]],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]])
|
|
|
|
self.assertEqual(self.get_decl('int zz(int p[const restrict static 10][5]);'),
|
|
['Decl', 'zz',
|
|
['FuncDecl',
|
|
[['Decl', 'p', ['ArrayDecl', '10', ['const', 'restrict', 'static'],
|
|
['ArrayDecl', '5', [],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]]]],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]])
|
|
|
|
# unnamed function parameter
|
|
self.assertEqual(self.get_decl('int zz(int [const 10]);'),
|
|
['Decl', 'zz',
|
|
['FuncDecl',
|
|
[['Typename', ['ArrayDecl', '10', ['const'],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]]],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]])
|
|
|
|
self.assertEqual(self.get_decl('int zz(int [restrict][5]);'),
|
|
['Decl', 'zz',
|
|
['FuncDecl',
|
|
[['Typename', ['ArrayDecl', '', ['restrict'],
|
|
['ArrayDecl', '5', [],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]]]],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]])
|
|
|
|
self.assertEqual(self.get_decl('int zz(int [const restrict volatile 10][5]);'),
|
|
['Decl', 'zz',
|
|
['FuncDecl',
|
|
[['Typename', ['ArrayDecl', '10', ['const', 'restrict', 'volatile'],
|
|
['ArrayDecl', '5', [],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]]]],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]])
|
|
|
|
def test_qualifiers_storage_specifiers(self):
|
|
def assert_qs(txt, index, quals, storage):
|
|
d = self.parse(txt).ext[index]
|
|
self.assertEqual(d.quals, quals)
|
|
self.assertEqual(d.storage, storage)
|
|
|
|
assert_qs("extern int p;", 0, [], ['extern'])
|
|
assert_qs("const long p = 6;", 0, ['const'], [])
|
|
|
|
d1 = "static const int p, q, r;"
|
|
for i in range(3):
|
|
assert_qs(d1, i, ['const'], ['static'])
|
|
|
|
d2 = "static char * const p;"
|
|
assert_qs(d2, 0, [], ['static'])
|
|
pdecl = self.parse(d2).ext[0].type
|
|
self.assertIsInstance(pdecl, PtrDecl)
|
|
self.assertEqual(pdecl.quals, ['const'])
|
|
|
|
def test_sizeof(self):
|
|
e = """
|
|
void foo()
|
|
{
|
|
int a = sizeof k;
|
|
int b = sizeof(int);
|
|
int c = sizeof(int**);;
|
|
|
|
char* p = "just to make sure this parses w/o error...";
|
|
int d = sizeof(int());
|
|
}
|
|
"""
|
|
compound = self.parse(e).ext[0].body
|
|
|
|
s1 = compound.block_items[0].init
|
|
self.assertIsInstance(s1, UnaryOp)
|
|
self.assertEqual(s1.op, 'sizeof')
|
|
self.assertIsInstance(s1.expr, ID)
|
|
self.assertEqual(s1.expr.name, 'k')
|
|
|
|
s2 = compound.block_items[1].init
|
|
self.assertEqual(expand_decl(s2.expr),
|
|
['Typename', ['TypeDecl', ['IdentifierType', ['int']]]])
|
|
|
|
s3 = compound.block_items[2].init
|
|
self.assertEqual(expand_decl(s3.expr),
|
|
['Typename',
|
|
['PtrDecl',
|
|
['PtrDecl',
|
|
['TypeDecl',
|
|
['IdentifierType', ['int']]]]]])
|
|
|
|
def test_offsetof(self):
|
|
def expand_ref(n):
|
|
if isinstance(n, StructRef):
|
|
return ['StructRef', expand_ref(n.name), expand_ref(n.field)]
|
|
elif isinstance(n, ArrayRef):
|
|
return ['ArrayRef', expand_ref(n.name), expand_ref(n.subscript)]
|
|
elif isinstance(n, ID):
|
|
return ['ID', n.name]
|
|
elif isinstance(n, Constant):
|
|
return ['Constant', n.type, n.value]
|
|
else:
|
|
raise TypeError("Unexpected type " + n.__class__.__name__)
|
|
|
|
e = """
|
|
void foo() {
|
|
int a = offsetof(struct S, p);
|
|
a.b = offsetof(struct sockaddr, sp) + strlen(bar);
|
|
int a = offsetof(struct S, p.q.r);
|
|
int a = offsetof(struct S, p[5].q[4][5]);
|
|
}
|
|
"""
|
|
compound = self.parse(e).ext[0].body
|
|
s1 = compound.block_items[0].init
|
|
self.assertIsInstance(s1, FuncCall)
|
|
self.assertIsInstance(s1.name, ID)
|
|
self.assertEqual(s1.name.name, 'offsetof')
|
|
self.assertIsInstance(s1.args.exprs[0], Typename)
|
|
self.assertIsInstance(s1.args.exprs[1], ID)
|
|
s3 = compound.block_items[2].init
|
|
self.assertIsInstance(s3.args.exprs[1], StructRef)
|
|
self.assertEqual(expand_ref(s3.args.exprs[1]),
|
|
['StructRef',
|
|
['StructRef', ['ID', 'p'], ['ID', 'q']],
|
|
['ID', 'r']])
|
|
s4 = compound.block_items[3].init
|
|
self.assertIsInstance(s4.args.exprs[1], ArrayRef)
|
|
self.assertEqual(expand_ref(s4.args.exprs[1]),
|
|
['ArrayRef',
|
|
['ArrayRef',
|
|
['StructRef',
|
|
['ArrayRef', ['ID', 'p'], ['Constant', 'int', '5']],
|
|
['ID', 'q']],
|
|
['Constant', 'int', '4']],
|
|
['Constant', 'int', '5']])
|
|
|
|
def test_compound_statement(self):
|
|
e = """
|
|
void foo() {
|
|
}
|
|
"""
|
|
compound = self.parse(e).ext[0].body
|
|
self.assertIsInstance(compound, Compound)
|
|
self.assert_coord(compound, 2)
|
|
|
|
# The C99 compound literal feature
|
|
#
|
|
def test_compound_literals(self):
|
|
ps1 = self.parse(r'''
|
|
void foo() {
|
|
p = (long long){k};
|
|
tc = (struct jk){.a = {1, 2}, .b[0] = t};
|
|
}''')
|
|
|
|
compound = ps1.ext[0].body.block_items[0].rvalue
|
|
self.assertEqual(expand_decl(compound.type),
|
|
['Typename', ['TypeDecl', ['IdentifierType', ['long', 'long']]]])
|
|
self.assertEqual(expand_init(compound.init),
|
|
[['ID', 'k']])
|
|
|
|
compound = ps1.ext[0].body.block_items[1].rvalue
|
|
self.assertEqual(expand_decl(compound.type),
|
|
['Typename', ['TypeDecl', ['Struct', 'jk', []]]])
|
|
self.assertEqual(expand_init(compound.init),
|
|
[
|
|
([['ID', 'a']], [['Constant', 'int', '1'], ['Constant', 'int', '2']]),
|
|
([['ID', 'b'], ['Constant', 'int', '0']], ['ID', 't'])])
|
|
|
|
def test_enums(self):
|
|
e1 = "enum mycolor op;"
|
|
e1_type = self.parse(e1).ext[0].type.type
|
|
|
|
self.assertIsInstance(e1_type, Enum)
|
|
self.assertEqual(e1_type.name, 'mycolor')
|
|
self.assertEqual(e1_type.values, None)
|
|
|
|
e2 = "enum mysize {large=20, small, medium} shoes;"
|
|
e2_type = self.parse(e2).ext[0].type.type
|
|
|
|
self.assertIsInstance(e2_type, Enum)
|
|
self.assertEqual(e2_type.name, 'mysize')
|
|
|
|
e2_elist = e2_type.values
|
|
self.assertIsInstance(e2_elist, EnumeratorList)
|
|
|
|
for e2_eval in e2_elist.enumerators:
|
|
self.assertIsInstance(e2_eval, Enumerator)
|
|
|
|
self.assertEqual(e2_elist.enumerators[0].name, 'large')
|
|
self.assertEqual(e2_elist.enumerators[0].value.value, '20')
|
|
self.assertEqual(e2_elist.enumerators[2].name, 'medium')
|
|
self.assertEqual(e2_elist.enumerators[2].value, None)
|
|
|
|
# enum with trailing comma (C99 feature)
|
|
e3 = """
|
|
enum
|
|
{
|
|
red,
|
|
blue,
|
|
green,
|
|
} color;
|
|
"""
|
|
|
|
e3_type = self.parse(e3).ext[0].type.type
|
|
self.assertIsInstance(e3_type, Enum)
|
|
e3_elist = e3_type.values
|
|
self.assertIsInstance(e3_elist, EnumeratorList)
|
|
|
|
for e3_eval in e3_elist.enumerators:
|
|
self.assertIsInstance(e3_eval, Enumerator)
|
|
|
|
self.assertEqual(e3_elist.enumerators[0].name, 'red')
|
|
self.assertEqual(e3_elist.enumerators[0].value, None)
|
|
self.assertEqual(e3_elist.enumerators[1].name, 'blue')
|
|
self.assertEqual(e3_elist.enumerators[2].name, 'green')
|
|
|
|
def test_typedef(self):
|
|
# without typedef, error
|
|
s1 = """
|
|
node k;
|
|
"""
|
|
self.assertRaises(ParseError, self.parse, s1)
|
|
|
|
# now with typedef, works
|
|
s2 = """
|
|
typedef void* node;
|
|
node k;
|
|
"""
|
|
ps2 = self.parse(s2)
|
|
self.assertEqual(expand_decl(ps2.ext[0]),
|
|
['Typedef', 'node',
|
|
['PtrDecl',
|
|
['TypeDecl', ['IdentifierType', ['void']]]]])
|
|
|
|
self.assertEqual(expand_decl(ps2.ext[1]),
|
|
['Decl', 'k',
|
|
['TypeDecl', ['IdentifierType', ['node']]]])
|
|
|
|
s3 = """
|
|
typedef int T;
|
|
typedef T *pT;
|
|
|
|
pT aa, bb;
|
|
"""
|
|
ps3 = self.parse(s3)
|
|
self.assertEqual(expand_decl(ps3.ext[3]),
|
|
['Decl', 'bb',
|
|
['TypeDecl', ['IdentifierType', ['pT']]]])
|
|
|
|
s4 = '''
|
|
typedef char* __builtin_va_list;
|
|
typedef __builtin_va_list __gnuc_va_list;
|
|
'''
|
|
ps4 = self.parse(s4)
|
|
self.assertEqual(expand_decl(ps4.ext[1]),
|
|
['Typedef', '__gnuc_va_list',
|
|
['TypeDecl',
|
|
['IdentifierType', ['__builtin_va_list']]]])
|
|
|
|
s5 = '''typedef struct tagHash Hash;'''
|
|
ps5 = self.parse(s5)
|
|
self.assertEqual(expand_decl(ps5.ext[0]),
|
|
['Typedef', 'Hash', ['TypeDecl', ['Struct', 'tagHash', []]]])
|
|
|
|
def test_struct_union(self):
|
|
s1 = """
|
|
struct {
|
|
int id;
|
|
char* name;
|
|
} joe;
|
|
"""
|
|
|
|
self.assertEqual(expand_decl(self.parse(s1).ext[0]),
|
|
['Decl', 'joe',
|
|
['TypeDecl', ['Struct', None,
|
|
[ ['Decl', 'id',
|
|
['TypeDecl',
|
|
['IdentifierType', ['int']]]],
|
|
['Decl', 'name',
|
|
['PtrDecl',
|
|
['TypeDecl',
|
|
['IdentifierType', ['char']]]]]]]]])
|
|
|
|
s2 = """
|
|
struct node p;
|
|
"""
|
|
self.assertEqual(expand_decl(self.parse(s2).ext[0]),
|
|
['Decl', 'p',
|
|
['TypeDecl', ['Struct', 'node', []]]])
|
|
|
|
s21 = """
|
|
union pri ra;
|
|
"""
|
|
self.assertEqual(expand_decl(self.parse(s21).ext[0]),
|
|
['Decl', 'ra',
|
|
['TypeDecl', ['Union', 'pri', []]]])
|
|
|
|
s3 = """
|
|
struct node* p;
|
|
"""
|
|
self.assertEqual(expand_decl(self.parse(s3).ext[0]),
|
|
['Decl', 'p',
|
|
['PtrDecl',
|
|
['TypeDecl', ['Struct', 'node', []]]]])
|
|
|
|
s4 = """
|
|
struct node;
|
|
"""
|
|
self.assertEqual(expand_decl(self.parse(s4).ext[0]),
|
|
['Decl', None,
|
|
['Struct', 'node', []]])
|
|
|
|
s5 = """
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
int type;
|
|
} n;
|
|
|
|
struct
|
|
{
|
|
int type;
|
|
int intnode;
|
|
} ni;
|
|
} u;
|
|
"""
|
|
self.assertEqual(expand_decl(self.parse(s5).ext[0]),
|
|
['Decl', 'u',
|
|
['TypeDecl',
|
|
['Union', None,
|
|
[['Decl', 'n',
|
|
['TypeDecl',
|
|
['Struct', None,
|
|
[['Decl', 'type',
|
|
['TypeDecl', ['IdentifierType', ['int']]]]]]]],
|
|
['Decl', 'ni',
|
|
['TypeDecl',
|
|
['Struct', None,
|
|
[['Decl', 'type',
|
|
['TypeDecl', ['IdentifierType', ['int']]]],
|
|
['Decl', 'intnode',
|
|
['TypeDecl', ['IdentifierType', ['int']]]]]]]]]]]])
|
|
|
|
s6 = """
|
|
typedef struct foo_tag
|
|
{
|
|
void* data;
|
|
} foo, *pfoo;
|
|
"""
|
|
s6_ast = self.parse(s6)
|
|
|
|
self.assertEqual(expand_decl(s6_ast.ext[0]),
|
|
['Typedef', 'foo',
|
|
['TypeDecl',
|
|
['Struct', 'foo_tag',
|
|
[['Decl', 'data',
|
|
['PtrDecl', ['TypeDecl', ['IdentifierType', ['void']]]]]]]]])
|
|
|
|
self.assertEqual(expand_decl(s6_ast.ext[1]),
|
|
['Typedef', 'pfoo',
|
|
['PtrDecl',
|
|
['TypeDecl',
|
|
['Struct', 'foo_tag',
|
|
[['Decl', 'data',
|
|
['PtrDecl', ['TypeDecl', ['IdentifierType', ['void']]]]]]]]]])
|
|
|
|
s7 = r"""
|
|
struct _on_exit_args {
|
|
void * _fnargs[32];
|
|
void * _dso_handle[32];
|
|
|
|
long _fntypes;
|
|
#line 77 "D:\eli\cpp_stuff\libc_include/sys/reent.h"
|
|
|
|
long _is_cxa;
|
|
};
|
|
"""
|
|
|
|
s7_ast = self.parse(s7, filename='test.c')
|
|
self.assert_coord(s7_ast.ext[0].type.decls[2], 6, 22, 'test.c')
|
|
self.assert_coord(s7_ast.ext[0].type.decls[3], 78, 22,
|
|
r'D:\eli\cpp_stuff\libc_include/sys/reent.h')
|
|
|
|
s8 = """
|
|
typedef enum tagReturnCode {SUCCESS, FAIL} ReturnCode;
|
|
|
|
typedef struct tagEntry
|
|
{
|
|
char* key;
|
|
char* value;
|
|
} Entry;
|
|
|
|
|
|
typedef struct tagNode
|
|
{
|
|
Entry* entry;
|
|
|
|
struct tagNode* next;
|
|
} Node;
|
|
|
|
typedef struct tagHash
|
|
{
|
|
unsigned int table_size;
|
|
|
|
Node** heads;
|
|
|
|
} Hash;
|
|
"""
|
|
s8_ast = self.parse(s8)
|
|
self.assertEqual(expand_decl(s8_ast.ext[3]),
|
|
['Typedef', 'Hash',
|
|
['TypeDecl', ['Struct', 'tagHash',
|
|
[['Decl', 'table_size',
|
|
['TypeDecl', ['IdentifierType', ['unsigned', 'int']]]],
|
|
['Decl', 'heads',
|
|
['PtrDecl', ['PtrDecl', ['TypeDecl', ['IdentifierType', ['Node']]]]]]]]]])
|
|
|
|
def test_struct_with_extra_semis_inside(self):
|
|
s1 = """
|
|
struct {
|
|
int a;;
|
|
} foo;
|
|
"""
|
|
s1_ast = self.parse(s1)
|
|
self.assertEqual(expand_decl(s1_ast.ext[0]),
|
|
['Decl', 'foo',
|
|
['TypeDecl', ['Struct', None,
|
|
[['Decl', 'a',
|
|
['TypeDecl', ['IdentifierType', ['int']]]]]]]])
|
|
|
|
s2 = """
|
|
struct {
|
|
int a;;;;
|
|
float b, c;
|
|
;;
|
|
char d;
|
|
} foo;
|
|
"""
|
|
s2_ast = self.parse(s2)
|
|
self.assertEqual(expand_decl(s2_ast.ext[0]),
|
|
['Decl', 'foo',
|
|
['TypeDecl', ['Struct', None,
|
|
[['Decl', 'a', ['TypeDecl', ['IdentifierType', ['int']]]],
|
|
['Decl', 'b', ['TypeDecl', ['IdentifierType', ['float']]]],
|
|
['Decl', 'c', ['TypeDecl', ['IdentifierType', ['float']]]],
|
|
['Decl', 'd',
|
|
['TypeDecl', ['IdentifierType', ['char']]]]]]]])
|
|
|
|
def test_struct_with_initial_semi(self):
|
|
s1 = """
|
|
struct {
|
|
;int a;
|
|
} foo;
|
|
"""
|
|
s1_ast = self.parse(s1)
|
|
self.assertEqual(expand_decl(s1_ast.ext[0]),
|
|
['Decl', 'foo',
|
|
['TypeDecl', ['Struct', None,
|
|
[['Decl', 'a',
|
|
['TypeDecl', ['IdentifierType', ['int']]]]]]]])
|
|
|
|
def test_anonymous_struct_union(self):
|
|
s1 = """
|
|
union
|
|
{
|
|
union
|
|
{
|
|
int i;
|
|
long l;
|
|
};
|
|
|
|
struct
|
|
{
|
|
int type;
|
|
int intnode;
|
|
};
|
|
} u;
|
|
"""
|
|
|
|
self.assertEqual(expand_decl(self.parse(s1).ext[0]),
|
|
['Decl', 'u',
|
|
['TypeDecl',
|
|
['Union', None,
|
|
[['Decl', None,
|
|
['Union', None,
|
|
[['Decl', 'i',
|
|
['TypeDecl',
|
|
['IdentifierType', ['int']]]],
|
|
['Decl', 'l',
|
|
['TypeDecl',
|
|
['IdentifierType', ['long']]]]]]],
|
|
['Decl', None,
|
|
['Struct', None,
|
|
[['Decl', 'type',
|
|
['TypeDecl',
|
|
['IdentifierType', ['int']]]],
|
|
['Decl', 'intnode',
|
|
['TypeDecl',
|
|
['IdentifierType', ['int']]]]]]]]]]])
|
|
|
|
s2 = """
|
|
struct
|
|
{
|
|
int i;
|
|
union
|
|
{
|
|
int id;
|
|
char* name;
|
|
};
|
|
float f;
|
|
} joe;
|
|
"""
|
|
|
|
self.assertEqual(expand_decl(self.parse(s2).ext[0]),
|
|
['Decl', 'joe',
|
|
['TypeDecl',
|
|
['Struct', None,
|
|
[['Decl', 'i',
|
|
['TypeDecl',
|
|
['IdentifierType', ['int']]]],
|
|
['Decl', None,
|
|
['Union', None,
|
|
[['Decl', 'id',
|
|
['TypeDecl',
|
|
['IdentifierType', ['int']]]],
|
|
['Decl', 'name',
|
|
['PtrDecl',
|
|
['TypeDecl',
|
|
['IdentifierType', ['char']]]]]]]],
|
|
['Decl', 'f',
|
|
['TypeDecl',
|
|
['IdentifierType', ['float']]]]]]]])
|
|
|
|
# ISO/IEC 9899:201x Commitee Draft 2010-11-16, N1539
|
|
# section 6.7.2.1, par. 19, example 1
|
|
s3 = """
|
|
struct v {
|
|
union {
|
|
struct { int i, j; };
|
|
struct { long k, l; } w;
|
|
};
|
|
int m;
|
|
} v1;
|
|
"""
|
|
|
|
self.assertEqual(expand_decl(self.parse(s3).ext[0]),
|
|
['Decl', 'v1',
|
|
['TypeDecl',
|
|
['Struct', 'v',
|
|
[['Decl', None,
|
|
['Union', None,
|
|
[['Decl', None,
|
|
['Struct', None,
|
|
[['Decl', 'i',
|
|
['TypeDecl',
|
|
['IdentifierType', ['int']]]],
|
|
['Decl', 'j',
|
|
['TypeDecl',
|
|
['IdentifierType', ['int']]]]]]],
|
|
['Decl', 'w',
|
|
['TypeDecl',
|
|
['Struct', None,
|
|
[['Decl', 'k',
|
|
['TypeDecl',
|
|
['IdentifierType', ['long']]]],
|
|
['Decl', 'l',
|
|
['TypeDecl',
|
|
['IdentifierType', ['long']]]]]]]]]]],
|
|
['Decl', 'm',
|
|
['TypeDecl',
|
|
['IdentifierType', ['int']]]]]]]])
|
|
|
|
s4 = """
|
|
struct v {
|
|
int i;
|
|
float;
|
|
} v2;"""
|
|
# just make sure this doesn't raise ParseError
|
|
self.parse(s4)
|
|
|
|
def test_struct_members_namespace(self):
|
|
""" Tests that structure/union member names reside in a separate
|
|
namespace and can be named after existing types.
|
|
"""
|
|
s1 = """
|
|
typedef int Name;
|
|
typedef Name NameArray[10];
|
|
|
|
struct {
|
|
Name Name;
|
|
Name NameArray[3];
|
|
} sye;
|
|
|
|
void main(void)
|
|
{
|
|
sye.Name = 1;
|
|
}
|
|
"""
|
|
|
|
s1_ast = self.parse(s1)
|
|
self.assertEqual(expand_decl(s1_ast.ext[2]),
|
|
['Decl', 'sye',
|
|
['TypeDecl', ['Struct', None,
|
|
[ ['Decl', 'Name',
|
|
['TypeDecl',
|
|
['IdentifierType', ['Name']]]],
|
|
['Decl', 'NameArray',
|
|
['ArrayDecl', '3', [],
|
|
['TypeDecl', ['IdentifierType', ['Name']]]]]]]]])
|
|
self.assertEqual(s1_ast.ext[3].body.block_items[0].lvalue.field.name, 'Name')
|
|
|
|
def test_struct_bitfields(self):
|
|
# a struct with two bitfields, one unnamed
|
|
s1 = """
|
|
struct {
|
|
int k:6;
|
|
int :2;
|
|
} joe;
|
|
"""
|
|
|
|
parsed_struct = self.parse(s1).ext[0]
|
|
|
|
# We can see here the name of the decl for the unnamed bitfield is
|
|
# None, but expand_decl doesn't show bitfield widths
|
|
# ...
|
|
self.assertEqual(expand_decl(parsed_struct),
|
|
['Decl', 'joe',
|
|
['TypeDecl', ['Struct', None,
|
|
[ ['Decl', 'k',
|
|
['TypeDecl',
|
|
['IdentifierType', ['int']]]],
|
|
['Decl', None,
|
|
['TypeDecl',
|
|
['IdentifierType', ['int']]]]]]]])
|
|
|
|
# ...
|
|
# so we test them manually
|
|
self.assertEqual(parsed_struct.type.type.decls[0].bitsize.value, '6')
|
|
self.assertEqual(parsed_struct.type.type.decls[1].bitsize.value, '2')
|
|
|
|
def test_struct_empty(self):
|
|
"""
|
|
Tests that parsing an empty struct works.
|
|
|
|
Empty structs do NOT follow C99 (See 6.2.5-20 of the C99 standard).
|
|
This is nevertheless supported by some compilers (clang, gcc),
|
|
especially when using FORTIFY code.
|
|
Some compilers (visual) will fail to compile with an error.
|
|
"""
|
|
# an empty struct. This is NOT C99 compliant
|
|
s1 = """
|
|
struct foo { };
|
|
"""
|
|
|
|
parsed_struct = self.parse(s1).ext[0]
|
|
self.assertEqual(expand_decl(parsed_struct),
|
|
['Decl', None, ['Struct', 'foo', []]])
|
|
|
|
s2 = """struct { } foo;"""
|
|
parsed_struct = self.parse(s2).ext[0]
|
|
self.assertEqual(expand_decl(parsed_struct),
|
|
['Decl', 'foo', ['TypeDecl', ['Struct', None, []]]])
|
|
|
|
s3 = """union { } foo;"""
|
|
parsed_struct = self.parse(s3).ext[0]
|
|
self.assertEqual(expand_decl(parsed_struct),
|
|
['Decl', 'foo', ['TypeDecl', ['Union', None, []]]])
|
|
|
|
def test_tags_namespace(self):
|
|
""" Tests that the tags of structs/unions/enums reside in a separate namespace and
|
|
can be named after existing types.
|
|
"""
|
|
s1 = """
|
|
typedef int tagEntry;
|
|
|
|
struct tagEntry
|
|
{
|
|
char* key;
|
|
char* value;
|
|
} Entry;
|
|
"""
|
|
|
|
s1_ast = self.parse(s1)
|
|
self.assertEqual(expand_decl(s1_ast.ext[1]),
|
|
['Decl', 'Entry',
|
|
['TypeDecl', ['Struct', 'tagEntry',
|
|
[['Decl', 'key',
|
|
['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]],
|
|
['Decl', 'value',
|
|
['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]]]]]])
|
|
|
|
s2 = """
|
|
struct tagEntry;
|
|
|
|
typedef struct tagEntry tagEntry;
|
|
|
|
struct tagEntry
|
|
{
|
|
char* key;
|
|
char* value;
|
|
} Entry;
|
|
"""
|
|
|
|
s2_ast = self.parse(s2)
|
|
self.assertEqual(expand_decl(s2_ast.ext[2]),
|
|
['Decl', 'Entry',
|
|
['TypeDecl', ['Struct', 'tagEntry',
|
|
[['Decl', 'key',
|
|
['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]],
|
|
['Decl', 'value',
|
|
['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]]]]]])
|
|
|
|
s3 = """
|
|
typedef int mytag;
|
|
|
|
enum mytag {ABC, CDE};
|
|
enum mytag joe;
|
|
"""
|
|
|
|
s3_type = self.parse(s3).ext[1].type
|
|
|
|
self.assertIsInstance(s3_type, Enum)
|
|
self.assertEqual(s3_type.name, 'mytag')
|
|
|
|
def test_multi_decls(self):
|
|
d1 = 'int a, b;'
|
|
|
|
self.assertEqual(self.get_decl(d1, 0),
|
|
['Decl', 'a', ['TypeDecl', ['IdentifierType', ['int']]]])
|
|
self.assertEqual(self.get_decl(d1, 1),
|
|
['Decl', 'b', ['TypeDecl', ['IdentifierType', ['int']]]])
|
|
|
|
d2 = 'char* p, notp, ar[4];'
|
|
self.assertEqual(self.get_decl(d2, 0),
|
|
['Decl', 'p',
|
|
['PtrDecl',
|
|
['TypeDecl', ['IdentifierType', ['char']]]]])
|
|
self.assertEqual(self.get_decl(d2, 1),
|
|
['Decl', 'notp', ['TypeDecl', ['IdentifierType', ['char']]]])
|
|
self.assertEqual(self.get_decl(d2, 2),
|
|
['Decl', 'ar',
|
|
['ArrayDecl', '4', [],
|
|
['TypeDecl', ['IdentifierType', ['char']]]]])
|
|
|
|
def test_invalid_multiple_types_error(self):
|
|
bad = [
|
|
'int enum {ab, cd} fubr;',
|
|
'enum kid char brbr;']
|
|
|
|
for b in bad:
|
|
self.assertRaises(ParseError, self.parse, b)
|
|
|
|
def test_duplicate_typedef(self):
|
|
""" Tests that redeclarations of existing types are parsed correctly.
|
|
This is non-standard, but allowed by many compilers.
|
|
"""
|
|
d1 = '''
|
|
typedef int numbertype;
|
|
typedef int numbertype;
|
|
'''
|
|
|
|
self.assertEqual(self.get_decl(d1, 0),
|
|
['Typedef', 'numbertype',
|
|
['TypeDecl', ['IdentifierType', ['int']]]])
|
|
self.assertEqual(self.get_decl(d1, 1),
|
|
['Typedef', 'numbertype',
|
|
['TypeDecl', ['IdentifierType', ['int']]]])
|
|
|
|
d2 = '''
|
|
typedef int (*funcptr)(int x);
|
|
typedef int (*funcptr)(int x);
|
|
'''
|
|
self.assertEqual(self.get_decl(d2, 0),
|
|
['Typedef', 'funcptr',
|
|
['PtrDecl', ['FuncDecl',
|
|
[['Decl', 'x', ['TypeDecl', ['IdentifierType', ['int']]]]],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]]])
|
|
self.assertEqual(self.get_decl(d2, 1),
|
|
['Typedef', 'funcptr',
|
|
['PtrDecl', ['FuncDecl',
|
|
[['Decl', 'x', ['TypeDecl', ['IdentifierType', ['int']]]]],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]]])
|
|
|
|
d3 = '''
|
|
typedef int numberarray[5];
|
|
typedef int numberarray[5];
|
|
'''
|
|
self.assertEqual(self.get_decl(d3, 0),
|
|
['Typedef', 'numberarray',
|
|
['ArrayDecl', '5', [],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]])
|
|
self.assertEqual(self.get_decl(d3, 1),
|
|
['Typedef', 'numberarray',
|
|
['ArrayDecl', '5', [],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]])
|
|
|
|
def test_decl_inits(self):
|
|
d1 = 'int a = 16;'
|
|
#~ self.parse(d1).show()
|
|
self.assertEqual(self.get_decl(d1),
|
|
['Decl', 'a', ['TypeDecl', ['IdentifierType', ['int']]]])
|
|
self.assertEqual(self.get_decl_init(d1),
|
|
['Constant', 'int', '16'])
|
|
|
|
d1_1 = 'float f = 0xEF.56p1;'
|
|
self.assertEqual(self.get_decl_init(d1_1),
|
|
['Constant', 'float', '0xEF.56p1'])
|
|
|
|
d1_2 = 'int bitmask = 0b1001010;'
|
|
self.assertEqual(self.get_decl_init(d1_2),
|
|
['Constant', 'int', '0b1001010'])
|
|
|
|
d2 = 'long ar[] = {7, 8, 9};'
|
|
self.assertEqual(self.get_decl(d2),
|
|
['Decl', 'ar',
|
|
['ArrayDecl', '', [],
|
|
['TypeDecl', ['IdentifierType', ['long']]]]])
|
|
self.assertEqual(self.get_decl_init(d2),
|
|
[ ['Constant', 'int', '7'],
|
|
['Constant', 'int', '8'],
|
|
['Constant', 'int', '9']])
|
|
|
|
d21 = 'long ar[4] = {};'
|
|
self.assertEqual(self.get_decl_init(d21), [])
|
|
|
|
d3 = 'char p = j;'
|
|
self.assertEqual(self.get_decl(d3),
|
|
['Decl', 'p', ['TypeDecl', ['IdentifierType', ['char']]]])
|
|
self.assertEqual(self.get_decl_init(d3),
|
|
['ID', 'j'])
|
|
|
|
d4 = "char x = 'c', *p = {0, 1, 2, {4, 5}, 6};"
|
|
self.assertEqual(self.get_decl(d4, 0),
|
|
['Decl', 'x', ['TypeDecl', ['IdentifierType', ['char']]]])
|
|
self.assertEqual(self.get_decl_init(d4, 0),
|
|
['Constant', 'char', "'c'"])
|
|
self.assertEqual(self.get_decl(d4, 1),
|
|
['Decl', 'p',
|
|
['PtrDecl',
|
|
['TypeDecl', ['IdentifierType', ['char']]]]])
|
|
|
|
self.assertEqual(self.get_decl_init(d4, 1),
|
|
[ ['Constant', 'int', '0'],
|
|
['Constant', 'int', '1'],
|
|
['Constant', 'int', '2'],
|
|
[['Constant', 'int', '4'],
|
|
['Constant', 'int', '5']],
|
|
['Constant', 'int', '6']])
|
|
|
|
d5 = 'float d = 1.0;'
|
|
self.assertEqual(self.get_decl_init(d5),
|
|
['Constant', 'double', '1.0'])
|
|
|
|
d51 = 'float ld = 1.0l;'
|
|
self.assertEqual(self.get_decl_init(d51),
|
|
['Constant', 'long double', '1.0l'])
|
|
|
|
d52 = 'float ld = 1.0L;'
|
|
self.assertEqual(self.get_decl_init(d52),
|
|
['Constant', 'long double', '1.0L'])
|
|
|
|
d53 = 'float ld = 1.0f;'
|
|
self.assertEqual(self.get_decl_init(d53),
|
|
['Constant', 'float', '1.0f'])
|
|
|
|
d54 = 'float ld = 1.0F;'
|
|
self.assertEqual(self.get_decl_init(d54),
|
|
['Constant', 'float', '1.0F'])
|
|
|
|
d55 = 'float ld = 0xDE.38p0;'
|
|
self.assertEqual(self.get_decl_init(d55),
|
|
['Constant', 'float', '0xDE.38p0'])
|
|
|
|
d6 = 'int i = 1;'
|
|
self.assertEqual(self.get_decl_init(d6),
|
|
['Constant', 'int', '1'])
|
|
|
|
d61 = 'long int li = 1l;'
|
|
self.assertEqual(self.get_decl_init(d61),
|
|
['Constant', 'long int', '1l'])
|
|
|
|
d62 = 'unsigned int ui = 1u;'
|
|
self.assertEqual(self.get_decl_init(d62),
|
|
['Constant', 'unsigned int', '1u'])
|
|
|
|
d63 = 'unsigned long long int ulli = 1LLU;'
|
|
self.assertEqual(self.get_decl_init(d63),
|
|
['Constant', 'unsigned long long int', '1LLU'])
|
|
|
|
def test_decl_named_inits(self):
|
|
d1 = 'int a = {.k = 16};'
|
|
self.assertEqual(self.get_decl_init(d1),
|
|
[( [['ID', 'k']],
|
|
['Constant', 'int', '16'])])
|
|
|
|
d2 = 'int a = { [0].a = {1}, [1].a[0] = 2 };'
|
|
self.assertEqual(self.get_decl_init(d2),
|
|
[
|
|
([['Constant', 'int', '0'], ['ID', 'a']],
|
|
[['Constant', 'int', '1']]),
|
|
([['Constant', 'int', '1'], ['ID', 'a'], ['Constant', 'int', '0']],
|
|
['Constant', 'int', '2'])])
|
|
|
|
d3 = 'int a = { .a = 1, .c = 3, 4, .b = 5};'
|
|
self.assertEqual(self.get_decl_init(d3),
|
|
[
|
|
([['ID', 'a']], ['Constant', 'int', '1']),
|
|
([['ID', 'c']], ['Constant', 'int', '3']),
|
|
['Constant', 'int', '4'],
|
|
([['ID', 'b']], ['Constant', 'int', '5'])])
|
|
|
|
def test_function_definitions(self):
|
|
def parse_fdef(str):
|
|
return self.parse(str).ext[0]
|
|
|
|
def fdef_decl(fdef):
|
|
return expand_decl(fdef.decl)
|
|
|
|
f1 = parse_fdef('''
|
|
int factorial(int p)
|
|
{
|
|
return 3;
|
|
}
|
|
''')
|
|
|
|
self.assertEqual(fdef_decl(f1),
|
|
['Decl', 'factorial',
|
|
['FuncDecl',
|
|
[['Decl', 'p', ['TypeDecl', ['IdentifierType', ['int']]]]],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]])
|
|
|
|
self.assertEqual(type(f1.body.block_items[0]), Return)
|
|
|
|
f2 = parse_fdef('''
|
|
char* zzz(int p, char* c)
|
|
{
|
|
int a;
|
|
char b;
|
|
|
|
a = b + 2;
|
|
return 3;
|
|
}
|
|
''')
|
|
|
|
self.assertEqual(fdef_decl(f2),
|
|
['Decl', 'zzz',
|
|
['FuncDecl',
|
|
[ ['Decl', 'p', ['TypeDecl', ['IdentifierType', ['int']]]],
|
|
['Decl', 'c', ['PtrDecl',
|
|
['TypeDecl', ['IdentifierType', ['char']]]]]],
|
|
['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]]])
|
|
|
|
self.assertEqual(list(map(type, f2.body.block_items)),
|
|
[Decl, Decl, Assignment, Return])
|
|
|
|
f3 = parse_fdef('''
|
|
char* zzz(p, c)
|
|
long p, *c;
|
|
{
|
|
int a;
|
|
char b;
|
|
|
|
a = b + 2;
|
|
return 3;
|
|
}
|
|
''')
|
|
|
|
self.assertEqual(fdef_decl(f3),
|
|
['Decl', 'zzz',
|
|
['FuncDecl',
|
|
[ ['ID', 'p'],
|
|
['ID', 'c']],
|
|
['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]]])
|
|
|
|
self.assertEqual(list(map(type, f3.body.block_items)),
|
|
[Decl, Decl, Assignment, Return])
|
|
|
|
self.assertEqual(expand_decl(f3.param_decls[0]),
|
|
['Decl', 'p', ['TypeDecl', ['IdentifierType', ['long']]]])
|
|
self.assertEqual(expand_decl(f3.param_decls[1]),
|
|
['Decl', 'c', ['PtrDecl', ['TypeDecl', ['IdentifierType', ['long']]]]])
|
|
|
|
# function return values and parameters may not have type information
|
|
f4 = parse_fdef('''
|
|
que(p)
|
|
{
|
|
return 3;
|
|
}
|
|
''')
|
|
|
|
self.assertEqual(fdef_decl(f4),
|
|
['Decl', 'que',
|
|
['FuncDecl',
|
|
[['ID', 'p']],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]])
|
|
|
|
def test_unified_string_literals(self):
|
|
# simple string, for reference
|
|
d1 = self.get_decl_init('char* s = "hello";')
|
|
self.assertEqual(d1, ['Constant', 'string', '"hello"'])
|
|
|
|
d2 = self.get_decl_init('char* s = "hello" " world";')
|
|
self.assertEqual(d2, ['Constant', 'string', '"hello world"'])
|
|
|
|
# the test case from issue 6
|
|
d3 = self.parse(r'''
|
|
int main() {
|
|
fprintf(stderr,
|
|
"Wrong Params?\n"
|
|
"Usage:\n"
|
|
"%s <binary_file_path>\n",
|
|
argv[0]
|
|
);
|
|
}
|
|
''')
|
|
|
|
self.assertEqual(
|
|
d3.ext[0].body.block_items[0].args.exprs[1].value,
|
|
r'"Wrong Params?\nUsage:\n%s <binary_file_path>\n"')
|
|
|
|
d4 = self.get_decl_init('char* s = "" "foobar";')
|
|
self.assertEqual(d4, ['Constant', 'string', '"foobar"'])
|
|
|
|
d5 = self.get_decl_init(r'char* s = "foo\"" "bar";')
|
|
self.assertEqual(d5, ['Constant', 'string', r'"foo\"bar"'])
|
|
|
|
def test_unified_wstring_literals(self):
|
|
d1 = self.get_decl_init('char* s = L"hello" L"world";')
|
|
self.assertEqual(d1, ['Constant', 'string', 'L"helloworld"'])
|
|
|
|
d2 = self.get_decl_init('char* s = L"hello " L"world" L" and I";')
|
|
self.assertEqual(d2, ['Constant', 'string', 'L"hello world and I"'])
|
|
|
|
def test_inline_specifier(self):
|
|
ps2 = self.parse('static inline void inlinefoo(void);')
|
|
self.assertEqual(ps2.ext[0].funcspec, ['inline'])
|
|
|
|
# variable length array
|
|
def test_vla(self):
|
|
ps2 = self.parse(r'''
|
|
int main() {
|
|
int size;
|
|
int var[size = 5];
|
|
|
|
int var2[*];
|
|
}
|
|
''')
|
|
self.assertIsInstance(ps2.ext[0].body.block_items[1].type.dim, Assignment)
|
|
self.assertIsInstance(ps2.ext[0].body.block_items[2].type.dim, ID)
|
|
|
|
def test_pragma(self):
|
|
s1 = r'''
|
|
#pragma bar
|
|
void main() {
|
|
#pragma foo
|
|
for(;;) {}
|
|
#pragma baz
|
|
{
|
|
int i = 0;
|
|
}
|
|
#pragma
|
|
}
|
|
struct s {
|
|
#pragma baz
|
|
} s;
|
|
'''
|
|
s1_ast = self.parse(s1)
|
|
self.assertIsInstance(s1_ast.ext[0], Pragma)
|
|
self.assertEqual(s1_ast.ext[0].string, 'bar')
|
|
self.assertEqual(s1_ast.ext[0].coord.line, 2)
|
|
|
|
self.assertIsInstance(s1_ast.ext[1].body.block_items[0], Pragma)
|
|
self.assertEqual(s1_ast.ext[1].body.block_items[0].string, 'foo')
|
|
self.assertEqual(s1_ast.ext[1].body.block_items[0].coord.line, 4)
|
|
|
|
self.assertIsInstance(s1_ast.ext[1].body.block_items[2], Pragma)
|
|
self.assertEqual(s1_ast.ext[1].body.block_items[2].string, 'baz')
|
|
self.assertEqual(s1_ast.ext[1].body.block_items[2].coord.line, 6)
|
|
|
|
self.assertIsInstance(s1_ast.ext[1].body.block_items[4], Pragma)
|
|
self.assertEqual(s1_ast.ext[1].body.block_items[4].string, '')
|
|
self.assertEqual(s1_ast.ext[1].body.block_items[4].coord.line, 10)
|
|
|
|
self.assertIsInstance(s1_ast.ext[2].type.type.decls[0], Pragma)
|
|
self.assertEqual(s1_ast.ext[2].type.type.decls[0].string, 'baz')
|
|
self.assertEqual(s1_ast.ext[2].type.type.decls[0].coord.line, 13)
|
|
|
|
def test_pragmacomp_or_statement(self):
|
|
s1 = r'''
|
|
void main() {
|
|
int sum = 0;
|
|
for (int i; i < 3; i++)
|
|
#pragma omp critical
|
|
sum += 1;
|
|
|
|
while(sum < 10)
|
|
#pragma omp critical
|
|
sum += 1;
|
|
|
|
mylabel:
|
|
#pragma foo
|
|
sum += 10;
|
|
|
|
if (sum > 10)
|
|
#pragma bar
|
|
sum = 10;
|
|
|
|
switch (sum)
|
|
case 10:
|
|
#pragma foo
|
|
sum = 20;
|
|
}
|
|
'''
|
|
s1_ast = self.parse(s1)
|
|
self.assertIsInstance(s1_ast.ext[0].body.block_items[1], For)
|
|
self.assertIsInstance(s1_ast.ext[0].body.block_items[1].stmt, Compound)
|
|
self.assertIsInstance(s1_ast.ext[0].body.block_items[1].stmt.block_items[0], Pragma)
|
|
self.assertIsInstance(s1_ast.ext[0].body.block_items[1].stmt.block_items[1], Assignment)
|
|
self.assertIsInstance(s1_ast.ext[0].body.block_items[2], While)
|
|
self.assertIsInstance(s1_ast.ext[0].body.block_items[2].stmt, Compound)
|
|
self.assertIsInstance(s1_ast.ext[0].body.block_items[2].stmt.block_items[0], Pragma)
|
|
self.assertIsInstance(s1_ast.ext[0].body.block_items[2].stmt.block_items[1], Assignment)
|
|
self.assertIsInstance(s1_ast.ext[0].body.block_items[3], Label)
|
|
self.assertIsInstance(s1_ast.ext[0].body.block_items[3].stmt, Compound)
|
|
self.assertIsInstance(s1_ast.ext[0].body.block_items[3].stmt.block_items[0], Pragma)
|
|
self.assertIsInstance(s1_ast.ext[0].body.block_items[3].stmt.block_items[1], Assignment)
|
|
self.assertIsInstance(s1_ast.ext[0].body.block_items[4], If)
|
|
self.assertIsInstance(s1_ast.ext[0].body.block_items[4].iftrue, Compound)
|
|
self.assertIsInstance(s1_ast.ext[0].body.block_items[4].iftrue.block_items[0], Pragma)
|
|
self.assertIsInstance(s1_ast.ext[0].body.block_items[4].iftrue.block_items[1], Assignment)
|
|
self.assertIsInstance(s1_ast.ext[0].body.block_items[5], Switch)
|
|
self.assertIsInstance(s1_ast.ext[0].body.block_items[5].stmt.stmts[0], Compound)
|
|
self.assertIsInstance(s1_ast.ext[0].body.block_items[5].stmt.stmts[0].block_items[0],
|
|
Pragma)
|
|
self.assertIsInstance(s1_ast.ext[0].body.block_items[5].stmt.stmts[0].block_items[1],
|
|
Assignment)
|
|
|
|
|
|
class TestCParser_whole_code(TestCParser_base):
|
|
""" Testing of parsing whole chunks of code.
|
|
|
|
Since I don't want to rely on the structure of ASTs too
|
|
much, most of these tests are implemented with visitors.
|
|
"""
|
|
# A simple helper visitor that lists the values of all the
|
|
# Constant nodes it sees.
|
|
#
|
|
class ConstantVisitor(NodeVisitor):
|
|
def __init__(self):
|
|
self.values = []
|
|
|
|
def visit_Constant(self, node):
|
|
self.values.append(node.value)
|
|
|
|
# This visitor counts the amount of references to the ID
|
|
# with the name provided to it in the constructor.
|
|
#
|
|
class IDNameCounter(NodeVisitor):
|
|
def __init__(self, name):
|
|
self.name = name
|
|
self.nrefs = 0
|
|
|
|
def visit_ID(self, node):
|
|
if node.name == self.name:
|
|
self.nrefs += 1
|
|
|
|
# Counts the amount of nodes of a given class
|
|
#
|
|
class NodeKlassCounter(NodeVisitor):
|
|
def __init__(self, node_klass):
|
|
self.klass = node_klass
|
|
self.n = 0
|
|
|
|
def generic_visit(self, node):
|
|
if node.__class__ == self.klass:
|
|
self.n += 1
|
|
|
|
NodeVisitor.generic_visit(self, node)
|
|
|
|
def assert_all_Constants(self, code, constants):
|
|
""" Asserts that the list of all Constant values (by
|
|
'preorder' appearance) in the chunk of code is as
|
|
given.
|
|
"""
|
|
if isinstance(code, str):
|
|
parsed = self.parse(code)
|
|
else:
|
|
parsed = code
|
|
|
|
cv = self.ConstantVisitor()
|
|
cv.visit(parsed)
|
|
self.assertEqual(cv.values, constants)
|
|
|
|
def assert_num_ID_refs(self, code, name, num):
|
|
""" Asserts the number of references to the ID with
|
|
the given name.
|
|
"""
|
|
if isinstance(code, str):
|
|
parsed = self.parse(code)
|
|
else:
|
|
parsed = code
|
|
|
|
iv = self.IDNameCounter(name)
|
|
iv.visit(parsed)
|
|
self.assertEqual(iv.nrefs, num)
|
|
|
|
def assert_num_klass_nodes(self, code, klass, num):
|
|
""" Asserts the amount of klass nodes in the code.
|
|
"""
|
|
if isinstance(code, str):
|
|
parsed = self.parse(code)
|
|
else:
|
|
parsed = code
|
|
|
|
cv = self.NodeKlassCounter(klass)
|
|
cv.visit(parsed)
|
|
self.assertEqual(cv.n, num)
|
|
|
|
def test_expressions(self):
|
|
e1 = '''int k = (r + 10.0) >> 6 + 8 << (3 & 0x14);'''
|
|
self.assert_all_Constants(e1, ['10.0', '6', '8', '3', '0x14'])
|
|
|
|
e2 = r'''char n = '\n', *prefix = "st_";'''
|
|
self.assert_all_Constants(e2, [r"'\n'", '"st_"'])
|
|
|
|
s1 = r'''int main() {
|
|
int i = 5, j = 6, k = 1;
|
|
if ((i=j && k == 1) || k > j)
|
|
printf("Hello, world\n");
|
|
return 0;
|
|
}'''
|
|
ps1 = self.parse(s1)
|
|
self.assert_all_Constants(ps1,
|
|
['5', '6', '1', '1', '"Hello, world\\n"', '0'])
|
|
self.assert_num_ID_refs(ps1, 'i', 1)
|
|
self.assert_num_ID_refs(ps1, 'j', 2)
|
|
|
|
|
|
def test_statements(self):
|
|
s1 = r'''
|
|
void foo(){
|
|
if (sp == 1)
|
|
if (optind >= argc ||
|
|
argv[optind][0] != '-' || argv[optind][1] == '\0')
|
|
return -1;
|
|
else if (strcmp(argv[optind], "--") == 0) {
|
|
optind++;
|
|
return -1;
|
|
}
|
|
}
|
|
'''
|
|
|
|
self.assert_all_Constants(s1,
|
|
['1', '0', r"'-'", '1', r"'\0'", '1', r'"--"', '0', '1'])
|
|
|
|
ps1 = self.parse(s1)
|
|
self.assert_num_ID_refs(ps1, 'argv', 3)
|
|
self.assert_num_ID_refs(ps1, 'optind', 5)
|
|
|
|
self.assert_num_klass_nodes(ps1, If, 3)
|
|
self.assert_num_klass_nodes(ps1, Return, 2)
|
|
self.assert_num_klass_nodes(ps1, FuncCall, 1) # strcmp
|
|
self.assert_num_klass_nodes(ps1, BinaryOp, 7)
|
|
|
|
# In the following code, Hash and Node were defined as
|
|
# int to pacify the parser that sees they're used as
|
|
# types
|
|
#
|
|
s2 = r'''
|
|
typedef int Hash, Node;
|
|
|
|
void HashDestroy(Hash* hash)
|
|
{
|
|
unsigned int i;
|
|
|
|
if (hash == NULL)
|
|
return;
|
|
|
|
for (i = 0; i < hash->table_size; ++i)
|
|
{
|
|
Node* temp = hash->heads[i];
|
|
|
|
while (temp != NULL)
|
|
{
|
|
Node* temp2 = temp;
|
|
|
|
free(temp->entry->key);
|
|
free(temp->entry->value);
|
|
free(temp->entry);
|
|
|
|
temp = temp->next;
|
|
|
|
free(temp2);
|
|
}
|
|
}
|
|
|
|
free(hash->heads);
|
|
hash->heads = NULL;
|
|
|
|
free(hash);
|
|
}
|
|
'''
|
|
|
|
ps2 = self.parse(s2)
|
|
self.assert_num_klass_nodes(ps2, FuncCall, 6)
|
|
self.assert_num_klass_nodes(ps2, FuncDef, 1)
|
|
self.assert_num_klass_nodes(ps2, For, 1)
|
|
self.assert_num_klass_nodes(ps2, While, 1)
|
|
self.assert_num_klass_nodes(ps2, StructRef, 10)
|
|
|
|
# declarations don't count
|
|
self.assert_num_ID_refs(ps2, 'hash', 6)
|
|
self.assert_num_ID_refs(ps2, 'i', 4)
|
|
|
|
s3 = r'''
|
|
void x(void) {
|
|
int a, b;
|
|
if (a < b)
|
|
do {
|
|
a = 0;
|
|
} while (0);
|
|
else if (a == b) {
|
|
a = 1;
|
|
}
|
|
}
|
|
'''
|
|
|
|
ps3 = self.parse(s3)
|
|
self.assert_num_klass_nodes(ps3, DoWhile, 1)
|
|
self.assert_num_ID_refs(ps3, 'a', 4)
|
|
self.assert_all_Constants(ps3, ['0', '0', '1'])
|
|
|
|
def test_empty_statements(self):
|
|
s1 = r'''
|
|
void foo(void){
|
|
;
|
|
return;;
|
|
|
|
;
|
|
}
|
|
'''
|
|
ps1 = self.parse(s1)
|
|
self.assert_num_klass_nodes(ps1, EmptyStatement, 3)
|
|
self.assert_num_klass_nodes(ps1, Return, 1)
|
|
self.assert_coord(ps1.ext[0].body.block_items[0], 3)
|
|
self.assert_coord(ps1.ext[0].body.block_items[1], 4)
|
|
self.assert_coord(ps1.ext[0].body.block_items[2], 4)
|
|
self.assert_coord(ps1.ext[0].body.block_items[3], 6)
|
|
|
|
def test_switch_statement(self):
|
|
def assert_case_node(node, const_value):
|
|
self.assertIsInstance(node, Case)
|
|
self.assertIsInstance(node.expr, Constant)
|
|
self.assertEqual(node.expr.value, const_value)
|
|
|
|
def assert_default_node(node):
|
|
self.assertIsInstance(node, Default)
|
|
|
|
s1 = r'''
|
|
int foo(void) {
|
|
switch (myvar) {
|
|
case 10:
|
|
k = 10;
|
|
p = k + 1;
|
|
return 10;
|
|
case 20:
|
|
case 30:
|
|
return 20;
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
'''
|
|
ps1 = self.parse(s1)
|
|
switch = ps1.ext[0].body.block_items[0]
|
|
|
|
block = switch.stmt.block_items
|
|
self.assertEqual(len(block), 4)
|
|
assert_case_node(block[0], '10')
|
|
self.assertEqual(len(block[0].stmts), 3)
|
|
assert_case_node(block[1], '20')
|
|
self.assertEqual(len(block[1].stmts), 0)
|
|
assert_case_node(block[2], '30')
|
|
self.assertEqual(len(block[2].stmts), 1)
|
|
assert_default_node(block[3])
|
|
|
|
s2 = r'''
|
|
int foo(void) {
|
|
switch (myvar) {
|
|
default:
|
|
joe = moe;
|
|
return 10;
|
|
case 10:
|
|
case 20:
|
|
case 30:
|
|
case 40:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
'''
|
|
ps2 = self.parse(s2)
|
|
switch = ps2.ext[0].body.block_items[0]
|
|
|
|
block = switch.stmt.block_items
|
|
self.assertEqual(len(block), 5)
|
|
assert_default_node(block[0])
|
|
self.assertEqual(len(block[0].stmts), 2)
|
|
assert_case_node(block[1], '10')
|
|
self.assertEqual(len(block[1].stmts), 0)
|
|
assert_case_node(block[2], '20')
|
|
self.assertEqual(len(block[1].stmts), 0)
|
|
assert_case_node(block[3], '30')
|
|
self.assertEqual(len(block[1].stmts), 0)
|
|
assert_case_node(block[4], '40')
|
|
self.assertEqual(len(block[4].stmts), 1)
|
|
|
|
s3 = r'''
|
|
int foo(void) {
|
|
switch (myvar) {
|
|
}
|
|
return 0;
|
|
}
|
|
'''
|
|
ps3 = self.parse(s3)
|
|
switch = ps3.ext[0].body.block_items[0]
|
|
|
|
self.assertEqual(switch.stmt.block_items, [])
|
|
|
|
def test_for_statement(self):
|
|
s2 = r'''
|
|
void x(void)
|
|
{
|
|
int i;
|
|
for (i = 0; i < 5; ++i) {
|
|
x = 50;
|
|
}
|
|
}
|
|
'''
|
|
ps2 = self.parse(s2)
|
|
self.assert_num_klass_nodes(ps2, For, 1)
|
|
# here there are 3 refs to 'i' since the declaration doesn't count as
|
|
# a ref in the visitor
|
|
#
|
|
self.assert_num_ID_refs(ps2, 'i', 3)
|
|
|
|
s3 = r'''
|
|
void x(void)
|
|
{
|
|
for (int i = 0; i < 5; ++i) {
|
|
x = 50;
|
|
}
|
|
}
|
|
'''
|
|
ps3 = self.parse(s3)
|
|
self.assert_num_klass_nodes(ps3, For, 1)
|
|
# here there are 2 refs to 'i' since the declaration doesn't count as
|
|
# a ref in the visitor
|
|
#
|
|
self.assert_num_ID_refs(ps3, 'i', 2)
|
|
|
|
s4 = r'''
|
|
void x(void) {
|
|
for (int i = 0;;)
|
|
i;
|
|
}
|
|
'''
|
|
ps4 = self.parse(s4)
|
|
self.assert_num_ID_refs(ps4, 'i', 1)
|
|
|
|
def _open_c_file(self, name):
|
|
""" Find a c file by name, taking into account the current dir can be
|
|
in a couple of typical places
|
|
"""
|
|
testdir = os.path.dirname(__file__)
|
|
name = os.path.join(testdir, 'c_files', name)
|
|
assert os.path.exists(name)
|
|
return io.open(name)
|
|
|
|
def test_whole_file(self):
|
|
# See how pycparser handles a whole, real C file.
|
|
#
|
|
with self._open_c_file('memmgr_with_h.c') as f:
|
|
code = f.read()
|
|
p = self.parse(code)
|
|
|
|
self.assert_num_klass_nodes(p, FuncDef, 5)
|
|
|
|
# each FuncDef also has a FuncDecl. 4 declarations
|
|
# + 5 definitions, overall 9
|
|
self.assert_num_klass_nodes(p, FuncDecl, 9)
|
|
|
|
self.assert_num_klass_nodes(p, Typedef, 4)
|
|
|
|
self.assertEqual(p.ext[4].coord.line, 88)
|
|
self.assertEqual(p.ext[4].coord.file, "./memmgr.h")
|
|
|
|
self.assertEqual(p.ext[6].coord.line, 10)
|
|
self.assertEqual(p.ext[6].coord.file, "memmgr.c")
|
|
|
|
def test_whole_file_with_stdio(self):
|
|
# Parse a whole file with stdio.h included by cpp
|
|
#
|
|
with self._open_c_file('cppd_with_stdio_h.c') as f:
|
|
code = f.read()
|
|
p = self.parse(code)
|
|
|
|
self.assertIsInstance(p.ext[0], Typedef)
|
|
self.assertEqual(p.ext[0].coord.line, 213)
|
|
self.assertEqual(p.ext[0].coord.file, r"D:\eli\cpp_stuff\libc_include/stddef.h")
|
|
|
|
self.assertIsInstance(p.ext[-1], FuncDef)
|
|
self.assertEqual(p.ext[-1].coord.line, 15)
|
|
self.assertEqual(p.ext[-1].coord.file, "example_c_file.c")
|
|
|
|
self.assertIsInstance(p.ext[-8], Typedef)
|
|
self.assertIsInstance(p.ext[-8].type, TypeDecl)
|
|
self.assertEqual(p.ext[-8].name, 'cookie_io_functions_t')
|
|
|
|
|
|
class TestCParser_typenames(TestCParser_base):
|
|
""" Test issues related to the typedef-name problem.
|
|
"""
|
|
def test_innerscope_typedef(self):
|
|
# should fail since TT is not a type in bar
|
|
s1 = r'''
|
|
void foo() {
|
|
typedef char TT;
|
|
TT x;
|
|
}
|
|
void bar() {
|
|
TT y;
|
|
}
|
|
'''
|
|
self.assertRaises(ParseError, self.parse, s1)
|
|
|
|
# should succeed since TT is not a type in bar
|
|
s2 = r'''
|
|
void foo() {
|
|
typedef char TT;
|
|
TT x;
|
|
}
|
|
void bar() {
|
|
unsigned TT;
|
|
}
|
|
'''
|
|
self.assertIsInstance(self.parse(s2), FileAST)
|
|
|
|
def test_ambiguous_parameters(self):
|
|
# From ISO/IEC 9899:TC2, 6.7.5.3.11:
|
|
# "If, in a parameter declaration, an identifier can be treated either
|
|
# as a typedef name or as a parameter name, it shall be taken as a
|
|
# typedef name."
|
|
|
|
# foo takes an int named aa
|
|
# bar takes a function taking a TT
|
|
s1 = r'''
|
|
typedef char TT;
|
|
int foo(int (aa));
|
|
int bar(int (TT));
|
|
'''
|
|
s1_ast = self.parse(s1)
|
|
self.assertEqual(expand_decl(s1_ast.ext[1].type.args.params[0]),
|
|
['Decl', 'aa', ['TypeDecl', ['IdentifierType', ['int']]]])
|
|
self.assertEqual(expand_decl(s1_ast.ext[2].type.args.params[0]),
|
|
['Typename', ['FuncDecl',
|
|
[['Typename', ['TypeDecl', ['IdentifierType', ['TT']]]]],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]])
|
|
|
|
# foo takes a function taking a char
|
|
# bar takes a function taking a function taking a char
|
|
s2 = r'''
|
|
typedef char TT;
|
|
int foo(int (aa (char)));
|
|
int bar(int (TT (char)));
|
|
'''
|
|
s2_ast = self.parse(s2)
|
|
self.assertEqual(expand_decl(s2_ast.ext[1].type.args.params[0]),
|
|
['Decl', 'aa', ['FuncDecl',
|
|
[['Typename', ['TypeDecl', ['IdentifierType', ['char']]]]],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]])
|
|
self.assertEqual(expand_decl(s2_ast.ext[2].type.args.params[0]),
|
|
['Typename', ['FuncDecl',
|
|
[['Typename', ['FuncDecl',
|
|
[['Typename', ['TypeDecl', ['IdentifierType', ['char']]]]],
|
|
['TypeDecl', ['IdentifierType', ['TT']]]]]],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]])
|
|
|
|
|
|
# foo takes an int array named aa
|
|
# bar takes a function taking a TT array
|
|
s3 = r'''
|
|
typedef char TT;
|
|
int foo(int (aa[]));
|
|
int bar(int (TT[]));
|
|
'''
|
|
s3_ast = self.parse(s3)
|
|
self.assertEqual(expand_decl(s3_ast.ext[1].type.args.params[0]),
|
|
['Decl', 'aa', ['ArrayDecl', '', [], ['TypeDecl', ['IdentifierType', ['int']]]]])
|
|
self.assertEqual(expand_decl(s3_ast.ext[2].type.args.params[0]),
|
|
['Typename', ['FuncDecl',
|
|
[['Typename', ['ArrayDecl', '', [], ['TypeDecl', ['IdentifierType', ['TT']]]]]],
|
|
['TypeDecl', ['IdentifierType', ['int']]]]])
|
|
|
|
def test_innerscope_reuse_typedef_name(self):
|
|
# identifiers can be reused in inner scopes; the original should be
|
|
# restored at the end of the block
|
|
s1 = r'''
|
|
typedef char TT;
|
|
void foo(void) {
|
|
unsigned TT;
|
|
TT = 10;
|
|
}
|
|
TT x = 5;
|
|
'''
|
|
s1_ast = self.parse(s1)
|
|
self.assertEqual(expand_decl(s1_ast.ext[1].body.block_items[0]),
|
|
['Decl', 'TT', ['TypeDecl', ['IdentifierType', ['unsigned']]]])
|
|
self.assertEqual(expand_decl(s1_ast.ext[2]),
|
|
['Decl', 'x', ['TypeDecl', ['IdentifierType', ['TT']]]])
|
|
|
|
# this should be recognized even with an initializer
|
|
s2 = r'''
|
|
typedef char TT;
|
|
void foo(void) {
|
|
unsigned TT = 10;
|
|
}
|
|
'''
|
|
s2_ast = self.parse(s2)
|
|
self.assertEqual(expand_decl(s2_ast.ext[1].body.block_items[0]),
|
|
['Decl', 'TT', ['TypeDecl', ['IdentifierType', ['unsigned']]]])
|
|
|
|
# before the second local variable, TT is a type; after, it's a
|
|
# variable
|
|
s3 = r'''
|
|
typedef char TT;
|
|
void foo(void) {
|
|
TT tt = sizeof(TT);
|
|
unsigned TT = 10;
|
|
}
|
|
'''
|
|
s3_ast = self.parse(s3)
|
|
self.assertEqual(expand_decl(s3_ast.ext[1].body.block_items[0]),
|
|
['Decl', 'tt', ['TypeDecl', ['IdentifierType', ['TT']]]])
|
|
self.assertEqual(expand_decl(s3_ast.ext[1].body.block_items[1]),
|
|
['Decl', 'TT', ['TypeDecl', ['IdentifierType', ['unsigned']]]])
|
|
|
|
# a variable and its type can even share the same name
|
|
s4 = r'''
|
|
typedef char TT;
|
|
void foo(void) {
|
|
TT TT = sizeof(TT);
|
|
unsigned uu = TT * 2;
|
|
}
|
|
'''
|
|
s4_ast = self.parse(s4)
|
|
self.assertEqual(expand_decl(s4_ast.ext[1].body.block_items[0]),
|
|
['Decl', 'TT', ['TypeDecl', ['IdentifierType', ['TT']]]])
|
|
self.assertEqual(expand_decl(s4_ast.ext[1].body.block_items[1]),
|
|
['Decl', 'uu', ['TypeDecl', ['IdentifierType', ['unsigned']]]])
|
|
|
|
# ensure an error is raised if a type, redeclared as a variable, is
|
|
# used as a type
|
|
s5 = r'''
|
|
typedef char TT;
|
|
void foo(void) {
|
|
unsigned TT = 10;
|
|
TT erroneous = 20;
|
|
}
|
|
'''
|
|
self.assertRaises(ParseError, self.parse, s5)
|
|
|
|
# reusing a type name should work with multiple declarators
|
|
s6 = r'''
|
|
typedef char TT;
|
|
void foo(void) {
|
|
unsigned TT, uu;
|
|
}
|
|
'''
|
|
s6_ast = self.parse(s6)
|
|
items = s6_ast.ext[1].body.block_items
|
|
self.assertEqual(expand_decl(items[0]),
|
|
['Decl', 'TT', ['TypeDecl', ['IdentifierType', ['unsigned']]]])
|
|
self.assertEqual(expand_decl(items[1]),
|
|
['Decl', 'uu', ['TypeDecl', ['IdentifierType', ['unsigned']]]])
|
|
|
|
# reusing a type name should work after a pointer
|
|
s7 = r'''
|
|
typedef char TT;
|
|
void foo(void) {
|
|
unsigned * TT;
|
|
}
|
|
'''
|
|
s7_ast = self.parse(s7)
|
|
items = s7_ast.ext[1].body.block_items
|
|
self.assertEqual(expand_decl(items[0]),
|
|
['Decl', 'TT', ['PtrDecl', ['TypeDecl', ['IdentifierType', ['unsigned']]]]])
|
|
|
|
# redefine a name in the middle of a multi-declarator declaration
|
|
s8 = r'''
|
|
typedef char TT;
|
|
void foo(void) {
|
|
int tt = sizeof(TT), TT, uu = sizeof(TT);
|
|
int uu = sizeof(tt);
|
|
}
|
|
'''
|
|
s8_ast = self.parse(s8)
|
|
items = s8_ast.ext[1].body.block_items
|
|
self.assertEqual(expand_decl(items[0]),
|
|
['Decl', 'tt', ['TypeDecl', ['IdentifierType', ['int']]]])
|
|
self.assertEqual(expand_decl(items[1]),
|
|
['Decl', 'TT', ['TypeDecl', ['IdentifierType', ['int']]]])
|
|
self.assertEqual(expand_decl(items[2]),
|
|
['Decl', 'uu', ['TypeDecl', ['IdentifierType', ['int']]]])
|
|
|
|
# Don't test this until we have support for it
|
|
# self.assertEqual(expand_init(items[0].init),
|
|
# ['UnaryOp', 'sizeof', ['Typename', ['TypeDecl', ['IdentifierType', ['TT']]]]])
|
|
# self.assertEqual(expand_init(items[2].init),
|
|
# ['UnaryOp', 'sizeof', ['ID', 'TT']])
|
|
|
|
def test_parameter_reuse_typedef_name(self):
|
|
# identifiers can be reused as parameter names; parameter name scope
|
|
# begins and ends with the function body; it's important that TT is
|
|
# used immediately before the LBRACE or after the RBRACE, to test
|
|
# a corner case
|
|
s1 = r'''
|
|
typedef char TT;
|
|
void foo(unsigned TT, TT bar) {
|
|
TT = 10;
|
|
}
|
|
TT x = 5;
|
|
'''
|
|
s1_ast = self.parse(s1)
|
|
self.assertEqual(expand_decl(s1_ast.ext[1].decl),
|
|
['Decl', 'foo',
|
|
['FuncDecl',
|
|
[ ['Decl', 'TT', ['TypeDecl', ['IdentifierType', ['unsigned']]]],
|
|
['Decl', 'bar', ['TypeDecl', ['IdentifierType', ['TT']]]]],
|
|
['TypeDecl', ['IdentifierType', ['void']]]]])
|
|
|
|
# the scope of a parameter name in a function declaration ends at the
|
|
# end of the declaration...so it is effectively never used; it's
|
|
# important that TT is used immediately after the declaration, to
|
|
# test a corner case
|
|
s2 = r'''
|
|
typedef char TT;
|
|
void foo(unsigned TT, TT bar);
|
|
TT x = 5;
|
|
'''
|
|
s2_ast = self.parse(s2)
|
|
self.assertEqual(expand_decl(s2_ast.ext[1]),
|
|
['Decl', 'foo',
|
|
['FuncDecl',
|
|
[ ['Decl', 'TT', ['TypeDecl', ['IdentifierType', ['unsigned']]]],
|
|
['Decl', 'bar', ['TypeDecl', ['IdentifierType', ['TT']]]]],
|
|
['TypeDecl', ['IdentifierType', ['void']]]]])
|
|
|
|
# ensure an error is raised if a type, redeclared as a parameter, is
|
|
# used as a type
|
|
s3 = r'''
|
|
typedef char TT;
|
|
void foo(unsigned TT, TT bar) {
|
|
TT erroneous = 20;
|
|
}
|
|
'''
|
|
self.assertRaises(ParseError, self.parse, s3)
|
|
|
|
def test_nested_function_decls(self):
|
|
# parameter names of nested function declarations must not escape into
|
|
# the top-level function _definition's_ scope; the following must
|
|
# succeed because TT is still a typedef inside foo's body
|
|
s1 = r'''
|
|
typedef char TT;
|
|
void foo(unsigned bar(int TT)) {
|
|
TT x = 10;
|
|
}
|
|
'''
|
|
self.assertIsInstance(self.parse(s1), FileAST)
|
|
|
|
def test_samescope_reuse_name(self):
|
|
# a typedef name cannot be reused as an object name in the same scope
|
|
s1 = r'''
|
|
typedef char TT;
|
|
char TT = 5;
|
|
'''
|
|
self.assertRaises(ParseError, self.parse, s1)
|
|
|
|
# ...and vice-versa
|
|
s2 = r'''
|
|
char TT = 5;
|
|
typedef char TT;
|
|
'''
|
|
self.assertRaises(ParseError, self.parse, s2)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
#~ suite = unittest.TestLoader().loadTestsFromNames(
|
|
#~ ['test_c_parser.TestCParser_fundamentals.test_typedef'])
|
|
|
|
#~ unittest.TextTestRunner(verbosity=2).run(suite)
|
|
unittest.main()
|