Skip to content

Commit

Permalink
Infer include directories (#34)
Browse files Browse the repository at this point in the history
* Infer include directories

* Parse related option in typer_core

* Fix failing test case

* Update typer_core just a bit

* Typo

* Fix some tests that were not actually running /cc @maco

* Always add includes, regardless of --files and --recursive

* The conjuring is back! 🧙

* Include subfolders

* Add more conjurings

* Add typer_core tests

Co-authored-by: Brujo Benavides <elbrujohalcon@gmail.com>
  • Loading branch information
Pablo Costas Sánchez and elbrujohalcon authored Jan 28, 2022
1 parent 1da2cc4 commit a77f331
Show file tree
Hide file tree
Showing 12 changed files with 146 additions and 16 deletions.
61 changes: 60 additions & 1 deletion src/rebar3_typer_prv.erl
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,10 @@ split_string(String) ->
%% if they're not set in either place.
-spec ensure_defaults(map(), rebar_state:t()) -> typer_core:opts().
ensure_defaults(Opts, State) ->
default_plt(default_src_dirs(default_io(default_mode_show(Opts)), State), State).
default_plt(default_include_dirs(default_src_dirs(default_io(default_mode_show(Opts)),
State),
State),
State).

-spec default_io(typer_core:opts()) -> typer_core:opts().
default_io(Opts) ->
Expand Down Expand Up @@ -191,6 +194,62 @@ dir_for_app(AppInfo) ->
rebar_dir:make_relative_path(
filename:join(Dir, "src"), rebar_dir:get_cwd()).

default_include_dirs(Opts, State) ->
case include_dirs_from_app_discovery(State) of
[] ->
Opts;
IncludeDirs ->
Opts#{includes => IncludeDirs}
end.

include_dirs_from_app_discovery(State) ->
ProjectIncludeDirs =
[IncludeDir
|| AppInfo <- rebar_state:project_apps(State), IncludeDir <- include_dirs(AppInfo)],
Profiles =
lists:reverse(
rebar_state:current_profiles(State)),
DepIncludeDirs =
[IncludeDir
|| Profile <- Profiles,
Dep <- rebar_state:get(State, {parsed_deps, Profile}, []),
IncludeDir <- include_dirs(Dep)],
DepIncludeDirs ++ ProjectIncludeDirs.

%% straight ot ouf rebar_compiler_erl
include_dirs(AppInfo) ->
OutDir = rebar_app_info:dir(AppInfo),
RebarOpts = rebar_app_info:opts(AppInfo),
ErlOpts = rebar_opts:erl_opts(RebarOpts),
ErlOptIncludes = proplists:get_all_values(i, ErlOpts),
% standard include path
% ++ includes from erl_opts
% ++ dep includes
% ++ top-level dir for legacy stuff
[filename:join(OutDir, "include") | lists:map(fun(Incl) -> filename:absname(Incl) end,
ErlOptIncludes)]
++ lists:append([find_recursive_incl(OutDir, Src, RebarOpts)
|| Src <- rebar_dir:all_src_dirs(RebarOpts, ["src"], [])])
++ [OutDir].

find_recursive_incl(Base, Src, Opts) ->
find_recursive_incl(Base, Src, Opts, rebar_dir:recursive(Opts, Src)).

find_recursive_incl(Base, Src, _Opts, false) ->
[filename:join(Base, Src)];
find_recursive_incl(Base, Src, Opts, true) ->
Dir = filename:join(Base, Src),
case file:list_dir(Dir) of
{error, _} ->
[Dir];
{ok, Files} ->
[Dir | [SubDir
|| File <- Files,
filelib:is_dir(
filename:join(Dir, File)),
SubDir <- find_recursive_incl(Dir, File, Opts)]]
end.

%% @todo consider adding shorthand versions to some (or all) options,
%% even if it doesn't exist on TypEr itself
opts() ->
Expand Down
9 changes: 4 additions & 5 deletions src/typer_core.erl
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@
files => [file:filename()],
files_r => [file:filename_all()],
macros => [{atom(), term()}],
includes => [file:filename_all()],
io => io()}.

