From 905a1fea393fd7917cccab15f4a8686b7de5e203 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 7 Apr 2021 14:21:28 -0400 Subject: [PATCH] Corrected parsing for decorators on 'this' parameters (#43175) * Corrected parsing for decorators on 'this' parameters * Moved checking to parser and added a specific test * Remove unrelated checker.ts blank line * Missed some baseeline updates... --- src/compiler/diagnosticMessages.json | 4 +++ src/compiler/parser.ts | 19 +++++++++----- ...ratorOnClassMethodThisParameter.errors.txt | 19 +++++++++----- .../decoratorOnClassMethodThisParameter.js | 16 +++++++++--- ...ecoratorOnClassMethodThisParameter.symbols | 16 ++++++++++-- .../decoratorOnClassMethodThisParameter.types | 14 ++++++++-- .../decoratorOnFunctionParameter.errors.txt | 15 +++++++++++ .../reference/decoratorOnFunctionParameter.js | 17 ++++++++++++ .../decoratorOnFunctionParameter.symbols | 26 +++++++++++++++++++ .../decoratorOnFunctionParameter.types | 26 +++++++++++++++++++ .../thisTypeInFunctionsNegative.errors.txt | 11 +++----- .../thisTypeInFunctionsNegative.symbols | 6 +++-- .../thisTypeInFunctionsNegative.types | 9 +++---- .../decoratorOnClassMethodThisParameter.ts | 4 +++ .../invalid/decoratorOnFunctionParameter.ts | 6 +++++ 15 files changed, 175 insertions(+), 33 deletions(-) create mode 100644 tests/baselines/reference/decoratorOnFunctionParameter.errors.txt create mode 100644 tests/baselines/reference/decoratorOnFunctionParameter.js create mode 100644 tests/baselines/reference/decoratorOnFunctionParameter.symbols create mode 100644 tests/baselines/reference/decoratorOnFunctionParameter.types create mode 100644 tests/cases/conformance/decorators/invalid/decoratorOnFunctionParameter.ts diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index bead4ccb271..c0ffe0bec42 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1360,6 +1360,10 @@ "category": "Error", "code": 1432 }, + "Decorators may not be applied to 'this' parameters.": { + "category": "Error", + "code": 1433 + }, "The types of '{0}' are incompatible between these types.": { "category": "Error", diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index cdd63c9c947..d37e1d102bb 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2991,9 +2991,16 @@ namespace ts { function parseParameterWorker(inOuterAwaitContext: boolean): ParameterDeclaration { const pos = getNodePos(); const hasJSDoc = hasPrecedingJSDocComment(); + + // FormalParameter [Yield,Await]: + // BindingElement[?Yield,?Await] + + // Decorators are parsed in the outer [Await] context, the rest of the parameter is parsed in the function's [Await] context. + const decorators = inOuterAwaitContext ? doInAwaitContext(parseDecorators) : parseDecorators(); + if (token() === SyntaxKind.ThisKeyword) { const node = factory.createParameterDeclaration( - /*decorators*/ undefined, + decorators, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, createIdentifier(/*isIdentifier*/ true), @@ -3001,14 +3008,14 @@ namespace ts { parseTypeAnnotation(), /*initializer*/ undefined ); + + if (decorators) { + parseErrorAtRange(decorators[0], Diagnostics.Decorators_may_not_be_applied_to_this_parameters); + } + return withJSDoc(finishNode(node, pos), hasJSDoc); } - // FormalParameter [Yield,Await]: - // BindingElement[?Yield,?Await] - - // Decorators are parsed in the outer [Await] context, the rest of the parameter is parsed in the function's [Await] context. - const decorators = inOuterAwaitContext ? doInAwaitContext(parseDecorators) : parseDecorators(); const savedTopLevel = topLevel; topLevel = false; const modifiers = parseModifiers(); diff --git a/tests/baselines/reference/decoratorOnClassMethodThisParameter.errors.txt b/tests/baselines/reference/decoratorOnClassMethodThisParameter.errors.txt index 75afc6a07b7..02fb67534e8 100644 --- a/tests/baselines/reference/decoratorOnClassMethodThisParameter.errors.txt +++ b/tests/baselines/reference/decoratorOnClassMethodThisParameter.errors.txt @@ -1,14 +1,21 @@ -tests/cases/conformance/decorators/class/method/parameter/decoratorOnClassMethodThisParameter.ts(4,17): error TS1359: Identifier expected. 'this' is a reserved word that cannot be used here. -tests/cases/conformance/decorators/class/method/parameter/decoratorOnClassMethodThisParameter.ts(4,17): error TS2680: A 'this' parameter must be the first parameter. +tests/cases/conformance/decorators/class/method/parameter/decoratorOnClassMethodThisParameter.ts(4,12): error TS1433: Decorators may not be applied to 'this' parameters. +tests/cases/conformance/decorators/class/method/parameter/decoratorOnClassMethodThisParameter.ts(8,29): error TS1433: Decorators may not be applied to 'this' parameters. +tests/cases/conformance/decorators/class/method/parameter/decoratorOnClassMethodThisParameter.ts(8,30): error TS2680: A 'this' parameter must be the first parameter. -==== tests/cases/conformance/decorators/class/method/parameter/decoratorOnClassMethodThisParameter.ts (2 errors) ==== +==== tests/cases/conformance/decorators/class/method/parameter/decoratorOnClassMethodThisParameter.ts (3 errors) ==== declare function dec(target: Object, propertyKey: string | symbol, parameterIndex: number): void; class C { method(@dec this: C) {} - ~~~~ -!!! error TS1359: Identifier expected. 'this' is a reserved word that cannot be used here. - ~~~~~~~ + ~~~~ +!!! error TS1433: Decorators may not be applied to 'this' parameters. + } + + class C2 { + method(@dec allowed: C2, @dec this: C2) {} + ~~~~~ +!!! error TS1433: Decorators may not be applied to 'this' parameters. + ~~~~~~~~~~~~~ !!! error TS2680: A 'this' parameter must be the first parameter. } \ No newline at end of file diff --git a/tests/baselines/reference/decoratorOnClassMethodThisParameter.js b/tests/baselines/reference/decoratorOnClassMethodThisParameter.js index 74b835c1383..5feeb0ef90e 100644 --- a/tests/baselines/reference/decoratorOnClassMethodThisParameter.js +++ b/tests/baselines/reference/decoratorOnClassMethodThisParameter.js @@ -3,6 +3,10 @@ declare function dec(target: Object, propertyKey: string | symbol, parameterInde class C { method(@dec this: C) {} +} + +class C2 { + method(@dec allowed: C2, @dec this: C2) {} } //// [decoratorOnClassMethodThisParameter.js] @@ -19,8 +23,14 @@ var C = /** @class */ (function () { function C() { } C.prototype.method = function () { }; - __decorate([ - __param(0, dec) - ], C.prototype, "method", null); return C; }()); +var C2 = /** @class */ (function () { + function C2() { + } + C2.prototype.method = function (allowed) { }; + __decorate([ + __param(0, dec), __param(1, dec) + ], C2.prototype, "method", null); + return C2; +}()); diff --git a/tests/baselines/reference/decoratorOnClassMethodThisParameter.symbols b/tests/baselines/reference/decoratorOnClassMethodThisParameter.symbols index 13d6bb152ee..b3ba9acadc7 100644 --- a/tests/baselines/reference/decoratorOnClassMethodThisParameter.symbols +++ b/tests/baselines/reference/decoratorOnClassMethodThisParameter.symbols @@ -12,7 +12,19 @@ class C { method(@dec this: C) {} >method : Symbol(C.method, Decl(decoratorOnClassMethodThisParameter.ts, 2, 9)) >dec : Symbol(dec, Decl(decoratorOnClassMethodThisParameter.ts, 0, 0)) -> : Symbol((Missing), Decl(decoratorOnClassMethodThisParameter.ts, 3, 11)) ->this : Symbol(this, Decl(decoratorOnClassMethodThisParameter.ts, 3, 15)) +>this : Symbol(this, Decl(decoratorOnClassMethodThisParameter.ts, 3, 11)) >C : Symbol(C, Decl(decoratorOnClassMethodThisParameter.ts, 0, 97)) } + +class C2 { +>C2 : Symbol(C2, Decl(decoratorOnClassMethodThisParameter.ts, 4, 1)) + + method(@dec allowed: C2, @dec this: C2) {} +>method : Symbol(C2.method, Decl(decoratorOnClassMethodThisParameter.ts, 6, 10)) +>dec : Symbol(dec, Decl(decoratorOnClassMethodThisParameter.ts, 0, 0)) +>allowed : Symbol(allowed, Decl(decoratorOnClassMethodThisParameter.ts, 7, 11)) +>C2 : Symbol(C2, Decl(decoratorOnClassMethodThisParameter.ts, 4, 1)) +>dec : Symbol(dec, Decl(decoratorOnClassMethodThisParameter.ts, 0, 0)) +>this : Symbol(this, Decl(decoratorOnClassMethodThisParameter.ts, 7, 28)) +>C2 : Symbol(C2, Decl(decoratorOnClassMethodThisParameter.ts, 4, 1)) +} diff --git a/tests/baselines/reference/decoratorOnClassMethodThisParameter.types b/tests/baselines/reference/decoratorOnClassMethodThisParameter.types index 8b58cb7b493..44bd95b4b0a 100644 --- a/tests/baselines/reference/decoratorOnClassMethodThisParameter.types +++ b/tests/baselines/reference/decoratorOnClassMethodThisParameter.types @@ -9,8 +9,18 @@ class C { >C : C method(@dec this: C) {} ->method : (: any, this: C) => void +>method : (this: C) => void >dec : (target: Object, propertyKey: string | symbol, parameterIndex: number) => void -> : any >this : C } + +class C2 { +>C2 : C2 + + method(@dec allowed: C2, @dec this: C2) {} +>method : (allowed: C2, this: C2) => void +>dec : (target: Object, propertyKey: string | symbol, parameterIndex: number) => void +>allowed : C2 +>dec : (target: Object, propertyKey: string | symbol, parameterIndex: number) => void +>this : C2 +} diff --git a/tests/baselines/reference/decoratorOnFunctionParameter.errors.txt b/tests/baselines/reference/decoratorOnFunctionParameter.errors.txt new file mode 100644 index 00000000000..a2259e0666a --- /dev/null +++ b/tests/baselines/reference/decoratorOnFunctionParameter.errors.txt @@ -0,0 +1,15 @@ +tests/cases/conformance/decorators/invalid/decoratorOnFunctionParameter.ts(5,17): error TS1433: Decorators may not be applied to 'this' parameters. +tests/cases/conformance/decorators/invalid/decoratorOnFunctionParameter.ts(6,17): error TS1433: Decorators may not be applied to 'this' parameters. + + +==== tests/cases/conformance/decorators/invalid/decoratorOnFunctionParameter.ts (2 errors) ==== + declare const dec: any; + + class C { n = true; } + + function direct(@dec this: C) { return this.n; } + ~~~~ +!!! error TS1433: Decorators may not be applied to 'this' parameters. + function called(@dec() this: C) { return this.n; } + ~~~~~~ +!!! error TS1433: Decorators may not be applied to 'this' parameters. \ No newline at end of file diff --git a/tests/baselines/reference/decoratorOnFunctionParameter.js b/tests/baselines/reference/decoratorOnFunctionParameter.js new file mode 100644 index 00000000000..be68689736a --- /dev/null +++ b/tests/baselines/reference/decoratorOnFunctionParameter.js @@ -0,0 +1,17 @@ +//// [decoratorOnFunctionParameter.ts] +declare const dec: any; + +class C { n = true; } + +function direct(@dec this: C) { return this.n; } +function called(@dec() this: C) { return this.n; } + +//// [decoratorOnFunctionParameter.js] +var C = /** @class */ (function () { + function C() { + this.n = true; + } + return C; +}()); +function direct() { return this.n; } +function called() { return this.n; } diff --git a/tests/baselines/reference/decoratorOnFunctionParameter.symbols b/tests/baselines/reference/decoratorOnFunctionParameter.symbols new file mode 100644 index 00000000000..9c93a7433e1 --- /dev/null +++ b/tests/baselines/reference/decoratorOnFunctionParameter.symbols @@ -0,0 +1,26 @@ +=== tests/cases/conformance/decorators/invalid/decoratorOnFunctionParameter.ts === +declare const dec: any; +>dec : Symbol(dec, Decl(decoratorOnFunctionParameter.ts, 0, 13)) + +class C { n = true; } +>C : Symbol(C, Decl(decoratorOnFunctionParameter.ts, 0, 23)) +>n : Symbol(C.n, Decl(decoratorOnFunctionParameter.ts, 2, 9)) + +function direct(@dec this: C) { return this.n; } +>direct : Symbol(direct, Decl(decoratorOnFunctionParameter.ts, 2, 21)) +>dec : Symbol(dec, Decl(decoratorOnFunctionParameter.ts, 0, 13)) +>this : Symbol(this, Decl(decoratorOnFunctionParameter.ts, 4, 16)) +>C : Symbol(C, Decl(decoratorOnFunctionParameter.ts, 0, 23)) +>this.n : Symbol(C.n, Decl(decoratorOnFunctionParameter.ts, 2, 9)) +>this : Symbol(this, Decl(decoratorOnFunctionParameter.ts, 4, 16)) +>n : Symbol(C.n, Decl(decoratorOnFunctionParameter.ts, 2, 9)) + +function called(@dec() this: C) { return this.n; } +>called : Symbol(called, Decl(decoratorOnFunctionParameter.ts, 4, 48)) +>dec : Symbol(dec, Decl(decoratorOnFunctionParameter.ts, 0, 13)) +>this : Symbol(this, Decl(decoratorOnFunctionParameter.ts, 5, 16)) +>C : Symbol(C, Decl(decoratorOnFunctionParameter.ts, 0, 23)) +>this.n : Symbol(C.n, Decl(decoratorOnFunctionParameter.ts, 2, 9)) +>this : Symbol(this, Decl(decoratorOnFunctionParameter.ts, 5, 16)) +>n : Symbol(C.n, Decl(decoratorOnFunctionParameter.ts, 2, 9)) + diff --git a/tests/baselines/reference/decoratorOnFunctionParameter.types b/tests/baselines/reference/decoratorOnFunctionParameter.types new file mode 100644 index 00000000000..cb0096b9031 --- /dev/null +++ b/tests/baselines/reference/decoratorOnFunctionParameter.types @@ -0,0 +1,26 @@ +=== tests/cases/conformance/decorators/invalid/decoratorOnFunctionParameter.ts === +declare const dec: any; +>dec : any + +class C { n = true; } +>C : C +>n : boolean +>true : true + +function direct(@dec this: C) { return this.n; } +>direct : (this: C) => boolean +>dec : any +>this : C +>this.n : boolean +>this : C +>n : boolean + +function called(@dec() this: C) { return this.n; } +>called : (this: C) => boolean +>dec() : any +>dec : any +>this : C +>this.n : boolean +>this : C +>n : boolean + diff --git a/tests/baselines/reference/thisTypeInFunctionsNegative.errors.txt b/tests/baselines/reference/thisTypeInFunctionsNegative.errors.txt index 6c726e5e6f9..b1812afa817 100644 --- a/tests/baselines/reference/thisTypeInFunctionsNegative.errors.txt +++ b/tests/baselines/reference/thisTypeInFunctionsNegative.errors.txt @@ -79,8 +79,7 @@ tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(168,26): e tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(169,23): error TS1359: Identifier expected. 'this' is a reserved word that cannot be used here. tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(169,23): error TS2680: A 'this' parameter must be the first parameter. tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(170,23): error TS1005: ',' expected. -tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(171,28): error TS1359: Identifier expected. 'this' is a reserved word that cannot be used here. -tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(171,28): error TS2680: A 'this' parameter must be the first parameter. +tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(171,20): error TS1433: Decorators may not be applied to 'this' parameters. tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(172,30): error TS1005: ',' expected. tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(172,32): error TS1359: Identifier expected. 'new' is a reserved word that cannot be used here. tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(172,37): error TS1005: ',' expected. @@ -95,7 +94,7 @@ tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(177,19): e tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(178,22): error TS2730: An arrow function cannot have a 'this' parameter. -==== tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts (65 errors) ==== +==== tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts (64 errors) ==== class C { n: number; explicitThis(this: this, m: number): number { @@ -407,10 +406,8 @@ tests/cases/conformance/types/thisType/thisTypeInFunctionsNegative.ts(178,22): e ~ !!! error TS1005: ',' expected. function decorated(@deco() this: C): number { return this.n; } - ~~~~ -!!! error TS1359: Identifier expected. 'this' is a reserved word that cannot be used here. - ~~~~~~~ -!!! error TS2680: A 'this' parameter must be the first parameter. + ~~~~~~~ +!!! error TS1433: Decorators may not be applied to 'this' parameters. function initializer(this: C = new C()): number { return this.n; } ~ !!! error TS1005: ',' expected. diff --git a/tests/baselines/reference/thisTypeInFunctionsNegative.symbols b/tests/baselines/reference/thisTypeInFunctionsNegative.symbols index 14566bbf946..77b4b6a15a7 100644 --- a/tests/baselines/reference/thisTypeInFunctionsNegative.symbols +++ b/tests/baselines/reference/thisTypeInFunctionsNegative.symbols @@ -646,9 +646,11 @@ function optional(this?: C): number { return this.n; } function decorated(@deco() this: C): number { return this.n; } >decorated : Symbol(decorated, Decl(thisTypeInFunctionsNegative.ts, 169, 54)) -> : Symbol((Missing), Decl(thisTypeInFunctionsNegative.ts, 170, 19)) ->this : Symbol(this, Decl(thisTypeInFunctionsNegative.ts, 170, 26)) +>this : Symbol(this, Decl(thisTypeInFunctionsNegative.ts, 170, 19)) >C : Symbol(C, Decl(thisTypeInFunctionsNegative.ts, 0, 0)) +>this.n : Symbol(C.n, Decl(thisTypeInFunctionsNegative.ts, 0, 9)) +>this : Symbol(this, Decl(thisTypeInFunctionsNegative.ts, 170, 19)) +>n : Symbol(C.n, Decl(thisTypeInFunctionsNegative.ts, 0, 9)) function initializer(this: C = new C()): number { return this.n; } >initializer : Symbol(initializer, Decl(thisTypeInFunctionsNegative.ts, 170, 62)) diff --git a/tests/baselines/reference/thisTypeInFunctionsNegative.types b/tests/baselines/reference/thisTypeInFunctionsNegative.types index 078d63d87c7..36837e31089 100644 --- a/tests/baselines/reference/thisTypeInFunctionsNegative.types +++ b/tests/baselines/reference/thisTypeInFunctionsNegative.types @@ -733,14 +733,13 @@ function optional(this?: C): number { return this.n; } >n : any function decorated(@deco() this: C): number { return this.n; } ->decorated : (: any, this: C) => number +>decorated : (this: C) => number >deco() : any >deco : any -> : any >this : C ->this.n : any ->this : any ->n : any +>this.n : number +>this : C +>n : number function initializer(this: C = new C()): number { return this.n; } >initializer : (this: C, : any, C: any) => any diff --git a/tests/cases/conformance/decorators/class/method/parameter/decoratorOnClassMethodThisParameter.ts b/tests/cases/conformance/decorators/class/method/parameter/decoratorOnClassMethodThisParameter.ts index d111fc9047a..7bc6570a8bb 100644 --- a/tests/cases/conformance/decorators/class/method/parameter/decoratorOnClassMethodThisParameter.ts +++ b/tests/cases/conformance/decorators/class/method/parameter/decoratorOnClassMethodThisParameter.ts @@ -4,4 +4,8 @@ declare function dec(target: Object, propertyKey: string | symbol, parameterInde class C { method(@dec this: C) {} +} + +class C2 { + method(@dec allowed: C2, @dec this: C2) {} } \ No newline at end of file diff --git a/tests/cases/conformance/decorators/invalid/decoratorOnFunctionParameter.ts b/tests/cases/conformance/decorators/invalid/decoratorOnFunctionParameter.ts new file mode 100644 index 00000000000..961be677ce1 --- /dev/null +++ b/tests/cases/conformance/decorators/invalid/decoratorOnFunctionParameter.ts @@ -0,0 +1,6 @@ +declare const dec: any; + +class C { n = true; } + +function direct(@dec this: C) { return this.n; } +function called(@dec() this: C) { return this.n; } \ No newline at end of file