Skip to content

Commit

Permalink
Instantiation expression can be followed by line break or binary oper…
Browse files Browse the repository at this point in the history
…ator (#49353)

* Allow instantiation expression to be followed by let or interface on new line

* Add tests

* Update src/compiler/parser.ts

* Instantiation expressions followed by line breaks or binary operators

* Add more tests

* Accept new baselines

* Fix lint error

* Update fourslash test

Co-authored-by: Daniel Rosenwasser <DanielRosenwasser@users.noreply.github.com>
  • Loading branch information
ahejlsberg and DanielRosenwasser committed Jun 10, 2022
1 parent b3a79db commit e6808c4
Show file tree
Hide file tree
Showing 7 changed files with 653 additions and 48 deletions.
13 changes: 11 additions & 2 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5783,9 +5783,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;
}

0 comments on commit e6808c4

Please sign in to comment.