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