fix(typecheck): fix error test case mapping for @ts-expect-error (#7125)

This commit is contained in:
Hiroshi Ogawa 2024-12-27 18:07:38 +09:00 committed by GitHub
parent 69571829a5
commit 27d340aa51
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 62 additions and 7 deletions

View File

@ -5,6 +5,8 @@ import { resolve } from 'pathe'
import { isPrimitive, notNullish } from './helpers'
export {
eachMapping,
type EachMapping,
generatedPositionFor,
originalPositionFor,
TraceMap,

View File

@ -1,6 +1,7 @@
import type { RawSourceMap } from '@ampproject/remapping'
import type { File, Task, TaskResultPack, TaskState } from '@vitest/runner'
import type { ParsedStack } from '@vitest/utils'
import type { EachMapping } from '@vitest/utils/source-map'
import type { ChildProcess } from 'node:child_process'
import type { Vitest } from '../node/core'
import type { TestProject } from '../node/project'
@ -10,7 +11,7 @@ import type { TscErrorInfo } from './types'
import { rm } from 'node:fs/promises'
import { performance } from 'node:perf_hooks'
import { getTasks } from '@vitest/runner/utils'
import { generatedPositionFor, TraceMap } from '@vitest/utils/source-map'
import { eachMapping, generatedPositionFor, TraceMap } from '@vitest/utils/source-map'
import { basename, extname, resolve } from 'pathe'
import { x } from 'tinyexec'
import { collectTests } from './collect'
@ -161,7 +162,7 @@ export class Typechecker {
}
errors.forEach(({ error, originalError }) => {
const processedPos = traceMap
? generatedPositionFor(traceMap, {
? findGeneratedPosition(traceMap, {
line: originalError.line,
column: originalError.column,
source: basename(path),
@ -364,3 +365,40 @@ export class Typechecker {
.map<TaskResultPack>(i => [i.id, i.result, { typecheck: true }])
}
}
function findGeneratedPosition(traceMap: TraceMap, { line, column, source }: { line: number; column: number; source: string }) {
const found = generatedPositionFor(traceMap, {
line,
column,
source,
})
if (found.line !== null) {
return found
}
// find the next source token position when the exact error position doesn't exist in source map.
// this can happen, for example, when the type error is in the comment "// @ts-expect-error"
// and comments are stripped away in the generated code.
const mappings: (EachMapping & { originalLine: number })[] = []
eachMapping(traceMap, (m) => {
if (
m.source === source
&& m.originalLine !== null
&& m.originalColumn !== null
&& (line === m.originalLine ? column < m.originalColumn : line < m.originalLine)
) {
mappings.push(m)
}
})
const next = mappings
.sort((a, b) =>
a.originalLine === b.originalLine ? a.originalColumn - b.originalColumn : a.originalLine - b.originalLine,
)
.at(0)
if (next) {
return {
line: next.generatedLine,
column: next.generatedColumn,
}
}
return { line: null, column: null }
}

View File

@ -1,5 +1,6 @@
import { expectTypeOf, test } from 'vitest'
//
test('failing test with expect-error', () => {
// @ts-expect-error expect nothing
expectTypeOf(1).toEqualTypeOf<number>()

View File

@ -24,12 +24,12 @@ TypeCheckError: This expression is not callable. Type 'ExpectVoid<number>' has n
exports[`should fail > typecheck files 3`] = `
" FAIL expect-error.test-d.ts > failing test with expect-error
TypeCheckError: Unused '@ts-expect-error' directive.
expect-error.test-d.ts:4:3
2|
3| test('failing test with expect-error', () => {
4| // @ts-expect-error expect nothing
expect-error.test-d.ts:5:3
3| //
4| test('failing test with expect-error', () => {
5| // @ts-expect-error expect nothing
| ^
5| expectTypeOf(1).toEqualTypeOf<number>()"
6| expectTypeOf(1).toEqualTypeOf<number>()"
`;
exports[`should fail > typecheck files 4`] = `

View File

@ -0,0 +1,14 @@
import { defineConfig } from 'vitest/config'
// pnpm -C test/typescript test -- -c vitest.config.fails.ts
export default defineConfig({
test: {
dir: './failing',
typecheck: {
enabled: true,
allowJs: true,
include: ['**/*.test-d.*'],
tsconfig: './tsconfig.fails.json',
},
},
})