Skip to content

Commit

Permalink
[flow][match] Use InternalBinding for internal bindings, fixing match…
Browse files Browse the repository at this point in the history
… refinement highlighting issue

Summary:
Use InternalBinding for internal bindings, fixing match refinement highlighting issue.

Changelog: [internal]

Reviewed By: SamChou19815, panagosg7

Differential Revision: D67830801

fbshipit-source-id: 68b5cbd80697b777b62ed8b8fe05b0e39ef2ee77
  • Loading branch information
gkz authored and facebook-github-bot committed Jan 7, 2025
1 parent b9cc018 commit 9022171
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 50 deletions.
126 changes: 76 additions & 50 deletions src/analysis/env_builder/name_resolver.ml
Original file line number Diff line number Diff line change
Expand Up @@ -2931,56 +2931,27 @@ module Make (Context : C) (FlowAPIUtils : F with type cx = Context.t) :
(match_root_ident arg_internal)
(Ast.Type.Missing ALoc.none);
ignore @@ this#identifier (match_root_ident arg_internal);
Base.List.iter cases ~f:(fun case ->
let (case_loc, { Case.pattern; body; guard; comments = _ }) = case in
let lexical_hoist =
new lexical_hoister ~flowmin_compatibility:false ~enable_enums
in
let bindings = lexical_hoist#eval lexical_hoist#match_pattern pattern in
this#with_bindings
~lexical:true
case_loc
bindings
(fun () ->
this#push_refinement_scope empty_refinements;
let arg =
( case_loc,
Ast.Expression.Identifier (Flow_ast_utils.match_root_ident case_loc)
)
in
this#match_pattern_refinements ~arg pattern;
let test_refinements = this#peek_new_refinements () in
let env_prev = this#env_snapshot_without_latest_refinements in
ignore @@ this#match_pattern pattern;
ignore @@ this#expression arg;
let completion_state =
this#run_to_completion (fun () ->
match guard with
| Some guard ->
this#push_refinement_scope empty_refinements;
ignore @@ this#expression_refinement guard;
ignore @@ this#expression body;
this#pop_refinement_scope ()
| None -> ignore @@ this#expression body
)
in
completion_states := completion_state :: !completion_states;
this#pop_refinement_scope ();
this#reset_env env_prev;
if Option.is_none guard then (
(* If there is a guard, it's possible the case didn't match
because of it, not because the pattern didn't match. *)
this#push_refinement_scope test_refinements;
this#negate_new_refinements ()
))
()
);
let arg_out =
( match_keyword_loc,
Ast.Expression.Identifier (Flow_ast_utils.match_root_ident match_keyword_loc)
)
in
ignore @@ this#expression arg_out)
Base.List.iter cases ~f:(this#visit_match_expression_case ~completion_states);
match
this#get_val_of_expression
(arg_internal, Ast.Expression.Identifier (match_root_ident arg_internal))
with
| Some refined_value ->
env_state <-
{
env_state with
values =
L.LMap.add
match_keyword_loc
{
def_loc = None;
value = refined_value;
val_binding_kind = Val.InternalBinding;
name = None;
}
env_state.values;
}
| None -> ())
();
let completion_states = !completion_states |> List.rev in
this#reset_env env0;
Expand All @@ -2990,6 +2961,61 @@ module Make (Context : C) (FlowAPIUtils : F with type cx = Context.t) :
| [] -> ());
x

method private visit_match_expression_case ~completion_states case =
let open Flow_ast.Expression.Match.Case in
let (case_loc, { pattern; body; guard; comments = _ }) = case in
let lexical_hoist = new lexical_hoister ~flowmin_compatibility:false ~enable_enums in
let bindings = lexical_hoist#eval lexical_hoist#match_pattern pattern in
this#with_bindings
~lexical:true
case_loc
bindings
(fun () ->
this#push_refinement_scope empty_refinements;
let arg =
(case_loc, Ast.Expression.Identifier (Flow_ast_utils.match_root_ident case_loc))
in
this#match_pattern_refinements ~arg pattern;
let test_refinements = this#peek_new_refinements () in
let env_prev = this#env_snapshot_without_latest_refinements in
ignore @@ this#match_pattern pattern;
(match this#get_val_of_expression arg with
| Some refined_value ->
let values =
L.LMap.add
case_loc
{
def_loc = None;
value = refined_value;
val_binding_kind = Val.InternalBinding;
name = None;
}
env_state.values
in
env_state <- { env_state with values }
| None -> ());
let completion_state =
this#run_to_completion (fun () ->
match guard with
| Some guard ->
this#push_refinement_scope empty_refinements;
ignore @@ this#expression_refinement guard;
ignore @@ this#expression body;
this#pop_refinement_scope ()
| None -> ignore @@ this#expression body
)
in
completion_states := completion_state :: !completion_states;
this#pop_refinement_scope ();
this#reset_env env_prev;
if Option.is_none guard then (
(* If there is a guard, it's possible the case didn't match
because of it, not because the pattern didn't match. *)
this#push_refinement_scope test_refinements;
this#negate_new_refinements ()
))
()

method! match_pattern pattern =
( if Flow_ast_utils.match_pattern_has_binding pattern then
let (loc, _) = pattern in
Expand Down
4 changes: 4 additions & 0 deletions tests/refinements_match/.flowconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[options]
all=true
experimental.pattern_matching_expressions=true
dev_only.refinement_info_as_errors=true
1 change: 1 addition & 0 deletions tests/refinements_match/refinements_match.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Found 0 errors
7 changes: 7 additions & 0 deletions tests/refinements_match/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
declare const x: number;

const e = match (x) { // OK: no refinement info on internal binding
1: true,
2 as const a: true, // OK: no refinement info on internal binding
_: false,
};

0 comments on commit 9022171

Please sign in to comment.