diff --git a/src/lint/rules/variable-use-def.ts b/src/lint/rules/variable-use-def.ts
index 8058248b..3a9e9361 100644
--- a/src/lint/rules/variable-use-def.ts
+++ b/src/lint/rules/variable-use-def.ts
@@ -265,9 +265,21 @@ function walkAlgorithm(
continue;
}
// TODO this kind of parsing should really be factored out
+ let varName = true;
for (let i = 0; i < paramList.items.length; ++i) {
const item = paramList.items[i];
- if (i % 2 === 0) {
+ if (varName) {
+ if (item.name === 'text' && item.contents === '...') {
+ if (i < paramList.items.length - 2) {
+ report({
+ ruleId: 'bad-ac',
+ message: `expected rest param to come last`,
+ ...offsetToLineAndColumn(algorithmSource, item.location.start.offset),
+ });
+ continue stepLoop;
+ }
+ continue;
+ }
if (item.name !== 'underscore') {
report({
ruleId: 'bad-ac',
@@ -287,6 +299,7 @@ function walkAlgorithm(
continue stepLoop;
}
}
+ varName = !varName;
}
}
diff --git a/test/lint-variable-use-def.js b/test/lint-variable-use-def.js
index 7679990a..81f77c3b 100644
--- a/test/lint-variable-use-def.js
+++ b/test/lint-variable-use-def.js
@@ -401,6 +401,102 @@ describe('variables are declared and used appropriately', () => {
);
});
+ it('abstract closure rest parameters must be last', async () => {
+ await assertLint(
+ positioned`
+
+ Object.fromEntries ( _obj_ )
+
+ 1. Let _closure_ be a new Abstract Closure with parameters (${M}..._x_, _y_) that captures nothing and performs the following steps when called:
+ 1. Do something with _x_.
+ 1. Return *undefined*.
+ 1. Return _closure_.
+
+
+ `,
+ {
+ ruleId: 'bad-ac',
+ nodeType: 'emu-alg',
+ message: 'expected rest param to come last',
+ },
+ );
+
+ await assertLint(
+ positioned`
+
+ Object.fromEntries ( _obj_ )
+
+ 1. Let _closure_ be a new Abstract Closure with parameters (${M}..._x_,) that captures nothing and performs the following steps when called:
+ 1. Do something with _x_.
+ 1. Return *undefined*.
+ 1. Return _closure_.
+
+
+ `,
+ {
+ ruleId: 'bad-ac',
+ nodeType: 'emu-alg',
+ message: 'expected rest param to come last',
+ },
+ );
+ });
+
+ it('abstract closure rest parameters must be variables', async () => {
+ await assertLint(
+ positioned`
+
+ Object.fromEntries ( _obj_ )
+
+ 1. Let _closure_ be a new Abstract Closure with parameters (${M}...x) that captures nothing and performs the following steps when called:
+ 1. Do something with _x_.
+ 1. Return *undefined*.
+ 1. Return _closure_.
+
+
+ `,
+ {
+ ruleId: 'bad-ac',
+ nodeType: 'emu-alg',
+ message: 'expected to find a parameter name here',
+ },
+ );
+
+ await assertLint(
+ positioned`
+
+ Object.fromEntries ( _obj_ )
+
+ 1. Let _closure_ be a new Abstract Closure with parameters (${M}..._x_,) that captures nothing and performs the following steps when called:
+ 1. Do something with _x_.
+ 1. Return *undefined*.
+ 1. Return _closure_.
+
+
+ `,
+ {
+ ruleId: 'bad-ac',
+ nodeType: 'emu-alg',
+ message: 'expected rest param to come last',
+ },
+ );
+ });
+
+ it('abstract closure rest parameters are visible', async () => {
+ await assertLintFree(
+ `
+
+ Object.fromEntries ( _obj_ )
+
+ 1. Let _closure_ be a new Abstract Closure with parameters (..._x_) that captures nothing and performs the following steps when called:
+ 1. Do something with _x_.
+ 1. Return *undefined*.
+ 1. Return _closure_.
+
+
+ `,
+ );
+ });
+
it('multiple declarations are visible', async () => {
await assertLintFree(
`