From c3055e585d014a65319a548b52a0b01c0cfbbe83 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 22 May 2019 11:20:07 -0700 Subject: [PATCH] Fix compiler crash with object rest in catch binding (#31522) --- src/compiler/checker.ts | 7 +++++- src/compiler/transformers/es2018.ts | 24 +++++++++++++++++++ .../baselines/reference/objectRestCatchES5.js | 21 ++++++++++++++++ .../reference/objectRestCatchES5.symbols | 9 +++++++ .../reference/objectRestCatchES5.types | 11 +++++++++ .../types/rest/objectRestCatchES5.ts | 2 ++ 6 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/objectRestCatchES5.js create mode 100644 tests/baselines/reference/objectRestCatchES5.symbols create mode 100644 tests/baselines/reference/objectRestCatchES5.types create mode 100644 tests/cases/conformance/types/rest/objectRestCatchES5.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9e5c864f6b975..fe17a6fbfedc5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -30169,12 +30169,17 @@ namespace ts { return undefined; } + function isSymbolOfDestructuredElementOfCatchBinding(symbol: Symbol) { + return isBindingElement(symbol.valueDeclaration) + && walkUpBindingElementsAndPatterns(symbol.valueDeclaration).parent.kind === SyntaxKind.CatchClause; + } + function isSymbolOfDeclarationWithCollidingName(symbol: Symbol): boolean { if (symbol.flags & SymbolFlags.BlockScoped && !isSourceFile(symbol.valueDeclaration)) { const links = getSymbolLinks(symbol); if (links.isDeclarationWithCollidingName === undefined) { const container = getEnclosingBlockScopeContainer(symbol.valueDeclaration); - if (isStatementWithLocals(container)) { + if (isStatementWithLocals(container) || isSymbolOfDestructuredElementOfCatchBinding(symbol)) { const nodeLinks = getNodeLinks(symbol.valueDeclaration); if (resolveName(container.parent, symbol.escapedName, SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ false)) { // redeclaration - always should be renamed diff --git a/src/compiler/transformers/es2018.ts b/src/compiler/transformers/es2018.ts index 504870b4ff784..89c43dcf599cf 100644 --- a/src/compiler/transformers/es2018.ts +++ b/src/compiler/transformers/es2018.ts @@ -77,6 +77,8 @@ namespace ts { return visitObjectLiteralExpression(node as ObjectLiteralExpression); case SyntaxKind.BinaryExpression: return visitBinaryExpression(node as BinaryExpression, noDestructuringValue); + case SyntaxKind.CatchClause: + return visitCatchClause(node as CatchClause); case SyntaxKind.VariableDeclaration: return visitVariableDeclaration(node as VariableDeclaration); case SyntaxKind.ForOfStatement: @@ -272,6 +274,28 @@ namespace ts { return visitEachChild(node, visitor, context); } + function visitCatchClause(node: CatchClause) { + if (node.variableDeclaration && + isBindingPattern(node.variableDeclaration.name) && + node.variableDeclaration.name.transformFlags & TransformFlags.ContainsObjectRestOrSpread) { + const name = getGeneratedNameForNode(node.variableDeclaration.name); + const updatedDecl = updateVariableDeclaration(node.variableDeclaration, node.variableDeclaration.name, /*type*/ undefined, name); + const visitedBindings = flattenDestructuringBinding(updatedDecl, visitor, context, FlattenLevel.ObjectRest); + let block = visitNode(node.block, visitor, isBlock); + if (some(visitedBindings)) { + block = updateBlock(block, [ + createVariableStatement(/*modifiers*/ undefined, visitedBindings), + ...block.statements, + ]); + } + return updateCatchClause( + node, + updateVariableDeclaration(node.variableDeclaration, name, /*type*/ undefined, /*initializer*/ undefined), + block); + } + return visitEachChild(node, visitor, context); + } + /** * Visits a VariableDeclaration node with a binding pattern. * diff --git a/tests/baselines/reference/objectRestCatchES5.js b/tests/baselines/reference/objectRestCatchES5.js new file mode 100644 index 0000000000000..210e8cec3bfea --- /dev/null +++ b/tests/baselines/reference/objectRestCatchES5.js @@ -0,0 +1,21 @@ +//// [objectRestCatchES5.ts] +let a = 1, b = 2; +try {} catch ({ a, ...b }) {} + +//// [objectRestCatchES5.js] +var __rest = (this && this.__rest) || function (s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +}; +var a = 1, b = 2; +try { } +catch (_a) { + var a_1 = _a.a, b_1 = __rest(_a, ["a"]); +} diff --git a/tests/baselines/reference/objectRestCatchES5.symbols b/tests/baselines/reference/objectRestCatchES5.symbols new file mode 100644 index 0000000000000..007424bd6c32c --- /dev/null +++ b/tests/baselines/reference/objectRestCatchES5.symbols @@ -0,0 +1,9 @@ +=== tests/cases/conformance/types/rest/objectRestCatchES5.ts === +let a = 1, b = 2; +>a : Symbol(a, Decl(objectRestCatchES5.ts, 0, 3)) +>b : Symbol(b, Decl(objectRestCatchES5.ts, 0, 10)) + +try {} catch ({ a, ...b }) {} +>a : Symbol(a, Decl(objectRestCatchES5.ts, 1, 15)) +>b : Symbol(b, Decl(objectRestCatchES5.ts, 1, 18)) + diff --git a/tests/baselines/reference/objectRestCatchES5.types b/tests/baselines/reference/objectRestCatchES5.types new file mode 100644 index 0000000000000..3d6b0a3e69530 --- /dev/null +++ b/tests/baselines/reference/objectRestCatchES5.types @@ -0,0 +1,11 @@ +=== tests/cases/conformance/types/rest/objectRestCatchES5.ts === +let a = 1, b = 2; +>a : number +>1 : 1 +>b : number +>2 : 2 + +try {} catch ({ a, ...b }) {} +>a : any +>b : any + diff --git a/tests/cases/conformance/types/rest/objectRestCatchES5.ts b/tests/cases/conformance/types/rest/objectRestCatchES5.ts new file mode 100644 index 0000000000000..0e568d32b5863 --- /dev/null +++ b/tests/cases/conformance/types/rest/objectRestCatchES5.ts @@ -0,0 +1,2 @@ +let a = 1, b = 2; +try {} catch ({ a, ...b }) {} \ No newline at end of file