Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Store hank output in json file #184

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,19 @@ $ rebar3 help hank

Usage: rebar3 hank [-u <unused_ignores>]

-u, --unused_ignores Warn on unused ignores (default: true).
Usage: rebar3 hank [-u <unused_ignores>] [-o <output_json_file>]

-u, --unused_ignores Warn on unused ignores (default: true).
-o, --output_json_file Emit output (in JSON format) to a file (default: empty string - meaning: donot emit output
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
-o, --output_json_file Emit output (in JSON format) to a file (default: empty string - meaning: donot emit output
-o, --output_json_file Emit output (in JSON format) to a file (default: empty string - meaning: do not emit output).


```
By default, Hank will emit warnings such as the following ones if you are ignoring rules that you don't need to ignore (more on that below). But you can turn those warnings off, by using `--unused_ignores=no`.

It's worth noting that, even when those warnings are printed, that doesn't affect the overall result of the command. That is, if Hank can't find any instances of oxbow code, it will return successfully (i.e. `exit code: 0`) even when it may print these warnings.

By default, if Hank detect issues in your code, it will print those issues on the console. But you can save this result in a file (in JSON format),
by using `--output_json_file=output_filename.json`.

```markdown
===> The following ignore specs are no longer needed and can be removed:
* src/ignore_not_empty.erl: unused_hrls
Expand Down
3 changes: 2 additions & 1 deletion rebar.config
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
{rebar3_format, "~> 1.2.0"},
{rebar3_lint, "~> 1.1.0"},
{rebar3_sheldon, "~> 0.4.2"},
{rebar3_ex_doc, "~> 0.2.11"}]}.
{rebar3_ex_doc, "~> 0.2.11"},
{jsx, "~> 3.1.0"}]}.

{dialyzer, [{warnings, [no_return, unmatched_returns, error_handling, underspecs]}]}.

Expand Down
31 changes: 31 additions & 0 deletions src/hank_rule.erl
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
-export([default_rules/0]).
-export([analyze/3]).
-export([is_ignored/3]).
-export([result_to_json/1]).

%% @doc The list of default rules to apply
-spec default_rules() -> [].
Expand Down Expand Up @@ -47,3 +48,33 @@ analyze(Rule, ASTs, Context) ->
-spec is_ignored(t(), ignore_pattern(), all | term()) -> boolean().
is_ignored(Rule, Pattern, IgnoreSpec) ->
IgnoreSpec =:= all orelse Rule:ignored(Pattern, IgnoreSpec).

-spec result_to_json(hank_rule:result()) -> map().
result_to_json(Data) ->
#{file := FileName, line := Line, rule := RuleBroken, text := Description} = Data,
#{<<"path">> => iolist_to_binary(FileName),
<<"start_line">> => Line,
<<"hank_rule_broken">> => atom_to_binary(RuleBroken),
<<"title">> => compute_title(RuleBroken),
<<"message">> => iolist_to_binary(Description)}.

-spec compute_title(atom()) -> binary().
compute_title(RuleBroken) ->
case RuleBroken of
unused_macros ->
<<"Unused Macros">>;
single_use_hrl_attrs ->
<<"Macro only used once">>;
unused_record_fields ->
<<"Unused field in record">>;
unused_hrls ->
<<"Unused hrl files">>;
unused_configuration_options ->
<<"Unused config">>;
unused_callbacks ->
<<"Unused callback functions">>;
unnecessary_function_arguments ->
<<"Unused function arguments">>;
single_use_hrls ->
<<"Hrl file only used once">>
end.
31 changes: 30 additions & 1 deletion src/rebar3_hank_prv.erl
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,13 @@ opts() ->
$u,
"unused_ignores",
boolean,
"Warn on unused ignores (default: true)."}].
"Warn on unused ignores (default: true)."},
{output_json_file,
$o,
"output_json_file",
string,
"Emit output (in JSON format) to a file (default: empty string, meaning: do not emit output"}
].

%% @private
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, iodata()}.
Expand Down Expand Up @@ -69,6 +75,7 @@ do(State) ->
unused_ignores := UnusedIgnores,
stats := Stats} ->
instrument(Stats, UnusedIgnores, State),
maybe_write_data_to_json_file(Results, State),
{error, format_results(Results)}
catch
Kind:Error:Stack ->
Expand Down Expand Up @@ -120,6 +127,28 @@ format_result(#{file := File,
text := Msg}) ->
hank_utils:format_text("~ts:~tp: ~ts", [File, Line, Msg]).

-spec maybe_write_data_to_json_file([hank_rule:result()], rebar_state:t()) -> ok.
maybe_write_data_to_json_file(Result, State) ->
{Args, _} = rebar_state:command_parsed_args(State),
ConvertedResult = convert_data_to_binary(Result),
EncodedResult = jsx:encode(ConvertedResult),
Comment on lines +133 to +134
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not terribly wrong, but I would prefer to only do this if we're going to use it (i.e. First try to come up with the name of the output file, if it's not empty,... Only then convert, encode, and emit the output).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And please document the rebar.config option in the README.md

case lists:keyfind(output_json_file, 1, Args) of
{output_json_file, JsonFilePath} ->
ok = file:write_file(JsonFilePath, EncodedResult);
_ ->
JsonFilePassed = proplists:get_value(output_json_file, rebar_state:get(State, hank, []), []),
case JsonFilePassed of
[] ->
ok;
_ ->
ok = file:write_file(JsonFilePassed, EncodedResult)
end
end.

-spec convert_data_to_binary([hank_rules:result()]) -> list().
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function doesn't return binaries. Therefore, its name is wrong. A better name would be results_to_json.

convert_data_to_binary(Results) ->
lists:map(fun hank_rule:result_to_json/1, Results).

%% @private
%% @doc Determines files that should be fully hidden to Hank.
is_hidden(Filename) ->
Expand Down