Skip to content

Commit

Permalink
[flow][match] Error on repeated object keys in object patterns
Browse files Browse the repository at this point in the history
Summary:
Error on repeated object keys in object patterns.

Changelog: [internal]

Reviewed By: SamChou19815

Differential Revision: D67502325

fbshipit-source-id: f05c9ff2a83f0f55d11199651121bdf2203f3df3
  • Loading branch information
gkz authored and facebook-github-bot committed Dec 20, 2024
1 parent 82c1fa3 commit f4f5e1e
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 12 deletions.
2 changes: 2 additions & 0 deletions src/typing/debug_js.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1910,6 +1910,8 @@ let dump_error_message =
| EMatchInvalidUnaryZero { loc } -> spf "EMatchInvalidUnaryZero (%s)" (string_of_aloc loc)
| EMatchInvalidUnaryPlusBigInt { loc } ->
spf "EMatchInvalidUnaryBigInt (%s)" (string_of_aloc loc)
| EMatchDuplicateObjectProperty { loc; name } ->
spf "EMatchDuplicateObjectProperty (%s) (%s)" (string_of_aloc loc) name
| EDevOnlyRefinedLocInfo { refined_loc; refining_locs = _ } ->
spf "EDevOnlyRefinedLocInfo {refined_loc=%s}" (string_of_aloc refined_loc)
| EDevOnlyInvalidatedRefinementInfo { read_loc; invalidation_info = _ } ->
Expand Down
13 changes: 12 additions & 1 deletion src/typing/errors/error_message.ml
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,10 @@ and 'loc t' =
| EMatchInvalidObjectPropertyLiteral of { loc: 'loc }
| EMatchInvalidUnaryZero of { loc: 'loc }
| EMatchInvalidUnaryPlusBigInt of { loc: 'loc }
| EMatchDuplicateObjectProperty of {
loc: 'loc;
name: string;
}
(* Dev only *)
| EDevOnlyRefinedLocInfo of {
refined_loc: 'loc;
Expand Down Expand Up @@ -1432,6 +1436,8 @@ let rec map_loc_of_error_message (f : 'a -> 'b) : 'a t' -> 'b t' =
| EMatchInvalidObjectPropertyLiteral { loc } -> EMatchInvalidObjectPropertyLiteral { loc = f loc }
| EMatchInvalidUnaryZero { loc } -> EMatchInvalidUnaryZero { loc = f loc }
| EMatchInvalidUnaryPlusBigInt { loc } -> EMatchInvalidUnaryPlusBigInt { loc = f loc }
| EMatchDuplicateObjectProperty { loc; name } ->
EMatchDuplicateObjectProperty { loc = f loc; name }
| EDevOnlyInvalidatedRefinementInfo { read_loc; invalidation_info } ->
EDevOnlyInvalidatedRefinementInfo
{
Expand Down Expand Up @@ -1733,7 +1739,8 @@ let util_use_op_of_msg nope util = function
| EMatchInvalidBindingKind _
| EMatchInvalidObjectPropertyLiteral _
| EMatchInvalidUnaryZero _
| EMatchInvalidUnaryPlusBigInt _ ->
| EMatchInvalidUnaryPlusBigInt _
| EMatchDuplicateObjectProperty _ ->
nope

(* Not all messages (i.e. those whose locations are based on use_ops) have locations that can be
Expand Down Expand Up @@ -1939,6 +1946,7 @@ let loc_of_msg : 'loc t' -> 'loc option = function
| EMatchInvalidObjectPropertyLiteral { loc } -> Some loc
| EMatchInvalidUnaryZero { loc } -> Some loc
| EMatchInvalidUnaryPlusBigInt { loc } -> Some loc
| EMatchDuplicateObjectProperty { loc; _ } -> Some loc
| EDevOnlyRefinedLocInfo { refined_loc; refining_locs = _ } -> Some refined_loc
| EDevOnlyInvalidatedRefinementInfo { read_loc; invalidation_info = _ } -> Some read_loc
| EUnableToSpread _
Expand Down Expand Up @@ -2892,6 +2900,8 @@ let friendly_message_of_msg = function
Normal MessageMatchInvalidObjectPropertyLiteral
| EMatchInvalidUnaryZero { loc = _ } -> Normal MessageMatchInvalidUnaryZero
| EMatchInvalidUnaryPlusBigInt { loc = _ } -> Normal MessageMatchInvalidUnaryPlusBigInt
| EMatchDuplicateObjectProperty { loc = _; name } ->
Normal (MessageMatchDuplicateObjectProperty { name })

let defered_in_speculation = function
| EUntypedTypeImport _
Expand Down Expand Up @@ -3233,3 +3243,4 @@ let error_code_of_message err : error_code option =
| EMatchInvalidObjectPropertyLiteral _ -> Some MatchInvalidPattern
| EMatchInvalidUnaryZero _ -> Some MatchInvalidPattern
| EMatchInvalidUnaryPlusBigInt _ -> Some MatchInvalidPattern
| EMatchDuplicateObjectProperty _ -> Some MatchInvalidPattern
2 changes: 2 additions & 0 deletions src/typing/errors/flow_intermediate_error.ml
Original file line number Diff line number Diff line change
Expand Up @@ -4006,6 +4006,8 @@ let to_printable_error :
[text "Unary pattern on "; code "0"; text " is not supported."]
| MessageMatchInvalidUnaryPlusBigInt ->
[text "Unary pattern "; code "+"; text " on bigint literal is not supported."]
| MessageMatchDuplicateObjectProperty { name } ->
[text "Duplicate property "; code name; text " in object pattern."]
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 @@ -902,6 +902,7 @@ type 'loc message =
| MessageMatchInvalidObjectPropertyLiteral
| MessageMatchInvalidUnaryZero
| MessageMatchInvalidUnaryPlusBigInt
| MessageMatchDuplicateObjectProperty of { name: string }

type 'loc intermediate_error = {
kind: Flow_errors_utils.error_kind;
Expand Down
23 changes: 13 additions & 10 deletions src/typing/match_pattern.ml
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,25 @@ let object_named_property acc loc name =

let object_property_key cx acc key :
(ALoc.t, ALoc.t) Flow_ast.Expression.t
* (ALoc.t, ALoc.t * Type.t) Ast.MatchPattern.ObjectPattern.Property.key =
* (ALoc.t, ALoc.t * Type.t) Ast.MatchPattern.ObjectPattern.Property.key
* string =
let open Ast.MatchPattern.ObjectPattern in
match key with
| Property.Identifier (loc, { Ast.Identifier.name; comments }) ->
let acc = object_named_property acc loc name in
let current = Unsoundness.at Type.NonBindingPattern loc in
(acc, Property.Identifier ((loc, current), { Ast.Identifier.name; comments }))
(acc, Property.Identifier ((loc, current), { Ast.Identifier.name; comments }), name)
| Property.StringLiteral (loc, ({ Ast.StringLiteral.value; _ } as lit)) ->
let acc = object_named_property acc loc value in
(acc, Property.StringLiteral (loc, lit))
(acc, Property.StringLiteral (loc, lit), value)
| Property.NumberLiteral (loc, ({ Ast.NumberLiteral.value; _ } as lit)) ->
let prop = Dtoa.ecma_string_of_float value in
if Js_number.is_float_safe_integer value then
let prop = Dtoa.ecma_string_of_float value in
let acc = object_named_property acc loc prop in
(acc, Property.NumberLiteral (loc, lit))
(acc, Property.NumberLiteral (loc, lit), prop)
else (
Flow_js.add_output cx (Error_message.EMatchInvalidObjectPropertyLiteral { loc });
(acc, Property.NumberLiteral (loc, lit))
(acc, Property.NumberLiteral (loc, lit), prop)
)

let binding cx ~on_binding ~kind acc name_loc name =
Expand Down Expand Up @@ -195,12 +196,14 @@ and array_elements cx ~on_identifier ~on_expression ~on_binding acc elements =

and object_properties cx ~on_identifier ~on_expression ~on_binding acc props =
let open Ast.MatchPattern.ObjectPattern in
let rec loop acc rev_props = function
let rec loop acc seen rev_props = function
| [] -> List.rev rev_props
| (loc, { Property.key; pattern = p; shorthand; comments }) :: props ->
let (acc, key) = object_property_key cx acc key in
let (acc, key, name) = object_property_key cx acc key in
if SSet.mem name seen then
Flow_js.add_output cx (Error_message.EMatchDuplicateObjectProperty { loc; name });
let p = pattern cx ~on_identifier ~on_expression ~on_binding acc p in
let prop = (loc, { Property.key; pattern = p; shorthand; comments }) in
loop acc (prop :: rev_props) props
loop acc (SSet.add name seen) (prop :: rev_props) props
in
loop acc [] props
loop acc SSet.empty [] props
26 changes: 25 additions & 1 deletion tests/match/match.exp
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,30 @@ Unary pattern `+` on bigint literal is not supported. [match-invalid-pattern]
^^^


Error ------------------------------------------------------------------------------------------ pattern-errors.js:56:17

Duplicate property `foo` in object pattern. [match-invalid-pattern]

56| {foo: true, foo: false}: 0, // ERROR
^^^


Error ------------------------------------------------------------------------------------------ pattern-errors.js:61:17

Duplicate property `foo` in object pattern. [match-invalid-pattern]

61| {const foo, foo: true}: 0, // ERROR
^^^


Error ------------------------------------------------------------------------------------------ pattern-errors.js:66:17

Duplicate property `foo` in object pattern. [match-invalid-pattern]

66| {foo: true, 'foo': false}: 0, // ERROR
^^^^^


Error -------------------------------------------------------------------------------------------------- patterns.js:9:3

Cannot cast `out` to empty because number [1] is incompatible with empty [2]. [incompatible-cast]
Expand Down Expand Up @@ -635,4 +659,4 @@ References:



Found 48 errors
Found 51 errors
20 changes: 20 additions & 0 deletions tests/match/pattern-errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,23 @@
1n: true, // OK
};
}

// Duplicate object keys banned
{
declare const x: {foo: boolean};

const e1 = match (x) {
{foo: true, foo: false}: 0, // ERROR
_: 0,
};

const e2 = match (x) {
{const foo, foo: true}: 0, // ERROR
_: 0,
};

const e3 = match (x) {
{foo: true, 'foo': false}: 0, // ERROR
_: 0,
};
}

0 comments on commit f4f5e1e

Please sign in to comment.