Skip to content

Commit

Permalink
[flow][match] Error on invalid match pattern binding kind
Browse files Browse the repository at this point in the history
Summary:
For `match` expressions, only `const` is allowed, since nothing else makes sense. (When we add support for `match` statements in Flow, those will also allow `let`, like destructuring does.)

Changelog: [internal]

Reviewed By: SamChou19815

Differential Revision: D67502326

fbshipit-source-id: 79e1ce3ff0b0668b2f0d8444764477e79f5ffb9b
  • Loading branch information
gkz authored and facebook-github-bot committed Dec 20, 2024
1 parent 9138251 commit db9f8a0
Show file tree
Hide file tree
Showing 9 changed files with 182 additions and 79 deletions.
2 changes: 2 additions & 0 deletions src/common/errors/error_codes.ml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ type error_code =
| InvalidTempType
| LintSetting
| MalformedPackage
| MatchInvalidPattern
| MatchNotExhaustive
| MethodUnbinding
| MissingLocalAnnot
Expand Down Expand Up @@ -320,6 +321,7 @@ let string_of_code : error_code -> string = function
| InvalidTempType -> "invalid-temp-type"
| LintSetting -> "lint-setting"
| MalformedPackage -> "malformed-package"
| MatchInvalidPattern -> "match-invalid-pattern"
| MatchNotExhaustive -> "match-not-exhaustive"
| MethodUnbinding -> "method-unbinding"
| MissingLocalAnnot -> "missing-local-annot"
Expand Down
5 changes: 5 additions & 0 deletions src/typing/debug_js.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1900,6 +1900,11 @@ let dump_error_message =
spf "ENegativeTypeGuardConsistency (%s)" (dump_reason cx reason)
| EMatchNotExhaustive { loc; reason } ->
spf "EMatchNotExhaustive (%s) (%s)" (string_of_aloc loc) (dump_reason cx reason)
| EMatchInvalidBindingKind { loc; kind } ->
spf
"EMatchInvalidBindingKind (%s) (%s)"
(string_of_aloc loc)
(Flow_ast_utils.string_of_variable_kind kind)
| EDevOnlyRefinedLocInfo { refined_loc; refining_locs = _ } ->
spf "EDevOnlyRefinedLocInfo {refined_loc=%s}" (string_of_aloc refined_loc)
| EDevOnlyInvalidatedRefinementInfo { read_loc; invalidation_info = _ } ->
Expand Down
11 changes: 10 additions & 1 deletion src/typing/errors/error_message.ml
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,10 @@ and 'loc t' =
loc: 'loc;
reason: 'loc virtual_reason;
}
| EMatchInvalidBindingKind of {
loc: 'loc;
kind: Flow_ast.Variable.kind;
}
(* Dev only *)
| EDevOnlyRefinedLocInfo of {
refined_loc: 'loc;
Expand Down Expand Up @@ -1421,6 +1425,7 @@ let rec map_loc_of_error_message (f : 'a -> 'b) : 'a t' -> 'b t' =
EDevOnlyRefinedLocInfo { refined_loc = f refined_loc; refining_locs = List.map f refining_locs }
| EMatchNotExhaustive { loc; reason } ->
EMatchNotExhaustive { loc = f loc; reason = map_reason reason }
| EMatchInvalidBindingKind { loc; kind } -> EMatchInvalidBindingKind { loc = f loc; kind }
| EDevOnlyInvalidatedRefinementInfo { read_loc; invalidation_info } ->
EDevOnlyInvalidatedRefinementInfo
{
Expand Down Expand Up @@ -1718,7 +1723,8 @@ let util_use_op_of_msg nope util = function
| EUnionOptimization _
| EUnionOptimizationOnNonUnion _
| ECannotCallReactComponent _
| EMatchNotExhaustive _ ->
| EMatchNotExhaustive _
| EMatchInvalidBindingKind _ ->
nope

(* Not all messages (i.e. those whose locations are based on use_ops) have locations that can be
Expand Down Expand Up @@ -1920,6 +1926,7 @@ let loc_of_msg : 'loc t' -> 'loc option = function
| EEmptyArrayNoProvider { loc } -> Some loc
| EUnusedPromise { loc; _ } -> Some loc
| EMatchNotExhaustive { loc; _ } -> Some loc
| EMatchInvalidBindingKind { loc; _ } -> Some loc
| EDevOnlyRefinedLocInfo { refined_loc; refining_locs = _ } -> Some refined_loc
| EDevOnlyInvalidatedRefinementInfo { read_loc; invalidation_info = _ } -> Some read_loc
| EUnableToSpread _
Expand Down Expand Up @@ -2868,6 +2875,7 @@ let friendly_message_of_msg = function
Normal (MessageInvalidUseOfFlowEnforceOptimized arg)
| ECannotCallReactComponent { reason } -> Normal (MessageCannotCallReactComponent reason)
| EMatchNotExhaustive { loc = _; reason } -> Normal (MessageMatchNotExhaustive reason)
| EMatchInvalidBindingKind { loc = _; kind } -> Normal (MessageMatchInvalidBindingKind { kind })

let defered_in_speculation = function
| EUntypedTypeImport _
Expand Down Expand Up @@ -3205,3 +3213,4 @@ let error_code_of_message err : error_code option =
| EUnionOptimizationOnNonUnion _ -> Some UnionUnoptimizable
| ECannotCallReactComponent _ -> Some ReactRuleCallComponent
| EMatchNotExhaustive _ -> Some MatchNotExhaustive
| EMatchInvalidBindingKind _ -> Some MatchInvalidPattern
8 changes: 8 additions & 0 deletions src/typing/errors/flow_intermediate_error.ml
Original file line number Diff line number Diff line change
Expand Up @@ -3989,6 +3989,14 @@ let to_printable_error :
ref reason;
text " has not been fully checked against by the match patterns below.";
]
| MessageMatchInvalidBindingKind { kind } ->
[
text "Cannot use ";
code (Flow_ast_utils.string_of_variable_kind kind);
text " for match pattern binding. Only ";
code "const";
text " is allowed.";
]
in
let rec convert_error_message { kind; loc; error_code; root; message; misplaced_source_file = _ }
=
Expand Down
1 change: 1 addition & 0 deletions src/typing/errors/flow_intermediate_error_types.ml
Original file line number Diff line number Diff line change
Expand Up @@ -898,6 +898,7 @@ type 'loc message =
null_loc: 'loc option;
}
| MessageMatchNotExhaustive of 'loc virtual_reason
| MessageMatchInvalidBindingKind of { kind: Flow_ast.Variable.kind }

type 'loc intermediate_error = {
kind: Flow_errors_utils.error_kind;
Expand Down
18 changes: 13 additions & 5 deletions src/typing/match_pattern.ml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*)

