diff --git a/docs/basics.rst b/docs/basics.rst index a3787fe8..6883c121 100644 --- a/docs/basics.rst +++ b/docs/basics.rst @@ -442,6 +442,15 @@ Global data access Get all variables in this compilation unit as a list of :py:class:`gcc.Variable` +.. py:class:: gcc.Variable + + Wrapper around GCC's `struct varpool_node`, representing a variable in + the code being compiled. + + .. py:attribute:: decl + + The declaration of this variable, as a :py:class:`gcc.Tree` + .. py:function:: gccutils.get_variables_as_dict() Get a dictionary of all variables, where the keys are the variable names diff --git a/docs/working-with-c.rst b/docs/working-with-c.rst index 73d66c20..c4665c71 100644 --- a/docs/working-with-c.rst +++ b/docs/working-with-c.rst @@ -76,7 +76,7 @@ these warnings are emitted on stderr: Finding global variables ------------------------ -This example adds a pass that warns about global variables: +This example adds a pass that warns about uses of global variables: .. code-block:: bash diff --git a/gcc-python-wrapper.c b/gcc-python-wrapper.c index fb6f87a0..951a04bf 100644 --- a/gcc-python-wrapper.c +++ b/gcc-python-wrapper.c @@ -316,13 +316,16 @@ my_walker(void *arg ATTRIBUTE_UNUSED) } } -static struct ggc_root_tab myroot = { (char*)"", 1, 1, my_walker, NULL }; +static struct ggc_root_tab myroottab[] = { + { (char*)"", 1, 1, my_walker, NULL }, + { NULL, } +}; void PyGcc_wrapper_init(void) { /* Register our GC root-walking callback: */ - ggc_register_root_tab(&myroot); + ggc_register_root_tab(myroottab); PyType_Ready(&PyGccWrapperMeta_TypeObj); } diff --git a/gcc-with-cpychecker b/gcc-with-cpychecker index ae397e85..1dbb8bf9 100755 --- a/gcc-with-cpychecker +++ b/gcc-with-cpychecker @@ -78,7 +78,7 @@ cmd = 'from libcpychecker import main; main(**{%s})' % dictstr # when setting CC=gcc-with-cpychecker) args = ['gcc', ('-fplugin=%s' % PLUGIN), - ('-fplugin-arg-python-command=%s' % cmd)] + ('-fplugin-arg-python-command=%s' % cmd)] args += other_args # (the args we didn't consume) # Beware of quoting: if the command is quoted within the Popen call, then diff --git a/generate-tree-c.py b/generate-tree-c.py index 65df19c5..cd43a2e3 100644 --- a/generate-tree-c.py +++ b/generate-tree-c.py @@ -388,6 +388,10 @@ def add_complex_getter(name, doc): add_simple_getter('%s_equivalent' % qual, 'PyGccTree_New(gcc_private_make_tree(build_qualified_type(self->t.inner, TYPE_QUAL_%s)))' % qual.upper(), 'The gcc.Type for the %s version of this type' % qual) + if tree_type.SYM == 'RECORD_TYPE': + add_simple_getter('const', + 'PyBool_FromLong(TYPE_READONLY(self->t.inner))', + "Boolean: does this type have the 'const' modifier?") if tree_type.SYM == 'INTEGER_TYPE': add_simple_getter('unsigned', diff --git a/run-test-suite.py b/run-test-suite.py index 8f2c544a..aedb203c 100644 --- a/run-test-suite.py +++ b/run-test-suite.py @@ -310,6 +310,10 @@ def run_test(testdir): args += ['-fplugin=%s' % os.path.abspath('%s.so' % PLUGIN_NAME), '-fplugin-arg-%s-script=%s' % (PLUGIN_NAME, script_py)] + # Force the signedness of char so that the tests have consistent + # behavior across all archs: + args += ['-fsigned-char'] + # Special-case: add the python include dir (for this runtime) if the C code # uses Python.h: def uses_python_headers(): diff --git a/tests/examples/find-global-state/input.c b/tests/examples/find-global-state/input.c index 98799538..ed0781d2 100644 --- a/tests/examples/find-global-state/input.c +++ b/tests/examples/find-global-state/input.c @@ -17,10 +17,12 @@ . */ +#include + static int a_global; struct { - int i; + int f; } bar; extern int foo; @@ -33,9 +35,36 @@ int test(int j) return i + 1; } -int test2(int j) +int test2(int p) +{ + static int q = 0; + q += p; + return p * q; +} + +int test3(int k) +{ + /* We should *not* report about __FUNCTION__ here: */ + printf("%s:%i:%s\n", __FILE__, __LINE__, __FUNCTION__); +} + +int test4() +{ + return foo; +} + +int test6() +{ + return bar.f; +} + +struct banana { + int f; +}; + +const struct banana a_banana; + +int test7() { - static int i = 0; - i += j; - return j * i; + return a_banana.f; } diff --git a/tests/examples/find-global-state/script.py b/tests/examples/find-global-state/script.py index 24b474a6..83778e0a 100644 --- a/tests/examples/find-global-state/script.py +++ b/tests/examples/find-global-state/script.py @@ -18,12 +18,82 @@ import gcc from gccutils import get_src_for_loc +DEBUG=0 + +def is_const(type_): + if DEBUG: + type_.debug() + + if hasattr(type_, 'const'): + if type_.const: + return True + + # Don't bother warning about an array of const e.g. + # const char [] + if isinstance(type_, gcc.ArrayType): + item_type = type_.dereference + if is_const(item_type): + return True + + +class StateFinder: + def __init__(self): + # Locate all declarations of variables holding "global" state: + self.global_decls = set() + + for var in gcc.get_variables(): + type_ = var.decl.type + + if DEBUG: + print('var.decl: %r' % var.decl) + print(type_) + + # Don't bother warning about const data: + if is_const(type_): + continue + + self.global_decls.add(var.decl) + if DEBUG: + print('self.global_decls: %r' % self.global_decls) + + self.state_users = set() + + def find_state_users(self, node, loc): + if isinstance(node, gcc.VarDecl): + if node in self.global_decls: + # store the state users for later replay, so that + # we can eliminate duplicates + # e.g. two references to "q" in "q += p" + # and replay in source-location order: + self.state_users.add( (loc, node) ) + + def flush(self): + # Emit warnings, sorted by source location: + for loc, node in sorted(self.state_users, + key=lambda pair:pair[0]): + gcc.inform(loc, + 'use of global state "%s %s" here' + % (node.type, node)) + def on_pass_execution(p, fn): if p.name == '*free_lang_data': - for var in gcc.get_variables(): - gcc.inform(var.decl.location, - 'global state "%s %s" defined here' - % (var.decl.type, var.decl)) + sf = StateFinder() + + # Locate uses of such variables: + for node in gcc.get_callgraph_nodes(): + fun = node.decl.function + if fun: + cfg = fun.cfg + if cfg: + for bb in cfg.basic_blocks: + stmts = bb.gimple + if stmts: + for stmt in stmts: + stmt.walk_tree(sf.find_state_users, + stmt.loc) + + # Flush the data that was found: + sf.flush() gcc.register_callback(gcc.PLUGIN_PASS_EXECUTION, on_pass_execution) diff --git a/tests/examples/find-global-state/stderr.txt b/tests/examples/find-global-state/stderr.txt index c8cea812..8a24a156 100644 --- a/tests/examples/find-global-state/stderr.txt +++ b/tests/examples/find-global-state/stderr.txt @@ -1,6 +1,7 @@ -tests/examples/find-global-state/input.c:38:14: note: global state "int i" defined here -tests/examples/find-global-state/input.c:24:3: note: global state "struct +tests/examples/find-global-state/input.c:41:nn: note: use of global state "int q" here +tests/examples/find-global-state/input.c:42:nn: note: use of global state "int q" here +tests/examples/find-global-state/input.c:53:nn: note: use of global state "int foo" here +tests/examples/find-global-state/input.c:58:nn: note: use of global state "struct { - int i; -} bar" defined here -tests/examples/find-global-state/input.c:20:12: note: global state "int a_global" defined here + int f; +} bar" here