diff --git a/src/typing/statement.ml b/src/typing/statement.ml index c281c2199f5..30a1d49c33f 100644 --- a/src/typing/statement.ml +++ b/src/typing/statement.ml @@ -672,9 +672,15 @@ and statement cx : 'a -> (ALoc.t, ALoc.t * Type.t) Ast.Statement.t = Ast.Stateme | Some p -> (match p with | loc, Identifier { Identifier.name = name_loc, ({ Ast.Identifier.name; comments= _ } as id); - annot = Ast.Type.Missing mloc; + annot; optional; } -> + let mloc = match annot with + | Ast.Type.Missing mloc -> mloc + | Ast.Type.Available (mloc, (loc, _)) -> + Flow.add_output cx Error_message.(EUnsupportedSyntax (loc, CatchParameterAnnotation)); + mloc + in let r = mk_reason (RCustom "catch") loc in let t = Tvar.mk cx r in @@ -695,11 +701,35 @@ and statement cx : 'a -> (ALoc.t, ALoc.t * Type.t) Ast.Statement.t = Ast.Stateme }, abnormal_opt - - | loc, Identifier _ -> - Flow.add_output cx - Error_message.(EUnsupportedSyntax (loc, CatchParameterAnnotation)); - Tast_utils.error_mapper#catch_clause catch_clause, None + | ((ploc, Object _) as id) + | ((ploc, Array _) as id) -> + let id_reason = mk_reason RDestructuring ploc in + let annot = Destructuring.type_of_pattern id in + let annot = match annot with + | Ast.Type.Available (mloc, (loc, _)) -> + Flow.add_output cx Error_message.(EUnsupportedSyntax (loc, CatchParameterAnnotation)); + Ast.Type.Missing mloc + | _ -> annot + in + let annot_t, _ = Anno.mk_type_annotation cx SMap.empty id_reason annot in + let init = Destructuring.empty annot_t ~annot:false in + let (stmts, abnormal_opt), id_ast = Env.in_lex_scope cx (fun () -> + let id_ast = Destructuring.pattern cx ~expr:expression init id ~f:(fun ~use_op loc name default t -> + let reason = mk_reason (RIdentifier name) loc in + Scope.(Env.bind_implicit_let ~state:State.Initialized Entry.CatchParamBinding cx name t loc); + Flow.flow cx (t, AssertImportIsValueT (reason, name)); + Option.iter default ~f:(fun d -> + let default_t = Flow.mk_default cx reason d in + Flow.flow cx (default_t, UseT (use_op, t)) + ) + ) in + check cx b, id_ast + ) in + { Try.CatchClause. + param = Some id_ast; + body = b_loc, { Block.body = stmts }; + }, + abnormal_opt | loc, _ -> Flow.add_output cx diff --git a/tests/try/destructuring-catch.js b/tests/try/destructuring-catch.js new file mode 100644 index 00000000000..b6888f14ab1 --- /dev/null +++ b/tests/try/destructuring-catch.js @@ -0,0 +1,44 @@ +/*** + * @flow + */ + +// object destructuring +function f() { + try { + + } catch ({message}) { + message // ok + const foo: string = 1; // it typechecks + } +} + +// array destructuring +function f() { + try { + + } catch ([bar, baz]) { + bar // ok + baz // ok + const foo: string = 1; // it typechecks + } +} + +// type annotation is banned +function f() { + try { + + } catch ({message}: any) { + message // ok + const foo: string = 1; // it typechecks + } +} + +// type annotation doesn't affect type +function f() { + try { + + } catch ({message}: {message: string}) { // error + ;(message: number) // ok + const foo: string = 1; // it typechecks + } +} diff --git a/tests/try/test.js b/tests/try/test.js index 2e313d09fb3..33222b8f161 100644 --- a/tests/try/test.js +++ b/tests/try/test.js @@ -71,3 +71,12 @@ function maz() { } var c: string = x[0]; // reachable } + +// type annotation is banned +function maz() { + try { + } catch (e: string) { // error + (e: number); // ok + var c: string = 1; // it typechecks + } +} diff --git a/tests/try/try.exp b/tests/try/try.exp index 2eb41bd78eb..fd07d6bce30 100644 --- a/tests/try/try.exp +++ b/tests/try/try.exp @@ -1,3 +1,75 @@ +Error ------------------------------------------------------------------------------------- destructuring-catch.js:11:25 + +Cannot assign `1` to `foo` because number [1] is incompatible with string [2]. + + destructuring-catch.js:11:25 + 11| const foo: string = 1; // it typechecks + ^ [1] + +References: + destructuring-catch.js:11:16 + 11| const foo: string = 1; // it typechecks + ^^^^^^ [2] + + +Error ------------------------------------------------------------------------------------- destructuring-catch.js:22:25 + +Cannot assign `1` to `foo` because number [1] is incompatible with string [2]. + + destructuring-catch.js:22:25 + 22| const foo: string = 1; // it typechecks + ^ [1] + +References: + destructuring-catch.js:22:16 + 22| const foo: string = 1; // it typechecks + ^^^^^^ [2] + + +Error ------------------------------------------------------------------------------------- destructuring-catch.js:30:23 + +Type annotations for catch parameters are not yet supported. + + 30| } catch ({message}: any) { + ^^^ + + +Error ------------------------------------------------------------------------------------- destructuring-catch.js:32:25 + +Cannot assign `1` to `foo` because number [1] is incompatible with string [2]. + + destructuring-catch.js:32:25 + 32| const foo: string = 1; // it typechecks + ^ [1] + +References: + destructuring-catch.js:32:16 + 32| const foo: string = 1; // it typechecks + ^^^^^^ [2] + + +Error ------------------------------------------------------------------------------------- destructuring-catch.js:40:23 + +Type annotations for catch parameters are not yet supported. + + 40| } catch ({message}: {message: string}) { // error + ^^^^^^^^^^^^^^^^^ + + +Error ------------------------------------------------------------------------------------- destructuring-catch.js:42:25 + +Cannot assign `1` to `foo` because number [1] is incompatible with string [2]. + + destructuring-catch.js:42:25 + 42| const foo: string = 1; // it typechecks + ^ [1] + +References: + destructuring-catch.js:42:16 + 42| const foo: string = 1; // it typechecks + ^^^^^^ [2] + + Error ---------------------------------------------------------------------------------------------------- init.js:34:20 Cannot assign `x` to `y` because possibly uninitialized variable [1] is incompatible with number [2]. @@ -337,5 +409,27 @@ References: ^^^^^^ [2] +Error ---------------------------------------------------------------------------------------------------- test.js:78:15 + +Type annotations for catch parameters are not yet supported. + + 78| } catch (e: string) { // error + ^^^^^^ + + +Error ---------------------------------------------------------------------------------------------------- test.js:80:21 + +Cannot assign `1` to `c` because number [1] is incompatible with string [2]. + + test.js:80:21 + 80| var c: string = 1; // it typechecks + ^ [1] + +References: + test.js:80:12 + 80| var c: string = 1; // it typechecks + ^^^^^^ [2] + + -Found 22 errors +Found 30 errors