From 8eeab3d2ba9660657b1f9ed02f7e02719769301b Mon Sep 17 00:00:00 2001 From: Thomas Broyer Date: Wed, 17 Jan 2024 10:38:39 +0100 Subject: [PATCH] Add decorator-related diagnostic codes to `expectError` (#207) --- source/lib/compiler.ts | 22 +++- source/lib/interfaces.ts | 10 ++ source/test/expect-error.ts | 12 ++ .../expect-error/decorators/index.d.ts | 15 +++ .../fixtures/expect-error/decorators/index.js | 6 + .../expect-error/decorators/index.test-d.ts | 77 ++++++++++++ .../expect-error/decorators/package.json | 3 + .../experimental-decorators/index.d.ts | 15 +++ .../experimental-decorators/index.js | 6 + .../experimental-decorators/index.test-d.ts | 111 ++++++++++++++++++ .../experimental-decorators/package.json | 8 ++ 11 files changed, 279 insertions(+), 6 deletions(-) create mode 100644 source/test/fixtures/expect-error/decorators/index.d.ts create mode 100644 source/test/fixtures/expect-error/decorators/index.js create mode 100644 source/test/fixtures/expect-error/decorators/index.test-d.ts create mode 100644 source/test/fixtures/expect-error/decorators/package.json create mode 100644 source/test/fixtures/expect-error/experimental-decorators/index.d.ts create mode 100644 source/test/fixtures/expect-error/experimental-decorators/index.js create mode 100644 source/test/fixtures/expect-error/experimental-decorators/index.test-d.ts create mode 100644 source/test/fixtures/expect-error/experimental-decorators/package.json diff --git a/source/lib/compiler.ts b/source/lib/compiler.ts index 1697084b..c9f7e64a 100644 --- a/source/lib/compiler.ts +++ b/source/lib/compiler.ts @@ -45,6 +45,16 @@ const expectErrorDiagnosticCodesToIgnore = new Set([ DiagnosticCode.StringLiteralTypeIsNotAssignableToUnionTypeWithSuggestion, DiagnosticCode.ObjectLiteralMayOnlySpecifyKnownProperties, DiagnosticCode.ObjectLiteralMayOnlySpecifyKnownProperties2, + DiagnosticCode.UnableToResolveSignatureOfClassDecorator, + DiagnosticCode.UnableToResolveSignatureOfParameterDecorator, + DiagnosticCode.UnableToResolveSignatureOfPropertyDecorator, + DiagnosticCode.UnableToResolveSignatureOfMethodDecorator, + DiagnosticCode.DecoratorCanOnlyDecorateMethodImplementation, + DiagnosticCode.DecoratorFunctionReturnTypeNotAssignableToType, + DiagnosticCode.DecoratorFunctionReturnTypeExpectedToBeVoidOrAny, + DiagnosticCode.RuntimeWillInvokeDecoratorWithXArgumentsButDecoratorExpectsY, + DiagnosticCode.RuntimeWillInvokeDecoratorWithXArgumentsButDecoratorExpectsAtLeastY, + DiagnosticCode.AcceptsTooFewArgumentsToBeUsedAsDecoratorHere, ]); type IgnoreDiagnosticResult = 'preserve' | 'ignore' | Location; @@ -76,6 +86,10 @@ const ignoreDiagnostic = ( // Diagnostic is inside of `expectError` clause if (diagnosticFileName === location.fileName && start > location.start && start < location.end) { + if (expectErrorDiagnosticCodesToIgnore.has(diagnostic.code)) { + return location; + } + // Ignore syntactical errors if (diagnostic.code < 2000) { expectedErrors.delete(location); @@ -83,12 +97,8 @@ const ignoreDiagnostic = ( } // Set diagnostic code on `ExpectedError` to log - if (!expectErrorDiagnosticCodesToIgnore.has(diagnostic.code)) { - error.code = diagnostic.code; - return 'preserve'; - } - - return location; + error.code = diagnostic.code; + return 'preserve'; } } diff --git a/source/lib/interfaces.ts b/source/lib/interfaces.ts index fbebc6d1..a6ded456 100644 --- a/source/lib/interfaces.ts +++ b/source/lib/interfaces.ts @@ -23,7 +23,17 @@ export interface Context { } export enum DiagnosticCode { + UnableToResolveSignatureOfClassDecorator = 1238, + UnableToResolveSignatureOfParameterDecorator = 1239, + UnableToResolveSignatureOfPropertyDecorator = 1240, + UnableToResolveSignatureOfMethodDecorator = 1241, + DecoratorCanOnlyDecorateMethodImplementation = 1249, + DecoratorFunctionReturnTypeNotAssignableToType = 1270, + DecoratorFunctionReturnTypeExpectedToBeVoidOrAny = 1271, + RuntimeWillInvokeDecoratorWithXArgumentsButDecoratorExpectsY = 1278, + RuntimeWillInvokeDecoratorWithXArgumentsButDecoratorExpectsAtLeastY = 1279, AwaitExpressionOnlyAllowedWithinAsyncFunction = 1308, + AcceptsTooFewArgumentsToBeUsedAsDecoratorHere = 1329, TopLevelAwaitOnlyAllowedWhenModuleESNextOrSystem = 1378, GenericTypeRequiresTypeArguments = 2314, GenericTypeRequiresBetweenXAndYTypeArugments = 2707, diff --git a/source/test/expect-error.ts b/source/test/expect-error.ts index 38b3e6d6..b5a65480 100644 --- a/source/test/expect-error.ts +++ b/source/test/expect-error.ts @@ -52,6 +52,18 @@ test('expectError for values (exactOptionalPropertyTypes enabled)', async t => { verify(t, diagnostics, []); }); +test('expectError for decorators', async t => { + const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/expect-error/decorators')}); + + verify(t, diagnostics, []); +}); + +test('expectError for experimental decorators', async t => { + const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/expect-error/experimental-decorators')}); + + verify(t, diagnostics, []); +}); + test('expectError should report missing diagnostic codes', async t => { const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/expect-error/missing-diagnostic-code')}); diff --git a/source/test/fixtures/expect-error/decorators/index.d.ts b/source/test/fixtures/expect-error/decorators/index.d.ts new file mode 100644 index 00000000..a60b4534 --- /dev/null +++ b/source/test/fixtures/expect-error/decorators/index.d.ts @@ -0,0 +1,15 @@ +export declare class Base { + dummy(a: string, b: number): boolean; +} + +export function classDec Base>(value: T, context: ClassDecoratorContext): T; +export function methodDec(value: (this: Base, a: string, b: number) => boolean, context: ClassMethodDecoratorContext boolean>): (this: Base, a: string, b: number) => boolean +export function getterDec(value: (this: Base) => number, context: ClassGetterDecoratorContext): (this: Base) => number; +export function setterDec(value: (this: Base, value: number) => void, context: ClassSetterDecoratorContext): (this: Base, value: number) => void; +export function accessorDec(value: ClassAccessorDecoratorTarget, context: ClassAccessorDecoratorContext): ClassAccessorDecoratorResult; +export function fieldDec(value: undefined, context: ClassFieldDecoratorContext): (initialValue: number) => number; + +export function tooFewArguments(value: Function): void; +export function badReturnType(value: ClassAccessorDecoratorTarget, context: ClassAccessorDecoratorContext): number; + +export function factory(arg: number): (value: (a: number) => void, context: ClassMethodDecoratorContext void>) => void; diff --git a/source/test/fixtures/expect-error/decorators/index.js b/source/test/fixtures/expect-error/decorators/index.js new file mode 100644 index 00000000..a568b854 --- /dev/null +++ b/source/test/fixtures/expect-error/decorators/index.js @@ -0,0 +1,6 @@ +module.exports.classDec = (value, context) => {}; +module.exports.methodDec = (value, context) => {}; +module.exports.getterDec = (value, context) => {}; +module.exports.setterDec = (value, context) => {}; +module.exports.accessorDec = (value, context) => {}; +module.exports.fieldDec = (value, context) => {}; diff --git a/source/test/fixtures/expect-error/decorators/index.test-d.ts b/source/test/fixtures/expect-error/decorators/index.test-d.ts new file mode 100644 index 00000000..63a8d2ce --- /dev/null +++ b/source/test/fixtures/expect-error/decorators/index.test-d.ts @@ -0,0 +1,77 @@ +import {expectError} from '../../../..'; +import {Base, classDec, methodDec, getterDec, setterDec, accessorDec, fieldDec, tooFewArguments, badReturnType, factory} from '.'; + +expectError(@classDec class {}); // 1238, 1270 +expectError(() => { // 1238, 1270 + @classDec + abstract class Test extends Base {} +}); + +expectError(class extends Base { // 1241 + @methodDec static foo(a: string, b: number) { return true; } +}); +expectError(class extends Base { // 1241, 1270 + @methodDec foo() {} +}); +expectError(class { // 1241 + @methodDec foo(a: string, b: number) { return true; } +}); +expectError(class extends Base { // 1249 + @methodDec override dummy(a: string, b: number): boolean + dummy(): void + dummy(a?: string, b?: number) : boolean|void {} +}); + +expectError(class extends Base { // 1241 + @getterDec static get foo() { return 42; } +}); +expectError(class extends Base { // 1241, 1270 + @getterDec get foo() { return "bar"; } +}); +expectError(class { // 1241 + @getterDec get foo() { return 42; } +}); + +expectError(class extends Base { // 1241 + @setterDec static set foo(value: number) {} +}); +expectError(class extends Base { // 1241, 1270 + @setterDec set foo(value: string) {} +}); +expectError(class { // 1241 + @setterDec set foo(value: number) {} +}); + +expectError(class extends Base { // 1240, 1270 + @accessorDec static accessor foo = "bar"; +}); +expectError(class extends Base { // 1240, 1270 + @accessorDec accessor foo = 42; +}); +expectError(class { // 1240, 1270 + @accessorDec accessor foo = "bar"; +}); + +expectError(class extends Base { // 1240 + @fieldDec static foo = 42; +}); +expectError(class extends Base { // 1240, 1270 + @fieldDec foo = "bar" +}); +expectError(class { // 1240 + @fieldDec foo = 42; +}); + +expectError(class { + @tooFewArguments foo() {} +}); +expectError(class extends Base { + @badReturnType accessor foo = 42; +}) + +expectError(class { + @factory("bar") foo(a: number) {} +}); +expectError(class { + @factory foo(a: number) {} +}); diff --git a/source/test/fixtures/expect-error/decorators/package.json b/source/test/fixtures/expect-error/decorators/package.json new file mode 100644 index 00000000..de6dc1db --- /dev/null +++ b/source/test/fixtures/expect-error/decorators/package.json @@ -0,0 +1,3 @@ +{ + "name": "foo" +} diff --git a/source/test/fixtures/expect-error/experimental-decorators/index.d.ts b/source/test/fixtures/expect-error/experimental-decorators/index.d.ts new file mode 100644 index 00000000..e6c7a1c8 --- /dev/null +++ b/source/test/fixtures/expect-error/experimental-decorators/index.d.ts @@ -0,0 +1,15 @@ +export declare class Base { + dummy(a: string, b: number): boolean; +} + +export function classDec Base>(constructor: T): T; +export function methodDec(target: Base, propertyKey: string, descriptor: TypedPropertyDescriptor<(a: number, b: number) => boolean>): void +export function getterDec(target: Base, propertyKey: string, descriptor: TypedPropertyDescriptor): void; +export function setterDec(target: Base, propertyKey: string, descriptor: TypedPropertyDescriptor): void; +export function propertyDec(target: Base, propertyKey: string): void; +export function parameterDec(target: Base, propertyKey: string, parameterIndex: number): void; + +export function tooFewArguments(target: Base): PropertyDescriptor; +export function badReturnType(target: Base, propertyKey: string, descriptor: PropertyDescriptor): number; + +export function factory(arg: number): (target: Base, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor; diff --git a/source/test/fixtures/expect-error/experimental-decorators/index.js b/source/test/fixtures/expect-error/experimental-decorators/index.js new file mode 100644 index 00000000..865ca6bb --- /dev/null +++ b/source/test/fixtures/expect-error/experimental-decorators/index.js @@ -0,0 +1,6 @@ +module.exports.classDec = (constructor) => {}; +module.exports.methodDec = (target, propertyKey, descriptor) => {}; +module.exports.getterDec = (target, propertyKey, descriptor) => {}; +module.exports.setterDec = (target, propertyKey, descriptor) => {}; +module.exports.accessorDec = (target, propertyKey, descriptor) => {}; +module.exports.fieldDec = (target, propertyKey, descriptor) => {}; diff --git a/source/test/fixtures/expect-error/experimental-decorators/index.test-d.ts b/source/test/fixtures/expect-error/experimental-decorators/index.test-d.ts new file mode 100644 index 00000000..fb709748 --- /dev/null +++ b/source/test/fixtures/expect-error/experimental-decorators/index.test-d.ts @@ -0,0 +1,111 @@ +import {expectError} from '../../../..'; +import {Base, classDec, methodDec, getterDec, setterDec, propertyDec, parameterDec, tooFewArguments, badReturnType, factory} from '.'; + +expectError(() => { // 1238, 1270 + @classDec + class Test {} +}); +expectError(() => { // 1238, 1270 + @classDec + abstract class Test extends Base {} +}); + +expectError(() => { // 1241 + class Test extends Base { + @methodDec static foo(a: string, b: number) { return true; } + } +}); +expectError(() => { // 1241 + class Test extends Base { + @methodDec foo() {} + } +}); +expectError(() => { // 1241 + class Test { + @methodDec foo(a: string, b: number) { return true; } + } +}); +expectError(() => { // 1249 + class Test extends Base { + @methodDec override dummy(a: string, b: number): boolean + dummy(): void + dummy(a?: string, b?: number) : boolean|void {} + } +}); + +expectError(() => { // 1241 + class Test extends Base { + @getterDec static get foo() { return 42; } + } +}); +expectError(() => { // 1241 + class Test extends Base { + @getterDec get foo() { return "bar"; } + } +}); +expectError(() => { // 1241 + class Test { + @getterDec get foo() { return 42; } + } +}); + +expectError(() => { // 1241 + class Test extends Base { + @setterDec static set foo(value: number) {} + } +}); +expectError(() => { // 1241 + class Test extends Base { + @setterDec static set foo(value: string) {} + } +}); +expectError(() => { // 1241 + class Test { + @setterDec set foo(value: number) {} + } +}); + +expectError(() => { // 1240 + class Test extends Base { + @propertyDec static foo = 42; + } +}); +expectError(() => { // 1240 + class Test { + @propertyDec foo = 42; + } +}); + +expectError(() => { // 1239 + class Test extends Base { + static foo(@parameterDec a: number) {} + } +}); +expectError(() => { // 1241 + class Test { + foo(@parameterDec a: number) {} + } +}); + + +expectError(() => { + class Test { + @tooFewArguments foo() {} + } +}); +expectError(() => { // 1271 + class Test extends Base { + @badReturnType accessor foo = 42; + } +}) + +expectError(() => { + class Test { + @factory("bar") foo(a: number) {} + } +}); +expectError(() => { + class Test { + @factory foo(a: number) {} + } +}); diff --git a/source/test/fixtures/expect-error/experimental-decorators/package.json b/source/test/fixtures/expect-error/experimental-decorators/package.json new file mode 100644 index 00000000..a0f5ca05 --- /dev/null +++ b/source/test/fixtures/expect-error/experimental-decorators/package.json @@ -0,0 +1,8 @@ +{ + "name": "foo", + "tsd": { + "compilerOptions": { + "experimentalDecorators": true + } + } +}