diff --git a/src/parser/plugins/validate-type.ts b/src/parser/plugins/validate-type.ts index 1d043919..3c42ba5d 100644 --- a/src/parser/plugins/validate-type.ts +++ b/src/parser/plugins/validate-type.ts @@ -1,17 +1,21 @@ import { getTypeBySource } from '../../type.js'; import { visitNode } from '../visit.js'; +import { AiScriptSyntaxError } from '../../error.js'; import type * as Ast from '../../node.js'; +function validateTypeParams(node: Ast.Fn): void { + const typeParamNames = new Set(); + for (const typeParam of node.typeParams) { + if (typeParamNames.has(typeParam.name)) { + throw new AiScriptSyntaxError(`type parameter name ${typeParam.name} is duplicate`, node.loc.start); + } + typeParamNames.add(typeParam.name); + } +} + function collectTypeParams(node: Ast.Node, ancestors: Ast.Node[]): Ast.TypeParam[] { const items = []; if (node.type === 'fn') { - const typeParamNames = new Set(); - for (const typeParam of node.typeParams) { - if (typeParamNames.has(typeParam.name)) { - throw new Error(`type parameter name ${typeParam.name} is duplicate`); - } - typeParamNames.add(typeParam.name); - } items.push(...node.typeParams); } for (let i = ancestors.length - 1; i >= 0; i--) { @@ -32,6 +36,7 @@ function validateNode(node: Ast.Node, ancestors: Ast.Node[]): Ast.Node { break; } case 'fn': { + validateTypeParams(node); for (const param of node.params) { if (param.argType != null) { getTypeBySource(param.argType, collectTypeParams(node, ancestors)); diff --git a/test/types.ts b/test/types.ts index d876e00a..63ad5db2 100644 --- a/test/types.ts +++ b/test/types.ts @@ -1,8 +1,8 @@ import * as assert from 'assert'; -import { describe, test } from 'vitest'; +import { describe, expect, test } from 'vitest'; import { utils } from '../src'; import { NUM, STR, NULL, ARR, OBJ, BOOL, TRUE, FALSE, ERROR ,FN_NATIVE } from '../src/interpreter/value'; -import { AiScriptRuntimeError } from '../src/error'; +import { AiScriptRuntimeError, AiScriptSyntaxError } from '../src/error'; import { exe, getMeta, eq } from './testutils'; describe('function types', () => { @@ -98,9 +98,15 @@ describe('generics', () => { }); test.concurrent('duplicate', async () => { - await assert.rejects(() => exe(` + await expect(() => exe(` @f(v: T) {} - `)); + `)).rejects.toThrow(AiScriptSyntaxError); + }); + + test.concurrent('duplicate (no param and ret types)', async () => { + await expect(() => exe(` + @f() {} + `)).rejects.toThrow(AiScriptSyntaxError); }); test.concurrent('empty', async () => { diff --git a/unreleased/fix-duplicate-type-parameter-name-error.md b/unreleased/fix-duplicate-type-parameter-name-error.md new file mode 100644 index 00000000..362fcea5 --- /dev/null +++ b/unreleased/fix-duplicate-type-parameter-name-error.md @@ -0,0 +1,2 @@ +- Fix: 関数の型引数の名前が重複した場合に適切なエラーが発生しない問題を修正 +- Fix: 関数の引数や返り値に型注釈が無く、型引数の名前が重複した場合にエラーが発生しない問題を修正