Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
40 changes: 40 additions & 0 deletions tsc/internal/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -4784,13 +4784,53 @@ func (p *Parser) parseBinaryExpressionRest(precedence ast.OperatorPrecedence, le
}
}
} else {
if operator == ast.KindAsteriskAsteriskToken {
p.checkExponentiationLeftOperand(leftOperand)
}
leftOperand = p.makeBinaryExpression(leftOperand, p.parseTokenNode(), p.parseBinaryExpressionOrHigher(newPrecedence), pos)
lastOperand = leftOperand
}
}
return leftOperand
}

// checkExponentiationLeftOperand reports an error when the left operand of an
// exponentiation expression is an 'as' or 'satisfies' expression whose operand could
// not appear in that position once the assertion is erased, mirroring the errors
// reported for the unasserted forms in parseUnaryExpressionOrHigher. For example,
// '-x as number ** 2' erases to '-x ** 2', which is not valid JavaScript.
// See https://github.com/microsoft/TypeScript/issues/65170.
func (p *Parser) checkExponentiationLeftOperand(leftOperand *ast.Expression) {
expression := leftOperand
for expression.Kind == ast.KindAsExpression || expression.Kind == ast.KindSatisfiesExpression {
expression = expression.Expression()
}
if expression == leftOperand {
return
}
pos := scanner.SkipTrivia(p.sourceText, leftOperand.Pos())
end := leftOperand.End()
switch expression.Kind {
case ast.KindPrefixUnaryExpression:
// Prefix '++' and '--' form update expressions, which are valid left
// operands of an exponentiation expression even after erasure.
switch operator := expression.AsPrefixUnaryExpression().Operator; operator {
case ast.KindPlusToken, ast.KindMinusToken, ast.KindTildeToken, ast.KindExclamationToken:
p.parseErrorAt(pos, end, diagnostics.An_unary_expression_with_the_0_operator_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses, scanner.TokenToString(operator))
}
case ast.KindDeleteExpression:
p.parseErrorAt(pos, end, diagnostics.An_unary_expression_with_the_0_operator_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses, scanner.TokenToString(ast.KindDeleteKeyword))
case ast.KindTypeOfExpression:
p.parseErrorAt(pos, end, diagnostics.An_unary_expression_with_the_0_operator_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses, scanner.TokenToString(ast.KindTypeOfKeyword))
case ast.KindVoidExpression:
p.parseErrorAt(pos, end, diagnostics.An_unary_expression_with_the_0_operator_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses, scanner.TokenToString(ast.KindVoidKeyword))
case ast.KindAwaitExpression:
p.parseErrorAt(pos, end, diagnostics.An_unary_expression_with_the_0_operator_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses, scanner.TokenToString(ast.KindAwaitKeyword))
case ast.KindTypeAssertionExpression:
p.parseErrorAt(pos, end, diagnostics.A_type_assertion_expression_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses)
}
}

