Skip to content

Commit f6c79ef

Browse files
authored
fix: 一部の構文について組み込みプラグインによる検証が働かない問題を修正 (#1005)
* 一部の構文について組み込みプラグインによる検証が働かない問題を修正 * ローカルのexpectを使用することに関してコメントを追加 * 読点が変
1 parent a3bc04a commit f6c79ef

6 files changed

Lines changed: 98 additions & 20 deletions

File tree

src/parser/visit.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,12 @@ function visitNodeInner(node: Ast.Node, fn: (node: Ast.Node, ancestors: Ast.Node
152152
result.target = visitNodeInner(result.target, fn, ancestors) as Ast.Prop['target'];
153153
break;
154154
}
155+
case 'break': {
156+
if (result.expr != null) {
157+
result.expr = visitNodeInner(result.expr, fn, ancestors) as Ast.Break['expr'];
158+
}
159+
break;
160+
}
155161
case 'ns': {
156162
for (let i = 0; i < result.members.length; i++) {
157163
result.members[i] = visitNodeInner(result.members[i]!, fn, ancestors) as (typeof result.members)[number];
@@ -208,10 +214,17 @@ function visitNodeInner(node: Ast.Node, fn: (node: Ast.Node, ancestors: Ast.Node
208214
break;
209215
}
210216

217+
case 'namedTypeSource': {
218+
if (result.inner != null) {
219+
result.inner = visitNodeInner(result.inner, fn, ancestors) as Ast.NamedTypeSource['inner'];
220+
}
221+
break;
222+
}
211223
case 'fnTypeSource': {
212224
for (let i = 0; i < result.params.length; i++) {
213225
result.params[i] = visitNodeInner(result.params[i]!, fn, ancestors) as Ast.FnTypeSource['params'][number];
214226
}
227+
result.result = visitNodeInner(result.result, fn, ancestors) as Ast.FnTypeSource['result'];
215228
break;
216229
}
217230
case 'unionTypeSource': {
@@ -220,6 +233,23 @@ function visitNodeInner(node: Ast.Node, fn: (node: Ast.Node, ancestors: Ast.Node
220233
}
221234
break;
222235
}
236+
237+
case 'str':
238+
case 'num':
239+
case 'bool':
240+
case 'null':
241+
case 'identifier':
242+
case 'attr':
243+
case 'continue':
244+
case 'meta': {
245+
break; // nop
246+
}
247+
248+
default: {
249+
// exhaustiveness check
250+
result satisfies never;
251+
throw new Error('invalid node type');
252+
}
223253
}
224254

225255
ancestors.pop();

test/identifiers.ts

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -253,10 +253,30 @@ const sampleCodes = Object.entries<[(definedName: string, referredName: string)
253253
}
254254
`, NULL],
255255

256+
break: [(definedName, referredName) =>
257+
`
258+
<: #label: eval {
259+
break #label eval {
260+
let ${definedName} = "ai"
261+
${referredName}
262+
}
263+
}
264+
`, STR('ai')],
265+
256266
typeParam: [(definedName, referredName) =>
257267
`
258268
@f<${definedName}>(x): ${referredName} { x }
259269
`, NULL],
270+
271+
innerType: [(definedName, referredName) =>
272+
`
273+
let x: arr<@<${definedName}>() => ${referredName}> = []
274+
`, NULL],
275+
276+
returnType: [(definedName, referredName) =>
277+
`
278+
let x: @() => @<${definedName}>() => ${referredName} = @() {}
279+
`, NULL],
260280
});
261281

262282
const parser = new Parser();
@@ -278,15 +298,18 @@ describe.each(
278298
parser.parse(sampleCode(wordCat, wordCat));
279299
});
280300

281-
test.concurrent.each(
301+
// グローバルの expect を使用すると expect.hasAssertions() が失敗するときがあるので、
302+
// ローカルの expect を使用する
303+
test.concurrent.for(
282304
identifierCases
283-
)('%s is allowed: %s', async (word, allowed) => {
305+
)('%s is allowed: %s', async ([word, allowed], { expect }) => {
284306
expect.hasAssertions();
285307
if (allowed) {
286308
const res = await exe(sampleCode(word, word));
287-
eq(res, expected);
309+
eq(res, expected, expect);
288310
} else {
289311
expect(() => parser.parse(sampleCode(word, word))).toThrow(AiScriptSyntaxError);
312+
await Promise.resolve(); // https://github.com/vitest-dev/vitest/issues/4750
290313
}
291314
});
292315

test/jump-statements.ts

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as assert from 'assert';
2-
import { describe, test } from 'vitest';
2+
import { describe, expect, test } from 'vitest';
33
import { errors, utils } from '../src';
44
import { NUM, STR, NULL, ARR, OBJ, BOOL, TRUE, FALSE, ERROR ,FN_NATIVE } from '../src/interpreter/value';
55
import { AiScriptRuntimeError, AiScriptSyntaxError } from '../src/error';
@@ -507,21 +507,26 @@ describe('return', () => {
507507
await assert.rejects(() => exe('return eval { return 1 } + 2'));
508508
});
509509

510-
test.concurrent('in break', async () => {
511-
const res = await exe(`
512-
@f() {
510+
describe('in break', () => {
511+
test.concurrent('valid', async () => {
512+
const res = await exe(`
513+
@f() {
514+
#l: eval {
515+
break #l eval { return 1 }
516+
}
517+
}
518+
<: f()
519+
`);
520+
eq(res, NUM(1));
521+
});
522+
523+
test.concurrent('invalid', async () => {
524+
await expect(() => exe(`
513525
#l: eval {
514526
break #l eval { return 1 }
515527
}
516-
}
517-
<: f()
518-
`);
519-
eq(res, NUM(1));
520-
await assert.rejects(() => exe(`
521-
#l: eval {
522-
break #l eval { return 1 }
523-
}
524-
`));
528+
`)).rejects.toThrow(AiScriptSyntaxError);
529+
});
525530
});
526531

527532
describe('in and', async () => {

test/testutils.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import * as assert from 'assert';
1+
import { expect as globalExpect } from 'vitest';
22
import { Parser, Interpreter } from '../src';
3+
import { Value } from '../src/interpreter/value';
34

45
export async function exe(script: string): Promise<Value | undefined> {
56
const parser = new Parser();
@@ -42,8 +43,15 @@ export const getMeta = (script: string) => {
4243
return metadata;
4344
};
4445

45-
export const eq = (a, b) => {
46-
assert.deepEqual(a.type, b.type);
47-
assert.deepEqual(a.value, b.value);
46+
export const eq = (a: Value | undefined, b: Value | undefined, expect = globalExpect) => {
47+
expect(a).not.toBeUndefined();
48+
expect(b).not.toBeUndefined();
49+
expect(a!.type).toEqual(b!.type);
50+
if ('value' in a!) {
51+
expect('value' in b!).toBe(true);
52+
expect(a.value).toEqual((b as { value: unknown }).value);
53+
} else {
54+
expect('value' in b!).toBe(false);
55+
}
4856
};
4957

test/types.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,3 +210,13 @@ describe('simple', () => {
210210
eq(res, NUM(1));
211211
});
212212
});
213+
214+
test.concurrent('in break', async () => {
215+
await expect(() => exe(`
216+
#l: eval {
217+
break #l eval {
218+
let x: invalid = 0
219+
}
220+
}
221+
`)).rejects.toThrow(AiScriptSyntaxError);
222+
});
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
- Fix: break文のもつ値の式について不正な変数やreturn文、break文、continue文、型注釈がある場合に文法エラーにならない問題を修正
2+
- Fix: 型注釈における型引数や関数型の返り値に不正な型注釈がある場合に文法エラーにならない問題を修正

0 commit comments

Comments
 (0)