Skip to content

fix parse error for arrow function in rhs #31110

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
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
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -4485,6 +4485,10 @@
"category": "Error",
"code": 17017
},
"Arrow functions are not syntactically valid here. Consider wrapping the function in parentheses.": {
"category": "Error",
"code": 17018
},

"Circularity detected while resolving configuration: {0}": {
"category": "Error",
Expand Down
38 changes: 36 additions & 2 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3857,13 +3857,47 @@ namespace ts {
}
}
else {
leftOperand = makeBinaryExpression(leftOperand, parseTokenNode(), parseBinaryExpressionOrHigher(newPrecedence));
leftOperand = makeBinaryExpression(leftOperand, parseTokenNode<Token<BinaryOperator>>(), tryParseArrowFunctionExpressionOrExpression() || parseBinaryExpressionOrHigher(newPrecedence));
}
}

return leftOperand;
}

function tryParseArrowFunctionExpressionOrExpression() {
// Much of the time, users will write code along the lines of
//
// let x = foo || () => { /*...*/ }
//
// However, arrow functions aren't valid in those positions in the ECMAScript grammar.
// Despite that, we'll try to parse it out anyway and give a decent error message.
if (isParenthesizedArrowFunctionExpression() !== Tristate.False) {
const maybeArrowFunction = lookAhead(tryParseParenthesizedArrowFunctionExpression);
if (maybeArrowFunction) {
parseErrorAtNode(maybeArrowFunction, Diagnostics.Arrow_functions_are_not_syntactically_valid_here_Consider_wrapping_the_function_in_parentheses);
return maybeArrowFunction;
}
}
if (lookAhead(isStartOfSimpleArrowFunction)) {
const arrowFunction = tryParseAsyncSimpleArrowFunctionExpression() || parseSimpleArrowFunctionExpression(<Identifier>parseBinaryExpressionOrHigher(/*precedence*/ 0));
parseErrorAtNode(arrowFunction, Diagnostics.Arrow_functions_are_not_syntactically_valid_here_Consider_wrapping_the_function_in_parentheses);
return arrowFunction;
}
return undefined;
}

function parseErrorAtNode(node: Node, message: DiagnosticMessage, arg0?: any): void {
const start = skipTrivia(sourceFile.text, node.pos)
parseErrorAtPosition(start, node.end - start, message, arg0)
}

function isStartOfSimpleArrowFunction() {
if (token() === SyntaxKind.AsyncKeyword) {
nextToken();
return isIdentifier() && nextToken() === SyntaxKind.EqualsGreaterThanToken || token() === SyntaxKind.EqualsGreaterThanToken;
}
return isIdentifier() && nextToken() === SyntaxKind.EqualsGreaterThanToken;
}

