Skip to content

Commit 289d568

Browse files
committed
less invasive approach
1 parent 45dc56b commit 289d568

File tree

3 files changed

+45
-36
lines changed

3 files changed

+45
-36
lines changed

packages/svelte/src/compiler/phases/2-analyze/index.js

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ function js(script, root, allow_reactive_declarations, parent) {
3838
body: []
3939
};
4040

41-
const { scope, scopes } = create_scopes(ast, root, allow_reactive_declarations, false, parent);
41+
const { scope, scopes } = create_scopes(ast, root, allow_reactive_declarations, parent);
4242

4343
return { ast, scope, scopes };
4444
}
@@ -191,7 +191,7 @@ function get_delegated_event(node, context) {
191191
* @returns {import('../types.js').Analysis}
192192
*/
193193
export function analyze_module(ast, options) {
194-
const { scope, scopes } = create_scopes(ast, new ScopeRoot(), false, false, null);
194+
const { scope, scopes } = create_scopes(ast, new ScopeRoot(), false, null);
195195

196196
for (const [name, references] of scope.references) {
197197
if (name[0] !== '$' || ReservedKeywords.includes(name)) continue;
@@ -242,7 +242,7 @@ export function analyze_component(root, options) {
242242
const module = js(root.module, scope_root, false, null);
243243
const instance = js(root.instance, scope_root, true, module.scope);
244244

245-
const { scope, scopes } = create_scopes(root.fragment, scope_root, false, true, instance.scope);
245+
const { scope, scopes } = create_scopes(root.fragment, scope_root, false, instance.scope);
246246

247247
/** @type {import('../types.js').Template} */
248248
const template = { ast: root.fragment, scope, scopes };
@@ -416,9 +416,24 @@ export function analyze_component(root, options) {
416416

417417
// warn on any nonstate declarations that are a) mutated and b) referenced in the template
418418
for (const scope of [module.scope, instance.scope]) {
419-
for (const [name, binding] of scope.declarations) {
420-
if (binding.kind === 'normal' && binding.mutated && binding.referenced_in_template) {
421-
warn(warnings, binding.node, [], 'non-state-reference', name);
419+
outer: for (const [name, binding] of scope.declarations) {
420+
if (binding.kind === 'normal' && binding.mutated) {
421+
for (const { path } of binding.references) {
422+
if (path[0].type !== 'Fragment') continue;
423+
for (let i = 1; i < path.length; i += 1) {
424+
const type = path[i].type;
425+
if (
426+
type === 'FunctionDeclaration' ||
427+
type === 'FunctionExpression' ||
428+
type === 'ArrowFunctionExpression'
429+
) {
430+
continue;
431+
}
432+
}
433+
434+
warn(warnings, binding.node, [], 'non-state-reference', name);
435+
break outer;
436+
}
422437
}
423438
}
424439
}

packages/svelte/src/compiler/phases/scope.js

Lines changed: 23 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -169,20 +169,18 @@ export class Scope {
169169
/**
170170
* @param {import('estree').Identifier} node
171171
* @param {import('#compiler').SvelteNode[]} path
172-
* @param {boolean} is_template
173172
*/
174-
reference(node, path, is_template) {
173+
reference(node, path) {
175174
let references = this.references.get(node.name);
176175
if (!references) this.references.set(node.name, (references = []));
177176

178177
references.push({ node, path });
179178

180179
const binding = this.declarations.get(node.name);
181180
if (binding) {
182-
if (is_template) binding.referenced_in_template = true;
183181
binding.references.push({ node, path });
184182
} else if (this.#parent) {
185-
this.#parent.reference(node, path, is_template);
183+
this.#parent.reference(node, path);
186184
} else {
187185
// no binding was found, and this is the top level scope,
188186
// which means this is a global
@@ -217,11 +215,10 @@ export class ScopeRoot {
217215
* @param {import('#compiler').SvelteNode} ast
218216
* @param {ScopeRoot} root
219217
* @param {boolean} allow_reactive_declarations
220-
* @param {boolean} is_template
221218
* @param {Scope | null} parent
222219
*/
223-
export function create_scopes(ast, root, allow_reactive_declarations, is_template, parent) {
224-
/** @typedef {{ scope: Scope, is_template: boolean }} State */
220+
export function create_scopes(ast, root, allow_reactive_declarations, parent) {
221+
/** @typedef {{ scope: Scope }} State */
225222

226223
/**
227224
* A map of node->associated scope. A node appearing in this map does not necessarily mean that it created a scope
@@ -232,9 +229,9 @@ export function create_scopes(ast, root, allow_reactive_declarations, is_templat
232229
scopes.set(ast, scope);
233230

234231
/** @type {State} */
235-
const state = { scope, is_template };
232+
const state = { scope };
236233

237-
/** @type {[Scope, { node: import('estree').Identifier; path: import('#compiler').SvelteNode[]; is_template: boolean }][]} */
234+
/** @type {[Scope, { node: import('estree').Identifier; path: import('#compiler').SvelteNode[] }][]} */
238235
const references = [];
239236

240237
/** @type {[Scope, import('estree').Pattern | import('estree').MemberExpression][]} */
@@ -265,7 +262,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, is_templat
265262
const scope = state.scope.child(true);
266263
scopes.set(node, scope);
267264

268-
next({ ...state, scope });
265+
next({ scope });
269266
};
270267

271268
/**
@@ -274,7 +271,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, is_templat
274271
const SvelteFragment = (node, { state, next }) => {
275272
const scope = analyze_let_directives(node, state.scope);
276273
scopes.set(node, scope);
277-
next({ ...state, scope });
274+
next({ scope });
278275
};
279276

280277
/**
@@ -320,10 +317,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, is_templat
320317
Identifier(node, { path, state }) {
321318
const parent = path.at(-1);
322319
if (parent && is_reference(node, /** @type {import('estree').Node} */ (parent))) {
323-
references.push([
324-
state.scope,
325-
{ node, path: path.slice(), is_template: state.is_template }
326-
]);
320+
references.push([state.scope, { node, path: path.slice() }]);
327321
}
328322
},
329323

@@ -346,15 +340,15 @@ export function create_scopes(ast, root, allow_reactive_declarations, is_templat
346340
}
347341
}
348342

349-
next({ ...state, scope });
343+
next({ scope });
350344
},
351345

352346
SvelteFragment,
353347
SvelteElement: SvelteFragment,
354348
RegularElement: SvelteFragment,
355349

356350
Component(node, { state, visit, path }) {
357-
state.scope.reference(b.id(node.name), path, false);
351+
state.scope.reference(b.id(node.name), path);
358352

359353
// let:x from the default slot is a weird one:
360354
// Its scope only applies to children that are not slots themselves.
@@ -378,7 +372,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, is_templat
378372
} else if (child.type === 'SnippetBlock') {
379373
visit(child);
380374
} else {
381-
visit(child, { ...state, scope });
375+
visit(child, { scope });
382376
}
383377
}
384378
},
@@ -412,7 +406,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, is_templat
412406
if (node.id) scope.declare(node.id, 'normal', 'function');
413407

414408
add_params(scope, node.params);
415-
next({ scope, is_template: false });
409+
next({ scope });
416410
},
417411

418412
FunctionDeclaration(node, { state, next }) {
@@ -422,15 +416,15 @@ export function create_scopes(ast, root, allow_reactive_declarations, is_templat
422416
scopes.set(node, scope);
423417

424418
add_params(scope, node.params);
425-
next({ scope, is_template: false });
419+
next({ scope });
426420
},
427421

428422
ArrowFunctionExpression(node, { state, next }) {
429423
const scope = state.scope.child();
430424
scopes.set(node, scope);
431425

432426
add_params(scope, node.params);
433-
next({ scope, is_template: false });
427+
next({ scope });
434428
},
435429

436430
ForStatement: create_block_scope,
@@ -475,7 +469,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, is_templat
475469
state.scope.declare(id, 'normal', 'let');
476470
}
477471

478-
next({ ...state, scope });
472+
next({ scope });
479473
} else {
480474
next();
481475
}
@@ -513,13 +507,13 @@ export function create_scopes(ast, root, allow_reactive_declarations, is_templat
513507
(node.key.type !== 'Identifier' || !node.index || node.key.name !== node.index);
514508
scope.declare(b.id(node.index), is_keyed ? 'derived' : 'normal', 'const');
515509
}
516-
if (node.key) visit(node.key, { ...state, scope });
510+
if (node.key) visit(node.key, { scope });
517511

518512
// children
519513
for (const child of node.body.nodes) {
520-
visit(child, { ...state, scope });
514+
visit(child, { scope });
521515
}
522-
if (node.fallback) visit(node.fallback, { ...state, scope });
516+
if (node.fallback) visit(node.fallback, { scope });
523517

524518
// Check if inner scope shadows something from outer scope.
525519
// This is necessary because we need access to the array expression of the each block
@@ -586,13 +580,13 @@ export function create_scopes(ast, root, allow_reactive_declarations, is_templat
586580
}
587581
}
588582

589-
context.next({ ...state, scope: child_scope });
583+
context.next({ scope: child_scope });
590584
},
591585

592586
Fragment: (node, context) => {
593587
const scope = context.state.scope.child(node.transparent);
594588
scopes.set(node, scope);
595-
context.next({ ...state, scope });
589+
context.next({ scope });
596590
},
597591

598592
BindDirective(node, context) {
@@ -629,8 +623,8 @@ export function create_scopes(ast, root, allow_reactive_declarations, is_templat
629623

630624
// we do this after the fact, so that we don't need to worry
631625
// about encountering references before their declarations
632-
for (const [scope, { node, path, is_template }] of references) {
633-
scope.reference(node, path, is_template);
626+
for (const [scope, { node, path }] of references) {
627+
scope.reference(node, path);
634628
}
635629

636630
for (const [scope, node] of updates) {

packages/svelte/tests/validator/samples/runes-state-rune-not-mutated/warnings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[
22
{
3-
"code": "state-rune-not-mutated",
3+
"code": "state-not-mutated",
44
"end": {
55
"column": 11,
66
"line": 3

0 commit comments

Comments
 (0)