Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
7 changes: 7 additions & 0 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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:
Expand Down
7 changes: 7 additions & 0 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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);
Expand Down
12 changes: 12 additions & 0 deletions src/compiler/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
29 changes: 28 additions & 1 deletion src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand All @@ -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();
}

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -5774,6 +5797,10 @@ namespace ts {
return nextToken() === SyntaxKind.OpenParenToken;
}

function nextTokenIsOpenBracket() {
return nextToken() === SyntaxKind.OpenBracketToken;
}

function nextTokenIsSlash() {
return nextToken() === SyntaxKind.SlashToken;
}
Expand Down
24 changes: 24 additions & 0 deletions src/compiler/transformers/esnext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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 changes: 6 additions & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ namespace ts {
FunctionExpression,
ArrowFunction,
DeleteExpression,
ThrowExpression,
TypeOfExpression,
VoidExpression,
AwaitExpression,
Expand Down Expand Up @@ -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;
Expand Down
8 changes: 8 additions & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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:
Expand Down
7 changes: 6 additions & 1 deletion src/compiler/visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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:
Expand Down
3 changes: 2 additions & 1 deletion src/services/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
16 changes: 16 additions & 0 deletions tests/baselines/reference/throwExpressions.es2015.js
Original file line number Diff line number Diff line change
@@ -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;
31 changes: 31 additions & 0 deletions tests/baselines/reference/throwExpressions.es2015.symbols
Original file line number Diff line number Diff line change
@@ -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))

47 changes: 47 additions & 0 deletions tests/baselines/reference/throwExpressions.es2015.types
Original file line number Diff line number Diff line change
@@ -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"

Loading