func (p *Parser) makeSatisfiesExpression(expression *ast.Expression, typeNode *ast.TypeNode) *ast.Node {
return p.checkJSSyntax(p.finishNode(p.factory.NewSatisfiesExpression(expression, typeNode), expression.Pos()))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,23 @@ disallowUnerasableAssertion.ts(71,44): error TS1005: ',' expected.
disallowUnerasableAssertion.ts(72,58): error TS1005: ',' expected.
disallowUnerasableAssertion.ts(79,44): error TS1005: ',' expected.
disallowUnerasableAssertion.ts(80,58): error TS1005: ',' expected.
disallowUnerasableAssertion.ts(93,20): error TS17006: An unary expression with the '-' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.
disallowUnerasableAssertion.ts(94,20): error TS17006: An unary expression with the '-' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.
disallowUnerasableAssertion.ts(95,20): error TS17006: An unary expression with the '-' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.
disallowUnerasableAssertion.ts(96,20): error TS17006: An unary expression with the '+' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.
disallowUnerasableAssertion.ts(97,20): error TS17006: An unary expression with the '~' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.
disallowUnerasableAssertion.ts(98,20): error TS17006: An unary expression with the '!' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.
disallowUnerasableAssertion.ts(99,20): error TS17006: An unary expression with the 'typeof' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.
disallowUnerasableAssertion.ts(100,20): error TS17006: An unary expression with the 'void' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.
disallowUnerasableAssertion.ts(101,20): error TS17006: An unary expression with the 'delete' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.
disallowUnerasableAssertion.ts(102,38): error TS17006: An unary expression with the 'await' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.
disallowUnerasableAssertion.ts(103,20): error TS17007: A type assertion expression is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.
disallowUnerasableAssertion.ts(104,20): error TS17006: An unary expression with the '-' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.
disallowUnerasableAssertion.ts(105,20): error TS17006: An unary expression with the '-' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.
disallowUnerasableAssertion.ts(106,20): error TS17006: An unary expression with the '-' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.


==== disallowUnerasableAssertion.ts (12 errors) ====
==== disallowUnerasableAssertion.ts (26 errors) ====
// https://github.com/microsoft/TypeScript/issues/63527

// Expressions of the form 'a ## b as T $$ c' are errors when $$ would bind before ## after
Expand Down Expand Up @@ -117,4 +131,68 @@ disallowUnerasableAssertion.ts(80,58): error TS1005: ',' expected.
export const y28 = 2 ** 3 satisfies any satisfies number ** 2; // Error
~~
!!! error TS1005: ',' expected.

// https://github.com/microsoft/TypeScript/issues/65170

// Erasing an assertion must not expose an invalid left operand of '**' either.
// A unary, await, or type assertion expression in that position is an error,
// just as it is without the assertion.

declare const num: number;
declare const promised: Promise<number>;
declare const obj: { y?: number };
declare let mutableNum: number;

export const z01 = -num as number ** 2; // Error
~~~~~~~~~~~~~~
!!! error TS17006: An unary expression with the '-' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.
export const z02 = -num satisfies number ** 2; // Error
~~~~~~~~~~~~~~~~~~~~~
!!! error TS17006: An unary expression with the '-' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.
export const z03 = -num as any as number ** 2; // Error
~~~~~~~~~~~~~~~~~~~~~
!!! error TS17006: An unary expression with the '-' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.
export const z04 = +num as number ** 2; // Error
~~~~~~~~~~~~~~
!!! error TS17006: An unary expression with the '+' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.
export const z05 = ~num as number ** 2; // Error
~~~~~~~~~~~~~~
!!! error TS17006: An unary expression with the '~' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.
export const z06 = !num as any ** 2; // Error
~~~~~~~~~~~
!!! error TS17006: An unary expression with the '!' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.
export const z07 = typeof num as any ** 2; // Error
~~~~~~~~~~~~~~~~~
!!! error TS17006: An unary expression with the 'typeof' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.
export const z08 = void num as any ** 2; // Error
~~~~~~~~~~~~~~~
!!! error TS17006: An unary expression with the 'void' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.
export const z09 = delete obj.y as any ** 2; // Error
~~~~~~~~~~~~~~~~~~~
!!! error TS17006: An unary expression with the 'delete' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.
export async function z10() { return await promised as number ** 2; } // Error
~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS17006: An unary expression with the 'await' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.
export const z11 = <number>-num as any ** 2; // Error
~~~~~~~~~~~~~~~~~~~
!!! error TS17007: A type assertion expression is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.
export const z12 = -(num) as number ** 2; // Error
~~~~~~~~~~~~~~~~
!!! error TS17006: An unary expression with the '-' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.
export const z13 = -num! as number ** 2; // Error
~~~~~~~~~~~~~~~
!!! error TS17006: An unary expression with the '-' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.
export const z14 = -num as number satisfies number ** 2; // Error
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS17006: An unary expression with the '-' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.

export const z15 = (-num) as number ** 2;
export async function z16() { return (await promised) as number ** 2; }
export const z17 = num as number ** 2;
export const z18 = -num as number * 2;
export const z19 = num! as number ** 2;
export const z20 = (-num)! as number ** 2;
export const z21 = (-num as number) as any ** 2;
export const z22 = ++mutableNum as number ** 2;
export const z23 = 2 ** -num as number;

Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,42 @@ export const y26 = 2 * 3 satisfies any satisfies number * 2;
// Equal-precedence right-associative operators do not.
export const y27 = 2 ** 3 satisfies number ** 2; // Error
export const y28 = 2 ** 3 satisfies any satisfies number ** 2; // Error

// https://github.com/microsoft/TypeScript/issues/65170

// Erasing an assertion must not expose an invalid left operand of '**' either.
// A unary, await, or type assertion expression in that position is an error,
// just as it is without the assertion.

declare const num: number;
declare const promised: Promise<number>;
declare const obj: { y?: number };
declare let mutableNum: number;

export const z01 = -num as number ** 2; // Error
export const z02 = -num satisfies number ** 2; // Error
export const z03 = -num as any as number ** 2; // Error
export const z04 = +num as number ** 2; // Error
export const z05 = ~num as number ** 2; // Error
export const z06 = !num as any ** 2; // Error
export const z07 = typeof num as any ** 2; // Error
export const z08 = void num as any ** 2; // Error
export const z09 = delete obj.y as any ** 2; // Error
export async function z10() { return await promised as number ** 2; } // Error
export const z11 = <number>-num as any ** 2; // Error
export const z12 = -(num) as number ** 2; // Error
export const z13 = -num! as number ** 2; // Error
export const z14 = -num as number satisfies number ** 2; // Error

export const z15 = (-num) as number ** 2;
export async function z16() { return (await promised) as number ** 2; }
export const z17 = num as number ** 2;
export const z18 = -num as number * 2;
export const z19 = num! as number ** 2;
export const z20 = (-num)! as number ** 2;
export const z21 = (-num as number) as any ** 2;
export const z22 = ++mutableNum as number ** 2;
export const z23 = 2 ** -num as number;


//// [disallowUnerasableAssertion.js]
Expand Down Expand Up @@ -159,3 +195,26 @@ export const y27 = 2 ** 3;
** 2; // Error
export const y28 = 2 ** 3;
** 2; // Error
export const z01 = (-num) ** 2; // Error
export const z02 = (-num) ** 2; // Error
export const z03 = (-num) ** 2; // Error
export const z04 = (+num) ** 2; // Error
export const z05 = (~num) ** 2; // Error
export const z06 = (!num) ** 2; // Error
export const z07 = (typeof num) ** 2; // Error
export const z08 = (void num) ** 2; // Error
export const z09 = (delete obj.y) ** 2; // Error
export async function z10() { return (await promised) ** 2; } // Error
export const z11 = (-num) ** 2; // Error
export const z12 = (-(num)) ** 2; // Error
export const z13 = (-num) ** 2; // Error
export const z14 = (-num) ** 2; // Error
export const z15 = (-num) ** 2;
export async function z16() { return (await promised) ** 2; }
export const z17 = num ** 2;
export const z18 = -num * 2;
export const z19 = num ** 2;
export const z20 = (-num) ** 2;
export const z21 = (-num) ** 2;
export const z22 = (++mutableNum) ** 2;
export const z23 = 2 ** -num;
Loading