From b51d6043ad40971e8cd91135cb4e1a9ae3c8e66f Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 2 Jun 2022 07:38:48 -0700 Subject: [PATCH 1/8] Allow instantiation expression to be followed by let or interface on new line --- src/compiler/parser.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 1b502dfc22f78..af2ef640fc743 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -5732,6 +5732,12 @@ namespace ts { case SyntaxKind.NoSubstitutionTemplateLiteral: // foo `...` case SyntaxKind.TemplateHead: // foo `...${100}...` return true; + // These strict mode reserved words are valid identifiers in non-strict mode, and can thus + // start an expression. However, when they follow a type argument list and are immediately + // preceded by a line break, we don't consider them expression starters. + case SyntaxKind.InterfaceKeyword: + case SyntaxKind.LetKeyword: + return scanner.hasPrecedingLineBreak(); } // Consider something a type argument list only if the following token can't start an expression. return !isStartOfExpression(); From 3ab7927616d8a378f825e133d4a96918c600c205 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 2 Jun 2022 07:39:15 -0700 Subject: [PATCH 2/8] Add tests --- .../instantiationExpressionErrors.errors.txt | 10 +++++++ .../instantiationExpressionErrors.js | 26 +++++++++++++++++++ .../instantiationExpressionErrors.symbols | 18 +++++++++++++ .../instantiationExpressionErrors.types | 20 ++++++++++++++ .../instantiationExpressionErrors.ts | 10 +++++++ 5 files changed, 84 insertions(+) diff --git a/tests/baselines/reference/instantiationExpressionErrors.errors.txt b/tests/baselines/reference/instantiationExpressionErrors.errors.txt index 5b60983d594dc..bb37d893e3052 100644 --- a/tests/baselines/reference/instantiationExpressionErrors.errors.txt +++ b/tests/baselines/reference/instantiationExpressionErrors.errors.txt @@ -77,4 +77,14 @@ tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpr const x4 = f if (true) {} + + // Parsed as instantiation expression + + const x5 = f + let yy = 0; + + // Parsed as instantiation expression + + const x6 = f + interface I {} \ No newline at end of file diff --git a/tests/baselines/reference/instantiationExpressionErrors.js b/tests/baselines/reference/instantiationExpressionErrors.js index 9f6b17f13787a..acd99130c4c2c 100644 --- a/tests/baselines/reference/instantiationExpressionErrors.js +++ b/tests/baselines/reference/instantiationExpressionErrors.js @@ -45,6 +45,16 @@ true; const x4 = f if (true) {} + +// Parsed as instantiation expression + +const x5 = f +let yy = 0; + +// Parsed as instantiation expression + +const x6 = f +interface I {} //// [instantiationExpressionErrors.js] @@ -78,6 +88,11 @@ true; // Parsed as instantiation expression var x4 = (f); if (true) { } +// Parsed as instantiation expression +var x5 = (f); +var yy = 0; +// Parsed as instantiation expression +var x6 = (f); //// [instantiationExpressionErrors.d.ts] @@ -113,3 +128,14 @@ declare const x4: { (): true; g(): U; }; +declare const x5: { + (): true; + g(): U; +}; +declare let yy: number; +declare const x6: { + (): true; + g(): U; +}; +interface I { +} diff --git a/tests/baselines/reference/instantiationExpressionErrors.symbols b/tests/baselines/reference/instantiationExpressionErrors.symbols index dc1e6d5d64b3c..02df3038d3cd5 100644 --- a/tests/baselines/reference/instantiationExpressionErrors.symbols +++ b/tests/baselines/reference/instantiationExpressionErrors.symbols @@ -107,3 +107,21 @@ const x4 = f if (true) {} +// Parsed as instantiation expression + +const x5 = f +>x5 : Symbol(x5, Decl(instantiationExpressionErrors.ts, 49, 5)) +>f : Symbol(f, Decl(instantiationExpressionErrors.ts, 0, 11)) + +let yy = 0; +>yy : Symbol(yy, Decl(instantiationExpressionErrors.ts, 50, 3)) + +// Parsed as instantiation expression + +const x6 = f +>x6 : Symbol(x6, Decl(instantiationExpressionErrors.ts, 54, 5)) +>f : Symbol(f, Decl(instantiationExpressionErrors.ts, 0, 11)) + +interface I {} +>I : Symbol(I, Decl(instantiationExpressionErrors.ts, 54, 18)) + diff --git a/tests/baselines/reference/instantiationExpressionErrors.types b/tests/baselines/reference/instantiationExpressionErrors.types index 334cb69865e01..28f3faad2837e 100644 --- a/tests/baselines/reference/instantiationExpressionErrors.types +++ b/tests/baselines/reference/instantiationExpressionErrors.types @@ -132,3 +132,23 @@ const x4 = f if (true) {} >true : true +// Parsed as instantiation expression + +const x5 = f +>x5 : { (): true; g(): U; } +>f : { (): T; g(): U; } +>true : true + +let yy = 0; +>yy : number +>0 : 0 + +// Parsed as instantiation expression + +const x6 = f +>x6 : { (): true; g(): U; } +>f : { (): T; g(): U; } +>true : true + +interface I {} + diff --git a/tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts b/tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts index 1a9377ae9c5c8..a7b480860cfe5 100644 --- a/tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts +++ b/tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts @@ -47,3 +47,13 @@ true; const x4 = f if (true) {} + +// Parsed as instantiation expression + +const x5 = f +let yy = 0; + +// Parsed as instantiation expression + +const x6 = f +interface I {} From e8df0981ba0b19b389ab655b5fb74222c69f37bc Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Thu, 2 Jun 2022 10:44:35 -0700 Subject: [PATCH 3/8] Update src/compiler/parser.ts --- src/compiler/parser.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index af2ef640fc743..a5248fce6dbf8 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -5732,9 +5732,14 @@ namespace ts { case SyntaxKind.NoSubstitutionTemplateLiteral: // foo `...` case SyntaxKind.TemplateHead: // foo `...${100}...` return true; - // These strict mode reserved words are valid identifiers in non-strict mode, and can thus + // Technically strict mode reserved words are valid identifiers in non-strict mode, and can thus // start an expression. However, when they follow a type argument list and are immediately // preceded by a line break, we don't consider them expression starters. + // This supports a semicolon-less style for code like the following: + // + // let specialFoo = foo + // let value = ... + // case SyntaxKind.InterfaceKeyword: case SyntaxKind.LetKeyword: return scanner.hasPrecedingLineBreak(); From 919b36f15b41e65b43882df94eb8a63c0cb538ea Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 9 Jun 2022 08:22:26 -0700 Subject: [PATCH 4/8] Instantiation expressions followed by line breaks or binary operators --- src/compiler/parser.ts | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index a5248fce6dbf8..46a1da3bdcafe 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -5732,20 +5732,18 @@ namespace ts { case SyntaxKind.NoSubstitutionTemplateLiteral: // foo `...` case SyntaxKind.TemplateHead: // foo `...${100}...` return true; - // Technically strict mode reserved words are valid identifiers in non-strict mode, and can thus - // start an expression. However, when they follow a type argument list and are immediately - // preceded by a line break, we don't consider them expression starters. - // This supports a semicolon-less style for code like the following: - // - // let specialFoo = foo - // let value = ... - // - case SyntaxKind.InterfaceKeyword: - case SyntaxKind.LetKeyword: - return scanner.hasPrecedingLineBreak(); + // 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. + case SyntaxKind.LessThanToken: + case SyntaxKind.GreaterThanToken: + // In this context, `+` and `-` are unary operators, not binary operators. + 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 { From 4c82a58a860e04662334064096ca73b477af91a3 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 9 Jun 2022 08:22:35 -0700 Subject: [PATCH 5/8] Add more tests --- .../instantiationExpressionErrors.errors.txt | 81 ++++++-- .../instantiationExpressionErrors.js | 180 ++++++++++++++++-- .../instantiationExpressionErrors.symbols | 148 ++++++++++++-- .../instantiationExpressionErrors.types | 169 ++++++++++++++-- .../instantiationExpressionErrors.ts | 59 +++++- 5 files changed, 567 insertions(+), 70 deletions(-) diff --git a/tests/baselines/reference/instantiationExpressionErrors.errors.txt b/tests/baselines/reference/instantiationExpressionErrors.errors.txt index bb37d893e3052..2745416807fc8 100644 --- a/tests/baselines/reference/instantiationExpressionErrors.errors.txt +++ b/tests/baselines/reference/instantiationExpressionErrors.errors.txt @@ -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; }' 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; g(): 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; g(): U; }' and 'boolean'. +tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts(44,12): error TS2365: Operator '<' cannot be applied to types '{ (): T; g(): 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; g(): 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, g(): U }; // Type arguments in member expressions @@ -54,6 +58,14 @@ tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpr ~~~~~~ !!! error TS2558: Expected 0 type arguments, but got 1. + // Instantiation expression and binary operators + + declare let g: ((x: T) => T) | undefined; + + const c1 = g || ((x: string) => x); + const c2 = g ?? ((x: string) => x); + const c3 = g && ((x: string) => x); + // Parsed as function call, even though this differs from JavaScript const x1 = f @@ -61,30 +73,71 @@ tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpr ~~~~ !!! error TS2554: Expected 0 arguments, but got 1. - // Parsed as relational expression + // Parsed as relational expressions - const x2 = f - ~~~~~~ + const r1 = f < true > true; + ~~~~~~~~ !!! error TS2365: Operator '<' cannot be applied to types '{ (): T; g(): U; }' and 'boolean'. - true; + const r2 = f < true > +1; + ~~~~~~~~ +!!! error TS2365: Operator '<' cannot be applied to types '{ (): T; g(): 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; g(): 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; + const x2 = f true; - // Parsed as instantiation expression + const x3 = f; + true; const x4 = f if (true) {} - // Parsed as instantiation expression - const x5 = f let yy = 0; - // Parsed as instantiation expression - const x6 = f interface I {} + + let x10 = f + this.bar() + + let x11 = f + function bar() {} + + let x12 = f + class C {} + + let x13 = f + bar() + + let x14 = f + void bar() + + class C1 { + static specialFoo = f + static bar = 123 + } + + class C2 { + public specialFoo = f + public bar = 123 + } + + class C3 { + private specialFoo = f + private bar = 123 + } + + class C4 { + protected specialFoo = f + protected bar = 123 + } \ No newline at end of file diff --git a/tests/baselines/reference/instantiationExpressionErrors.js b/tests/baselines/reference/instantiationExpressionErrors.js index acd99130c4c2c..c29046d913e85 100644 --- a/tests/baselines/reference/instantiationExpressionErrors.js +++ b/tests/baselines/reference/instantiationExpressionErrors.js @@ -26,40 +26,81 @@ const b2 = f?.(); const b3 = f?.(); const b4 = f?.(); // Error, expected no type arguments +// Instantiation expression and binary operators + +declare let g: ((x: T) => T) | undefined; + +const c1 = g || ((x: string) => x); +const c2 = g ?? ((x: string) => x); +const c3 = g && ((x: string) => x); + // Parsed as function call, even though this differs from JavaScript const x1 = f (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; -// Parsed as instantiation expression - const x3 = f; true; -// Parsed as instantiation expression - const x4 = f if (true) {} -// Parsed as instantiation expression - const x5 = f let yy = 0; -// Parsed as instantiation expression - const x6 = f interface I {} + +let x10 = f +this.bar() + +let x11 = f +function bar() {} + +let x12 = f +class C {} + +let x13 = f +bar() + +let x14 = f +void bar() + +class C1 { + static specialFoo = f + static bar = 123 +} + +class C2 { + public specialFoo = f + public bar = 123 +} + +class C3 { + private specialFoo = f + private bar = 123 +} + +class C4 { + protected specialFoo = f + 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; } var a2 = (f.g); // () => number @@ -77,22 +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) { } -// Parsed as instantiation expression var x5 = (f); var yy = 0; -// Parsed as instantiation expression 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] @@ -118,8 +204,18 @@ declare const b1: number; declare const b2: number; declare const b3: number; declare const b4: number; +declare let g: ((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; +}; declare const x3: { (): true; g(): U; @@ -139,3 +235,51 @@ declare const x6: { }; interface I { } +declare let x10: { + (): true; + g(): U; +}; +declare let x11: { + (): true; + g(): U; +}; +declare function bar(): void; +declare let x12: { + (): true; + g(): U; +}; +declare class C { +} +declare let x13: { + (): true; + g(): U; +}; +declare let x14: { + (): true; + g(): U; +}; +declare class C1 { + static specialFoo: { + (): string; + g(): U; + }; + static bar: number; +} +declare class C2 { + specialFoo: { + (): string; + g(): U; + }; + bar: number; +} +declare class C3 { + private specialFoo; + private bar; +} +declare class C4 { + protected specialFoo: { + (): string; + g(): U; + }; + protected bar: number; +} diff --git a/tests/baselines/reference/instantiationExpressionErrors.symbols b/tests/baselines/reference/instantiationExpressionErrors.symbols index 02df3038d3cd5..86f97631ea8db 100644 --- a/tests/baselines/reference/instantiationExpressionErrors.symbols +++ b/tests/baselines/reference/instantiationExpressionErrors.symbols @@ -75,53 +75,167 @@ const b4 = f?.(); // Error, expected no type arguments >b4 : Symbol(b4, Decl(instantiationExpressionErrors.ts, 25, 5)) >f : Symbol(f, Decl(instantiationExpressionErrors.ts, 0, 11)) +// Instantiation expression and binary operators + +declare let g: ((x: T) => T) | undefined; +>g : Symbol(g, Decl(instantiationExpressionErrors.ts, 29, 11)) +>T : Symbol(T, Decl(instantiationExpressionErrors.ts, 29, 17)) +>x : Symbol(x, Decl(instantiationExpressionErrors.ts, 29, 20)) +>T : Symbol(T, Decl(instantiationExpressionErrors.ts, 29, 17)) +>T : Symbol(T, Decl(instantiationExpressionErrors.ts, 29, 17)) + +const c1 = g || ((x: string) => x); +>c1 : Symbol(c1, Decl(instantiationExpressionErrors.ts, 31, 5)) +>g : Symbol(g, Decl(instantiationExpressionErrors.ts, 29, 11)) +>x : Symbol(x, Decl(instantiationExpressionErrors.ts, 31, 26)) +>x : Symbol(x, Decl(instantiationExpressionErrors.ts, 31, 26)) + +const c2 = g ?? ((x: string) => x); +>c2 : Symbol(c2, Decl(instantiationExpressionErrors.ts, 32, 5)) +>g : Symbol(g, Decl(instantiationExpressionErrors.ts, 29, 11)) +>x : Symbol(x, Decl(instantiationExpressionErrors.ts, 32, 26)) +>x : Symbol(x, Decl(instantiationExpressionErrors.ts, 32, 26)) + +const c3 = g && ((x: string) => x); +>c3 : Symbol(c3, Decl(instantiationExpressionErrors.ts, 33, 5)) +>g : Symbol(g, Decl(instantiationExpressionErrors.ts, 29, 11)) +>x : Symbol(x, Decl(instantiationExpressionErrors.ts, 33, 26)) +>x : Symbol(x, Decl(instantiationExpressionErrors.ts, 33, 26)) + // Parsed as function call, even though this differs from JavaScript const x1 = f ->x1 : Symbol(x1, Decl(instantiationExpressionErrors.ts, 29, 5)) +>x1 : Symbol(x1, Decl(instantiationExpressionErrors.ts, 37, 5)) >f : Symbol(f, Decl(instantiationExpressionErrors.ts, 0, 11)) (true); -// Parsed as relational expression +// Parsed as relational expressions + +const r1 = f < true > true; +>r1 : Symbol(r1, Decl(instantiationExpressionErrors.ts, 42, 5)) +>f : Symbol(f, Decl(instantiationExpressionErrors.ts, 0, 11)) + +const r2 = f < true > +1; +>r2 : Symbol(r2, Decl(instantiationExpressionErrors.ts, 43, 5)) +>f : Symbol(f, Decl(instantiationExpressionErrors.ts, 0, 11)) + +const r3 = f < true > -1; +>r3 : Symbol(r3, Decl(instantiationExpressionErrors.ts, 44, 5)) +>f : Symbol(f, Decl(instantiationExpressionErrors.ts, 0, 11)) + +// All of the following are parsed as instantiation expressions const x2 = f ->x2 : Symbol(x2, Decl(instantiationExpressionErrors.ts, 34, 5)) +>x2 : Symbol(x2, Decl(instantiationExpressionErrors.ts, 48, 5)) >f : Symbol(f, Decl(instantiationExpressionErrors.ts, 0, 11)) true; -// Parsed as instantiation expression - const x3 = f; ->x3 : Symbol(x3, Decl(instantiationExpressionErrors.ts, 39, 5)) +>x3 : Symbol(x3, Decl(instantiationExpressionErrors.ts, 51, 5)) >f : Symbol(f, Decl(instantiationExpressionErrors.ts, 0, 11)) true; -// Parsed as instantiation expression - const x4 = f ->x4 : Symbol(x4, Decl(instantiationExpressionErrors.ts, 44, 5)) +>x4 : Symbol(x4, Decl(instantiationExpressionErrors.ts, 54, 5)) >f : Symbol(f, Decl(instantiationExpressionErrors.ts, 0, 11)) if (true) {} -// Parsed as instantiation expression - const x5 = f ->x5 : Symbol(x5, Decl(instantiationExpressionErrors.ts, 49, 5)) +>x5 : Symbol(x5, Decl(instantiationExpressionErrors.ts, 57, 5)) >f : Symbol(f, Decl(instantiationExpressionErrors.ts, 0, 11)) let yy = 0; ->yy : Symbol(yy, Decl(instantiationExpressionErrors.ts, 50, 3)) - -// Parsed as instantiation expression +>yy : Symbol(yy, Decl(instantiationExpressionErrors.ts, 58, 3)) const x6 = f ->x6 : Symbol(x6, Decl(instantiationExpressionErrors.ts, 54, 5)) +>x6 : Symbol(x6, Decl(instantiationExpressionErrors.ts, 60, 5)) >f : Symbol(f, Decl(instantiationExpressionErrors.ts, 0, 11)) interface I {} ->I : Symbol(I, Decl(instantiationExpressionErrors.ts, 54, 18)) +>I : Symbol(I, Decl(instantiationExpressionErrors.ts, 60, 18)) + +let x10 = f +>x10 : Symbol(x10, Decl(instantiationExpressionErrors.ts, 63, 3)) +>f : Symbol(f, Decl(instantiationExpressionErrors.ts, 0, 11)) + +this.bar() +>this.bar : Symbol(bar, Decl(instantiationExpressionErrors.ts, 66, 17)) +>this : Symbol(globalThis) +>bar : Symbol(bar, Decl(instantiationExpressionErrors.ts, 66, 17)) + +let x11 = f +>x11 : Symbol(x11, Decl(instantiationExpressionErrors.ts, 66, 3)) +>f : Symbol(f, Decl(instantiationExpressionErrors.ts, 0, 11)) + +function bar() {} +>bar : Symbol(bar, Decl(instantiationExpressionErrors.ts, 66, 17)) + +let x12 = f +>x12 : Symbol(x12, Decl(instantiationExpressionErrors.ts, 69, 3)) +>f : Symbol(f, Decl(instantiationExpressionErrors.ts, 0, 11)) + +class C {} +>C : Symbol(C, Decl(instantiationExpressionErrors.ts, 69, 17)) + +let x13 = f +>x13 : Symbol(x13, Decl(instantiationExpressionErrors.ts, 72, 3)) +>f : Symbol(f, Decl(instantiationExpressionErrors.ts, 0, 11)) + +bar() +>bar : Symbol(bar, Decl(instantiationExpressionErrors.ts, 66, 17)) + +let x14 = f +>x14 : Symbol(x14, Decl(instantiationExpressionErrors.ts, 75, 3)) +>f : Symbol(f, Decl(instantiationExpressionErrors.ts, 0, 11)) + +void bar() +>bar : Symbol(bar, Decl(instantiationExpressionErrors.ts, 66, 17)) + +class C1 { +>C1 : Symbol(C1, Decl(instantiationExpressionErrors.ts, 76, 10)) + + static specialFoo = f +>specialFoo : Symbol(C1.specialFoo, Decl(instantiationExpressionErrors.ts, 78, 10)) +>f : Symbol(f, Decl(instantiationExpressionErrors.ts, 0, 11)) + + static bar = 123 +>bar : Symbol(C1.bar, Decl(instantiationExpressionErrors.ts, 79, 33)) +} + +class C2 { +>C2 : Symbol(C2, Decl(instantiationExpressionErrors.ts, 81, 1)) + + public specialFoo = f +>specialFoo : Symbol(C2.specialFoo, Decl(instantiationExpressionErrors.ts, 83, 10)) +>f : Symbol(f, Decl(instantiationExpressionErrors.ts, 0, 11)) + + public bar = 123 +>bar : Symbol(C2.bar, Decl(instantiationExpressionErrors.ts, 84, 33)) +} + +class C3 { +>C3 : Symbol(C3, Decl(instantiationExpressionErrors.ts, 86, 1)) + + private specialFoo = f +>specialFoo : Symbol(C3.specialFoo, Decl(instantiationExpressionErrors.ts, 88, 10)) +>f : Symbol(f, Decl(instantiationExpressionErrors.ts, 0, 11)) + + private bar = 123 +>bar : Symbol(C3.bar, Decl(instantiationExpressionErrors.ts, 89, 34)) +} + +class C4 { +>C4 : Symbol(C4, Decl(instantiationExpressionErrors.ts, 91, 1)) + + protected specialFoo = f +>specialFoo : Symbol(C4.specialFoo, Decl(instantiationExpressionErrors.ts, 93, 10)) +>f : Symbol(f, Decl(instantiationExpressionErrors.ts, 0, 11)) + + protected bar = 123 +>bar : Symbol(C4.bar, Decl(instantiationExpressionErrors.ts, 94, 36)) +} diff --git a/tests/baselines/reference/instantiationExpressionErrors.types b/tests/baselines/reference/instantiationExpressionErrors.types index 28f3faad2837e..abe746c06ad88 100644 --- a/tests/baselines/reference/instantiationExpressionErrors.types +++ b/tests/baselines/reference/instantiationExpressionErrors.types @@ -89,6 +89,39 @@ const b4 = f?.(); // Error, expected no type arguments >f?.() : number >f : { (): T; g(): U; } +// Instantiation expression and binary operators + +declare let g: ((x: T) => T) | undefined; +>g : ((x: T) => T) | undefined +>x : T + +const c1 = g || ((x: string) => x); +>c1 : (x: string) => string +>g || ((x: string) => x) : (x: string) => string +>g : ((x: T) => T) | undefined +>((x: string) => x) : (x: string) => string +>(x: string) => x : (x: string) => string +>x : string +>x : string + +const c2 = g ?? ((x: string) => x); +>c2 : (x: string) => string +>g ?? ((x: string) => x) : (x: string) => string +>g : ((x: T) => T) | undefined +>((x: string) => x) : (x: string) => string +>(x: string) => x : (x: string) => string +>x : string +>x : string + +const c3 = g && ((x: string) => x); +>c3 : ((x: string) => string) | undefined +>g && ((x: string) => x) : ((x: string) => string) | undefined +>g : ((x: T) => T) | undefined +>((x: string) => x) : (x: string) => string +>(x: string) => x : (x: string) => string +>x : string +>x : string + // Parsed as function call, even though this differs from JavaScript const x1 = f @@ -100,20 +133,44 @@ const x1 = f (true); >true : true -// Parsed as relational expression +// Parsed as relational expressions + +const r1 = f < true > true; +>r1 : boolean +>f < true > true : boolean +>f < true : boolean +>f : { (): T; g(): U; } +>true : true +>true : true + +const r2 = f < true > +1; +>r2 : boolean +>f < true > +1 : boolean +>f < true : boolean +>f : { (): T; g(): U; } +>true : true +>+1 : 1 +>1 : 1 + +const r3 = f < true > -1; +>r3 : boolean +>f < true > -1 : boolean +>f < true : boolean +>f : { (): T; g(): U; } +>true : true +>-1 : -1 +>1 : 1 + +// All of the following are parsed as instantiation expressions const x2 = f ->x2 : boolean ->ftrue : boolean ->fx2 : { (): true; g(): U; } >f : { (): T; g(): U; } >true : true true; >true : true -// Parsed as instantiation expression - const x3 = f; >x3 : { (): true; g(): U; } >f : { (): T; g(): U; } @@ -122,8 +179,6 @@ const x3 = f; true; >true : true -// Parsed as instantiation expression - const x4 = f >x4 : { (): true; g(): U; } >f : { (): T; g(): U; } @@ -132,8 +187,6 @@ const x4 = f if (true) {} >true : true -// Parsed as instantiation expression - const x5 = f >x5 : { (): true; g(): U; } >f : { (): T; g(): U; } @@ -143,8 +196,6 @@ let yy = 0; >yy : number >0 : 0 -// Parsed as instantiation expression - const x6 = f >x6 : { (): true; g(): U; } >f : { (): T; g(): U; } @@ -152,3 +203,97 @@ const x6 = f interface I {} +let x10 = f +>x10 : { (): true; g(): U; } +>f : { (): T; g(): U; } +>true : true + +this.bar() +>this.bar() : void +>this.bar : () => void +>this : typeof globalThis +>bar : () => void + +let x11 = f +>x11 : { (): true; g(): U; } +>f : { (): T; g(): U; } +>true : true + +function bar() {} +>bar : () => void + +let x12 = f +>x12 : { (): true; g(): U; } +>f : { (): T; g(): U; } +>true : true + +class C {} +>C : C + +let x13 = f +>x13 : { (): true; g(): U; } +>f : { (): T; g(): U; } +>true : true + +bar() +>bar() : void +>bar : () => void + +let x14 = f +>x14 : { (): true; g(): U; } +>f : { (): T; g(): U; } +>true : true + +void bar() +>void bar() : undefined +>bar() : void +>bar : () => void + +class C1 { +>C1 : C1 + + static specialFoo = f +>specialFoo : { (): string; g(): U; } +>f : { (): T; g(): U; } + + static bar = 123 +>bar : number +>123 : 123 +} + +class C2 { +>C2 : C2 + + public specialFoo = f +>specialFoo : { (): string; g(): U; } +>f : { (): T; g(): U; } + + public bar = 123 +>bar : number +>123 : 123 +} + +class C3 { +>C3 : C3 + + private specialFoo = f +>specialFoo : { (): string; g(): U; } +>f : { (): T; g(): U; } + + private bar = 123 +>bar : number +>123 : 123 +} + +class C4 { +>C4 : C4 + + protected specialFoo = f +>specialFoo : { (): string; g(): U; } +>f : { (): T; g(): U; } + + protected bar = 123 +>bar : number +>123 : 123 +} + diff --git a/tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts b/tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts index a7b480860cfe5..83e8f392830c3 100644 --- a/tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts +++ b/tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts @@ -28,32 +28,73 @@ const b2 = f?.(); const b3 = f?.(); const b4 = f?.(); // Error, expected no type arguments +// Instantiation expression and binary operators + +declare let g: ((x: T) => T) | undefined; + +const c1 = g || ((x: string) => x); +const c2 = g ?? ((x: string) => x); +const c3 = g && ((x: string) => x); + // Parsed as function call, even though this differs from JavaScript const x1 = f (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; -// Parsed as instantiation expression - const x3 = f; true; -// Parsed as instantiation expression - const x4 = f if (true) {} -// Parsed as instantiation expression - const x5 = f let yy = 0; -// Parsed as instantiation expression - const x6 = f interface I {} + +let x10 = f +this.bar() + +let x11 = f +function bar() {} + +let x12 = f +class C {} + +let x13 = f +bar() + +let x14 = f +void bar() + +class C1 { + static specialFoo = f + static bar = 123 +} + +class C2 { + public specialFoo = f + public bar = 123 +} + +class C3 { + private specialFoo = f + private bar = 123 +} + +class C4 { + protected specialFoo = f + protected bar = 123 +} From fe46a9bb7f927bcab73c37bc73f48e737d1d0c6b Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 9 Jun 2022 09:11:08 -0700 Subject: [PATCH 6/8] Accept new baselines --- .../reference/instantiationExpressionErrors.types | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/baselines/reference/instantiationExpressionErrors.types b/tests/baselines/reference/instantiationExpressionErrors.types index fe69b9a3361de..9b77ad997aee0 100644 --- a/tests/baselines/reference/instantiationExpressionErrors.types +++ b/tests/baselines/reference/instantiationExpressionErrors.types @@ -109,6 +109,7 @@ declare let g: ((x: T) => T) | undefined; const c1 = g || ((x: string) => x); >c1 : (x: string) => string >g || ((x: string) => x) : (x: string) => string +>g : ((x: string) => string) | undefined >g : ((x: T) => T) | undefined >((x: string) => x) : (x: string) => string >(x: string) => x : (x: string) => string @@ -118,6 +119,7 @@ const c1 = g || ((x: string) => x); const c2 = g ?? ((x: string) => x); >c2 : (x: string) => string >g ?? ((x: string) => x) : (x: string) => string +>g : ((x: string) => string) | undefined >g : ((x: T) => T) | undefined >((x: string) => x) : (x: string) => string >(x: string) => x : (x: string) => string @@ -127,6 +129,7 @@ const c2 = g ?? ((x: string) => x); const c3 = g && ((x: string) => x); >c3 : ((x: string) => string) | undefined >g && ((x: string) => x) : ((x: string) => string) | undefined +>g : ((x: string) => string) | undefined >g : ((x: T) => T) | undefined >((x: string) => x) : (x: string) => string >(x: string) => x : (x: string) => string @@ -176,6 +179,7 @@ const r3 = f < true > -1; const x2 = f >x2 : { (): true; g(): U; } +>f : { (): true; g(): U; } >f : { (): T; g(): U; } >true : true @@ -202,6 +206,7 @@ if (true) {} const x5 = f >x5 : { (): true; g(): U; } +>f : { (): true; g(): U; } >f : { (): T; g(): U; } >true : true @@ -211,6 +216,7 @@ let yy = 0; const x6 = f >x6 : { (): true; g(): U; } +>f : { (): true; g(): U; } >f : { (): T; g(): U; } >true : true @@ -218,6 +224,7 @@ interface I {} let x10 = f >x10 : { (): true; g(): U; } +>f : { (): true; g(): U; } >f : { (): T; g(): U; } >true : true @@ -229,6 +236,7 @@ this.bar() let x11 = f >x11 : { (): true; g(): U; } +>f : { (): true; g(): U; } >f : { (): T; g(): U; } >true : true @@ -237,6 +245,7 @@ function bar() {} let x12 = f >x12 : { (): true; g(): U; } +>f : { (): true; g(): U; } >f : { (): T; g(): U; } >true : true @@ -245,6 +254,7 @@ class C {} let x13 = f >x13 : { (): true; g(): U; } +>f : { (): true; g(): U; } >f : { (): T; g(): U; } >true : true @@ -254,6 +264,7 @@ bar() let x14 = f >x14 : { (): true; g(): U; } +>f : { (): true; g(): U; } >f : { (): T; g(): U; } >true : true @@ -267,6 +278,7 @@ class C1 { static specialFoo = f >specialFoo : { (): string; g(): U; } +>f : { (): string; g(): U; } >f : { (): T; g(): U; } static bar = 123 @@ -279,6 +291,7 @@ class C2 { public specialFoo = f >specialFoo : { (): string; g(): U; } +>f : { (): string; g(): U; } >f : { (): T; g(): U; } public bar = 123 @@ -291,6 +304,7 @@ class C3 { private specialFoo = f >specialFoo : { (): string; g(): U; } +>f : { (): string; g(): U; } >f : { (): T; g(): U; } private bar = 123 @@ -303,6 +317,7 @@ class C4 { protected specialFoo = f >specialFoo : { (): string; g(): U; } +>f : { (): string; g(): U; } >f : { (): T; g(): U; } protected bar = 123 From 1699825eab4af979f8904050879997909d7e3aa2 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 9 Jun 2022 09:30:01 -0700 Subject: [PATCH 7/8] Fix lint error --- src/compiler/parser.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 9797a308e387f..fbcf3b279714c 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -5733,10 +5733,10 @@ namespace ts { case SyntaxKind.TemplateHead: // foo `...${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. + // 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: - // In this context, `+` and `-` are unary operators, not binary operators. case SyntaxKind.PlusToken: case SyntaxKind.MinusToken: return false; From 816553a6a495c4ae243404a839883492832735cd Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 9 Jun 2022 09:30:09 -0700 Subject: [PATCH 8/8] Update fourslash test --- .../cases/fourslash/completionListInUnclosedTypeArguments.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/cases/fourslash/completionListInUnclosedTypeArguments.ts b/tests/cases/fourslash/completionListInUnclosedTypeArguments.ts index 8a100a9616e3d..5bc1a2d08132a 100644 --- a/tests/cases/fourslash/completionListInUnclosedTypeArguments.ts +++ b/tests/cases/fourslash/completionListInUnclosedTypeArguments.ts @@ -13,8 +13,8 @@ ////f2 -////f2 +////f2 +////f2 ////f2(); //// ////f2