From 62ff3b5d32426a6bab4f6558c4f2b59556710f60 Mon Sep 17 00:00:00 2001 From: Thomas Broyer Date: Tue, 16 Jan 2024 11:34:38 +0100 Subject: [PATCH 1/2] Allow expectError diagnostic codes to ignore to override syntactical errors There are a few diagnostic codes that are < 2000 but aren't really syntactical errors. They can thus be listed in expectErrorDiagnosticCodesToIgnore to be "expected" by expecteError. --- source/lib/compiler.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source/lib/compiler.ts b/source/lib/compiler.ts index 1697084..8330362 100644 --- a/source/lib/compiler.ts +++ b/source/lib/compiler.ts @@ -76,6 +76,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 +87,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'; } } From 205d42f14daffe245a75b40e6d4f0ec0d4b60fcf Mon Sep 17 00:00:00 2001 From: Thomas Broyer Date: Tue, 16 Jan 2024 11:35:27 +0100 Subject: [PATCH 2/2] Add decorator-related diagnostic codes to expectError Fixes #206 --- source/lib/compiler.ts | 10 ++ 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, 273 insertions(+) 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 8330362..c9f7e64 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; diff --git a/source/lib/interfaces.ts b/source/lib/interfaces.ts index fbebc6d..a6ded45 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 38b3e6d..b5a6548 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 0000000..a60b453 --- /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 0000000..a568b85 --- /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 0000000..63a8d2c --- /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 0000000..de6dc1d --- /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 0000000..e6c7a1c --- /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 0000000..865ca6b --- /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 0000000..fb70974 --- /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 0000000..a0f5ca0 --- /dev/null +++ b/source/test/fixtures/expect-error/experimental-decorators/package.json @@ -0,0 +1,8 @@ +{ + "name": "foo", + "tsd": { + "compilerOptions": { + "experimentalDecorators": true + } + } +}