mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-03-06 12:32:12 -06:00
Allow destructuring in catch clauses
This commit is contained in:
parent
3f234f2e7f
commit
fab0859869
@ -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.
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
//// [catchClauseWithBindingPattern1.ts]
|
||||
try {
|
||||
}
|
||||
catch ({a}) {
|
||||
}
|
||||
|
||||
//// [catchClauseWithBindingPattern1.js]
|
||||
try {
|
||||
}
|
||||
catch (a = (void 0).a) {
|
||||
}
|
||||
59
tests/baselines/reference/destructuringCatch.js
Normal file
59
tests/baselines/reference/destructuringCatch.js
Normal 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];
|
||||
}
|
||||
51
tests/baselines/reference/destructuringCatch.symbols
Normal file
51
tests/baselines/reference/destructuringCatch.symbols
Normal 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))
|
||||
|
||||
}
|
||||
|
||||
65
tests/baselines/reference/destructuringCatch.types
Normal file
65
tests/baselines/reference/destructuringCatch.types
Normal 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
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
try {
|
||||
}
|
||||
catch ({a}) {
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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]) {
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user