Allow destructuring in catch clauses

This commit is contained in:
Andy Hanson 2016-09-15 09:51:32 -07:00
parent 3f234f2e7f
commit fab0859869
15 changed files with 310 additions and 53 deletions

View File

@ -1007,7 +1007,7 @@ namespace ts {
currentFlow = finishFlowLabel(preFinallyLabel);
bind(node.finallyBlock);
// if flow after finally is unreachable - keep it
// otherwise check if flows after try and after catch are unreachable
// otherwise check if flows after try and after catch are unreachable
// if yes - convert current flow to unreachable
// i.e.
// try { return "1" } finally { console.log(1); }
@ -2421,6 +2421,9 @@ namespace ts {
case SyntaxKind.HeritageClause:
return computeHeritageClause(<HeritageClause>node, subtreeFlags);
case SyntaxKind.CatchClause:
return computeCatchClause(<CatchClause>node, subtreeFlags);
case SyntaxKind.ExpressionWithTypeArguments:
return computeExpressionWithTypeArguments(<ExpressionWithTypeArguments>node, subtreeFlags);
@ -2650,6 +2653,17 @@ namespace ts {
return transformFlags & ~TransformFlags.NodeExcludes;
}
function computeCatchClause(node: CatchClause, subtreeFlags: TransformFlags) {
let transformFlags = subtreeFlags;
if (node.variableDeclaration && isBindingPattern(node.variableDeclaration.name)) {
transformFlags |= TransformFlags.AssertES2015;
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
return transformFlags & ~TransformFlags.NodeExcludes;
}
function computeExpressionWithTypeArguments(node: ExpressionWithTypeArguments, subtreeFlags: TransformFlags) {
// An ExpressionWithTypeArguments is ES6 syntax, as it is used in the
// extends clause of a class.

View File

@ -3304,7 +3304,7 @@ namespace ts {
}
// Handle catch clause variables
const declaration = symbol.valueDeclaration;
if (declaration.parent.kind === SyntaxKind.CatchClause) {
if (isCatchClauseVariableDeclarationOrBindingElement(declaration)) {
return links.type = anyType;
}
// Handle export default expressions
@ -16906,22 +16906,20 @@ namespace ts {
if (catchClause) {
// Grammar checking
if (catchClause.variableDeclaration) {
if (catchClause.variableDeclaration.name.kind !== SyntaxKind.Identifier) {
grammarErrorOnFirstToken(catchClause.variableDeclaration.name, Diagnostics.Catch_clause_variable_name_must_be_an_identifier);
}
else if (catchClause.variableDeclaration.type) {
if (catchClause.variableDeclaration.type) {
grammarErrorOnFirstToken(catchClause.variableDeclaration.type, Diagnostics.Catch_clause_variable_cannot_have_a_type_annotation);
}
else if (catchClause.variableDeclaration.initializer) {
grammarErrorOnFirstToken(catchClause.variableDeclaration.initializer, Diagnostics.Catch_clause_variable_cannot_have_an_initializer);
}
else {
const identifierName = (<Identifier>catchClause.variableDeclaration.name).text;
const locals = catchClause.block.locals;
if (locals) {
const localSymbol = locals[identifierName];
if (localSymbol && (localSymbol.flags & SymbolFlags.BlockScopedVariable) !== 0) {
grammarErrorOnNode(localSymbol.valueDeclaration, Diagnostics.Cannot_redeclare_identifier_0_in_catch_clause, identifierName);
const blockLocals = catchClause.block.locals;
if (blockLocals) {
for (const caughtName in catchClause.locals) {
const blockLocal = blockLocals[caughtName];
if (blockLocal && (blockLocal.flags & SymbolFlags.BlockScopedVariable) !== 0) {
grammarErrorOnNode(blockLocal.valueDeclaration, Diagnostics.Cannot_redeclare_identifier_0_in_catch_clause, caughtName);
}
}
}
}

View File

@ -603,10 +603,6 @@
"category": "Error",
"code": 1194
},
"Catch clause variable name must be an identifier.": {
"category": "Error",
"code": 1195
},
"Catch clause variable cannot have a type annotation.": {
"category": "Error",
"code": 1196

View File

@ -362,6 +362,9 @@ namespace ts {
case SyntaxKind.ObjectLiteralExpression:
return visitObjectLiteralExpression(<ObjectLiteralExpression>node);
case SyntaxKind.CatchClause:
return visitCatchClause(<CatchClause>node);
case SyntaxKind.ShorthandPropertyAssignment:
return visitShorthandPropertyAssignment(<ShorthandPropertyAssignment>node);
@ -2622,6 +2625,24 @@ namespace ts {
return expression;
}
function visitCatchClause(node: CatchClause): CatchClause {
Debug.assert(isBindingPattern(node.variableDeclaration.name));
const temp = createTempVariable(undefined);
const newVariableDeclaration = createVariableDeclaration(temp, undefined, undefined, node.variableDeclaration);
const vars = flattenVariableDestructuring(node.variableDeclaration, temp, visitor);
const list = createVariableDeclarationList(vars, /*location*/node.variableDeclaration, /*flags*/node.variableDeclaration.flags);
const destructure = createVariableStatement(undefined, list);
return updateCatchClause(node, newVariableDeclaration, addStatementToStartOfBlock(node.block, destructure));
}
function addStatementToStartOfBlock(block: Block, statement: Statement): Block {
const transformedStatements = visitNodes(block.statements, visitor, isStatement);
return updateBlock(block, [statement].concat(transformedStatements));
}
/**
* Visits a MethodDeclaration of an ObjectLiteralExpression and transforms it into a
* PropertyAssignment.

View File

@ -406,7 +406,12 @@ namespace ts {
export function isBlockOrCatchScoped(declaration: Declaration) {
return (getCombinedNodeFlags(declaration) & NodeFlags.BlockScoped) !== 0 ||
isCatchClauseVariableDeclaration(declaration);
isCatchClauseVariableDeclarationOrBindingElement(declaration);
}
export function isCatchClauseVariableDeclarationOrBindingElement(declaration: Declaration) {
const node = getRootDeclaration(declaration);
return node.kind === SyntaxKind.VariableDeclaration && node.parent.kind === SyntaxKind.CatchClause;
}
export function isAmbientModule(node: Node): boolean {
@ -489,13 +494,6 @@ namespace ts {
}
}
export function isCatchClauseVariableDeclaration(declaration: Declaration) {
return declaration &&
declaration.kind === SyntaxKind.VariableDeclaration &&
declaration.parent &&
declaration.parent.kind === SyntaxKind.CatchClause;
}
// Return display name of an identifier
// Computed property names will just be emitted as "[<expr>]", where <expr> is the source
// text of the expression in the computed property.

View File

@ -1,10 +0,0 @@
tests/cases/compiler/catchClauseWithBindingPattern1.ts(3,8): error TS1195: Catch clause variable name must be an identifier.
==== tests/cases/compiler/catchClauseWithBindingPattern1.ts (1 errors) ====
try {
}
catch ({a}) {
~
!!! error TS1195: Catch clause variable name must be an identifier.
}

View File

@ -1,11 +0,0 @@
//// [catchClauseWithBindingPattern1.ts]
try {
}
catch ({a}) {
}
//// [catchClauseWithBindingPattern1.js]
try {
}
catch (a = (void 0).a) {
}

View File

@ -0,0 +1,59 @@
//// [destructuringCatch.ts]
try {
throw [0, 1];
}
catch ([a, b]) {
a + b;
}
try {
throw { a: 0, b: 1 };
}
catch ({a, b}) {
a + b;
}
try {
throw [{ x: [0], z: 1 }];
}
catch ([{x: [y], z}]) {
y + z;
}
// Test of comment ranges. A fix to GH#11755 should update this.
try {
}
catch (/*Test comment ranges*/[/*a*/a]) {
}
//// [destructuringCatch.js]
try {
throw [0, 1];
}
catch (_a) {
var a = _a[0], b = _a[1];
a + b;
}
try {
throw { a: 0, b: 1 };
}
catch (_b) {
var a = _b.a, b = _b.b;
a + b;
}
try {
throw [{ x: [0], z: 1 }];
}
catch (_c) {
var _d = _c[0], y = _d.x[0], z = _d.z;
y + z;
}
// Test of comment ranges. A fix to GH#11755 should update this.
try {
}
catch (_e) {
var /*a*/ a = _e[0];
}

View File

@ -0,0 +1,51 @@
=== tests/cases/conformance/es6/destructuring/destructuringCatch.ts ===
try {
throw [0, 1];
}
catch ([a, b]) {
>a : Symbol(a, Decl(destructuringCatch.ts, 4, 8))
>b : Symbol(b, Decl(destructuringCatch.ts, 4, 10))
a + b;
>a : Symbol(a, Decl(destructuringCatch.ts, 4, 8))
>b : Symbol(b, Decl(destructuringCatch.ts, 4, 10))
}
try {
throw { a: 0, b: 1 };
>a : Symbol(a, Decl(destructuringCatch.ts, 9, 11))
>b : Symbol(b, Decl(destructuringCatch.ts, 9, 17))
}
catch ({a, b}) {
>a : Symbol(a, Decl(destructuringCatch.ts, 11, 8))
>b : Symbol(b, Decl(destructuringCatch.ts, 11, 10))
a + b;
>a : Symbol(a, Decl(destructuringCatch.ts, 11, 8))
>b : Symbol(b, Decl(destructuringCatch.ts, 11, 10))
}
try {
throw [{ x: [0], z: 1 }];
>x : Symbol(x, Decl(destructuringCatch.ts, 16, 12))
>z : Symbol(z, Decl(destructuringCatch.ts, 16, 20))
}
catch ([{x: [y], z}]) {
>x : Symbol(x)
>y : Symbol(y, Decl(destructuringCatch.ts, 18, 13))
>z : Symbol(z, Decl(destructuringCatch.ts, 18, 16))
y + z;
>y : Symbol(y, Decl(destructuringCatch.ts, 18, 13))
>z : Symbol(z, Decl(destructuringCatch.ts, 18, 16))
}
// Test of comment ranges. A fix to GH#11755 should update this.
try {
}
catch (/*Test comment ranges*/[/*a*/a]) {
>a : Symbol(a, Decl(destructuringCatch.ts, 25, 31))
}

View File

@ -0,0 +1,65 @@
=== tests/cases/conformance/es6/destructuring/destructuringCatch.ts ===
try {
throw [0, 1];
>[0, 1] : number[]
>0 : 0
>1 : 1
}
catch ([a, b]) {
>a : any
>b : any
a + b;
>a + b : any
>a : any
>b : any
}
try {
throw { a: 0, b: 1 };
>{ a: 0, b: 1 } : { a: number; b: number; }
>a : number
>0 : 0
>b : number
>1 : 1
}
catch ({a, b}) {
>a : any
>b : any
a + b;
>a + b : any
>a : any
>b : any
}
try {
throw [{ x: [0], z: 1 }];
>[{ x: [0], z: 1 }] : { x: number[]; z: number; }[]
>{ x: [0], z: 1 } : { x: number[]; z: number; }
>x : number[]
>[0] : number[]
>0 : 0
>z : number
>1 : 1
}
catch ([{x: [y], z}]) {
>x : any
>y : any
>z : any
y + z;
>y + z : any
>y : any
>z : any
}
// Test of comment ranges. A fix to GH#11755 should update this.
try {
}
catch (/*Test comment ranges*/[/*a*/a]) {
>a : any
}

View File

@ -1,8 +1,11 @@
tests/cases/compiler/redeclareParameterInCatchBlock.ts(5,11): error TS2492: Cannot redeclare identifier 'e' in catch clause
tests/cases/compiler/redeclareParameterInCatchBlock.ts(11,9): error TS2492: Cannot redeclare identifier 'e' in catch clause
tests/cases/compiler/redeclareParameterInCatchBlock.ts(17,15): error TS2492: Cannot redeclare identifier 'b' in catch clause
tests/cases/compiler/redeclareParameterInCatchBlock.ts(22,15): error TS2451: Cannot redeclare block-scoped variable 'x'.
tests/cases/compiler/redeclareParameterInCatchBlock.ts(22,21): error TS2451: Cannot redeclare block-scoped variable 'x'.
==== tests/cases/compiler/redeclareParameterInCatchBlock.ts (2 errors) ====
==== tests/cases/compiler/redeclareParameterInCatchBlock.ts (5 errors) ====
try {
@ -22,10 +25,27 @@ tests/cases/compiler/redeclareParameterInCatchBlock.ts(11,9): error TS2492: Cann
try {
} catch ([a, b]) {
const [c, b] = [0, 1];
~
!!! error TS2492: Cannot redeclare identifier 'b' in catch clause
}
try {
} catch ({ a: x, b: x }) {
~
!!! error TS2451: Cannot redeclare block-scoped variable 'x'.
~
!!! error TS2451: Cannot redeclare block-scoped variable 'x'.
}
try {
} catch(e) {
function test() {
let e;
}
}

View File

@ -14,12 +14,23 @@ try {
try {
} catch ([a, b]) {
const [c, b] = [0, 1];
}
try {
} catch ({ a: x, b: x }) {
}
try {
} catch(e) {
function test() {
let e;
}
}
//// [redeclareParameterInCatchBlock.js]
@ -35,6 +46,15 @@ catch (e) {
}
try {
}
catch ([a, b]) {
const [c, b] = [0, 1];
}
try {
}
catch ({ a: x, b: x }) {
}
try {
}
catch (e) {
function test() {
let e;

View File

@ -1,4 +0,0 @@
try {
}
catch ({a}) {
}

View File

@ -14,9 +14,20 @@ try {
try {
} catch ([a, b]) {
const [c, b] = [0, 1];
}
try {
} catch ({ a: x, b: x }) {
}
try {
} catch(e) {
function test() {
let e;
}
}

View File

@ -0,0 +1,29 @@
// @noImplicitAny: true
try {
throw [0, 1];
}
catch ([a, b]) {
a + b;
}
try {
throw { a: 0, b: 1 };
}
catch ({a, b}) {
a + b;
}
try {
throw [{ x: [0], z: 1 }];
}
catch ([{x: [y], z}]) {
y + z;
}
// Test of comment ranges. A fix to GH#11755 should update this.
try {
}
catch (/*Test comment ranges*/[/*a*/a]) {
}