-export_type([opts/0]).
-export_type([mode/0, opts/0]).

-record(analysis,
{mode :: mode() | undefined,
Expand Down Expand Up @@ -643,16 +644,14 @@ analyze_result({trusted, Val}, Args, Analysis) ->
{Args#args{trusted = NewVal}, Analysis};
analyze_result({edoc, Value}, Args, Analysis) ->
{Args, Analysis#analysis{edoc = Value}};
%% Get useful information for actual analysis
analyze_result({io, Val}, Args, Analysis) ->
{Args, Analysis#analysis{io = Val}};
analyze_result({mode, Mode}, Args, Analysis) ->
{Args, Analysis#analysis{mode = Mode}};
analyze_result({macros, Macros}, Args, Analysis) ->
{Args, Analysis#analysis{macros = Macros}};
analyze_result({inc, Val}, Args, Analysis) ->
NewVal = Analysis#analysis.includes ++ [Val],
{Args, Analysis#analysis{includes = NewVal}};
analyze_result({includes, Includes}, Args, Analysis) ->
{Args, Analysis#analysis{includes = Includes}};
analyze_result({plt, Plt}, Args, Analysis) ->
{Args, Analysis#analysis{plt = Plt}};
analyze_result({show_succ, Value}, Args, Analysis) ->
Expand Down
3 changes: 3 additions & 0 deletions test/files/dummy/include/inc.hrl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-hank ignore.

-define(FRUITS, [bananas, apples]).
2 changes: 2 additions & 0 deletions test/files/dummy/other_include/inc2.hrl
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-hank ignore.
-define(MORE, [more, things]).
2 changes: 1 addition & 1 deletion test/files/dummy/rebar.config
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{erl_opts, [debug_info]}.
{erl_opts, [debug_info, {i, "other_include"}]}.

{deps, []}.

9 changes: 6 additions & 3 deletions test/files/dummy/src/dummy.erl
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
-module(dummy).

-compile([export_all, nowarn_export_all]).
-include("inc.hrl").
-include("inc2.hrl").

a(L) ->
L ++ [1, 2, 3].
-export([a/0]).

a() ->
?FRUITS ++ ?MORE.
4 changes: 4 additions & 0 deletions test/files/subs/rebar.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{erl_opts, [debug_info]}.

{deps, []}.

3 changes: 3 additions & 0 deletions test/files/subs/src/include/inc.hrl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-hank ignore.

-define(FRUITS, [bananas, apples]).
1 change: 1 addition & 0 deletions test/files/subs/src/subs.app.src
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{application, subs, [{vsn, "0"}]}.
8 changes: 8 additions & 0 deletions test/files/subs/src/subs.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-module(subs).

-include("inc.hrl").

-compile([export_all, nowarn_export_all]).

a(L) ->
L ++ [1, 2, 3] ++ ?FRUITS.
39 changes: 36 additions & 3 deletions test/rebar3_typer_prv_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
-behaviour(ct_suite).

-export([all/0, init_per_testcase/2, end_per_testcase/2]).
-export([no_options/1, recursive/1, files/1, good_modes/1, colliding_modes/1,
-export([no_options/1, recursive/1, includes/1, files/1, good_modes/1, colliding_modes/1,
show_success_typings/1, no_spec/1, edoc/1, plt/1, typespec_files/1, unrecognized_opt/1,
format_error/1]).

all() ->
[no_options,
recursive,
includes,
files,
good_modes,
colliding_modes,
show_success_typings,
Expand Down Expand Up @@ -92,6 +94,37 @@ recursive(_Config) ->

{comment, ""}.

%% @doc Proper include folder discovery
%% @todo Add tests for includes in rebar deps
includes(_Config) ->
ct:comment("Regular include folder and erl_opts are correctly picked up"),
{includes, Paths} = lists:keyfind(includes, 1, get_opts_from("dummy")),
["ymmud/selif/" ++ _,
"edulcni/ymmud/selif/" ++ _,
"edulcni_rehto/ymmud/selif/" ++ _,
"crs/ymmud/selif/" ++ _] =
[lists:reverse(Path) || Path <- lists:sort(Paths)],

ct:comment("Includes in subdirs are correctly picked up"),
{includes, SubPaths} = lists:keyfind(includes, 1, get_opts_from("subs")),
["sbus/selif/" ++ _,
"edulcni/sbus/selif/" ++ _,
"crs/sbus/selif/" ++ _,
"edulcni/crs/sbus/selif/" ++ _] =
[lists:reverse(Path) || Path <- lists:sort(SubPaths)],

ct:comment("Includes in umbrella projects are correctly picked up"),
{includes, UmbPaths} = lists:keyfind(includes, 1, get_opts_from("umbrella")),
["1ppa/sppa/allerbmu/selif/" ++ _,
"edulcni/1ppa/sppa/allerbmu/selif/" ++ _,
"crs/1ppa/sppa/allerbmu/selif/" ++ _,
"2ppa/sppa/allerbmu/selif/" ++ _,
"edulcni/2ppa/sppa/allerbmu/selif/" ++ _,
"crs/2ppa/sppa/allerbmu/selif/" ++ _] =
[lists:reverse(Path) || Path <- lists:sort(UmbPaths)],

{comment, ""}.

%% @doc --files / files
files(_Config) ->
{ok, State} =
Expand All @@ -104,15 +137,15 @@ files(_Config) ->
{files, Files} = lists:keyfind(files, 1, get_opts(State1)),

ct:comment("files prevents default files_r"),
{files_r, []} = lists:keyfind(files_r, 1, get_opts(State1)),
false = lists:keyfind(files_r, 1, get_opts(State1)),

ct:comment("--files takes precedence over rebar.config"),
State2 =
rebar_state:command_parsed_args(State1, {[{files, "files/single_file/single.erl"}], []}),
{files, ["files/single_file/single.erl"]} = lists:keyfind(files, 1, get_opts(State2)),

ct:comment("--files prevents default files_r"),
{files_r, []} = lists:keyfind(files_r, 1, get_opts(State2)),
false = lists:keyfind(files_r, 1, get_opts(State2)),

{comment, ""}.

Expand Down
21 changes: 18 additions & 3 deletions test/typer_core_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

-export([all/0]).
-export([empty/1, bad_plt/1, single_file/1, annotate/1, annotate_in_place/1, trusted/1,
show_succ/1, files/1, def/1, no_spec/1, ann_erl/1]).
show_succ/1, files/1, macros/1, includes/1, no_spec/1, ann_erl/1]).

all() ->
[empty,
Expand All @@ -16,7 +16,8 @@ all() ->
trusted,
show_succ,
files,
def,
macros,
includes,
no_spec,
ann_erl].

Expand Down Expand Up @@ -143,7 +144,7 @@ show_succ(_) ->
run_typer(#{files_r => [abs_test_path("show_succ")], show_succ => true}),
{comment, ""}.

def(_) ->
macros(_) ->
ct:comment("Without a macro definition.. we get an error"),
[{abort, <<"typer: Analysis failed with error report:", _/binary>>}] =
run_typer(#{files_r => [abs_test_path("def")]}),
Expand All @@ -155,6 +156,20 @@ def(_) ->
run_typer(#{files_r => [abs_test_path("def")], macros => [{'DEF', d1}]}),
{comment, ""}.

includes(_) ->
ct:comment("Without includes definition.. we get an error"),
[{abort, <<"typer: Analysis failed with error report:", _/binary>>}] =
run_typer(#{files_r => [abs_test_path("dummy/src/")]}),

ct:comment("With a single module... we get its types"),
[{info, <<"\n%% File", _/binary>>},
{info, <<"%% ----", _/binary>>},
{info, <<"-spec a() -> ['apples' | 'bananas' | 'more' | 'things',...].">>}] =
run_typer(#{files_r => [abs_test_path("dummy/src/")],
includes =>
[abs_test_path("dummy/include"), abs_test_path("dummy/other_include")]}),
{comment, ""}.

no_spec(_) ->
ct:comment("Show original spec if false"),
[{info, <<"\n%% File", _/binary>>},
Expand Down

0 comments on commit a77f331

Please sign in to comment.