# rebar3_hank [![Build Status](]( [![Hex pm](](
### The Erlang Dead Code Cleaner

<img src="" width="300" alt="Kill it with Fire" />

> Mr. Scorpio says productivity is up 2%, and it's all because of my motivational techniques, like donuts and the possibility of more donuts to come.
> _(Homer Simpson)_

## Sample Output

If Hank detects issues in your code, it will report them as follows…

src/lapp.erl:18: maybe_evaluate/3 doesn't need its #2 argument
src/lapp.erl:15: maybe_evaluate/2 doesn't need its #1 argument
src/lapp.erl:5: ?DEFAULT_SAMPLE_RATE is unused
src/ sample_rate is not used anywhere in the code
src/include/header.hrl:2: ?SOME_MACRO/1 is used only at src/lapp.erl
src/my_behaviour.erl:3: Callback process/1 is not used anywhere in the module
src/include/header.hrl:1: ?APP_HEADER is unused
src/main.erl:8: Field color in record state is unused

## Build

$ rebar3 compile

## Test

$ rebar3 test

## Usage

Add the plugin to your rebar config:

{project_plugins, [rebar3_hank]}

Then just call the plugin directly in an existing application:

$ rebar3 hank # or…
$ rebar3 kiwf # (Kill It With Fire)

This will review your project, analyzing every `*.[he]rl` file in it (optionally skipping some folders/files if you want to - see below).
Note that Hank will **not** consider files from your project dependencies for the analysis. It will only check the source code in your current application (_applications_, if you're working in an umbrella project).

It will then apply its rules and produce a list of all the dead code (_specially **oxbow** code_) that you can effectively delete and/or refactor.

## Certainty

In principle, Hank should have _Dialyzer_ levels of certainty. That is: if Hank points at some code, you can be **100% sure** it's _dead_.

That means that if you find some false positives (i.e. Hank pointed at some code that was not really dead / should not be deleted), **please** report it as **a bug** and we'll take care of fixing it.

## Configuration

The plugin supports the following configuration options in the `hank` section of `rebar.config`:

* `rules` (`[hank_rule:t()]`):
    - This is the list of rules to apply to the analyzed code. Each rule is a module that should apply the `hank_rule` behavior.
    - If this option is not defined, Hank will apply all [the default rules](src/rules).
* `parsing_style` (`hank:parsing_style()`):
    - This parameter determines if Hank should parse files in a parallel (`rpc:pmap/3`) or sequential (`lists:map/2`) fashion.
    - The default value is `parallel` since it's faster.
    - It's recommended to use `sequential` when reporting bugs since the error descriptions are usually more detailed.
* `ignore` (`[file:filename_all() | {file:filename_all(), hank_rule:t() | [hank_rule:t()]} | {file:filename_all(), hank_rule:t() | [hank_rule:t()], list()}]`):
    - List of wildcard patterns representing the files and rules that Hank will ignore when formatting. Tuple format is used to ignore either a specific rule or a set of rules in those files.
  % single rule
   [{ignore, [
    {"test/**/*.erl", unnecessary_function_arguments}
  % set of rules (after expansion, the same wildcard and options are used for all rules)
   [{ignore, [
    {"test/**/*.erl", [unused_macros, unnecessary_function_arguments]}
    - For ignoring options for `unused_configuration_options` rule, set a list of keys from the .config files you want to ignore.
   [{ignore, [
    {"sys.config", unused_configuration_options, [not_used_but_ignored]}
    - You can also ignore a specific file adding the attribute `-hank ignore.` to it.
    - And you can ignore specific rules adding the attribute `-hank [hank_rule:t()].` with the list of rules you want to ignore.

### Ignore specific rule items
You can even ignore specific rule items with the `-hank` attribute by giving extra _ignore specifications_ for each rule, example:
-hank([single_use_hrls, %% Will ignore the whole rule within the module
          ["ALL", %% Will ignore ?ALL, ?ALL() and ?ALL(X)
           {"ZERO", 0}, %% Will ignore ?ZERO() but not ?ZERO(X) nor ?ZERO
           {"ONE",  1}, %% Will ignore ?ONE(X) but not ?ONE()   nor ?ONE
           {"NONE", none} %% Will ignore ?NONE but not ?NONE(X) nor ?NONE()
           [a_record, %% Will ignore all fields in #a_record
            {a_record, a_field} %% Will ignore #a_record.a_field
           [all, %% Will ignore all versions of the all callback (i.e. any arity)
            {just, 1} %% Will ignore just(term()) but not just() nor just(_, _) callbacks
           [option, %% Will ignore any appearance of option
            {"this_file.config", option} %% Will ignore option if it appears in "this_file.config"
           ["ALL",          %% Will ignore ?ALL, ?ALL() and ?ALL(X)
            {"ZERO", 0},    %% Will ignore ?ZERO() but not ?ZERO(X) nor ?ZERO
            {"ONE",  1},    %% Will ignore ?ONE(X) but not ?ONE()   nor ?ONE
            {"NONE", none}, %% Will ignore ?NONE   but not ?NONE(X) nor ?NONE()
            record_name     %% Will ignore #record_name
           [{ignore_me, 2}, %% Will ignore any unused argument from ignore_me/2
            {ignore_me_too, 3, 2}, %% Will ignore the 2nd argument from ignore_me_too/3
            ignore_me_again]}]). %% Will ignore any unused argument from any `ignore_me_again/x` within the module (no matter the arity)
Refer to each rule documentation for further details.

## Rules

Find detailed information about the rules provided by Hank in [hex docs](

## Full Example

[**@elbrujohalcon**]( presented Hank in a lightning talk at CodeBEAM V SF 2021. Watch his talk where he shows an example of using Hank in an iterative process:

[![Hank @ CodeBEAM V SF 2021](]( "Hank @ CodeBEAM V SF 2021")