module Ast = Flow_ast
module Tast_utils = Typed_ast_utils
open Reason
open Type

Expand Down Expand Up @@ -67,10 +68,17 @@ let binding_identifier cx ~on_binding ~kind acc (loc, { Ast.Identifier.name; com
let t = binding cx ~on_binding ~kind acc loc name in
((loc, t), { Ast.Identifier.name; comments })

let binding_pattern cx ~on_binding acc binding =
let binding_pattern cx ~on_binding ~loc acc binding =
let open Ast.MatchPattern.BindingPattern in
let { kind; id; comments } = binding in
let id = binding_identifier cx ~on_binding ~kind acc id in
let id =
match kind with
| Ast.Variable.Var
| Ast.Variable.Let ->
Flow_js.add_output cx (Error_message.EMatchInvalidBindingKind { loc; kind });
Tast_utils.error_mapper#t_identifier id
| Ast.Variable.Const -> binding_identifier cx ~on_binding ~kind acc id
in
{ kind; id; comments }

let rec member cx ~on_identifier ~on_expression mem =
Expand Down Expand Up @@ -113,7 +121,7 @@ let rest_pattern cx ~on_binding acc rest =
{
argument =
Base.Option.map argument ~f:(fun (arg_loc, arg) ->
(arg_loc, binding_pattern cx ~on_binding acc arg)
(arg_loc, binding_pattern cx ~on_binding ~loc:arg_loc acc arg)
);
comments;
}
Expand Down Expand Up @@ -144,15 +152,15 @@ let rec pattern cx ~on_identifier ~on_expression ~on_binding acc (loc, p) :
let target =
match target with
| AsPattern.Binding (loc, binding) ->
AsPattern.Binding (loc, binding_pattern cx ~on_binding acc binding)
AsPattern.Binding (loc, binding_pattern cx ~on_binding ~loc acc binding)
| AsPattern.Identifier id ->
AsPattern.Identifier (binding_identifier cx ~on_binding ~kind:Ast.Variable.Const acc id)
in
AsPattern { AsPattern.pattern = p; target; comments }
| IdentifierPattern (loc, x) ->
let t = on_identifier cx x loc in
IdentifierPattern ((loc, t), x)
| BindingPattern x -> BindingPattern (binding_pattern cx ~on_binding acc x)
| BindingPattern x -> BindingPattern (binding_pattern cx ~on_binding ~loc acc x)
| WildcardPattern x -> WildcardPattern x
| ArrayPattern { ArrayPattern.elements; rest; comments } ->
let rest = rest_pattern cx ~on_binding acc rest in
Expand Down
Loading

0 comments on commit db9f8a0

Please sign in to comment.