diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index a6dab51d9c8ab..69add2945f4ba 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -80,6 +80,7 @@ namespace ts { reportNonlocalAugmentation }; let errorNameNode: DeclarationName | undefined; + let errorFallbackNode: Declaration | undefined; let currentSourceFile: SourceFile; let refs: ESMap; @@ -161,9 +162,9 @@ namespace ts { } function reportPrivateInBaseOfClassExpression(propertyName: string) { - if (errorNameNode) { + if (errorNameNode || errorFallbackNode) { context.addDiagnostic( - createDiagnosticForNode(errorNameNode, Diagnostics.Property_0_of_exported_class_expression_may_not_be_private_or_protected, propertyName)); + createDiagnosticForNode((errorNameNode || errorFallbackNode)!, Diagnostics.Property_0_of_exported_class_expression_may_not_be_private_or_protected, propertyName)); } } @@ -199,8 +200,8 @@ namespace ts { } function reportTruncationError() { - if (errorNameNode) { - context.addDiagnostic(createDiagnosticForNode(errorNameNode, Diagnostics.The_inferred_type_of_this_node_exceeds_the_maximum_length_the_compiler_will_serialize_An_explicit_type_annotation_is_needed)); + if (errorNameNode || errorFallbackNode) { + context.addDiagnostic(createDiagnosticForNode((errorNameNode || errorFallbackNode)!, Diagnostics.The_inferred_type_of_this_node_exceeds_the_maximum_length_the_compiler_will_serialize_An_explicit_type_annotation_is_needed)); } } @@ -1102,7 +1103,9 @@ namespace ts { diagnosticMessage: Diagnostics.Default_export_of_the_module_has_or_is_using_private_name_0, errorNode: input }); + errorFallbackNode = input; const varDecl = factory.createVariableDeclaration(newId, /*exclamationToken*/ undefined, resolver.createTypeOfExpression(input.expression, input, declarationEmitNodeBuilderFlags, symbolTracker), /*initializer*/ undefined); + errorFallbackNode = undefined; const statement = factory.createVariableStatement(needsDeclare ? [factory.createModifier(SyntaxKind.DeclareKeyword)] : [], factory.createVariableDeclarationList([varDecl], NodeFlags.Const)); return [statement, factory.updateExportAssignment(input, input.decorators, input.modifiers, newId)]; } @@ -1326,6 +1329,8 @@ namespace ts { } } case SyntaxKind.ClassDeclaration: { + errorNameNode = input.name; + errorFallbackNode = input; const modifiers = factory.createNodeArray(ensureModifiers(input)); const typeParameters = ensureTypeParams(input, input.typeParameters); const ctor = getFirstConstructorWithBody(input); @@ -1462,6 +1467,8 @@ namespace ts { if (node as Node === input) { return node; } + errorFallbackNode = undefined; + errorNameNode = undefined; return node && setOriginalNode(preserveJsDoc(node, input), input); } } diff --git a/tests/baselines/reference/declarationEmitMixinPrivateProtected.errors.txt b/tests/baselines/reference/declarationEmitMixinPrivateProtected.errors.txt new file mode 100644 index 0000000000000..bb737d0c15ca0 --- /dev/null +++ b/tests/baselines/reference/declarationEmitMixinPrivateProtected.errors.txt @@ -0,0 +1,53 @@ +tests/cases/compiler/another.ts(11,1): error TS4094: Property '_assertIsStripped' of exported class expression may not be private or protected. +tests/cases/compiler/another.ts(11,1): error TS4094: Property '_onDispose' of exported class expression may not be private or protected. +tests/cases/compiler/first.ts(12,1): error TS4094: Property '_assertIsStripped' of exported class expression may not be private or protected. +tests/cases/compiler/first.ts(12,1): error TS4094: Property '_onDispose' of exported class expression may not be private or protected. +tests/cases/compiler/first.ts(13,14): error TS4094: Property '_assertIsStripped' of exported class expression may not be private or protected. +tests/cases/compiler/first.ts(13,14): error TS4094: Property '_onDispose' of exported class expression may not be private or protected. + + +==== tests/cases/compiler/first.ts (4 errors) ==== + declare function mix(mixin: TMix): TMix; + + const DisposableMixin = class { + protected _onDispose() { + this._assertIsStripped() + } + private _assertIsStripped() { + } + }; + + // No error, but definition is wrong. + export default mix(DisposableMixin); + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS4094: Property '_assertIsStripped' of exported class expression may not be private or protected. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS4094: Property '_onDispose' of exported class expression may not be private or protected. + export class Monitor extends mix(DisposableMixin) { + ~~~~~~~ +!!! error TS4094: Property '_assertIsStripped' of exported class expression may not be private or protected. + ~~~~~~~ +!!! error TS4094: Property '_onDispose' of exported class expression may not be private or protected. + protected _onDispose() { + } + } + +==== tests/cases/compiler/another.ts (2 errors) ==== + declare function mix(mixin: TMix): TMix; + + const DisposableMixin = class { + protected _onDispose() { + this._assertIsStripped() + } + private _assertIsStripped() { + } + }; + + export default class extends mix(DisposableMixin) { + ~~~~~~ +!!! error TS4094: Property '_assertIsStripped' of exported class expression may not be private or protected. + ~~~~~~ +!!! error TS4094: Property '_onDispose' of exported class expression may not be private or protected. + protected _onDispose() { + } + } \ No newline at end of file diff --git a/tests/baselines/reference/declarationEmitMixinPrivateProtected.js b/tests/baselines/reference/declarationEmitMixinPrivateProtected.js new file mode 100644 index 0000000000000..8c578e62e7c52 --- /dev/null +++ b/tests/baselines/reference/declarationEmitMixinPrivateProtected.js @@ -0,0 +1,115 @@ +//// [tests/cases/compiler/declarationEmitMixinPrivateProtected.ts] //// + +//// [first.ts] +declare function mix(mixin: TMix): TMix; + +const DisposableMixin = class { + protected _onDispose() { + this._assertIsStripped() + } + private _assertIsStripped() { + } +}; + +// No error, but definition is wrong. +export default mix(DisposableMixin); +export class Monitor extends mix(DisposableMixin) { + protected _onDispose() { + } +} + +//// [another.ts] +declare function mix(mixin: TMix): TMix; + +const DisposableMixin = class { + protected _onDispose() { + this._assertIsStripped() + } + private _assertIsStripped() { + } +}; + +export default class extends mix(DisposableMixin) { + protected _onDispose() { + } +} + +//// [first.js] +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +exports.__esModule = true; +exports.Monitor = void 0; +var DisposableMixin = /** @class */ (function () { + function class_1() { + } + class_1.prototype._onDispose = function () { + this._assertIsStripped(); + }; + class_1.prototype._assertIsStripped = function () { + }; + return class_1; +}()); +// No error, but definition is wrong. +exports["default"] = mix(DisposableMixin); +var Monitor = /** @class */ (function (_super) { + __extends(Monitor, _super); + function Monitor() { + return _super !== null && _super.apply(this, arguments) || this; + } + Monitor.prototype._onDispose = function () { + }; + return Monitor; +}(mix(DisposableMixin))); +exports.Monitor = Monitor; +//// [another.js] +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +exports.__esModule = true; +var DisposableMixin = /** @class */ (function () { + function class_1() { + } + class_1.prototype._onDispose = function () { + this._assertIsStripped(); + }; + class_1.prototype._assertIsStripped = function () { + }; + return class_1; +}()); +var default_1 = /** @class */ (function (_super) { + __extends(default_1, _super); + function default_1() { + return _super !== null && _super.apply(this, arguments) || this; + } + default_1.prototype._onDispose = function () { + }; + return default_1; +}(mix(DisposableMixin))); +exports["default"] = default_1; diff --git a/tests/baselines/reference/declarationEmitMixinPrivateProtected.symbols b/tests/baselines/reference/declarationEmitMixinPrivateProtected.symbols new file mode 100644 index 0000000000000..8f543a285d7be --- /dev/null +++ b/tests/baselines/reference/declarationEmitMixinPrivateProtected.symbols @@ -0,0 +1,71 @@ +=== tests/cases/compiler/first.ts === +declare function mix(mixin: TMix): TMix; +>mix : Symbol(mix, Decl(first.ts, 0, 0)) +>TMix : Symbol(TMix, Decl(first.ts, 0, 21)) +>mixin : Symbol(mixin, Decl(first.ts, 0, 27)) +>TMix : Symbol(TMix, Decl(first.ts, 0, 21)) +>TMix : Symbol(TMix, Decl(first.ts, 0, 21)) + +const DisposableMixin = class { +>DisposableMixin : Symbol(DisposableMixin, Decl(first.ts, 2, 5)) + + protected _onDispose() { +>_onDispose : Symbol(DisposableMixin._onDispose, Decl(first.ts, 2, 31)) + + this._assertIsStripped() +>this._assertIsStripped : Symbol(DisposableMixin._assertIsStripped, Decl(first.ts, 5, 5)) +>this : Symbol(DisposableMixin, Decl(first.ts, 2, 23)) +>_assertIsStripped : Symbol(DisposableMixin._assertIsStripped, Decl(first.ts, 5, 5)) + } + private _assertIsStripped() { +>_assertIsStripped : Symbol(DisposableMixin._assertIsStripped, Decl(first.ts, 5, 5)) + } +}; + +// No error, but definition is wrong. +export default mix(DisposableMixin); +>mix : Symbol(mix, Decl(first.ts, 0, 0)) +>DisposableMixin : Symbol(DisposableMixin, Decl(first.ts, 2, 5)) + +export class Monitor extends mix(DisposableMixin) { +>Monitor : Symbol(Monitor, Decl(first.ts, 11, 36)) +>mix : Symbol(mix, Decl(first.ts, 0, 0)) +>DisposableMixin : Symbol(DisposableMixin, Decl(first.ts, 2, 5)) + + protected _onDispose() { +>_onDispose : Symbol(Monitor._onDispose, Decl(first.ts, 12, 51)) + } +} + +=== tests/cases/compiler/another.ts === +declare function mix(mixin: TMix): TMix; +>mix : Symbol(mix, Decl(another.ts, 0, 0)) +>TMix : Symbol(TMix, Decl(another.ts, 0, 21)) +>mixin : Symbol(mixin, Decl(another.ts, 0, 27)) +>TMix : Symbol(TMix, Decl(another.ts, 0, 21)) +>TMix : Symbol(TMix, Decl(another.ts, 0, 21)) + +const DisposableMixin = class { +>DisposableMixin : Symbol(DisposableMixin, Decl(another.ts, 2, 5)) + + protected _onDispose() { +>_onDispose : Symbol(DisposableMixin._onDispose, Decl(another.ts, 2, 31)) + + this._assertIsStripped() +>this._assertIsStripped : Symbol(DisposableMixin._assertIsStripped, Decl(another.ts, 5, 5)) +>this : Symbol(DisposableMixin, Decl(another.ts, 2, 23)) +>_assertIsStripped : Symbol(DisposableMixin._assertIsStripped, Decl(another.ts, 5, 5)) + } + private _assertIsStripped() { +>_assertIsStripped : Symbol(DisposableMixin._assertIsStripped, Decl(another.ts, 5, 5)) + } +}; + +export default class extends mix(DisposableMixin) { +>mix : Symbol(mix, Decl(another.ts, 0, 0)) +>DisposableMixin : Symbol(DisposableMixin, Decl(another.ts, 2, 5)) + + protected _onDispose() { +>_onDispose : Symbol(default._onDispose, Decl(another.ts, 10, 51)) + } +} diff --git a/tests/baselines/reference/declarationEmitMixinPrivateProtected.types b/tests/baselines/reference/declarationEmitMixinPrivateProtected.types new file mode 100644 index 0000000000000..77dcaba30eb6e --- /dev/null +++ b/tests/baselines/reference/declarationEmitMixinPrivateProtected.types @@ -0,0 +1,72 @@ +=== tests/cases/compiler/first.ts === +declare function mix(mixin: TMix): TMix; +>mix : (mixin: TMix) => TMix +>mixin : TMix + +const DisposableMixin = class { +>DisposableMixin : typeof DisposableMixin +>class { protected _onDispose() { this._assertIsStripped() } private _assertIsStripped() { }} : typeof DisposableMixin + + protected _onDispose() { +>_onDispose : () => void + + this._assertIsStripped() +>this._assertIsStripped() : void +>this._assertIsStripped : () => void +>this : this +>_assertIsStripped : () => void + } + private _assertIsStripped() { +>_assertIsStripped : () => void + } +}; + +// No error, but definition is wrong. +export default mix(DisposableMixin); +>mix(DisposableMixin) : typeof DisposableMixin +>mix : (mixin: TMix) => TMix +>DisposableMixin : typeof DisposableMixin + +export class Monitor extends mix(DisposableMixin) { +>Monitor : Monitor +>mix(DisposableMixin) : DisposableMixin +>mix : (mixin: TMix) => TMix +>DisposableMixin : typeof DisposableMixin + + protected _onDispose() { +>_onDispose : () => void + } +} + +=== tests/cases/compiler/another.ts === +declare function mix(mixin: TMix): TMix; +>mix : (mixin: TMix) => TMix +>mixin : TMix + +const DisposableMixin = class { +>DisposableMixin : typeof DisposableMixin +>class { protected _onDispose() { this._assertIsStripped() } private _assertIsStripped() { }} : typeof DisposableMixin + + protected _onDispose() { +>_onDispose : () => void + + this._assertIsStripped() +>this._assertIsStripped() : void +>this._assertIsStripped : () => void +>this : this +>_assertIsStripped : () => void + } + private _assertIsStripped() { +>_assertIsStripped : () => void + } +}; + +export default class extends mix(DisposableMixin) { +>mix(DisposableMixin) : DisposableMixin +>mix : (mixin: TMix) => TMix +>DisposableMixin : typeof DisposableMixin + + protected _onDispose() { +>_onDispose : () => void + } +} diff --git a/tests/baselines/reference/emitClassExpressionInDeclarationFile2.errors.txt b/tests/baselines/reference/emitClassExpressionInDeclarationFile2.errors.txt index 4541cf2a58aea..25c0bc8c50b8b 100644 --- a/tests/baselines/reference/emitClassExpressionInDeclarationFile2.errors.txt +++ b/tests/baselines/reference/emitClassExpressionInDeclarationFile2.errors.txt @@ -1,9 +1,10 @@ tests/cases/compiler/emitClassExpressionInDeclarationFile2.ts(1,12): error TS4094: Property 'p' of exported class expression may not be private or protected. tests/cases/compiler/emitClassExpressionInDeclarationFile2.ts(1,12): error TS4094: Property 'ps' of exported class expression may not be private or protected. tests/cases/compiler/emitClassExpressionInDeclarationFile2.ts(16,17): error TS4094: Property 'property' of exported class expression may not be private or protected. +tests/cases/compiler/emitClassExpressionInDeclarationFile2.ts(23,14): error TS4094: Property 'property' of exported class expression may not be private or protected. -==== tests/cases/compiler/emitClassExpressionInDeclarationFile2.ts (3 errors) ==== +==== tests/cases/compiler/emitClassExpressionInDeclarationFile2.ts (4 errors) ==== export var noPrivates = class { ~~~~~~~~~~ !!! error TS4094: Property 'p' of exported class expression may not be private or protected. @@ -33,6 +34,8 @@ tests/cases/compiler/emitClassExpressionInDeclarationFile2.ts(16,17): error TS40 } export class Test extends WithTags(FooItem) {} + ~~~~ +!!! error TS4094: Property 'property' of exported class expression may not be private or protected. const test = new Test(); diff --git a/tests/cases/compiler/declarationEmitMixinPrivateProtected.ts b/tests/cases/compiler/declarationEmitMixinPrivateProtected.ts new file mode 100644 index 0000000000000..65a2fa0d25a71 --- /dev/null +++ b/tests/cases/compiler/declarationEmitMixinPrivateProtected.ts @@ -0,0 +1,34 @@ +// @declaration: true +// @filename: first.ts +declare function mix(mixin: TMix): TMix; + +const DisposableMixin = class { + protected _onDispose() { + this._assertIsStripped() + } + private _assertIsStripped() { + } +}; + +// No error, but definition is wrong. +export default mix(DisposableMixin); +export class Monitor extends mix(DisposableMixin) { + protected _onDispose() { + } +} + +// @filename: another.ts +declare function mix(mixin: TMix): TMix; + +const DisposableMixin = class { + protected _onDispose() { + this._assertIsStripped() + } + private _assertIsStripped() { + } +}; + +export default class extends mix(DisposableMixin) { + protected _onDispose() { + } +} \ No newline at end of file