[erl-frontend] Add support for guards

Summary: Add support for guards, both in function clauses and case clauses.

Reviewed By: rgrig

Differential Revision: D29634937

fbshipit-source-id: 5a9f8ec2d
master
Akos Hajdu 3 years ago committed by Facebook GitHub Bot
parent dbe39024bd
commit 88beede7dc

@ -348,6 +348,38 @@ let rec translate_pattern env (value : Ident.t) {Ast.line; simple_expression} :
Block.make_failure env
and translate_guard_expression env (expression : Ast.expression) : Ident.t * Block.t =
let id = Ident.create_fresh Ident.knormal in
let block = translate_expression {env with result= Present (Exp.Var id)} expression in
(* If we'd like to catch "silent" errors later, we might do it here *)
(id, block)
and translate_guard env (expressions : Ast.expression list) : Block.t =
match expressions with
| [] ->
Block.make_success env
| _ ->
let ids_blocks = List.map ~f:(translate_guard_expression env) expressions in
let ids, expr_blocks = List.unzip ids_blocks in
let make_and (e1 : Exp.t) (e2 : Exp.t) = Exp.BinOp (LAnd, e1, e2) in
let make_var (id : Ident.t) : Exp.t = Var id in
let cond = List.reduce_exn (List.map ids ~f:make_var) ~f:make_and in
let start = Node.make_nop env in
let exit_success = Node.make_if env true cond in
let exit_failure = Node.make_if env false cond in
start |~~> [exit_success; exit_failure] ;
Block.all env (expr_blocks @ [{Block.start; exit_success; exit_failure}])
and translate_guard_sequence env (guards : Ast.expression list list) : Block.t =
match guards with
| [] ->
Block.make_success env
| _ ->
Block.any env (List.map ~f:(translate_guard env) guards)
and translate_expression env {Ast.line; simple_expression} =
let env = update_location line env in
let any = ptr_typ_of_name Any in
@ -504,22 +536,20 @@ and translate_body env body : Block.t =
(** Assumes that the values on which patterns should be matched have been loaded into the
identifiers listed in [values]. *)
and translate_case_clause env (values : Ident.t list) {Ast.line= _; patterns; guards= _; body} :
and translate_case_clause env (values : Ident.t list) {Ast.line= _; patterns; guards; body} :
Block.t =
let matchers_block =
let f (one_value, one_pattern) = translate_pattern env one_value one_pattern in
let matchers = List.map ~f (List.zip_exn values patterns) in
Block.all env matchers
in
let f (one_value, one_pattern) = translate_pattern env one_value one_pattern in
let matchers = List.map ~f (List.zip_exn values patterns) in
let guard_block = translate_guard_sequence env guards in
let matchers_and_guards = Block.all env [Block.all env matchers; guard_block] in
let body_block = translate_body env body in
(* TODO: Evaluate the guards. *)
matchers_block.exit_success |~~> [body_block.start] ;
matchers_and_guards.exit_success |~~> [body_block.start] ;
let () =
let (Present procdesc) = env.procdesc in
body_block.exit_failure |~~> [Procdesc.get_exit_node procdesc]
in
{ start= matchers_block.start
; exit_failure= matchers_block.exit_failure
{ start= matchers_and_guards.start
; exit_failure= matchers_and_guards.exit_failure
; exit_success= body_block.exit_success }

@ -2,6 +2,10 @@ codetoanalyze/erlang/nonmatch/src/case_expression.erl, case_simple/1, 1, NONEXHA
codetoanalyze/erlang/nonmatch/src/case_expression.erl, case_test_simple3_Bad/0, -14, NONEXHAUSTIVE_PATTERN_MATCH, no_bucket, ERROR, [calling context starts here,in call to `case_simple/1`,no pattern match here]
codetoanalyze/erlang/nonmatch/src/case_expression.erl, case_test_tail3_Bad/0, -15, NONEXHAUSTIVE_PATTERN_MATCH, no_bucket, ERROR, [calling context starts here,in call to `tail_with_case/1`,no pattern match here]
codetoanalyze/erlang/nonmatch/src/case_expression.erl, tail_with_case/1, 1, NONEXHAUSTIVE_PATTERN_MATCH, no_bucket, ERROR, [*** LATENT ***,no pattern match here]
codetoanalyze/erlang/nonmatch/src/case_guards.erl, accepts_positive/1, 1, NONEXHAUSTIVE_PATTERN_MATCH, no_bucket, ERROR, [*** LATENT ***,no pattern match here]
codetoanalyze/erlang/nonmatch/src/case_guards.erl, accepts_positive2/1, 1, NONEXHAUSTIVE_PATTERN_MATCH, no_bucket, ERROR, [*** LATENT ***,no pattern match here]
codetoanalyze/erlang/nonmatch/src/case_guards.erl, test_accepts_positive2_Bad/0, -16, NONEXHAUSTIVE_PATTERN_MATCH, no_bucket, ERROR, [calling context starts here,in call to `accepts_positive2/1`,no pattern match here]
codetoanalyze/erlang/nonmatch/src/case_guards.erl, test_accepts_positive_Bad/0, -15, NONEXHAUSTIVE_PATTERN_MATCH, no_bucket, ERROR, [calling context starts here,in call to `accepts_positive/1`,no pattern match here]
codetoanalyze/erlang/nonmatch/src/function.erl, assert_empty/1, 0, NONEXHAUSTIVE_PATTERN_MATCH, no_bucket, ERROR, [*** LATENT ***,no pattern match here]
codetoanalyze/erlang/nonmatch/src/function.erl, assert_second_is_nil/1, 0, NONEXHAUSTIVE_PATTERN_MATCH, no_bucket, ERROR, [*** LATENT ***,no pattern match here]
codetoanalyze/erlang/nonmatch/src/function.erl, fp_list_match_test_secondnil1_Ok/0, -17, NONEXHAUSTIVE_PATTERN_MATCH, no_bucket, ERROR, [calling context starts here,in call to `assert_second_is_nil/1`,no pattern match here]
@ -11,6 +15,13 @@ codetoanalyze/erlang/nonmatch/src/function.erl, list_match_test_empty3_Bad/0, -1
codetoanalyze/erlang/nonmatch/src/function.erl, list_match_test_secondnil3_Bad/0, -22, NONEXHAUSTIVE_PATTERN_MATCH, no_bucket, ERROR, [calling context starts here,in call to `assert_second_is_nil/1`,no pattern match here]
codetoanalyze/erlang/nonmatch/src/function.erl, list_match_test_tail3_Bad/0, -8, NONEXHAUSTIVE_PATTERN_MATCH, no_bucket, ERROR, [calling context starts here,in call to `tail/1`,no pattern match here]
codetoanalyze/erlang/nonmatch/src/function.erl, tail/1, 0, NONEXHAUSTIVE_PATTERN_MATCH, no_bucket, ERROR, [*** LATENT ***,no pattern match here]
codetoanalyze/erlang/nonmatch/src/function_guards.erl, accepts_positive/1, 0, NONEXHAUSTIVE_PATTERN_MATCH, no_bucket, ERROR, [*** LATENT ***,no pattern match here]
codetoanalyze/erlang/nonmatch/src/function_guards.erl, accepts_positive2/1, 0, NONEXHAUSTIVE_PATTERN_MATCH, no_bucket, ERROR, [*** LATENT ***,no pattern match here]
codetoanalyze/erlang/nonmatch/src/function_guards.erl, fp_accepts_all_tricky3/1, 0, NONEXHAUSTIVE_PATTERN_MATCH, no_bucket, ERROR, [*** LATENT ***,no pattern match here]
codetoanalyze/erlang/nonmatch/src/function_guards.erl, possible_exception/1, 0, NONEXHAUSTIVE_PATTERN_MATCH, no_bucket, ERROR, [*** LATENT ***,no pattern match here]
codetoanalyze/erlang/nonmatch/src/function_guards.erl, test_accepts_positive2_Bad/0, -23, NONEXHAUSTIVE_PATTERN_MATCH, no_bucket, ERROR, [calling context starts here,in call to `accepts_positive2/1`,no pattern match here]
codetoanalyze/erlang/nonmatch/src/function_guards.erl, test_accepts_positive_Bad/0, -19, NONEXHAUSTIVE_PATTERN_MATCH, no_bucket, ERROR, [calling context starts here,in call to `accepts_positive/1`,no pattern match here]
codetoanalyze/erlang/nonmatch/src/function_guards.erl, test_possible_exception_Bad/0, -37, NONEXHAUSTIVE_PATTERN_MATCH, no_bucket, ERROR, [calling context starts here,in call to `possible_exception/1`,no pattern match here]
codetoanalyze/erlang/nonmatch/src/match.erl, fp_match_test_c_Ok/0, 1, NONEXHAUSTIVE_PATTERN_MATCH, no_bucket, ERROR, [no pattern match here]
codetoanalyze/erlang/nonmatch/src/match.erl, fp_match_test_d_Ok/0, -13, NONEXHAUSTIVE_PATTERN_MATCH, no_bucket, ERROR, [calling context starts here,in call to `tail/1`,no pattern match here]
codetoanalyze/erlang/nonmatch/src/match.erl, match_test_b_Bad/0, 1, CONSTANT_ADDRESS_DEREFERENCE, no_bucket, WARNING, [in call to `two/0`,is the constant 2,assigned,returned,return from call to `two/0`,invalid access occurs here]

@ -0,0 +1,46 @@
% Copyright (c) Facebook, Inc. and its affiliates.
%
% This source code is licensed under the MIT license found in the
% LICENSE file in the root directory of this source tree.
-module(case_guards).
-export([
test_accepts_positive_Bad/0,
test_accepts_positive_Ok/0,
test_accepts_positive2_Bad/0,
test_accepts_positive2_Ok/0,
test_accepts_all_Ok/0
]).
accepts_positive(X) ->
case X of
X when X > 0 -> ok
end.
accepts_positive2(X) ->
case X of
X when 1 =:= 1, 1 =:= 0; X > 0 -> ok
end.
accepts_all(X) ->
case X of
X when X > 0 -> ok;
X when not (X > 0) -> ok
end.
test_accepts_positive_Bad() ->
accepts_positive(0).
test_accepts_positive_Ok() ->
accepts_positive(1).
test_accepts_positive2_Bad() ->
accepts_positive2(0).
test_accepts_positive2_Ok() ->
accepts_positive2(1).
test_accepts_all_Ok() ->
accepts_all(0),
accepts_all(1).

@ -0,0 +1,82 @@
% Copyright (c) Facebook, Inc. and its affiliates.
%
% This source code is licensed under the MIT license found in the
% LICENSE file in the root directory of this source tree.
-module(function_guards).
-export([
test_accepts_positive_Bad/0,
test_accepts_positive_Ok/0,
test_accepts_positive2_Bad/0,
test_accepts_positive2_Ok/0,
test_accepts_all_basic_Ok/0,
test_accepts_all_basic2_Ok/0,
test_accepts_all_tricky_Ok/0,
test_accepts_all_tricky2_Ok/0,
test_accepts_all_tricky3_Ok/0,
test_possible_exception_Ok/0,
test_possible_exception_Bad/0,
fn_test_possible_exception2_Bad/0
]).
accepts_positive(X) when X > 0 -> ok.
accepts_positive2(X) when 1 =:= 1, 1 =:= 0; X > 0 -> ok.
accepts_all_basic(X) when X > 0 -> ok;
accepts_all_basic(_) -> ok.
accepts_all_basic2(X) when X > 0; 1 =:= 1 -> ok.
accepts_all_tricky(X) when X > 0; not (X > 0) -> ok.
accepts_all_tricky2(X) when X > 0 -> ok;
accepts_all_tricky2(X) when not (X > 0) -> ok.
% FP (T95259136)
fp_accepts_all_tricky3(X) when X > 0; X =< 0 -> ok.
possible_exception(X) when 1 div X =:= 1 -> ok.
test_accepts_positive_Bad() ->
accepts_positive(0).
test_accepts_positive_Ok() ->
accepts_positive(1).
test_accepts_positive2_Bad() ->
accepts_positive2(0).
test_accepts_positive2_Ok() ->
accepts_positive2(1).
test_accepts_all_basic_Ok() ->
accepts_all_basic(0),
accepts_all_basic(1).
test_accepts_all_basic2_Ok() ->
accepts_all_basic2(0),
accepts_all_basic2(1).
test_accepts_all_tricky_Ok() ->
accepts_all_tricky(0),
accepts_all_tricky(1).
test_accepts_all_tricky2_Ok() ->
accepts_all_tricky2(0),
accepts_all_tricky2(1).
test_accepts_all_tricky3_Ok() ->
fp_accepts_all_tricky3(0),
fp_accepts_all_tricky3(1).
test_possible_exception_Ok() ->
possible_exception(1).
test_possible_exception_Bad() ->
possible_exception(2).
% FN (T95472386)
fn_test_possible_exception2_Bad() ->
possible_exception(0).
Loading…
Cancel
Save