From 207bb415142fd8a2087e335a9204784759178838 Mon Sep 17 00:00:00 2001 From: Alexander T Date: Tue, 25 Aug 2020 12:46:30 +0300 Subject: [PATCH] fix(40222): fix crash on using destructuring in a catch clause --- src/compiler/checker.ts | 13 ++++--- .../catchClauseWithTypeAnnotation.errors.txt | 15 ++++++- .../catchClauseWithTypeAnnotation.js | 39 +++++++++++++++++++ .../catchClauseWithTypeAnnotation.symbols | 33 ++++++++++++++++ .../catchClauseWithTypeAnnotation.types | 36 +++++++++++++++++ .../catchClauseWithTypeAnnotation.ts | 7 ++++ 6 files changed, 137 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1eb750c8939ba..ab887b47c9072 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -34181,12 +34181,15 @@ namespace ts { if (catchClause) { // Grammar checking if (catchClause.variableDeclaration) { - if (catchClause.variableDeclaration.type && getTypeOfNode(catchClause.variableDeclaration) === errorType) { - grammarErrorOnFirstToken(catchClause.variableDeclaration.type, - Diagnostics.Catch_clause_variable_type_annotation_must_be_any_or_unknown_if_specified); + const declaration = catchClause.variableDeclaration; + if (declaration.type) { + const type = getTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ false); + if (type && !(type.flags & TypeFlags.AnyOrUnknown)) { + grammarErrorOnFirstToken(declaration.type, Diagnostics.Catch_clause_variable_type_annotation_must_be_any_or_unknown_if_specified); + } } - else if (catchClause.variableDeclaration.initializer) { - grammarErrorOnFirstToken(catchClause.variableDeclaration.initializer, Diagnostics.Catch_clause_variable_cannot_have_an_initializer); + else if (declaration.initializer) { + grammarErrorOnFirstToken(declaration.initializer, Diagnostics.Catch_clause_variable_cannot_have_an_initializer); } else { const blockLocals = catchClause.block.locals; diff --git a/tests/baselines/reference/catchClauseWithTypeAnnotation.errors.txt b/tests/baselines/reference/catchClauseWithTypeAnnotation.errors.txt index e5bd5be16ef09..870a7cf87080a 100644 --- a/tests/baselines/reference/catchClauseWithTypeAnnotation.errors.txt +++ b/tests/baselines/reference/catchClauseWithTypeAnnotation.errors.txt @@ -4,9 +4,11 @@ tests/cases/conformance/statements/tryStatements/catchClauseWithTypeAnnotation.t tests/cases/conformance/statements/tryStatements/catchClauseWithTypeAnnotation.ts(20,23): error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified. tests/cases/conformance/statements/tryStatements/catchClauseWithTypeAnnotation.ts(29,29): error TS2492: Cannot redeclare identifier 'x' in catch clause. tests/cases/conformance/statements/tryStatements/catchClauseWithTypeAnnotation.ts(30,29): error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type 'boolean', but here has type 'string'. +tests/cases/conformance/statements/tryStatements/catchClauseWithTypeAnnotation.ts(38,27): error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified. +tests/cases/conformance/statements/tryStatements/catchClauseWithTypeAnnotation.ts(39,27): error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified. -==== tests/cases/conformance/statements/tryStatements/catchClauseWithTypeAnnotation.ts (6 errors) ==== +==== tests/cases/conformance/statements/tryStatements/catchClauseWithTypeAnnotation.ts (8 errors) ==== type any1 = any; type unknown1 = unknown; @@ -52,5 +54,16 @@ tests/cases/conformance/statements/tryStatements/catchClauseWithTypeAnnotation.t !!! related TS6203 tests/cases/conformance/statements/tryStatements/catchClauseWithTypeAnnotation.ts:4:13: 'x' was also declared here. try { } catch (x) { var x: boolean; } + try { } catch ({ x }) { } // should be OK + try { } catch ({ x }: any) { x.foo; } // should be OK + try { } catch ({ x }: any1) { x.foo;} // should be OK + try { } catch ({ x }: unknown) { console.log(x); } // should be OK + try { } catch ({ x }: unknown1) { console.log(x); } // should be OK + try { } catch ({ x }: object) { } // error in the type + ~~~~~~ +!!! error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified. + try { } catch ({ x }: Error) { } // error in the type + ~~~~~ +!!! error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified. } \ No newline at end of file diff --git a/tests/baselines/reference/catchClauseWithTypeAnnotation.js b/tests/baselines/reference/catchClauseWithTypeAnnotation.js index e6ecc235af46b..0337d65b43713 100644 --- a/tests/baselines/reference/catchClauseWithTypeAnnotation.js +++ b/tests/baselines/reference/catchClauseWithTypeAnnotation.js @@ -31,6 +31,13 @@ function fn(x: boolean) { try { } catch (x) { var x: string; } try { } catch (x) { var x: boolean; } + try { } catch ({ x }) { } // should be OK + try { } catch ({ x }: any) { x.foo; } // should be OK + try { } catch ({ x }: any1) { x.foo;} // should be OK + try { } catch ({ x }: unknown) { console.log(x); } // should be OK + try { } catch ({ x }: unknown1) { console.log(x); } // should be OK + try { } catch ({ x }: object) { } // error in the type + try { } catch ({ x }: Error) { } // error in the type } @@ -99,4 +106,36 @@ function fn(x) { catch (x) { var x; } + try { } + catch (_a) { + var x_2 = _a.x; + } // should be OK + try { } + catch (_b) { + var x_3 = _b.x; + x_3.foo; + } // should be OK + try { } + catch (_c) { + var x_4 = _c.x; + x_4.foo; + } // should be OK + try { } + catch (_d) { + var x_5 = _d.x; + console.log(x_5); + } // should be OK + try { } + catch (_e) { + var x_6 = _e.x; + console.log(x_6); + } // should be OK + try { } + catch (_f) { + var x_7 = _f.x; + } // error in the type + try { } + catch (_g) { + var x_8 = _g.x; + } // error in the type } diff --git a/tests/baselines/reference/catchClauseWithTypeAnnotation.symbols b/tests/baselines/reference/catchClauseWithTypeAnnotation.symbols index b8774e2086976..08eaec37b70e9 100644 --- a/tests/baselines/reference/catchClauseWithTypeAnnotation.symbols +++ b/tests/baselines/reference/catchClauseWithTypeAnnotation.symbols @@ -100,5 +100,38 @@ function fn(x: boolean) { >x : Symbol(x, Decl(catchClauseWithTypeAnnotation.ts, 30, 19)) >x : Symbol(x, Decl(catchClauseWithTypeAnnotation.ts, 3, 12), Decl(catchClauseWithTypeAnnotation.ts, 29, 27), Decl(catchClauseWithTypeAnnotation.ts, 30, 27)) + try { } catch ({ x }) { } // should be OK +>x : Symbol(x, Decl(catchClauseWithTypeAnnotation.ts, 32, 20)) + + try { } catch ({ x }: any) { x.foo; } // should be OK +>x : Symbol(x, Decl(catchClauseWithTypeAnnotation.ts, 33, 20)) +>x : Symbol(x, Decl(catchClauseWithTypeAnnotation.ts, 33, 20)) + + try { } catch ({ x }: any1) { x.foo;} // should be OK +>x : Symbol(x, Decl(catchClauseWithTypeAnnotation.ts, 34, 20)) +>any1 : Symbol(any1, Decl(catchClauseWithTypeAnnotation.ts, 0, 0)) +>x : Symbol(x, Decl(catchClauseWithTypeAnnotation.ts, 34, 20)) + + try { } catch ({ x }: unknown) { console.log(x); } // should be OK +>x : Symbol(x, Decl(catchClauseWithTypeAnnotation.ts, 35, 20)) +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>x : Symbol(x, Decl(catchClauseWithTypeAnnotation.ts, 35, 20)) + + try { } catch ({ x }: unknown1) { console.log(x); } // should be OK +>x : Symbol(x, Decl(catchClauseWithTypeAnnotation.ts, 36, 20)) +>unknown1 : Symbol(unknown1, Decl(catchClauseWithTypeAnnotation.ts, 0, 16)) +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>x : Symbol(x, Decl(catchClauseWithTypeAnnotation.ts, 36, 20)) + + try { } catch ({ x }: object) { } // error in the type +>x : Symbol(x, Decl(catchClauseWithTypeAnnotation.ts, 37, 20)) + + try { } catch ({ x }: Error) { } // error in the type +>x : Symbol(x, Decl(catchClauseWithTypeAnnotation.ts, 38, 20)) +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) } diff --git a/tests/baselines/reference/catchClauseWithTypeAnnotation.types b/tests/baselines/reference/catchClauseWithTypeAnnotation.types index a0d1eec0ab590..d0de759bbbd79 100644 --- a/tests/baselines/reference/catchClauseWithTypeAnnotation.types +++ b/tests/baselines/reference/catchClauseWithTypeAnnotation.types @@ -111,5 +111,41 @@ function fn(x: boolean) { >x : any >x : boolean + try { } catch ({ x }) { } // should be OK +>x : any + + try { } catch ({ x }: any) { x.foo; } // should be OK +>x : any +>x.foo : any +>x : any +>foo : any + + try { } catch ({ x }: any1) { x.foo;} // should be OK +>x : any +>x.foo : any +>x : any +>foo : any + + try { } catch ({ x }: unknown) { console.log(x); } // should be OK +>x : any +>console.log(x) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>x : any + + try { } catch ({ x }: unknown1) { console.log(x); } // should be OK +>x : any +>console.log(x) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>x : any + + try { } catch ({ x }: object) { } // error in the type +>x : any + + try { } catch ({ x }: Error) { } // error in the type +>x : any } diff --git a/tests/cases/conformance/statements/tryStatements/catchClauseWithTypeAnnotation.ts b/tests/cases/conformance/statements/tryStatements/catchClauseWithTypeAnnotation.ts index e33aa3ba4451a..d7517e021d1fc 100644 --- a/tests/cases/conformance/statements/tryStatements/catchClauseWithTypeAnnotation.ts +++ b/tests/cases/conformance/statements/tryStatements/catchClauseWithTypeAnnotation.ts @@ -30,4 +30,11 @@ function fn(x: boolean) { try { } catch (x) { var x: string; } try { } catch (x) { var x: boolean; } + try { } catch ({ x }) { } // should be OK + try { } catch ({ x }: any) { x.foo; } // should be OK + try { } catch ({ x }: any1) { x.foo;} // should be OK + try { } catch ({ x }: unknown) { console.log(x); } // should be OK + try { } catch ({ x }: unknown1) { console.log(x); } // should be OK + try { } catch ({ x }: object) { } // error in the type + try { } catch ({ x }: Error) { } // error in the type }