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

Infer include directories #34

Merged
merged 12 commits into from
Jan 28, 2022
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