We will use oug to launch commands in a shell. Therefore we suppose that Oug is installed and oug is in a directory of the PATH environment variable.
The code to analyse must be compiled, because Oug performs various analysis of the OCaml compiler (all stages up to the typing stage) and to do so needs the compiled interfaces of the used modules.
Oug analyses the given OCaml source files and builds a graph with "holes" for each source file. Then, these graphs are gathered into a global graph. This allow to re-analyse only source files which changed since the previous analysis. It is no longer necessary to given source files in their dependency order.
Another point is that one must give Oug the same options used to compile the source files, like the -I and -pp options (see this section).
One can indicate to the tool the interface files (.mli), this will create export edges in the graph, giving explicitly useful elements.
Rather than analyse the source files each time we want to search something in the global graph, it is better (because faster) to create a dump of the graph once built and load this graph to perform the needed searches. This is the method explained below.
The --dump option allows to store the global graph with the analysis environment. This is useful to perform incremental analysis, for example when some source files need different -pp options. In this case, we proceed the following way:
# oug --dump tmp.oug -pp command1 file1.ml # oug --load tmp.oug --dump tmp.oug -pp command2 file2.ml # ...
By default, the graph stored in the file tmp.oug by this command is reduced, that is the expression nodes are removed from the graph, except idents. To prevent this reduction and keep the complete graph (with all expression nodes) we will use the --no-reduce option to store, with the command below, the non-reduced graph in the graph.oug file:
# oug --no-reduce --dump graph.oug -I foo -I +bar file1.ml file2.ml ...
Of course, all these commands can be put in a Makefile, for example with the stored graph depending on the source files.
The analysis of the graph to find useless elements is triggered by the --useless option of oug, with a filename to store this list, in text format:
# oug --load graph.oug --no-reduce --useless useless.txt
The --no-reduce option indicates not to reduce the graph before the analysis. Since this analysis can take somes minutes to complete when the graph contains thousands of elements, we will use the --progress option to display the progress.
Here is an exemple of result:
... File "oug_data.ml", line 505, char 56: in_mod_id [p] File "oug_data.ml", line 583, char 25: functor_env [v] File "oug_data.ml", line 583, char 38: functor_args [v] File "oug_data.ml", line 808, char 4: Oug_data.orig_mod_list [v] File "oug_data.ml", line 880, char 24: modname [p] ...
The character between brackets next to of each element name is the kind of the element: (v)alue, (M)odule, (t)ype, (C)lass, type (f)ield, (m)ethod, (i)nstance variable, (p)attern ident, (e)xpression.
Filters allow to select elements of the graph, to perform some searches. The language of filters and examples are described in section filters.
Two options of oug allow to use a filter:
Then it is possible to perform various actions one after another (see section actions) by using these options:
# oug --load graph.oug --filter "<Foo.*>" --keep "v:<*>" \ --dump tmp.oug --filter "<Foo.a*>"
The command above performs the following actions, in this order:
Filters can be applied on the reduced graph or on the complete graph.
Here are two examples of useful filters which can be used on the global graph. The first one lists the variant constructors not used to create a value:
oug --load graph.oug --filter "f:<*> & ! (<*> -create->)"
The second one lists the record fields never read:
oug --load graph.oug --filter "f:<*> & ! (<*> -use->)"
These two filters give the intended results under two assumptions:
You can also use Oug from your favorite IDE