-module(guards).

-compile([export_all]).

-record(r, {f}).

%% This is the reduced original test (no warnings)

-define(g1(A), ((A#r.f =:= a) orelse (A#r.f =:= b))).

t1(A) when     ?g1(A) -> ok;
t1(A) when not ?g1(A) -> ko.

%% This should emit a warning

t1_s(A) when ?g1(A) -> ok.

t1_s_a() ->
    t1_s(#r{f=c}).

%% Same as t1 with 'or' instead of 'orelse' (no warnings)

-define(g2(A), ((A#r.f =:= a) or (A#r.f =:= b))).

t2(A) when     ?g2(A) -> ok;
t2(A) when not ?g2(A) -> ko.

%% This should emit a warning

t2_s(A) when ?g2(A) -> ok.

t2_s_a() ->
    t2_s(#r{f=c}).

%% This could probably give a warning

-define(g3(A), (A#r.f =:= a orelse is_atom(A))).

t3(A) when     ?g3(A) -> ok;
t3(A) when not ?g3(A) -> ko.

%% This could probably give a warning as well

-define(g4(A), ((A#r.f =:= a) or is_atom(A))).

t4(A) when     ?g4(A) -> ok;
t4(A) when not ?g4(A) -> ko.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Some shots in the dark on guard abuse %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Should give a warning

t5(A) when is_atom(A) and is_integer(A) -> never.

%% Should give the same warning as t5

t6(A) when is_atom(A) andalso is_integer(A) -> never.

%% Should give a warning

t7(A) when is_atom(A) or is_integer(A) -> ok.

at7(42) -> t7(42);
at7('a') -> t7('a');
at7({42}) -> t7({42}).

%% Should give a warning

t8(A) when is_atom(A) orelse is_integer(A) -> ok.

at8(42) -> t8(42);
at8('a') -> t8('a');
at8({42}) -> t8({42}).

%% Should give a warning

t9(A) when is_atom(A) orelse is_integer(A) -> ok;
t9(A) when is_atom(A) -> redundant.

%% Should give a warning

t10(A) when is_atom(A) or is_integer(A) -> ok;
t10(A) when is_atom(A) -> redundant.

%% Should give a warning

t11(A, B) when is_atom(A) and is_atom(B) ->
    case {is_atom(A), is_atom(B)} of
	{true, true} -> ok;
	_ -> redundant
    end.

%% Should give a warning

t12(A, B) when is_atom(A) andalso is_atom(B) ->
    case {is_atom(A), is_atom(B)} of
	{true, true} -> ok;
	_ -> redundant
    end.

%% Should give two warnings

t13(A, B) when is_atom(A) or is_atom(B) ->
    case {is_atom(A), is_atom(B)} of
	{true , true } -> ok;
	{true , false} -> ok;
	{false, true } -> ok;
	{false, false} -> never;
	{_    , _    } -> even_more_never
    end.

%% Should give two warnings

t14(A, B) when is_atom(A) orelse is_atom(B) ->
    case {is_atom(A), is_atom(B)} of
	{true , true } -> ok;
	{true , false} -> ok;
	{false, true } -> ok;
	{false, false} -> never;
	{_    , _    } -> even_more_never
    end.

%% Should give two warnings

t15(A) when ((A =:= a) or (A =:= b)) and ((A =:= b) or (A =:= c)) -> ok.

t15_a() -> t15(a), t15(b), t15(c).

%% Should give two warnings

t16(A) when ((A =:= a) orelse (A =:= b)) andalso
	    ((A =:= b) orelse (A =:= c)) -> ok.

t16_a() -> t16(a), t16(b), t16(c).

%% Should give a warning

t17(X) when #{x => X} ->
    ok.

%% Should give a warning

t18(X) when <<X>> ->
    ok.

%% Coverage

cover1() when 1 < 2; 1 > 2 ->
    ok.

cover2() when is_atom(1 < 2); is_atom(1 > 2) ->
    ok.

cover3() when not(1 < 2); not(1 > 2) ->
    ok.
