2 # * Licensed under the Apache License, Version 2.0 (the "License");
3 # * you may not use this file except in compliance with the License.
4 # * You may obtain a copy of the License at
6 # * http://www.apache.org/licenses/LICENSE-2.0
13 export CLANG_BIND_DIR="/dsk/src/llvm/tools/clang/bindings/python"
14 export CLANG_LIB_DIR="/opt/llvm/lib"
16 python2 clang_array_check.py somefile.c -DSOME_DEFINE -I/some/include
18 ... defines and includes are optional
22 # delay parsing functions until we need them
24 USE_EXACT_COMPARE = False
26 # -----------------------------------------------------------------------------
27 # predefined function/arg sizes, handy sometimes, but not complete...
33 "glColor3ubv": {0: 3},
34 "glColor4ubv": {0: 4},
36 "glColor4usv": {0: 3},
37 "glColor4usv": {0: 4},
45 "glVertex2fv": {0: 2},
46 "glVertex3fv": {0: 3},
47 "glVertex4fv": {0: 4},
49 "glEvalCoord1fv": {0: 1},
50 "glEvalCoord1dv": {0: 1},
51 "glEvalCoord2fv": {0: 2},
52 "glEvalCoord2dv": {0: 2},
54 "glRasterPos2dv": {0: 2},
55 "glRasterPos3dv": {0: 3},
56 "glRasterPos4dv": {0: 4},
58 "glRasterPos2fv": {0: 2},
59 "glRasterPos3fv": {0: 3},
60 "glRasterPos4fv": {0: 4},
62 "glRasterPos2sv": {0: 2},
63 "glRasterPos3sv": {0: 3},
64 "glRasterPos4sv": {0: 4},
66 "glTexCoord2fv": {0: 2},
67 "glTexCoord3fv": {0: 3},
68 "glTexCoord4fv": {0: 4},
70 "glTexCoord2dv": {0: 2},
71 "glTexCoord3dv": {0: 3},
72 "glTexCoord4dv": {0: 4},
74 "glNormal3fv": {0: 3},
75 "glNormal3dv": {0: 3},
76 "glNormal3bv": {0: 3},
77 "glNormal3iv": {0: 3},
78 "glNormal3sv": {0: 3},
81 # -----------------------------------------------------------------------------
86 # Examples with LLVM as the root dir: '/dsk/src/llvm'
88 # path containing 'clang/__init__.py'
89 CLANG_BIND_DIR = "/dsk/src/llvm/tools/clang/bindings/python"
91 # path containing libclang.so
92 CLANG_LIB_DIR = "/opt/llvm/lib"
95 CLANG_BIND_DIR = os.environ.get("CLANG_BIND_DIR")
96 CLANG_LIB_DIR = os.environ.get("CLANG_LIB_DIR")
98 if CLANG_BIND_DIR is None:
99 print("$CLANG_BIND_DIR python binding dir not set")
100 if CLANG_LIB_DIR is None:
101 print("$CLANG_LIB_DIR clang lib dir not set")
103 sys.path.append(CLANG_BIND_DIR)
107 from clang.cindex import (CursorKind,
111 clang.cindex.Config.set_library_path(CLANG_LIB_DIR)
113 index = clang.cindex.Index.create()
118 tu = index.parse(sys.argv[1], args)
119 # print('Translation unit: %s' % tu.spelling)
120 filepath = tu.spelling
122 # -----------------------------------------------------------------------------
125 def function_parm_wash_tokens(parm):
127 assert parm.kind in (CursorKind.PARM_DECL,
128 CursorKind.VAR_DECL, # XXX, double check this
129 CursorKind.FIELD_DECL,
133 Return tolens without trailing commads and 'const'
136 tokens = [t for t in parm.get_tokens()]
140 # if tokens[-1].kind == To
141 # remove trailing char
142 if tokens[-1].kind == TokenKind.PUNCTUATION:
143 if tokens[-1].spelling in (",", ")", ";"):
146 # print(tokens[-1].spelling)
151 t_spelling = t.spelling
153 if t_kind == TokenKind.KEYWORD:
154 if t_spelling in ("const", "restrict", "volatile"):
156 elif t_spelling.startswith("__"):
157 ok = False # __restrict
158 elif t_kind in (TokenKind.COMMENT, ):
162 elif t_kind in (TokenKind.LITERAL,
163 TokenKind.PUNCTUATION,
164 TokenKind.IDENTIFIER):
169 print("Unknown!", t_kind, t_spelling)
171 # if its OK we will add
177 def parm_size(node_child):
178 tokens = function_parm_wash_tokens(node_child)
180 # print(" ".join([t.spelling for t in tokens]))
182 # NOT PERFECT CODE, EXTRACT SIZE FROM TOKENS
183 if len(tokens) >= 3: # foo [ 1 ]
184 if ((tokens[-3].kind == TokenKind.PUNCTUATION and tokens[-3].spelling == "[") and
185 (tokens[-2].kind == TokenKind.LITERAL and tokens[-2].spelling.isdigit()) and
186 (tokens[-1].kind == TokenKind.PUNCTUATION and tokens[-1].spelling == "]")):
188 return int(tokens[-2].spelling)
192 def function_get_arg_sizes(node):
193 # Return a dict if (index: size) items
194 # {arg_indx: arg_array_size, ... ]
197 if 1: # node.spelling == "BM_vert_create", for debugging
198 node_parms = [node_child for node_child in node.get_children()
199 if node_child.kind == CursorKind.PARM_DECL]
201 for i, node_child in enumerate(node_parms):
203 # print(node_child.kind, node_child.spelling)
204 # print(node_child.type.kind, node_child.spelling)
205 if node_child.type.kind == TypeKind.CONSTANTARRAY:
206 pointee = node_child.type.get_pointee()
207 size = parm_size(node_child)
214 # -----------------------------------------------------------------------------
218 def lookup_function_size_def(func_id):
220 result = _defs.get(func_id, {})
221 if type(result) != dict:
222 result = _defs[func_id] = function_get_arg_sizes(result)
225 return _defs.get(func_id, {})
227 # -----------------------------------------------------------------------------
230 def file_check_arg_sizes(tu):
232 # main checking function
233 def validate_arg_size(node):
235 Loop over args and validate sizes for args we KNOW the size of.
237 assert node.kind == CursorKind.CALL_EXPR
242 [" ".join([t.spelling for t in C.get_tokens()])
243 for C in node.get_children()]
245 # print(node.location)
247 # first child is the function call, skip that.
248 children = list(node.get_children())
251 return # XXX, look into this, happens on C++
255 # get the func declaration!
256 # works but we can better scan for functions ahead of time.
258 func_dec = func.get_definition()
260 print("FD", " ".join([t.spelling for t in func_dec.get_tokens()]))
262 # HRMP'f - why does this fail?
263 print("AA", " ".join([t.spelling for t in node.get_tokens()]))
265 args_size_definition = () # dummy
268 tok = list(func.get_tokens())
270 func_id = tok[0].spelling
271 args_size_definition = lookup_function_size_def(func_id)
273 if not args_size_definition:
276 children = children[1:]
277 for i, node_child in enumerate(children):
278 children = list(node_child.get_children())
280 # skip if we dont have an index...
281 size_def = args_size_definition.get(i, -1)
286 # print([c.kind for c in children])
287 # print(" ".join([t.spelling for t in node_child.get_tokens()]))
289 if len(children) == 1:
291 if arg.kind in (CursorKind.DECL_REF_EXPR,
292 CursorKind.UNEXPOSED_EXPR):
294 if arg.type.kind == TypeKind.CONSTANTARRAY:
295 dec = arg.get_definition()
297 size = parm_size(dec)
299 # size == 0 is for 'float *a'
300 if size != -1 and size != 0:
304 print("".join([t.spelling for t in func.get_tokens()]),
306 " ".join([t.spelling for t in dec.get_tokens()]))
311 if USE_EXACT_COMPARE:
312 # is_err = (size != size_def) and (size != 4 and size_def != 3)
313 is_err = (size != size_def)
315 is_err = (size < size_def)
318 location = node.location
319 # if "math_color_inline.c" not in str(location.file):
321 print("%s:%d:%d: argument %d is size %d, should be %d (from %s)" %
325 i + 1, size, size_def,
326 filepath # always the same but useful when running threaded
329 # we dont really care what we are looking at, just scan entire file for
332 def recursive_func_call_check(node):
333 if node.kind == CursorKind.CALL_EXPR:
334 validate_arg_size(node)
336 for c in node.get_children():
337 recursive_func_call_check(c)
339 recursive_func_call_check(tu.cursor)
342 # -- first pass, cache function definitions sizes
345 def recursive_arg_sizes(node, ):
346 # print(node.kind, node.spelling)
347 if node.kind == CursorKind.FUNCTION_DECL:
351 args_sizes = function_get_arg_sizes(node)
353 # print(node.spelling, args_sizes)
354 _defs[node.spelling] = args_sizes
355 # print("adding", node.spelling)
356 for c in node.get_children():
357 recursive_arg_sizes(c)
358 # cache function sizes
359 recursive_arg_sizes(tu.cursor)
360 _defs.update(defs_precalc)
362 # --- second pass, check against def's
363 file_check_arg_sizes(tu)