Initial support for throw expressions

This commit is contained in:
Ron Buckton
2017-09-12 16:54:44 -07:00
parent 2fbc2259a1
commit e7ec9b132c
18 changed files with 307 additions and 3 deletions
+4
View File
@@ -3255,6 +3255,10 @@ namespace ts {
let excludeFlags = TransformFlags.NodeExcludes;
switch (kind) {
case SyntaxKind.ThrowExpression:
transformFlags |= TransformFlags.AssertESNext;
break;
case SyntaxKind.AsyncKeyword:
case SyntaxKind.AwaitExpression:
// async/await is ES2017 syntax, but may be ESNext syntax (for async generators)
+7
View File
@@ -17178,6 +17178,11 @@ namespace ts {
return booleanType;
}
function checkThrowExpression(node: ThrowExpression): Type {
checkExpression(node.expression);
return neverType;
}
function checkTypeOfExpression(node: TypeOfExpression): Type {
checkExpression(node.expression);
return typeofType;
@@ -18140,6 +18145,8 @@ namespace ts {
return checkMetaProperty(<MetaProperty>node);
case SyntaxKind.DeleteExpression:
return checkDeleteExpression(<DeleteExpression>node);
case SyntaxKind.ThrowExpression:
return checkThrowExpression(<ThrowExpression>node);
case SyntaxKind.VoidExpression:
return checkVoidExpression(<VoidExpression>node);
case SyntaxKind.AwaitExpression:
+7
View File
@@ -784,6 +784,8 @@ namespace ts {
return emitArrowFunction(<ArrowFunction>node);
case SyntaxKind.DeleteExpression:
return emitDeleteExpression(<DeleteExpression>node);
case SyntaxKind.ThrowExpression:
return emitThrowExpression(<ThrowExpression>node);
case SyntaxKind.TypeOfExpression:
return emitTypeOfExpression(<TypeOfExpression>node);
case SyntaxKind.VoidExpression:
@@ -1300,6 +1302,11 @@ namespace ts {
emitExpression(node.expression);
}
function emitThrowExpression(node: ThrowExpression) {
write("throw ");
emitExpression(node.expression);
}
function emitTypeOfExpression(node: TypeOfExpression) {
write("typeof ");
emitExpression(node.expression);
+12
View File
@@ -1074,6 +1074,18 @@ namespace ts {
: node;
}
export function createThrowExpression(expression: Expression) {
const node = <ThrowExpression>createSynthesizedNode(SyntaxKind.ThrowExpression);
node.expression = parenthesizePrefixOperand(expression);
return node;
}
export function updateThrowExpression(node: ThrowExpression, expression: Expression) {
return node.expression !== expression
? updateNode(createThrowExpression(expression), node)
: node;
}
export function createTypeOf(expression: Expression) {
const node = <TypeOfExpression>createSynthesizedNode(SyntaxKind.TypeOfExpression);
node.expression = parenthesizePrefixOperand(expression);
+28 -1
View File
@@ -185,6 +185,8 @@ namespace ts {
return visitNode(cbNode, (<ParenthesizedExpression>node).expression);
case SyntaxKind.DeleteExpression:
return visitNode(cbNode, (<DeleteExpression>node).expression);
case SyntaxKind.ThrowExpression:
return visitNode(cbNode, (<ThrowExpression>node).expression);
case SyntaxKind.TypeOfExpression:
return visitNode(cbNode, (<TypeOfExpression>node).expression);
case SyntaxKind.VoidExpression:
@@ -2962,6 +2964,7 @@ namespace ts {
case SyntaxKind.ExclamationToken:
case SyntaxKind.DeleteKeyword:
case SyntaxKind.TypeOfKeyword:
case SyntaxKind.ThrowKeyword:
case SyntaxKind.VoidKeyword:
case SyntaxKind.PlusPlusToken:
case SyntaxKind.MinusMinusToken:
@@ -2986,11 +2989,21 @@ namespace ts {
}
function isStartOfExpressionStatement(): boolean {
// As per the grammar, none of '{' or 'function' or 'class' can start an expression statement.
// As per the grammar, none of '{', 'function', 'async [no LineTerminator here] function', 'class', 'let [', or 'throw' can start an expression statement:
//
// An |ExpressionStatement| cannot start with a U+007B (LEFT CURLY BRACKET) because that might make it ambiguous with a |Block|.
// An |ExpressionStatement| cannot start with the `function` or `class` keywords because that would make it ambiguous with a |FunctionDeclaration|, a |GeneratorDeclaration|, or a |ClassDeclaration|.
// An |ExpressionStatement| cannot start with `async` `function` because that would make it ambiguous with an |AsyncFunctionDeclaration|.
// An |ExpressionStatement| cannot start with the two token sequence `let` `[` because that would make it ambiguous with a `let` |LexicalDeclaration| whose first |LexicalBinding| was an |ArrayBindingPattern|.
// An |ExpressionStatement| cannot start with `throw` because that would make it ambiguous with a |ThrowStatement|.
//
return token() !== SyntaxKind.OpenBraceToken &&
token() !== SyntaxKind.FunctionKeyword &&
token() !== SyntaxKind.ClassKeyword &&
token() !== SyntaxKind.ThrowKeyword &&
token() !== SyntaxKind.AtToken &&
(token() !== SyntaxKind.AsyncKeyword || !lookAhead(nextTokenIsFunctionKeywordOnSameLine)) &&
(token() !== SyntaxKind.LetKeyword || !lookAhead(nextTokenIsOpenBracket)) &&
isStartOfExpression();
}
@@ -3624,6 +3637,13 @@ namespace ts {
return finishNode(node);
}
function parseThrowExpression() {
const node = <ThrowExpression>createNode(SyntaxKind.ThrowExpression);
nextToken();
node.expression = parseSimpleUnaryExpression();
return finishNode(node);
}
function parseTypeOfExpression() {
const node = <TypeOfExpression>createNode(SyntaxKind.TypeOfExpression);
nextToken();
@@ -3730,6 +3750,8 @@ namespace ts {
return parsePrefixUnaryExpression();
case SyntaxKind.DeleteKeyword:
return parseDeleteExpression();
case SyntaxKind.ThrowKeyword:
return parseThrowExpression();
case SyntaxKind.TypeOfKeyword:
return parseTypeOfExpression();
case SyntaxKind.VoidKeyword:
@@ -3768,6 +3790,7 @@ namespace ts {
case SyntaxKind.TildeToken:
case SyntaxKind.ExclamationToken:
case SyntaxKind.DeleteKeyword:
case SyntaxKind.ThrowKeyword:
case SyntaxKind.TypeOfKeyword:
case SyntaxKind.VoidKeyword:
case SyntaxKind.AwaitKeyword:
@@ -5774,6 +5797,10 @@ namespace ts {
return nextToken() === SyntaxKind.OpenParenToken;
}
function nextTokenIsOpenBracket() {
return nextToken() === SyntaxKind.OpenBracketToken;
}
function nextTokenIsSlash() {
return nextToken() === SyntaxKind.SlashToken;
}
+24
View File
@@ -79,6 +79,8 @@ namespace ts {
return visitForOfStatement(node as ForOfStatement, /*outermostLabeledStatement*/ undefined);
case SyntaxKind.ForStatement:
return visitForStatement(node as ForStatement);
case SyntaxKind.ThrowExpression:
return visitThrowExpression(node as ThrowExpression);
case SyntaxKind.VoidExpression:
return visitVoidExpression(node as VoidExpression);
case SyntaxKind.Constructor:
@@ -278,6 +280,10 @@ namespace ts {
);
}
function visitThrowExpression(node: ThrowExpression) {
return createThrowHelper(context, visitNode(node.expression, visitor, isExpression), node);
}
function visitVoidExpression(node: VoidExpression) {
return visitEachChild(node, visitorNoDestructuringValue, context);
}
@@ -987,4 +993,22 @@ namespace ts {
location
);
}
const throwHelper: EmitHelper = {
name: "typescript:throw",
scoped: false,
text: `var __throw = (this && this.__throw) || function (e) { throw e; };`
};
function createThrowHelper(context: TransformationContext, expression: Expression, location?: TextRange) {
context.requestEmitHelper(throwHelper);
return setTextRange(
createCall(
getHelperName("__throw"),
/*typeArguments*/ undefined,
[expression]
),
location
);
}
}
+6
View File
@@ -257,6 +257,7 @@ namespace ts {
FunctionExpression,
ArrowFunction,
DeleteExpression,
ThrowExpression,
TypeOfExpression,
VoidExpression,
AwaitExpression,
@@ -1155,6 +1156,11 @@ namespace ts {
expression: UnaryExpression;
}
export interface ThrowExpression extends UnaryExpression {
kind: SyntaxKind.ThrowExpression;
expression: UnaryExpression;
}
export interface TypeOfExpression extends UnaryExpression {
kind: SyntaxKind.TypeOfExpression;
expression: UnaryExpression;
+8
View File
@@ -1227,6 +1227,7 @@ namespace ts {
case SyntaxKind.ArrowFunction:
case SyntaxKind.VoidExpression:
case SyntaxKind.DeleteExpression:
case SyntaxKind.ThrowExpression:
case SyntaxKind.TypeOfExpression:
case SyntaxKind.PrefixUnaryExpression:
case SyntaxKind.PostfixUnaryExpression:
@@ -2129,6 +2130,7 @@ namespace ts {
case SyntaxKind.PrefixUnaryExpression:
case SyntaxKind.TypeOfExpression:
case SyntaxKind.VoidExpression:
case SyntaxKind.ThrowExpression:
case SyntaxKind.DeleteExpression:
case SyntaxKind.AwaitExpression:
case SyntaxKind.ConditionalExpression:
@@ -2216,6 +2218,7 @@ namespace ts {
case SyntaxKind.PrefixUnaryExpression:
case SyntaxKind.TypeOfExpression:
case SyntaxKind.VoidExpression:
case SyntaxKind.ThrowExpression:
case SyntaxKind.DeleteExpression:
case SyntaxKind.AwaitExpression:
return 15;
@@ -4294,6 +4297,10 @@ namespace ts {
return node.kind === SyntaxKind.DeleteExpression;
}
export function isThrowExpression(node: Node): node is ThrowExpression {
return node.kind === SyntaxKind.ThrowExpression;
}
export function isTypeOfExpression(node: Node): node is TypeOfExpression {
return node.kind === SyntaxKind.AwaitExpression;
}
@@ -5112,6 +5119,7 @@ namespace ts {
case SyntaxKind.PrefixUnaryExpression:
case SyntaxKind.PostfixUnaryExpression:
case SyntaxKind.DeleteExpression:
case SyntaxKind.ThrowExpression:
case SyntaxKind.TypeOfExpression:
case SyntaxKind.VoidExpression:
case SyntaxKind.AwaitExpression:
+6 -1
View File
@@ -495,6 +495,10 @@ namespace ts {
return updateDelete(<DeleteExpression>node,
visitNode((<DeleteExpression>node).expression, visitor, isExpression));
case SyntaxKind.ThrowExpression:
return updateThrowExpression(<ThrowExpression>node,
visitNode((<ThrowExpression>node).expression, visitor, isExpression));
case SyntaxKind.TypeOfExpression:
return updateTypeOf(<TypeOfExpression>node,
visitNode((<TypeOfExpression>node).expression, visitor, isExpression));
@@ -1104,13 +1108,14 @@ namespace ts {
case SyntaxKind.ParenthesizedExpression:
case SyntaxKind.DeleteExpression:
case SyntaxKind.ThrowExpression:
case SyntaxKind.TypeOfExpression:
case SyntaxKind.VoidExpression:
case SyntaxKind.AwaitExpression:
case SyntaxKind.YieldExpression:
case SyntaxKind.SpreadElement:
case SyntaxKind.NonNullExpression:
result = reduceNode((<ParenthesizedExpression | DeleteExpression | TypeOfExpression | VoidExpression | AwaitExpression | YieldExpression | SpreadElement | NonNullExpression>node).expression, cbNode, result);
result = reduceNode((<ParenthesizedExpression | DeleteExpression | ThrowExpression | TypeOfExpression | VoidExpression | AwaitExpression | YieldExpression | SpreadElement | NonNullExpression>node).expression, cbNode, result);
break;
case SyntaxKind.PrefixUnaryExpression:
+2 -1
View File
@@ -535,12 +535,13 @@ namespace ts {
case SyntaxKind.TypeQuery:
return isCompletedNode((<TypeQueryNode>n).exprName, sourceFile);
case SyntaxKind.ThrowExpression:
case SyntaxKind.TypeOfExpression:
case SyntaxKind.DeleteExpression:
case SyntaxKind.VoidExpression:
case SyntaxKind.YieldExpression:
case SyntaxKind.SpreadElement:
const unaryWordExpression = n as (TypeOfExpression | DeleteExpression | VoidExpression | YieldExpression | SpreadElement);
const unaryWordExpression = n as (ThrowExpression | TypeOfExpression | DeleteExpression | VoidExpression | YieldExpression | SpreadElement);
return isCompletedNode(unaryWordExpression.expression, sourceFile);
case SyntaxKind.TaggedTemplateExpression:
@@ -0,0 +1,16 @@
//// [throwExpressions.es2015.ts]
declare const condition: boolean;
const a = condition ? 1 : throw new Error();
const b = condition || throw new Error();
function c(d = throw new TypeError()) { }
const x = "x", y = "y", z = "z";
const w = condition ? throw true ? x : y : z;
//// [throwExpressions.es2015.js]
var __throw = (this && this.__throw) || function (e) { throw e; };
const a = condition ? 1 : __throw(new Error());
const b = condition || __throw(new Error());
function c(d = __throw(new TypeError())) { }
const x = "x", y = "y", z = "z";
const w = condition ? __throw(true) ? x : y : z;
@@ -0,0 +1,31 @@
=== tests/cases/conformance/expressions/throwExpressions/throwExpressions.es2015.ts ===
declare const condition: boolean;
>condition : Symbol(condition, Decl(throwExpressions.es2015.ts, 0, 13))
const a = condition ? 1 : throw new Error();
>a : Symbol(a, Decl(throwExpressions.es2015.ts, 1, 5))
>condition : Symbol(condition, Decl(throwExpressions.es2015.ts, 0, 13))
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
const b = condition || throw new Error();
>b : Symbol(b, Decl(throwExpressions.es2015.ts, 2, 5))
>condition : Symbol(condition, Decl(throwExpressions.es2015.ts, 0, 13))
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
function c(d = throw new TypeError()) { }
>c : Symbol(c, Decl(throwExpressions.es2015.ts, 2, 41))
>d : Symbol(d, Decl(throwExpressions.es2015.ts, 3, 11))
>TypeError : Symbol(TypeError, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
const x = "x", y = "y", z = "z";
>x : Symbol(x, Decl(throwExpressions.es2015.ts, 5, 5))
>y : Symbol(y, Decl(throwExpressions.es2015.ts, 5, 14))
>z : Symbol(z, Decl(throwExpressions.es2015.ts, 5, 23))
const w = condition ? throw true ? x : y : z;
>w : Symbol(w, Decl(throwExpressions.es2015.ts, 6, 5))
>condition : Symbol(condition, Decl(throwExpressions.es2015.ts, 0, 13))
>x : Symbol(x, Decl(throwExpressions.es2015.ts, 5, 5))
>y : Symbol(y, Decl(throwExpressions.es2015.ts, 5, 14))
>z : Symbol(z, Decl(throwExpressions.es2015.ts, 5, 23))
@@ -0,0 +1,47 @@
=== tests/cases/conformance/expressions/throwExpressions/throwExpressions.es2015.ts ===
declare const condition: boolean;
>condition : boolean
const a = condition ? 1 : throw new Error();
>a : 1
>condition ? 1 : throw new Error() : 1
>condition : boolean
>1 : 1
>throw new Error() : never
>new Error() : Error
>Error : ErrorConstructor
const b = condition || throw new Error();
>b : true
>condition || throw new Error() : true
>condition : true
>throw new Error() : never
>new Error() : Error
>Error : ErrorConstructor
function c(d = throw new TypeError()) { }
>c : (d?: never) => void
>d : never
>throw new TypeError() : never
>new TypeError() : TypeError
>TypeError : TypeErrorConstructor
const x = "x", y = "y", z = "z";
>x : "x"
>"x" : "x"
>y : "y"
>"y" : "y"
>z : "z"
>"z" : "z"
const w = condition ? throw true ? x : y : z;
>w : "x" | "y" | "z"
>condition ? throw true ? x : y : z : "x" | "y" | "z"
>condition : true
>throw true ? x : y : "x" | "y"
>throw true : never
>true : true
>x : "x"
>y : "y"
>z : "z"
@@ -0,0 +1,15 @@
//// [throwExpressions.esnext.ts]
declare const condition: boolean;
const a = condition ? 1 : throw new Error();
const b = condition || throw new Error();
function c(d = throw new TypeError()) { }
const x = "x", y = "y", z = "z";
const w = condition ? throw true ? x : y : z;
//// [throwExpressions.esnext.js]
const a = condition ? 1 : throw new Error();
const b = condition || throw new Error();
function c(d = throw new TypeError()) { }
const x = "x", y = "y", z = "z";
const w = condition ? throw true ? x : y : z;
@@ -0,0 +1,31 @@
=== tests/cases/conformance/expressions/throwExpressions/throwExpressions.esnext.ts ===
declare const condition: boolean;
>condition : Symbol(condition, Decl(throwExpressions.esnext.ts, 0, 13))
const a = condition ? 1 : throw new Error();
>a : Symbol(a, Decl(throwExpressions.esnext.ts, 1, 5))
>condition : Symbol(condition, Decl(throwExpressions.esnext.ts, 0, 13))
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
const b = condition || throw new Error();
>b : Symbol(b, Decl(throwExpressions.esnext.ts, 2, 5))
>condition : Symbol(condition, Decl(throwExpressions.esnext.ts, 0, 13))
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
function c(d = throw new TypeError()) { }
>c : Symbol(c, Decl(throwExpressions.esnext.ts, 2, 41))
>d : Symbol(d, Decl(throwExpressions.esnext.ts, 3, 11))
>TypeError : Symbol(TypeError, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
const x = "x", y = "y", z = "z";
>x : Symbol(x, Decl(throwExpressions.esnext.ts, 5, 5))
>y : Symbol(y, Decl(throwExpressions.esnext.ts, 5, 14))
>z : Symbol(z, Decl(throwExpressions.esnext.ts, 5, 23))
const w = condition ? throw true ? x : y : z;
>w : Symbol(w, Decl(throwExpressions.esnext.ts, 6, 5))
>condition : Symbol(condition, Decl(throwExpressions.esnext.ts, 0, 13))
>x : Symbol(x, Decl(throwExpressions.esnext.ts, 5, 5))
>y : Symbol(y, Decl(throwExpressions.esnext.ts, 5, 14))
>z : Symbol(z, Decl(throwExpressions.esnext.ts, 5, 23))
@@ -0,0 +1,47 @@
=== tests/cases/conformance/expressions/throwExpressions/throwExpressions.esnext.ts ===
declare const condition: boolean;
>condition : boolean
const a = condition ? 1 : throw new Error();
>a : 1
>condition ? 1 : throw new Error() : 1
>condition : boolean
>1 : 1
>throw new Error() : never
>new Error() : Error
>Error : ErrorConstructor
const b = condition || throw new Error();
>b : true
>condition || throw new Error() : true
>condition : true
>throw new Error() : never
>new Error() : Error
>Error : ErrorConstructor
function c(d = throw new TypeError()) { }
>c : (d?: never) => void
>d : never
>throw new TypeError() : never
>new TypeError() : TypeError
>TypeError : TypeErrorConstructor
const x = "x", y = "y", z = "z";
>x : "x"
>"x" : "x"
>y : "y"
>"y" : "y"
>z : "z"
>"z" : "z"
const w = condition ? throw true ? x : y : z;
>w : "x" | "y" | "z"
>condition ? throw true ? x : y : z : "x" | "y" | "z"
>condition : true
>throw true ? x : y : "x" | "y"
>throw true : never
>true : true
>x : "x"
>y : "y"
>z : "z"
@@ -0,0 +1,8 @@
// @target: es2015
declare const condition: boolean;
const a = condition ? 1 : throw new Error();
const b = condition || throw new Error();
function c(d = throw new TypeError()) { }
const x = "x", y = "y", z = "z";
const w = condition ? throw true ? x : y : z;
@@ -0,0 +1,8 @@
// @target: esnext
declare const condition: boolean;
const a = condition ? 1 : throw new Error();
const b = condition || throw new Error();
function c(d = throw new TypeError()) { }
const x = "x", y = "y", z = "z";
const w = condition ? throw true ? x : y : z;