Making clang GObject aware

Before it’s Christmas again, I should blog about the nifty hack I did last Christmas, already nine months ago.

Often, I am quickly coding up test cases. Compiling and running usually goes like this (insert cheering after successful compile):

$ clang `pkg-config --libs --cflags gtk+-3.0` -o test/foo3 test/foo3.c
$ ./test/foo3  

(foo3:84880): GLib-GObject-WARNING **: g_object_set_valist: object class `GtkWindow' has no property named `width'

(foo3:84880): GLib-GObject-WARNING **: gsignal.c:2293: signal `delet-event' is invalid for instance `0x101865000'

And insert a sigh of disappointment after witnessing the GObject warnings. I figured that with all the GObject Introspection work going on, it would be quite nice to teach the compiler about GObject Introspection. So while snow was pouring down last Christmas, I hacked up a quick and dirty clang plugin. It looks like this:

$ clang -cc1 -fcolor-diagnostics `pkg-config --cflags gtk+-3.0` -load libGObjectHelper.dylib -plugin gobject-helper test/foo3.c
test/foo3.c:23:25: warning: object 'GtkWindow' has no property 'width'
  g_object_set (window, "width", 320, "height", 240, NULL);
                        ^
test/foo3.c:23:39: warning: object 'GtkWindow' has no property 'height' 
  g_object_set (window, "width", 320, "height", 240, NULL);
                                      ^
test/foo3.c:24:29: warning: object 'GtkWindow' has no signal 'delet-event' 
  g_signal_connect (window, "delet-event",
                            ^
3 warnings generated.

One of the features I wanted to add before blogging, was to also use the clang-style spelling corrections. For the last warning, it would say "did you mean ‘delete-event’?" Of course, there’s still much more to do than that to make the plugin feature complete.

This plugin will not work just with GTK+ source code. Because it’s using GObject Introspection data, it should work for any GObject library for which the GObject Introspection data has been generated.

The code is not ready for public consumption, so I am not releasing it just yet. My plan is to find time in the very near feature once my critical bugs are under control (or worst case during Christmas again :) and work on bringing the plugin into shape and trying it out on some larger examples. See below why trying it on larger examples first is quite important to figure out whether this plugin will ever be useful for the general public. When that is done, I plan to dump it in a public git repository for all to enjoy.

So, how does it work?

At its very core, the plugin walks over the Abstract Syntax Tree (AST) produced by clang. For the above example we simply handle calls to g_signal_connect_data and g_object_set. In order to verify whether the given signal or property name exists, we need to know the exact type of the object we are calling this function on. This is a problem that cannot always be solved.

What the plugin can currently do to solve this problem, is to determine where exactly the object pointer is assigned in the same function. If the object pointer is a GtkWidget *, we could at least assume that the object contains all signals and properties belonging to GtkWidget objects. In the next line, it is possible that the value of a call to gtk_label_new() is assigned to the object pointer. In this case we know the object is of type GtkLabel and as a result we can catch more errors.

The above strategy works fine on simple examples. The challenge is to make this work for larger programs. It might be a reasonable assumption to say that often an object is created, properties set and signals attached in the same function. However, there might be many cases where an object is created in a separate function and only a GtkWidget * type is returned. When the function is in the same compilation unit, it will still be possible to find out the full type though.

It is already clear that the plugin will never be able to catch all errors. Still I expect it to be pretty helpful. Another case which is interesting to handle is the following:

GtkWidget *widget;

if (can_edit)
  widget = gtk_entry_new ();
else
  widget = gtk_label_new ();

g_object_set (widget, ...

We will never be able to report all possible errors in this case. For example, if widget is assigned a GtkLabel and we use a property from GtkEntry, we cannot catch it. What we can do is tag the widget variable with both the GtkLabel and GtkEntry types and put out an error if a property or signal is used that is neither in GtkLabel nor in GtkEntry.

Why not just program in a higher-level language, then you would not need a plugin like this?

Good question.


To be continued …

ANSI to HTML conversion courtesy of a2h