Reputation: 7712
I'm trying to use erl_lint() to build a simple Erlang syntax and style checker. I've gotten far enough to load the file and parse it into Forms and to get erl_lint to partially understand it, but then erl_lint complains about undefined functions that are defined. What am I doing wrong?
erlint.erl :
-module(erlint).
-export([lint/1]).
% based on http://stackoverflow.com/a/28086396/13675
lint(File) ->
{ok, B} = file:read_file(File),
Forms = scan(erl_scan:tokens([],binary_to_list(B),1),[]),
F = fun(X) -> {ok,Y} = erl_parse:parse_form(X), Y end,
erl_lint:module([F(X) || X <- Forms],File).
scan({done,{ok,T,N},S},Res) ->
scan(erl_scan:tokens([],S,N),[T|Res]);
scan(_,Res) ->
lists:reverse(Res).
hello.erl :
-module(hello).
-export([hello_world/0]).
hello_world() -> io:fwrite("hello, world\n").
attempt to use :
1> c(erlint).
{ok,erlint}
2> erlint:lint("hello.erl").
{error,[{"hello.erl",
[{2,erl_lint,{undefined_function,{hello_world,0}}}]}],
[]}
Upvotes: 2
Views: 428
Reputation: 20004
I'm not sure this approach fits with your overall plan, but you could instead compile the input file, extract its abstract forms from the resulting beam, and pass them to erl_lint
:
-module(erlint).
-export([lint/1]).
lint(File) ->
{ok,_,Bin} = compile:file(File,[debug_info,binary]),
{ok,{_,[{abstract_code,{_,AC}}]}} = beam_lib:chunks(Bin,[abstract_code]),
erl_lint:module(AC,File).
Let's change your hello.erl
to include an unused variable:
-module(hello).
-export([hello_world/0]).
hello_world() ->
X = io:fwrite("hello, world\n").
We see that this version of erlint:lint/1
correctly reports it:
1> erlint:lint("hello.erl").
{ok,[{"hello.erl",[{5,erl_lint,{unused_var,'X'}}]}]}
If you need them for your overall purposes, note that you can retrieve source code forms from the abstract forms variable AC
by calling erl_syntax:form_list(AC)
.
Upvotes: 2