function isBinaryOperator() {
if (inDisallowInContext() && token() === SyntaxKind.InKeyword) {
return false;
Expand Down
29 changes: 16 additions & 13 deletions tests/baselines/reference/ambiguousGenericAssertion1.errors.txt
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
tests/cases/compiler/ambiguousGenericAssertion1.ts(4,10): error TS1109: Expression expected.
tests/cases/compiler/ambiguousGenericAssertion1.ts(4,15): error TS2304: Cannot find name 'x'.
tests/cases/compiler/ambiguousGenericAssertion1.ts(4,16): error TS1005: ')' expected.
tests/cases/compiler/ambiguousGenericAssertion1.ts(4,19): error TS1005: ',' expected.
tests/cases/compiler/ambiguousGenericAssertion1.ts(4,21): error TS1005: ';' expected.
tests/cases/compiler/ambiguousGenericAssertion1.ts(4,10): error TS2365: Operator '>' cannot be applied to types 'number' and '(x: any) => boolean'.
tests/cases/compiler/ambiguousGenericAssertion1.ts(4,12): error TS2304: Cannot find name 'T'.
tests/cases/compiler/ambiguousGenericAssertion1.ts(4,14): error TS17018: Arrow functions are not syntactically valid here. Consider wrapping the function in parentheses.
tests/cases/compiler/ambiguousGenericAssertion1.ts(4,18): error TS2304: Cannot find name 'T'.
tests/cases/compiler/ambiguousGenericAssertion1.ts(4,24): error TS2304: Cannot find name 'T'.


==== tests/cases/compiler/ambiguousGenericAssertion1.ts (5 errors) ====
==== tests/cases/compiler/ambiguousGenericAssertion1.ts (6 errors) ====
function f<T>(x: T): T { return null; }
var r = <T>(x: T) => x;
var r2 = < <T>(x: T) => T>f; // valid
var r3 = <<T>(x: T) => T>f; // ambiguous, appears to the parser as a << operation
~~
!!! error TS1109: Expression expected.
~
!!! error TS2304: Cannot find name 'x'.
~
!!! error TS1005: ')' expected.
~
!!! error TS1005: ',' expected.
~~
!!! error TS1005: ';' expected.
~~~~
!!! error TS2365: Operator '>' cannot be applied to types 'number' and '(x: any) => boolean'.
~
!!! error TS2304: Cannot find name 'T'.
~~~~~~~~~~~~~
!!! error TS17018: Arrow functions are not syntactically valid here. Consider wrapping the function in parentheses.
~
!!! error TS2304: Cannot find name 'T'.
~
!!! error TS2304: Cannot find name 'T'.

4 changes: 2 additions & 2 deletions tests/baselines/reference/ambiguousGenericAssertion1.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ var r3 = <<T>(x: T) => T>f; // ambiguous, appears to the parser as a << operatio
function f(x) { return null; }
var r = function (x) { return x; };
var r2 = f; // valid
var r3 = << T > (x), T;
T > f; // ambiguous, appears to the parser as a << operation
var r3 = << T > (function (x) { return T > f; });
(function (x) { return T > f; }); // ambiguous, appears to the parser as a << operation
6 changes: 3 additions & 3 deletions tests/baselines/reference/ambiguousGenericAssertion1.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ var r2 = < <T>(x: T) => T>f; // valid

var r3 = <<T>(x: T) => T>f; // ambiguous, appears to the parser as a << operation
>r3 : Symbol(r3, Decl(ambiguousGenericAssertion1.ts, 3, 3))
>T : Symbol(T, Decl(ambiguousGenericAssertion1.ts, 3, 16))
>T : Symbol(T, Decl(ambiguousGenericAssertion1.ts, 3, 16))
>T : Symbol(T, Decl(ambiguousGenericAssertion1.ts, 3, 16))
>x : Symbol(x, Decl(ambiguousGenericAssertion1.ts, 3, 14))
>f : Symbol(f, Decl(ambiguousGenericAssertion1.ts, 0, 0))
>x : Symbol(x, Decl(ambiguousGenericAssertion1.ts, 3, 14))
>f : Symbol(f, Decl(ambiguousGenericAssertion1.ts, 0, 0))

8 changes: 6 additions & 2 deletions tests/baselines/reference/ambiguousGenericAssertion1.types
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,17 @@ var r2 = < <T>(x: T) => T>f; // valid

var r3 = <<T>(x: T) => T>f; // ambiguous, appears to the parser as a << operation
>r3 : boolean
><<T>(x : boolean
><<T> : boolean
><<T : number
> : any
>T : any
>(x : any
>(x: T) => T>f : (x: any) => boolean
>x : any
>T>f : boolean
>T : any
>f : <T>(x: T) => T
>(x: T) => T>f : (x: any) => boolean
>x : any
>T>f : boolean
>T : any
>f : <T>(x: T) => T
Expand Down
65 changes: 65 additions & 0 deletions tests/baselines/reference/incorrectArrowFunction.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
tests/cases/compiler/incorrectArrowFunction.ts(7,9): error TS17018: Arrow functions are not syntactically valid here. Consider wrapping the function in parentheses.
tests/cases/compiler/incorrectArrowFunction.ts(8,9): error TS17018: Arrow functions are not syntactically valid here. Consider wrapping the function in parentheses.
tests/cases/compiler/incorrectArrowFunction.ts(9,9): error TS17018: Arrow functions are not syntactically valid here. Consider wrapping the function in parentheses.
tests/cases/compiler/incorrectArrowFunction.ts(10,9): error TS17018: Arrow functions are not syntactically valid here. Consider wrapping the function in parentheses.
tests/cases/compiler/incorrectArrowFunction.ts(11,9): error TS17018: Arrow functions are not syntactically valid here. Consider wrapping the function in parentheses.
tests/cases/compiler/incorrectArrowFunction.ts(12,9): error TS17018: Arrow functions are not syntactically valid here. Consider wrapping the function in parentheses.
tests/cases/compiler/incorrectArrowFunction.ts(13,9): error TS17018: Arrow functions are not syntactically valid here. Consider wrapping the function in parentheses.
tests/cases/compiler/incorrectArrowFunction.ts(14,9): error TS17018: Arrow functions are not syntactically valid here. Consider wrapping the function in parentheses.
tests/cases/compiler/incorrectArrowFunction.ts(15,9): error TS17018: Arrow functions are not syntactically valid here. Consider wrapping the function in parentheses.
tests/cases/compiler/incorrectArrowFunction.ts(16,9): error TS17018: Arrow functions are not syntactically valid here. Consider wrapping the function in parentheses.


==== tests/cases/compiler/incorrectArrowFunction.ts (10 errors) ====
declare const foo;

interface Foo {
a: number
}

true || () => 1;
~~~~~~~
!!! error TS17018: Arrow functions are not syntactically valid here. Consider wrapping the function in parentheses.
true || a => 1;
~~~~~~
!!! error TS17018: Arrow functions are not syntactically valid here. Consider wrapping the function in parentheses.
true || a => b => 1;
~~~~~~~~~~~
!!! error TS17018: Arrow functions are not syntactically valid here. Consider wrapping the function in parentheses.
true || (a) => 2;
~~~~~~~~
!!! error TS17018: Arrow functions are not syntactically valid here. Consider wrapping the function in parentheses.
true || foo => 1;
~~~~~~~~
!!! error TS17018: Arrow functions are not syntactically valid here. Consider wrapping the function in parentheses.
true || (a: number) => 1;
~~~~~~~~~~~~~~~~
!!! error TS17018: Arrow functions are not syntactically valid here. Consider wrapping the function in parentheses.
true || ({ a }: Foo) => 1;
~~~~~~~~~~~~~~~~~
!!! error TS17018: Arrow functions are not syntactically valid here. Consider wrapping the function in parentheses.
true || async() => 1;
~~~~~~~~~~~~
!!! error TS17018: Arrow functions are not syntactically valid here. Consider wrapping the function in parentheses.
true || async a => 1;
~~~~~~~~~~~~
!!! error TS17018: Arrow functions are not syntactically valid here. Consider wrapping the function in parentheses.
true || async => 1;
~~~~~~~~~~
!!! error TS17018: Arrow functions are not syntactically valid here. Consider wrapping the function in parentheses.

true || (() => 1);
true || (a => 1);
true || ((a) => 2);
true || (foo => 1);
true || ((a: number) => 1);
true || (({ a }: Foo) => 1);
true || (async () => 1);
true || (async a => 1);
true || (async => 1);
(() => 1) || (() => 2);

true || (false);
true || false;
true || foo;

118 changes: 118 additions & 0 deletions tests/baselines/reference/incorrectArrowFunction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
//// [incorrectArrowFunction.ts]
declare const foo;

interface Foo {
a: number
}

true || () => 1;
true || a => 1;
true || a => b => 1;
true || (a) => 2;
true || foo => 1;
true || (a: number) => 1;
true || ({ a }: Foo) => 1;
true || async() => 1;
true || async a => 1;
true || async => 1;

true || (() => 1);
true || (a => 1);
true || ((a) => 2);
true || (foo => 1);
true || ((a: number) => 1);
true || (({ a }: Foo) => 1);
true || (async () => 1);
true || (async a => 1);
true || (async => 1);
(() => 1) || (() => 2);

true || (false);
true || false;
true || foo;


//// [incorrectArrowFunction.js]
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var _this = this;
true || function () { return 1; };
(function () { return 1; });
true || function (a) { return 1; };
true || function (a) { return function (b) { return 1; }; };
true || function (a) { return 2; };
(function (a) { return 2; });
true || function (foo) { return 1; };
true || (function (a) { return 1; });
(function (a) { return 1; });
true || (function (_a) {
var a = _a.a;
return 1;
});
(function (_a) {
var a = _a.a;
return 1;
});
true || (function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
return [2 /*return*/, 1];
}); }); });
(function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
return [2 /*return*/, 1];
}); }); });
true || (function (a) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
return [2 /*return*/, 1];
}); }); });
true || function (async) { return 1; };
true || (function () { return 1; });
true || (function (a) { return 1; });
true || (function (a) { return 2; });
true || (function (foo) { return 1; });
true || (function (a) { return 1; });
true || (function (_a) {
var a = _a.a;
return 1;
});
true || (function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
return [2 /*return*/, 1];
}); }); });
true || (function (a) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
return [2 /*return*/, 1];
}); }); });
true || (function (async) { return 1; });
(function () { return 1; }) || (function () { return 2; });
true || (false);
true || false;
true || foo;
Loading