2020-03-08 18:41:43 +03:00
|
|
|
# Weeder
|
|
|
|
|
|
|
|
Weeder is an application to perform whole-program dead-code analysis. Dead code
|
|
|
|
is code that is written, but never reachable from any other code. Over the
|
|
|
|
lifetime of a project, this happens as code is added and removed, and leftover
|
|
|
|
code is never cleaned up. While GHC has warnings to detect dead code is a single
|
|
|
|
module, these warnings don't extend across module boundaries - this is where
|
|
|
|
Weeder comes in.
|
|
|
|
|
|
|
|
Weeder uses HIE files produced by GHC - these files can be thought of as source
|
|
|
|
code that has been enhanced by GHC, adding full symbol resolution and type
|
|
|
|
information. Weeder builds a dependency graph from these files to understand how
|
|
|
|
code interacts. Once all analysis is done, Weeder performs a traversal of this
|
|
|
|
graph from a set of roots (e.g., your `main` function), and determines which
|
|
|
|
code is reachable and which code is dead.
|
|
|
|
|
|
|
|
# Using Weeder
|
|
|
|
|
|
|
|
## Preparing Your Code for Weeder
|
|
|
|
|
2020-03-16 23:58:48 +03:00
|
|
|
To use Weeder, you will need to generate `.hie` files from your source code.
|
|
|
|
|
|
|
|
### Cabal
|
|
|
|
|
|
|
|
If you use Cabal, this is easily done by adding one line to your
|
2020-03-08 18:41:43 +03:00
|
|
|
`cabal.project.local` file:
|
|
|
|
|
|
|
|
``` cabal
|
|
|
|
package *
|
|
|
|
ghc-options: -fwrite-ide-info
|
|
|
|
```
|
|
|
|
|
|
|
|
Once this has been added, perform a full rebuild of your project:
|
|
|
|
|
|
|
|
``` shell
|
|
|
|
cabal clean
|
|
|
|
cabal build all
|
|
|
|
```
|
|
|
|
|
2020-03-16 23:58:48 +03:00
|
|
|
### Stack
|
|
|
|
|
|
|
|
If you use `stack`, add the following to your `stack.yaml`:
|
|
|
|
|
|
|
|
``` yaml
|
|
|
|
ghc-options:
|
|
|
|
"$locals": -fwrite-ide-info
|
|
|
|
```
|
|
|
|
|
|
|
|
and rebuild:
|
|
|
|
|
|
|
|
``` shell
|
|
|
|
stack clean
|
|
|
|
stack build
|
|
|
|
```
|
|
|
|
|
2020-03-08 18:41:43 +03:00
|
|
|
## Calling Weeder
|
|
|
|
|
2020-04-01 15:41:09 +03:00
|
|
|
To call Weeder, you first need to provide a configuration file, `weeder.dhall`. Weeder uses
|
2020-03-15 18:05:19 +03:00
|
|
|
[Dhall](https://dhall-lang.org) as its configuration format, and configuration
|
|
|
|
files have the type:
|
2020-03-08 18:41:43 +03:00
|
|
|
|
2020-03-15 18:05:19 +03:00
|
|
|
``` dhall
|
|
|
|
{ roots : List Text, type-class-roots : Bool }
|
|
|
|
```
|
|
|
|
|
|
|
|
`roots` is a list of regular expressions of symbols that are considered as
|
|
|
|
alive. If you're building an executable, the pattern `^Main.main$` is a
|
2020-04-10 15:35:17 +03:00
|
|
|
good starting point - specifying that `main` is a root. Weeder currently doesn't
|
2021-05-17 11:12:21 +03:00
|
|
|
add all exported functions as roots automatically but in many cases `main` from a
|
2020-04-10 15:35:17 +03:00
|
|
|
test suite could be a good workaround for that
|
2020-03-15 18:05:19 +03:00
|
|
|
|
|
|
|
`type-class-roots` configures whether or not Weeder should consider anything in
|
|
|
|
a type class instance as a root. Weeder is currently unable to add dependency
|
|
|
|
edges into type class instances, and without this flag may produce false
|
|
|
|
positives. It's recommended to initially set this to `True`:
|
|
|
|
|
|
|
|
``` dhall
|
|
|
|
{ roots = [ "^Main.main$" ], type-class-roots = True }
|
2020-03-08 18:41:43 +03:00
|
|
|
```
|
|
|
|
|
2020-03-15 18:05:19 +03:00
|
|
|
Now invoke the `weeder` executable, and - if your project has weeds - you will
|
|
|
|
see something like the following:
|
|
|
|
|
|
|
|
``` shell
|
|
|
|
$ weeder
|
2021-10-11 12:12:18 +03:00
|
|
|
src/Dhall/TH.hs:187: toDeclaration
|
|
|
|
src/Dhall/TH.hs:196: toNestedHaskellType
|
2020-03-15 18:05:19 +03:00
|
|
|
```
|
|
|
|
|
2021-10-11 12:12:18 +03:00
|
|
|
… which indicates the location of two unused symbols.
|
2020-03-15 18:05:19 +03:00
|
|
|
(Please note these warnings are just for demonstration and not necessarily weeds
|
|
|
|
in the Dhall project).
|
2020-03-08 18:41:43 +03:00
|
|
|
|
2020-04-02 17:26:13 +03:00
|
|
|
# Tips
|
|
|
|
|
|
|
|
- You may want to add `^Paths_.*` to the roots in `weeder.dhall` to ignore the
|
|
|
|
`Paths_packageName` module automatically generated by Cabal.
|
|
|
|
|
2020-03-08 18:41:43 +03:00
|
|
|
# Limitations
|
|
|
|
|
|
|
|
Weeder currently has a few limitations:
|
|
|
|
|
|
|
|
## Type Class Instances
|
|
|
|
|
|
|
|
Weeder is not currently able to analyse whether a type class instance is used.
|
|
|
|
For this reason, Weeder adds all symbols referenced to from a type class
|
|
|
|
instance to the root set, keeping this code alive. In short, this means Weeder
|
|
|
|
might not detect dead code if it's used from a type class instance which is
|
|
|
|
never actually needed.
|
|
|
|
|
2020-03-15 18:05:19 +03:00
|
|
|
You can toggle whether Weeder consider type class instances as roots with the
|
|
|
|
`type-class-roots` configuration option.
|
|
|
|
|
2020-03-08 18:41:43 +03:00
|
|
|
## Template Haskell
|
|
|
|
|
|
|
|
Weeder is currently unable to parse the result of a Template Haskell splice. If
|
|
|
|
some Template Haskell code refers to other source code, this dependency won't be
|
|
|
|
tracked by Weeder, and thus Weeder might end up with false positives.
|