- Notifications
You must be signed in to change notification settings - Fork20
Experimental analyses for ReScript and OCaml: globally dead values/types, exception analysis, and termination analysis.
License
rescript-lang/reanalyze
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
Warning
This repository is only for keeping OCaml compatibility with an old version of reanalyze. It is further developed in therescript monorepo.
Program analysis for ReScript and OCaml projects targeting JS (ReScript) as well as native code (dune):
- Globally dead values, redundant optional arguments, dead modules, dead types (records and variants).
- Exception analysis.
- Termination.
Early release. While the core functionality is reasonably stable, the CLI and annotations are subject to change. However, this is a tiny surface at the moment.
The rest of this document describes the dead code analysis.For theException Analysis, build instructions are the same, and the command-line invocation is different.
Build and run on existing projects using the Build and Try instructions below. The analysis uses.cmt[i] files which are generated during compilation, so should be runafter building your project. Remember to rebuild the project before running again.
# dead code analysisreanalyze.exe -dce# exception analysisreanalyze.exe -exception
The requirement is thatbsconfig.json can be found by walking up the current directory.
# dead code analysisreanalyze.exe -dce-cmt root/containing/cmt/files# exception analysisreanalyze.exe -exception-cmt root/containing/cmt/files
Subdirectories are scanned recursively looking for.cmt[i] files.
The requirement is that thecurrent directory is where file paths start from. So if the file path seen by the compiler is relativesrc/core/version.ml then the current directory should containsrc as a subdirectory. The analysis only reports on existing files, so getting this wrong means no reporting.
The dead code analysis reports on globally dead values, redundant optional arguments, dead modules, dead types (records and variants).
A valuex is dead if it is never used, or if it is used by a value which itself is dead (transitivity). At the top level, function calls such asJs.log(x), or other expressions that might cause side effects, keep valuex live.
An optional argument~argName to a function is redundant if all the calls to the function supply the argument, or if no call does.
A module is considered dead if all the elements defined it in are dead.
The type analysis repots on variant cases, and record labels.
A variant case
| A(int)is dead if a value such asA(3)is never constructed. But it can be deconstructed via pattern matching| A(n) => ...or checked for equalityx == A(3)without making the caseAlive.A record label
xintype r = {x:int, y:int}is dead if it is never read (by direct accessr.xor pattern matching| {x:n, y:m} => ...). However, creating a valuelet r = {x:3, y:4}does not makexandylive.Note that reading a valuerdoes not maker.xorr.ylive.
While dead values can be removed automatically (see below), dead types require a bit more work. A dead variant case requires changing the type definition, and the various accesses to it. A dead record label requires changing the type definition, and removing the label from any expressions that create a value of that type.
The dead code analysis supports 2 annotations:
@deadsuppresses reporting on the value/type, but can also be used to force the analysis to consider a value as dead. Typically used to acknowledge cases of dead code you are not planning to address right now, but can be searched easily later.@livetells the analysis that the value should be considered live, even though it might appear to be dead. This is typically used in case of FFI where there are indirect ways to access values. In case of bucklescript projects usinggenType, export annotations immediately qualify values as live, because they are potentially reachable from JS.
The main difference between@dead and@live is the transitive behaviour:@dead values don't keep alive values they use, while@live values do.
Several examples can be found inexamples/deadcode/src/DeadTest.res
Takes a comma-separated list of path-prefixes. Don't report on files whose path has a prefix in the list (but still use them for analysis).
reanalyze.exe -suppress one/path,another/path
Takes a comma-separated list of path-prefixes. Report on files whose path has a prefix in the list, overriding-suppress (no-op if-suppress is not specified).
reanalyze.exe -unsuppress one/path,another/path/File.res
Print debug information during the analysis
reanalyze.exe -debug ...
This overwrites your source files automatically with dead code annotations:
reanalyze.exe -write ...
There's a dead code ppx (values only, not types) in this repository. It can be used to automatically remove code annotated@dead, as if it had been commented out.Can be used after adding annotations automatically. The combination of automatic annotation and automatic elimination is a form of automatic dead code elimination. For projects that use a library, or that in general have code which is dead only temporarily.There's obviously a level of risk in doing this automatic elimination. The safety net you can rely on is that the code must still compile.
This automatically annotates@live all the items calledfoo orbar:
-live-names foo,bar
This automatically annotates@live all the items in fileHello.res:
-live-paths Hello.res
This automatically annotates@live all the items in thesrc/test andtmp folders:
-live-paths src/test,tmp
If a native project uses code generation and emit the generated files only in the build directory, reanalyze may not be able to locate them.This is due to the paths being not relative to the project root directory.An example of it can be caused by using tools likeocamlyacc.
For example, you might want to set_build/default for projects that use the default dune build target:
-native-build-target _build/default
The-config option can be used to read the configuration frombsconfig.json: toset what analyses should be run, as well assuppress andunsuppress configuration.
Example configuration insidebsconfig.json:
{"reanalyze": {"analysis": ["dce","exception"],"suppress": ["src/ToSuppress.res"],"unsuppress": ["this","that"] }}This is equivalent to adding-dce -exception -suppress src/ToSuppress.res -unsuppress this,that to the command line in place of-config. Note that the options are additive, so it's possible to use e.g.-config -exception to add exception analysis on top of what the configuration does.
npm add --save-dev reanalyzeopam install dunenpm run build406# _build/default/src/Reanalyze.exeopam install dunedune build# _build/default/src/Reanalyze.exenpm run build# or whatever command to build the projectnpm add --save-dev reanalyzenpx reanalyze -dceMake sure thatdune builds both.cmt and.cmti files by enabling bytecode compilation. This is normally done by adding(modes byte exe) to theexecutable stanza in your dune file (seeocaml/dune#3182):
This project is itself written in OCaml and can be analyzed as follows.
dune build./_build/default/src/Reanalyze.exe -suppress src/compiler-libs-406 -dce-cmt _build
About
Experimental analyses for ReScript and OCaml: globally dead values/types, exception analysis, and termination analysis.
Topics
Resources
License
Contributing
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.