diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index b3fc017eb050f..c0a2a558b97a4 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -4125,6 +4125,10 @@ "category": "Error", "code": 17017 }, + "Invalid arrow-function arguments, parentheses around the arrow-function may help.": { + "category": "Error", + "code": 17018 + }, "Circularity detected while resolving configuration: {0}": { "category": "Error", diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 070e774d82f7b..5558017bfadcc 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3791,7 +3791,18 @@ namespace ts { } } else { - leftOperand = makeBinaryExpression(leftOperand, parseTokenNode(), parseBinaryExpressionOrHigher(newPrecedence)); + const op = parseTokenNode>(); + if (isParenthesizedArrowFunctionExpression() !== Tristate.False) { + const maybeArrow = lookAhead(tryParseParenthesizedArrowFunctionExpression); + if (maybeArrow) { + parseErrorAtRange(maybeArrow, Diagnostics.Invalid_arrow_function_arguments_parentheses_around_the_arrow_function_may_help); + } + } + else if (lookAhead(isStartOfSimpleArrowFunction)) { + const maybeArrow = tryParseAsyncSimpleArrowFunctionExpression() || lookAhead(() => parseSimpleArrowFunctionExpression(parseBinaryExpressionOrHigher(/*precedence*/ 0))); + parseErrorAtRange(maybeArrow, Diagnostics.Invalid_arrow_function_arguments_parentheses_around_the_arrow_function_may_help); + } + leftOperand = makeBinaryExpression(leftOperand, op, parseBinaryExpressionOrHigher(newPrecedence)); } } @@ -6015,6 +6026,14 @@ namespace ts { return nextToken() === SyntaxKind.SlashToken; } + function isStartOfSimpleArrowFunction () { + if (token() === SyntaxKind.AsyncKeyword) { + nextToken(); + return isIdentifier() && nextToken() === SyntaxKind.EqualsGreaterThanToken || token() === SyntaxKind.EqualsGreaterThanToken; + } + return isIdentifier() && nextToken() === SyntaxKind.EqualsGreaterThanToken; + } + function parseNamespaceExportDeclaration(node: NamespaceExportDeclaration): NamespaceExportDeclaration { node.kind = SyntaxKind.NamespaceExportDeclaration; parseExpected(SyntaxKind.AsKeyword); diff --git a/tests/baselines/reference/ambiguousGenericAssertion1.errors.txt b/tests/baselines/reference/ambiguousGenericAssertion1.errors.txt index 54b463109b140..30bcca2d78a2c 100644 --- a/tests/baselines/reference/ambiguousGenericAssertion1.errors.txt +++ b/tests/baselines/reference/ambiguousGenericAssertion1.errors.txt @@ -1,17 +1,20 @@ tests/cases/compiler/ambiguousGenericAssertion1.ts(4,10): error TS1109: Expression expected. +tests/cases/compiler/ambiguousGenericAssertion1.ts(4,14): error TS17018: Invalid arrow-function arguments, parentheses around the arrow-function may help. 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 (5 errors) ==== +==== tests/cases/compiler/ambiguousGenericAssertion1.ts (6 errors) ==== function f(x: T): T { return null; } var r = (x: T) => x; var r2 = < (x: T) => T>f; // valid var r3 = <(x: T) => T>f; // ambiguous, appears to the parser as a << operation ~~ !!! error TS1109: Expression expected. + ~~~~~~~~~~~~~ +!!! error TS17018: Invalid arrow-function arguments, parentheses around the arrow-function may help. ~ !!! error TS2304: Cannot find name 'x'. ~ diff --git a/tests/baselines/reference/incorrectArrowFunction.errors.txt b/tests/baselines/reference/incorrectArrowFunction.errors.txt new file mode 100644 index 0000000000000..4c45e6590c4f1 --- /dev/null +++ b/tests/baselines/reference/incorrectArrowFunction.errors.txt @@ -0,0 +1,127 @@ +tests/cases/compiler/incorrectArrowFunction.ts(7,8): error TS17018: Invalid arrow-function arguments, parentheses around the arrow-function may help. +tests/cases/compiler/incorrectArrowFunction.ts(7,10): error TS1109: Expression expected. +tests/cases/compiler/incorrectArrowFunction.ts(7,12): error TS1005: ';' expected. +tests/cases/compiler/incorrectArrowFunction.ts(8,8): error TS17018: Invalid arrow-function arguments, parentheses around the arrow-function may help. +tests/cases/compiler/incorrectArrowFunction.ts(8,9): error TS2304: Cannot find name 'a'. +tests/cases/compiler/incorrectArrowFunction.ts(8,11): error TS1005: ';' expected. +tests/cases/compiler/incorrectArrowFunction.ts(9,8): error TS17018: Invalid arrow-function arguments, parentheses around the arrow-function may help. +tests/cases/compiler/incorrectArrowFunction.ts(9,10): error TS2304: Cannot find name 'a'. +tests/cases/compiler/incorrectArrowFunction.ts(9,13): error TS1005: ';' expected. +tests/cases/compiler/incorrectArrowFunction.ts(10,8): error TS17018: Invalid arrow-function arguments, parentheses around the arrow-function may help. +tests/cases/compiler/incorrectArrowFunction.ts(10,13): error TS1005: ';' expected. +tests/cases/compiler/incorrectArrowFunction.ts(11,8): error TS17018: Invalid arrow-function arguments, parentheses around the arrow-function may help. +tests/cases/compiler/incorrectArrowFunction.ts(11,10): error TS2304: Cannot find name 'a'. +tests/cases/compiler/incorrectArrowFunction.ts(11,11): error TS1005: ')' expected. +tests/cases/compiler/incorrectArrowFunction.ts(11,13): error TS2693: 'number' only refers to a type, but is being used as a value here. +tests/cases/compiler/incorrectArrowFunction.ts(11,19): error TS1005: ';' expected. +tests/cases/compiler/incorrectArrowFunction.ts(11,21): error TS1128: Declaration or statement expected. +tests/cases/compiler/incorrectArrowFunction.ts(12,8): error TS17018: Invalid arrow-function arguments, parentheses around the arrow-function may help. +tests/cases/compiler/incorrectArrowFunction.ts(12,12): error TS2304: Cannot find name 'a'. +tests/cases/compiler/incorrectArrowFunction.ts(12,15): error TS1005: ')' expected. +tests/cases/compiler/incorrectArrowFunction.ts(12,17): error TS2693: 'Foo' only refers to a type, but is being used as a value here. +tests/cases/compiler/incorrectArrowFunction.ts(12,20): error TS1005: ';' expected. +tests/cases/compiler/incorrectArrowFunction.ts(12,22): error TS1128: Declaration or statement expected. +tests/cases/compiler/incorrectArrowFunction.ts(13,8): error TS17018: Invalid arrow-function arguments, parentheses around the arrow-function may help. +tests/cases/compiler/incorrectArrowFunction.ts(13,9): error TS2304: Cannot find name 'async'. +tests/cases/compiler/incorrectArrowFunction.ts(13,18): error TS1005: ';' expected. +tests/cases/compiler/incorrectArrowFunction.ts(14,8): error TS17018: Invalid arrow-function arguments, parentheses around the arrow-function may help. +tests/cases/compiler/incorrectArrowFunction.ts(14,21): error TS1109: Expression expected. +tests/cases/compiler/incorrectArrowFunction.ts(15,8): error TS17018: Invalid arrow-function arguments, parentheses around the arrow-function may help. +tests/cases/compiler/incorrectArrowFunction.ts(15,9): error TS2304: Cannot find name 'async'. +tests/cases/compiler/incorrectArrowFunction.ts(15,15): error TS1005: ';' expected. + + +==== tests/cases/compiler/incorrectArrowFunction.ts (31 errors) ==== + declare const foo; + + interface Foo { + a: number + } + + true || () => 1; + ~~~~~~~~ +!!! error TS17018: Invalid arrow-function arguments, parentheses around the arrow-function may help. + ~ +!!! error TS1109: Expression expected. + ~~ +!!! error TS1005: ';' expected. + true || a => 1; + ~~~~~~~ +!!! error TS17018: Invalid arrow-function arguments, parentheses around the arrow-function may help. + ~ +!!! error TS2304: Cannot find name 'a'. + ~~ +!!! error TS1005: ';' expected. + true || (a) => 2; + ~~~~~~~~~ +!!! error TS17018: Invalid arrow-function arguments, parentheses around the arrow-function may help. + ~ +!!! error TS2304: Cannot find name 'a'. + ~~ +!!! error TS1005: ';' expected. + true || foo => 1; + ~~~~~~~~~ +!!! error TS17018: Invalid arrow-function arguments, parentheses around the arrow-function may help. + ~~ +!!! error TS1005: ';' expected. + true || (a: number) => 1; + ~~~~~~~~~~~~~~~~~ +!!! error TS17018: Invalid arrow-function arguments, parentheses around the arrow-function may help. + ~ +!!! error TS2304: Cannot find name 'a'. + ~ +!!! error TS1005: ')' expected. + ~~~~~~ +!!! error TS2693: 'number' only refers to a type, but is being used as a value here. + ~ +!!! error TS1005: ';' expected. + ~~ +!!! error TS1128: Declaration or statement expected. + true || ({ a }: Foo) => 1; + ~~~~~~~~~~~~~~~~~~ +!!! error TS17018: Invalid arrow-function arguments, parentheses around the arrow-function may help. + ~ +!!! error TS2304: Cannot find name 'a'. + ~ +!!! error TS1005: ')' expected. + ~~~ +!!! error TS2693: 'Foo' only refers to a type, but is being used as a value here. + ~ +!!! error TS1005: ';' expected. + ~~ +!!! error TS1128: Declaration or statement expected. + true || async () => 1; + ~~~~~~~~~~~~~~ +!!! error TS17018: Invalid arrow-function arguments, parentheses around the arrow-function may help. + ~~~~~ +!!! error TS2304: Cannot find name 'async'. + ~~ +!!! error TS1005: ';' expected. + true || async a => 1; + ~~~~~~~~~~~~~ +!!! error TS17018: Invalid arrow-function arguments, parentheses around the arrow-function may help. + ~ +!!! error TS1109: Expression expected. + true || async => 1; + ~~~~~~~~~~~ +!!! error TS17018: Invalid arrow-function arguments, parentheses around the arrow-function may help. + ~~~~~ +!!! error TS2304: Cannot find name 'async'. + ~~ +!!! error TS1005: ';' expected. + + 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; + \ No newline at end of file diff --git a/tests/baselines/reference/incorrectArrowFunction.js b/tests/baselines/reference/incorrectArrowFunction.js new file mode 100644 index 0000000000000..e6c91ed2945d9 --- /dev/null +++ b/tests/baselines/reference/incorrectArrowFunction.js @@ -0,0 +1,109 @@ +//// [incorrectArrowFunction.ts] +declare const foo; + +interface Foo { + a: number +} + +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; + +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 || (); +1; +true || a; +1; +true || (a); +2; +true || foo; +1; +true || (a); +number; +1; +true || ({ a: a }); +Foo; +1; +true || async(); +1; +true || ; +true || async; +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; diff --git a/tests/baselines/reference/incorrectArrowFunction.symbols b/tests/baselines/reference/incorrectArrowFunction.symbols new file mode 100644 index 0000000000000..3abf61d965a08 --- /dev/null +++ b/tests/baselines/reference/incorrectArrowFunction.symbols @@ -0,0 +1,56 @@ +=== tests/cases/compiler/incorrectArrowFunction.ts === +declare const foo; +>foo : Symbol(foo, Decl(incorrectArrowFunction.ts, 0, 13)) + +interface Foo { +>Foo : Symbol(Foo, Decl(incorrectArrowFunction.ts, 0, 18)) + + a: number +>a : Symbol(Foo.a, Decl(incorrectArrowFunction.ts, 2, 15)) +} + +true || () => 1; +true || a => 1; +true || (a) => 2; +true || foo => 1; +>foo : Symbol(foo, Decl(incorrectArrowFunction.ts, 0, 13)) + +true || (a: number) => 1; +true || ({ a }: Foo) => 1; +>a : Symbol(a, Decl(incorrectArrowFunction.ts, 11, 10)) + +true || async () => 1; +true || async a => 1; +true || async => 1; + +true || (() => 1); +true || (a => 1); +>a : Symbol(a, Decl(incorrectArrowFunction.ts, 17, 9)) + +true || ((a) => 2); +>a : Symbol(a, Decl(incorrectArrowFunction.ts, 18, 10)) + +true || (foo => 1); +>foo : Symbol(foo, Decl(incorrectArrowFunction.ts, 19, 9)) + +true || ((a: number) => 1); +>a : Symbol(a, Decl(incorrectArrowFunction.ts, 20, 10)) + +true || (({ a }: Foo) => 1); +>a : Symbol(a, Decl(incorrectArrowFunction.ts, 21, 11)) +>Foo : Symbol(Foo, Decl(incorrectArrowFunction.ts, 0, 18)) + +true || (async () => 1); +true || (async a => 1); +>a : Symbol(a, Decl(incorrectArrowFunction.ts, 23, 14)) + +true || (async => 1); +>async : Symbol(async, Decl(incorrectArrowFunction.ts, 24, 9)) + +(() => 1) || (() => 2); + +true || (false); +true || false; +true || foo; +>foo : Symbol(foo, Decl(incorrectArrowFunction.ts, 0, 13)) + diff --git a/tests/baselines/reference/incorrectArrowFunction.types b/tests/baselines/reference/incorrectArrowFunction.types new file mode 100644 index 0000000000000..0b4e37ff45586 --- /dev/null +++ b/tests/baselines/reference/incorrectArrowFunction.types @@ -0,0 +1,168 @@ +=== tests/cases/compiler/incorrectArrowFunction.ts === +declare const foo; +>foo : any + +interface Foo { +>Foo : Foo + + a: number +>a : number +} + +true || () => 1; +>true || () : any +>true : true +>() : any +> : any +>1 : 1 + +true || a => 1; +>true || a : any +>true : true +>a : any +>1 : 1 + +true || (a) => 2; +>true || (a) : any +>true : true +>(a) : any +>a : any +>2 : 2 + +true || foo => 1; +>true || foo : any +>true : true +>foo : any +>1 : 1 + +true || (a: number) => 1; +>true || (a : any +>true : true +>(a : any +>a : any +>number : any +>1 : 1 + +true || ({ a }: Foo) => 1; +>true || ({ a } : true | { a: any; } +>true : true +>({ a } : { a: any; } +>{ a } : { a: any; } +>a : any +>Foo : any +>1 : 1 + +true || async () => 1; +>true || async () : any +>true : true +>async () : any +>async : any +>1 : 1 + +true || async a => 1; +>true || async a => 1 : any +>true : true +> : any + +true || async => 1; +>true || async : any +>true : true +>async : any +>1 : 1 + +true || (() => 1); +>true || (() => 1) : true | (() => number) +>true : true +>(() => 1) : () => number +>() => 1 : () => number +>1 : 1 + +true || (a => 1); +>true || (a => 1) : true | ((a: any) => number) +>true : true +>(a => 1) : (a: any) => number +>a => 1 : (a: any) => number +>a : any +>1 : 1 + +true || ((a) => 2); +>true || ((a) => 2) : true | ((a: any) => number) +>true : true +>((a) => 2) : (a: any) => number +>(a) => 2 : (a: any) => number +>a : any +>2 : 2 + +true || (foo => 1); +>true || (foo => 1) : true | ((foo: any) => number) +>true : true +>(foo => 1) : (foo: any) => number +>foo => 1 : (foo: any) => number +>foo : any +>1 : 1 + +true || ((a: number) => 1); +>true || ((a: number) => 1) : true | ((a: number) => number) +>true : true +>((a: number) => 1) : (a: number) => number +>(a: number) => 1 : (a: number) => number +>a : number +>1 : 1 + +true || (({ a }: Foo) => 1); +>true || (({ a }: Foo) => 1) : true | (({ a }: Foo) => number) +>true : true +>(({ a }: Foo) => 1) : ({ a }: Foo) => number +>({ a }: Foo) => 1 : ({ a }: Foo) => number +>a : number +>Foo : Foo +>1 : 1 + +true || (async () => 1); +>true || (async () => 1) : true | (() => Promise) +>true : true +>(async () => 1) : () => Promise +>async () => 1 : () => Promise +>1 : 1 + +true || (async a => 1); +>true || (async a => 1) : true | ((a: any) => Promise) +>true : true +>(async a => 1) : (a: any) => Promise +>async a => 1 : (a: any) => Promise +>a : any +>1 : 1 + +true || (async => 1); +>true || (async => 1) : true | ((async: any) => number) +>true : true +>(async => 1) : (async: any) => number +>async => 1 : (async: any) => number +>async : any +>1 : 1 + +(() => 1) || (() => 2); +>(() => 1) || (() => 2) : () => number +>(() => 1) : () => number +>() => 1 : () => number +>1 : 1 +>(() => 2) : () => number +>() => 2 : () => number +>2 : 2 + +true || (false); +>true || (false) : boolean +>true : true +>(false) : false +>false : false + +true || false; +>true || false : boolean +>true : true +>false : false + +true || foo; +>true || foo : any +>true : true +>foo : any + diff --git a/tests/cases/compiler/incorrectArrowFunction.ts b/tests/cases/compiler/incorrectArrowFunction.ts new file mode 100644 index 0000000000000..c0f5d272b0eb5 --- /dev/null +++ b/tests/cases/compiler/incorrectArrowFunction.ts @@ -0,0 +1,30 @@ +declare const foo; + +interface Foo { + a: number +} + +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; + +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;