Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/utils/src/source-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { resolve } from 'pathe'
import { isPrimitive, notNullish } from './helpers'

export {
eachMapping,
type EachMapping,
generatedPositionFor,
originalPositionFor,
TraceMap,
Expand Down
42 changes: 40 additions & 2 deletions packages/vitest/src/typecheck/typechecker.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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'
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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 }
}
1 change: 1 addition & 0 deletions test/typescript/failing/expect-error.test-d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { expectTypeOf, test } from 'vitest'

//
test('failing test with expect-error', () => {
// @ts-expect-error expect nothing
expectTypeOf(1).toEqualTypeOf<number>()
Expand Down
10 changes: 5 additions & 5 deletions test/typescript/test/__snapshots__/runner.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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`] = `
Expand Down
14 changes: 14 additions & 0 deletions test/typescript/vitest.config.fails.ts
Original file line number Diff line number Diff line change
@@ -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',
},
},
})
Loading