Calling dead_code_analyzer --help provides the following output, describing
the main command line aspect, different options available and their effects.
The <path> argument is any number of directory, .cmt and .cmi files.
These files can be produced using the compiler flags -keep-locs (on by default)
for .cmi and -bin-annot for .cmt.
The directories are traversed looking for such files.
Tip
If you are using dune for your project, the files can be obtained via the
@check alias
Important
The order of the command line arguments matter.
Using dead_code_analyzer <path> <options> ignores the <options> when
analyzing <path>.
Similarly, some options may override the effects of
others. E.g. dead_code_analyzer --nothing --all <path> is the same as
dead_code_analyzer --all <path>.
Usage: dead_code_analyzer <options> <path>
Options are:
--exclude <path> Exclude given path from research.
--references <path> Consider given path to collect references.
--underscore Show names starting with an underscore
--verbose Verbose mode (ie., show scanned files)
-v See --verbose
--internal Keep internal uses as exported values uses when the interface is given. This is the default behaviour when only the implementation is found
--nothing Disable all warnings
-a See --nothing
--all Enable all warnings
-A See --all
-E <display> Enable/Disable unused exported values warnings.
<display> can be:
all
nothing
"threshold:<integer>": report elements used up to the given integer
"calls:<integer>": like threshold + show call sites
-M <display> Enable/Disable unused methods warnings.
See option -E for the syntax of <display>
-Oa <display> Enable/Disable optional arguments always used warnings.
<display> can be:
all
nothing
<threshold>
"calls:<threshold>" like <threshold> + show call sites
<threshold> can be:
"both:<integer>,<float>": both the number max of exceptions (given through the integer) and the percent of valid cases (given as a float) mu
st be respected for the element to be reported
"percent:<float>": percent of valid cases to be reported
-On <display> Enable/Disable optional arguments never used warnings.
See option -Oa for the syntax of <display>
-S Enable/Disable coding style warnings.
Delimiters '+' and '-' determine if the following option is to enable or disable.
Options (can be used together):
bind: useless binding
opt: optional arg in arg
seq: use sequence
unit: unit pattern
all: bind & opt & seq & unit
-T <display> Enable/Disable unused constructors/records fields warnings.
See option -E for the syntax of <display>
-help Display this list of options
--help Display this list of options
options : --nothing, --all
To ease the usage of the tool to only focus on some kinds of reports, 2 options are available :
--nothingdeactivates all the reports. One can then selectively activate the ones they are interested in using the corresponding options (see the sections below). This is equivalent to-E nothing -M nothing -T nothing -On nothing -Oa nothing -S -all.--allactivates all the reports. One can then selectively deactivate the ones they are not interested in using the corresponding options (see the sections below). This is equivalent to-E all -M all -T all -On all -Oa all -S +all.
Activating or deactivating a report section actually activates or deactivates some parts of the analysis. Therefore, it impacts the cost (memory and time) of the analysis.
The dead_code_analyzer's command line arguments are processed and applied
in the order they appear.
Consequently, those 2 options are generally expected to appear before all the
other report-related options. Using them after would simply override the effects
of the others.
Examples :
dead_code_analyzer --nothing <options> <path>deactivates all the reports and then apply the<options>before analyzing<path>.dead_code_analyzer <options> --nothing <path>apply the<options>and then decativates all the reports before analyzing<path>, which is not very effective.
options : -E <display>, -M <display>, -T <display>
By default the dead_code_analyzer only looks for unused exported values,
unused methods, and unused constructors and record fields. They can respectively
be deactivated using -E nothing, -M nothing, or -T nothing.
Example : dead_code_analyzer -M nothing -T nothing will only analyze and
report unused exported values.
Similarly to --nothing and --all, one can selectivey activate the reports
they are interested in by using respectively -E all, -M all, or -T all.
Example : dead_code_analyzer --nothing -E all will analyze and report
unused exported values.
Note
dead_code_analyzer <path> is equivalent to
dead_code_analyzer -E all -M all -T all <path>.
Warning
Analyzing unused methods can be memory intensive.
options : -On <display>, -Oa <display>
As discussed in the introduction, another important
focus of the dead_code_analyzer is on the optional arguments never or always used.
Analyzing and reporting on those can be turned on using respectively -On all, or
-Oa all. Similarly with the previous options, turning these reports off is
done using the nothing argument instead of all: respectively -On nothing,
or -Oa nothing. Using --nothing or --all also affects these sections.
Example : dead_code_analyzer --nothing -On all will enable the analysis of
optional arguments and only report those that are never used.
Note
Reporting for either the optional arguments never or always used actually relies on the same analysis which can be memory intensive.
option : -S <warning>
Finally, as a bonus, one can activate some stylistic reports by using -S +all.
Notice the extra + before all. The -S option expects slightly different
arguments from the previous options. Rather than expecting all or nothing,
it expects a description of the desired stylistic issues to report, with +
indicating a category to activate and - one to deactivate.
Using --nothing or --all also affects this section.
Example : dead_code_analyzer --nothing -S +all-bind only reports stylistic issues,
and reports all of them but the "useless binding" ones
Note
-S +all is equivalent to -S +bind+opt+seq+unit, and -S -all to
-S -bind-opt-seq-unit
Imagine a file tree like the one below, with foo.* and lib/* the actual code
of the product, and debug/* code intended for debugging purpose only during
the development.
src
├── debug
│ ├── debug.cmi
│ ├── debug.cmt
│ └── debug.ml
├── foo.cmi
├── foo.cmt
├── foo.ml
├── foo.mli
└── lib
├── lib.cmi
├── lib.cmt
├── lib.ml
└── lib.mli
dead_code_analyzer src will analyze and find unused elements of code in all
of src. Some reports may point to locations in src/debug/debug.ml, adding
noise because this module is actually used occasionnaly but no reference to it
should subsist in production code.
To ignore that module, both for declarations and uses, one can use the
--exclude <path> option.
dead_code_analyzer --exclude src/debug src only reports locations outside of
src/debug.
Using the previous command line, some reported elements (e.g. located in
src/lib/lib.mli) could be used by the Debug module. In this situation,
completely excluding src/debug from the analysis leads to false
positives (FP): invalid reports.
To fix the situation, one can use the --references <path> option. This option
includes the <path> when observing uses.
As a result, dead_code_analyzer --exclude src/debug --references src/debug src
only reports elements outside of src/debug and does not report elements used by
code in src/debug.
The compiler ignores unused values when their names are prefixed with an
underscore (e.g. let _x = ...). The dead_code_analyzer imitates that
behavior. One can enable the analysis and reports on such names using the
--underscore option.
Note
This behavior (ignoring names starting with underscore) is not only limited
to unused exported values but applied to all the report kinds. Thus, they are
all affected by the --underscore option.
The compiler already warns the user about unused values that are not exported.
That is, values that are not exposed in the signature of a module. In case no
interface is available, all the toplevel declaration are exported. Consequently,
none of them can be reported by the compiler. In order to complement the
compiler well, and fit some coding habits, the dead_code_analyzer keeps track
of internal uses in implementations (.ml) without corresponding interfaces (.mli).
Using the --internal option activates the analysis of internal use for
implementations that have a corresponding interface file, similarly to
implementations without interface as described above. This can be useful to
reduce the volume of reports. It also expands the notion of unused exported
values from unused externally (can be removed from the .mli) to not used at
all (can be removed from the codebase).
Examples :
fis exported and used internally. Neither the compiler nor thedead_code_analyzerwill report it.(* foo.ml *) (* foo.mli does not exist *) let f x = x let () = f ()
fis exported and not used internally. Assuming it is not used externally either, thedead_code_analyzerwill report it, while the compiler will not.(* foo.ml *) (* foo.mli does not exist *) let f x = x
fis exported viafoo.mliand used internally. Assuming it is not used externally, thedead_code_analyzerwill report it by default.(* foo.ml *) let f x = x let () = f ()
By using the(* foo.mli *) val f : 'a -> 'a
--internaloption, thedead_code_analyzerwill not reportfas unused anymore.
When using the dead_code_analyzer, one may encounter false negatives (FN):
missing reports. Users may also encounter false positives (FP): invlaid reports.
In both cases, chances are that some files were not analyzed.
An easy way to see the list of analyzed files is by using the --verbose option.
This will print out Scanning <file_path> for every file it tries to read.
In case there is an issue reading the file, then it prints out a second line
indicating the <file_path> and the issue. In this case, the analyzer ignores
the file and moves on.
- If a file is ignored and it is not obvious why, then opening an issue is welcome.
- If no file is ignored, check that no file is missing. There should be a
.cmiand a.cmtfile for each expected<file_path>. - If no
.cmior.cmtfile is missing then the false negatives must be due to limitations of the tool and opening an issue is welcome.
options : -E <display>, -M <display>, -T <display>
In addition to activating/deactivating some reports, one can extend the details of the reports using thresholds. The idea with thresholds is to not only report unused elements of code but also report almost unused ones, up to a desired maximum amount: the threshold.
For the 3 default reports (exported values, methods, and constructors and record
fields), the threshold argument's format is threshold:<int>, with <int> a
positive integer. Using this argument will not only enable the corresponding
report but also some subreports for values used exactly n times with
0 < n <= <int>.
Example : dead_code_analyzer --nothing -E threshold:2 will only analyze
exported values and report those never used, used exactly once, and used exactly
twice.
Note
The use of an element of code is defined by its syntactic use. Consequently, a function could actually be called mutliple times dynamically but always at the same location in code and therefore be reported as used exactly once.
The threshold argument has an ever more detailed variant : calls:<int>. This
time, the subreports will not only indicate the elements used exactly n times
but also display the locations of their use.
options : -On <display>, -Oa <display>
The options for optional arguments always/never used also accept a threshold
argument. However it looks a bit different from the one for the default reports.
Instead of using threshold:<int>, there are 2 different modes available:
percent:<float> and both:<int>,<float> with 0.0 <= <float> <= 1.0 and
0 <= <int>.
Using the percent:<float> argument will activate the reports for optional arguments
that are used/unused at least the provided percent of the time.
Examples :
dead_code_analyzer --nothing -Oa percent:0.9will report optional arguments always used, and those used at least 90% of the time.dead_code_analyzer --nothing -On percent:0.9will report optional arguments never used, and those discarded at least 90% of the time.
Using the both:<int>,<float> argument will activate the reports for optional
arguments that are always/never used, with at most <int> number of exceptions
and at least the provided percent of the time.
Example : dead_code_analyzer --nothing -Oa both:1,0.9 will report optional
arguments always used, and those unused at most once but used at least 90% of
the time.
Tip
Using 0 for <float> provides the same effect as the threshold:<int>
argument for the default reports.
Example : dead_code_analyzer --nothing -Oa both:1,0 will report optional
arguments always used, and those unused at most once.
Similarly to the default reports, it is possible to get the callsites of the
exceptions. This is done by prefixing the threshold argument with calls:.
Example : dead_code_analyzer --nothing -Oa calls:both:1,0 will report optional
arguments always used, and those unused at most once with the locations where
they are discarded.