Skip to content

Commit c891a92

Browse files
committed
PoC: preventing infinite loops idea
1 parent 1b23349 commit c891a92

File tree

10 files changed

+81
-16
lines changed

10 files changed

+81
-16
lines changed

packages/eslint-plugin-svelte/src/rules/no-dynamic-slot-name.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { AST } from 'svelte-eslint-parser';
22
import type { TSESTree } from '@typescript-eslint/types';
33
import { createRule } from '../utils/index.js';
44
import {
5-
findVariable,
5+
findVariableSafe,
66
getAttributeValueQuoteAndRange,
77
getStringIfConstant
88
} from '../utils/ast-utils.js';
@@ -72,23 +72,19 @@ export default createRule('no-dynamic-slot-name', {
7272
}
7373

7474
/** Find data expression */
75-
function findRootExpression(
76-
node: TSESTree.Expression,
77-
already = new Set<TSESTree.Identifier>()
78-
): TSESTree.Expression {
79-
if (node.type !== 'Identifier' || already.has(node)) {
75+
function findRootExpression(node: TSESTree.Expression): TSESTree.Expression {
76+
if (node.type !== 'Identifier') {
8077
return node;
8178
}
82-
already.add(node);
83-
const variable = findVariable(context, node);
79+
const variable = findVariableSafe(findRootExpression, context, node);
8480
if (!variable || variable.defs.length !== 1) {
8581
return node;
8682
}
8783
const def = variable.defs[0];
8884
if (def.type === 'Variable') {
8985
if (def.parent.kind === 'const' && def.node.init) {
9086
const init = def.node.init;
91-
return findRootExpression(init, already);
87+
return findRootExpression(init);
9288
}
9389
}
9490
return node;

packages/eslint-plugin-svelte/src/rules/no-navigation-without-resolve.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { TSESTree } from '@typescript-eslint/types';
22
import { createRule } from '../utils/index.js';
33
import { ReferenceTracker } from '@eslint-community/eslint-utils';
4-
import { findVariable } from '../utils/ast-utils.js';
4+
import { findVariableSafe } from '../utils/ast-utils.js';
55
import type { RuleContext } from '../types.js';
66
import type { AST } from 'svelte-eslint-parser';
77

@@ -126,7 +126,7 @@ function extractResolveReferences(
126126
}
127127
})) {
128128
if (node.type === 'ImportSpecifier') {
129-
const variable = findVariable(context, node.local);
129+
const variable = findVariableSafe(extractResolveReferences, context, node.local);
130130
if (variable === null) {
131131
continue;
132132
}
@@ -228,7 +228,7 @@ function isResolveCall(
228228
return true;
229229
}
230230
if (node.type === 'Identifier') {
231-
const variable = findVariable(context, node);
231+
const variable = findVariableSafe(isResolveCall, context, node);
232232
if (
233233
variable !== null &&
234234
variable.identifiers.length > 0 &&

packages/eslint-plugin-svelte/src/utils/ast-utils.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,43 @@ export function findVariable(context: RuleContext, node: TSESTree.Identifier): V
230230
// Remove the $ and search for the variable again, as it may be a store access variable.
231231
return eslintUtils.findVariable(initialScope, node.name.slice(1));
232232
}
233+
234+
const findVariableSafeVisited = new WeakMap<
235+
RuleContext,
236+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type -- ignore
237+
WeakMap<Function, Set<TSESTree.Identifier>>
238+
>();
239+
240+
/**
241+
* Find the variable of a given name safely, avoiding infinite recursion.
242+
* This should be used when the caller function may be called recursively.
243+
* @param caller The caller function. This is used to track recursion.
244+
* @param context The rule context.
245+
* @param node The identifier node to find.
246+
*/
247+
export function findVariableSafe(
248+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type -- ignore
249+
caller: Function,
250+
context: RuleContext,
251+
node: TSESTree.Identifier
252+
): Variable | null {
253+
let visited = findVariableSafeVisited.get(context);
254+
if (!visited) {
255+
visited = new WeakMap();
256+
findVariableSafeVisited.set(context, visited);
257+
}
258+
let visitedNodes = visited.get(caller);
259+
if (!visitedNodes) {
260+
visitedNodes = new Set();
261+
visited.set(caller, visitedNodes);
262+
}
263+
if (visitedNodes.has(node)) {
264+
return null;
265+
}
266+
visitedNodes.add(node);
267+
return findVariable(context, node);
268+
}
269+
233270
/**
234271
* Iterate the identifiers of a given pattern node.
235272
*/

packages/eslint-plugin-svelte/src/utils/expression-affixes.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { TSESTree } from '@typescript-eslint/types';
2-
import { findVariable } from './ast-utils.js';
2+
import { findVariableSafe } from './ast-utils.js';
33
import type { RuleContext } from '../types.js';
44
import type { AST } from 'svelte-eslint-parser';
55

@@ -36,7 +36,7 @@ function extractVariablePrefixVariable(
3636
context: RuleContext,
3737
expression: TSESTree.Identifier
3838
): TSESTree.Identifier | null {
39-
const variable = findVariable(context, expression);
39+
const variable = findVariableSafe(extractVariablePrefixVariable, context, expression);
4040
if (
4141
variable === null ||
4242
variable.identifiers.length !== 1 ||
@@ -111,7 +111,7 @@ function extractVariablePrefixLiteral(
111111
context: RuleContext,
112112
expression: TSESTree.Identifier
113113
): string | null {
114-
const variable = findVariable(context, expression);
114+
const variable = findVariableSafe(extractVariablePrefixLiteral, context, expression);
115115
if (
116116
variable === null ||
117117
variable.identifiers.length !== 1 ||
@@ -176,7 +176,7 @@ function extractVariableSuffixLiteral(
176176
context: RuleContext,
177177
expression: TSESTree.Identifier
178178
): string | null {
179-
const variable = findVariable(context, expression);
179+
const variable = findVariableSafe(extractVariableSuffixLiteral, context, expression);
180180
if (
181181
variable === null ||
182182
variable.identifiers.length !== 1 ||
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<script>
2+
const a = derived;
3+
const derived = a;
4+
</script>
5+
6+
<a id={derived}>Click me!</a>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
- message: '`<slot>` name cannot be dynamic.'
2+
line: 6
3+
column: 12
4+
suggestions: null
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<script>
2+
const f = SLOT_NAME;
3+
const SLOT_NAME = f;
4+
</script>
5+
6+
<slot name={SLOT_NAME} />
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<script>
2+
const f = SLOT_NAME;
3+
const SLOT_NAME = f;
4+
</script>
5+
6+
<slot name={SLOT_NAME} />
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
- message: Found a link with a url that isn't resolved.
2+
line: 6
3+
column: 9
4+
suggestions: null
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<script>
2+
const a = value;
3+
const value = a;
4+
</script>
5+
6+
<a href={value}>Click me!</a>

0 commit comments

Comments
 (0)