Skip to content
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

Instantiation expression can be followed by line break or binary operator #49353

Merged
merged 9 commits into from
Jun 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 11 additions & 2 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5732,9 +5732,18 @@ namespace ts {
case SyntaxKind.NoSubstitutionTemplateLiteral: // foo<T> `...`
case SyntaxKind.TemplateHead: // foo<T> `...${100}...`
return true;
// A type argument list followed by `<` never makes sense, and a type argument list followed
// by `>` is ambiguous with a (re-scanned) `>>` operator, so we disqualify both. Also, in
// this context, `+` and `-` are unary operators, not binary operators.
case SyntaxKind.LessThanToken:
case SyntaxKind.GreaterThanToken:
case SyntaxKind.PlusToken:
case SyntaxKind.MinusToken:
return false;
}
// Consider something a type argument list only if the following token can't start an expression.
return !isStartOfExpression();
// We favor the type argument list interpretation when it is immediately followed by
// a line break, a binary operator, or something that can't start an expression.
return scanner.hasPrecedingLineBreak() || isBinaryOperator() || !isStartOfExpression();
}

function parsePrimaryExpression(): PrimaryExpression {
Expand Down
83 changes: 73 additions & 10 deletions tests/baselines/reference/instantiationExpressionErrors.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpr
tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts(19,24): error TS2635: Type '{ (): number; g<U>(): U; }' has no signatures for which the type argument list is applicable.
tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts(23,23): error TS1005: '(' expected.
tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts(26,24): error TS2558: Expected 0 type arguments, but got 1.
tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts(31,2): error TS2554: Expected 0 arguments, but got 1.
tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts(35,12): error TS2365: Operator '<' cannot be applied to types '{ <T>(): T; g<U>(): U; }' and 'boolean'.
tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts(39,2): error TS2554: Expected 0 arguments, but got 1.
tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts(43,12): error TS2365: Operator '<' cannot be applied to types '{ <T>(): T; g<U>(): U; }' and 'boolean'.
tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts(44,12): error TS2365: Operator '<' cannot be applied to types '{ <T>(): T; g<U>(): U; }' and 'boolean'.
tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts(44,12): error TS2365: Operator '>' cannot be applied to types 'boolean' and 'number'.
tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts(45,12): error TS2365: Operator '<' cannot be applied to types '{ <T>(): T; g<U>(): U; }' and 'boolean'.
tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts(45,12): error TS2365: Operator '>' cannot be applied to types 'boolean' and 'number'.


==== tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts (10 errors) ====
==== tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts (14 errors) ====
declare let f: { <T>(): T, g<U>(): U };

// Type arguments in member expressions
Expand Down Expand Up @@ -54,27 +58,86 @@ tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpr
~~~~~~
!!! error TS2558: Expected 0 type arguments, but got 1.

// Instantiation expression and binary operators

declare let g: (<T>(x: T) => T) | undefined;

const c1 = g<string> || ((x: string) => x);
const c2 = g<string> ?? ((x: string) => x);
const c3 = g<string> && ((x: string) => x);

// Parsed as function call, even though this differs from JavaScript

const x1 = f<true>
(true);
~~~~
!!! error TS2554: Expected 0 arguments, but got 1.

// Parsed as relational expression
// Parsed as relational expressions

const x2 = f<true>
~~~~~~
const r1 = f < true > true;
~~~~~~~~
!!! error TS2365: Operator '<' cannot be applied to types '{ <T>(): T; g<U>(): U; }' and 'boolean'.
true;
const r2 = f < true > +1;
~~~~~~~~
!!! error TS2365: Operator '<' cannot be applied to types '{ <T>(): T; g<U>(): U; }' and 'boolean'.
~~~~~~~~~~~~~
!!! error TS2365: Operator '>' cannot be applied to types 'boolean' and 'number'.
const r3 = f < true > -1;
~~~~~~~~
!!! error TS2365: Operator '<' cannot be applied to types '{ <T>(): T; g<U>(): U; }' and 'boolean'.
~~~~~~~~~~~~~
!!! error TS2365: Operator '>' cannot be applied to types 'boolean' and 'number'.

// Parsed as instantiation expression
// All of the following are parsed as instantiation expressions

const x3 = f<true>;
const x2 = f<true>
true;

// Parsed as instantiation expression
const x3 = f<true>;
true;

const x4 = f<true>
if (true) {}

const x5 = f<true>
let yy = 0;

const x6 = f<true>
interface I {}

let x10 = f<true>
this.bar()

let x11 = f<true>
function bar() {}

let x12 = f<true>
class C {}

let x13 = f<true>
bar()

let x14 = f<true>
void bar()

class C1 {
static specialFoo = f<string>
static bar = 123
}

class C2 {
public specialFoo = f<string>
public bar = 123
}

class C3 {
private specialFoo = f<string>
private bar = 123
}

class C4 {
protected specialFoo = f<string>
protected bar = 123
}

194 changes: 182 additions & 12 deletions tests/baselines/reference/instantiationExpressionErrors.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,30 +26,81 @@ const b2 = f?.<number>();
const b3 = f<number>?.();
const b4 = f<number>?.<number>(); // Error, expected no type arguments

// Instantiation expression and binary operators

declare let g: (<T>(x: T) => T) | undefined;

const c1 = g<string> || ((x: string) => x);
const c2 = g<string> ?? ((x: string) => x);
const c3 = g<string> && ((x: string) => x);

// Parsed as function call, even though this differs from JavaScript

const x1 = f<true>
(true);

// Parsed as relational expression
// Parsed as relational expressions

const r1 = f < true > true;
const r2 = f < true > +1;
const r3 = f < true > -1;

// All of the following are parsed as instantiation expressions

const x2 = f<true>
true;

// Parsed as instantiation expression

const x3 = f<true>;
true;

// Parsed as instantiation expression

const x4 = f<true>
if (true) {}

const x5 = f<true>
let yy = 0;

const x6 = f<true>
interface I {}

let x10 = f<true>
this.bar()

let x11 = f<true>
function bar() {}

let x12 = f<true>
class C {}

let x13 = f<true>
bar()

let x14 = f<true>
void bar()

class C1 {
static specialFoo = f<string>
static bar = 123
}

class C2 {
public specialFoo = f<string>
public bar = 123
}

class C3 {
private specialFoo = f<string>
private bar = 123
}

class C4 {
protected specialFoo = f<string>
protected bar = 123
}


//// [instantiationExpressionErrors.js]
"use strict";
var _a, _b;
var _a, _b, _c;
// Type arguments in member expressions
var a1 = (f); // { (): number; g<U>(): U; }
var a2 = (f.g); // () => number
Expand All @@ -67,17 +118,67 @@ var b1 = f === null || f === void 0 ? void 0 : f(); // Error, `(` expected
var b2 = f === null || f === void 0 ? void 0 : f();
var b3 = (_a = (f)) === null || _a === void 0 ? void 0 : _a();
var b4 = (_b = (f)) === null || _b === void 0 ? void 0 : _b(); // Error, expected no type arguments
var c1 = (g) || (function (x) { return x; });
var c2 = (_c = (g)) !== null && _c !== void 0 ? _c : (function (x) { return x; });
var c3 = (g) && (function (x) { return x; });
// Parsed as function call, even though this differs from JavaScript
var x1 = f(true);
// Parsed as relational expression
var x2 = f < true >
true;
// Parsed as instantiation expression
// Parsed as relational expressions
var r1 = f < true > true;
var r2 = f < true > +1;
var r3 = f < true > -1;
// All of the following are parsed as instantiation expressions
var x2 = (f);
true;
var x3 = (f);
true;
// Parsed as instantiation expression
var x4 = (f);
if (true) { }
var x5 = (f);
var yy = 0;
var x6 = (f);
var x10 = (f);
this.bar();
var x11 = (f);
function bar() { }
var x12 = (f);
var C = /** @class */ (function () {
function C() {
}
return C;
}());
var x13 = (f);
bar();
var x14 = (f);
void bar();
var C1 = /** @class */ (function () {
function C1() {
}
C1.specialFoo = (f);
C1.bar = 123;
return C1;
}());
var C2 = /** @class */ (function () {
function C2() {
this.specialFoo = (f);
this.bar = 123;
}
return C2;
}());
var C3 = /** @class */ (function () {
function C3() {
this.specialFoo = (f);
this.bar = 123;
}
return C3;
}());
var C4 = /** @class */ (function () {
function C4() {
this.specialFoo = (f);
this.bar = 123;
}
return C4;
}());


//// [instantiationExpressionErrors.d.ts]
Expand All @@ -103,8 +204,18 @@ declare const b1: number;
declare const b2: number;
declare const b3: number;
declare const b4: number;
declare let g: (<T>(x: T) => T) | undefined;
declare const c1: (x: string) => string;
declare const c2: (x: string) => string;
declare const c3: ((x: string) => string) | undefined;
declare const x1: true;
declare const x2: boolean;
declare const r1: boolean;
declare const r2: boolean;
declare const r3: boolean;
declare const x2: {
(): true;
g<U>(): U;
};
declare const x3: {
(): true;
g<U>(): U;
Expand All @@ -113,3 +224,62 @@ declare const x4: {
(): true;
g<U>(): U;
};
declare const x5: {
(): true;
g<U>(): U;
};
declare let yy: number;
declare const x6: {
(): true;
g<U>(): U;
};
interface I {
}
declare let x10: {
(): true;
g<U>(): U;
};
declare let x11: {
(): true;
g<U>(): U;
};
declare function bar(): void;
declare let x12: {
(): true;
g<U>(): U;
};
declare class C {
}
declare let x13: {
(): true;
g<U>(): U;
};
declare let x14: {
(): true;
g<U>(): U;
};
declare class C1 {
static specialFoo: {
(): string;
g<U>(): U;
};
static bar: number;
}
declare class C2 {
specialFoo: {
(): string;
g<U>(): U;
};
bar: number;
}
declare class C3 {
private specialFoo;
private bar;
}
declare class C4 {
protected specialFoo: {
(): string;
g<U>(): U;
};
protected bar: number;
}