MCPcopy Create free account
hub / github.com/PHPantom-dev/phpantom_lsp / collect_capture_scopes

Method collect_capture_scopes

src/references/mod.rs:356–506  ·  view source on GitHub ↗

Collect all scopes reachable from `root_scope` for `var_name` through closure `use` captures and implicit arrow-function captures. Returns a set containing `root_scope` plus every nested closure/arrow scope that captures the variable without shadowing it with a new parameter of the same name.

(
        symbol_map: &SymbolMap,
        var_name: &str,
        root_scope: u32,
    )

Source from the content-addressed store, hash-verified

354 /// closure/arrow scope that captures the variable without shadowing
355 /// it with a new parameter of the same name.
356 fn collect_capture_scopes(
357 symbol_map: &SymbolMap,
358 var_name: &str,
359 root_scope: u32,
360 ) -> HashSet<u32> {
361 let mut reachable = HashSet::new();
362 reachable.insert(root_scope);
363
364 // Explicit closure captures: `function () use ($var) { … }`
365 // These have VarDefKind::ClosureCapture with scope_start
366 // pointing to the closure body.
367 for def in &symbol_map.var_defs {
368 if def.name != var_name || def.kind != VarDefKind::ClosureCapture {
369 continue;
370 }
371 // The `use ($var)` token sits physically in the outer scope.
372 // Check if the outer scope is already reachable.
373 let outer_scope = symbol_map.find_enclosing_scope(def.offset);
374 if reachable.contains(&outer_scope) {
375 reachable.insert(def.scope_start);
376 }
377 }
378
379 // Implicit arrow-function captures: `fn () => $var`
380 // Arrow functions have a scope entry but no ClosureCapture def.
381 // A variable is implicitly captured if:
382 // 1. The arrow scope is directly nested in a reachable scope.
383 // 2. There is no parameter with the same name in the arrow scope.
384 for &(scope_start, _scope_end) in &symbol_map.scopes {
385 if reachable.contains(&scope_start) {
386 continue; // Already reachable, skip.
387 }
388 // Find the parent scope of this scope.
389 let parent = symbol_map.find_enclosing_scope(scope_start.saturating_sub(1));
390 if !reachable.contains(&parent) {
391 continue;
392 }
393 // Check if this is an arrow function scope (no ClosureCapture
394 // or Parameter def that would indicate a closure with `use`).
395 // Arrow scopes don't have braces; their scope_start is the
396 // arrow function expression's start offset.
397 //
398 // Skip if there's a parameter with the same name (shadowed).
399 let has_shadowing_param = symbol_map.var_defs.iter().any(|d| {
400 d.name == var_name
401 && d.scope_start == scope_start
402 && d.kind == VarDefKind::Parameter
403 });
404 if has_shadowing_param {
405 continue;
406 }
407 // Check if this scope actually uses the variable (has a
408 // Variable span in it). Only include it if the variable
409 // appears there to avoid false positives with unrelated
410 // nested functions.
411 //
412 // But we also need to check: is this scope a closure body
413 // (not an arrow function)? Closures create new variable

Callers

nothing calls this directly

Calls 6

find_enclosing_scopeMethod · 0.80
containsMethod · 0.80
iterMethod · 0.80
find_variable_scopeMethod · 0.80
cloneMethod · 0.80
insertMethod · 0.45

Tested by

no test coverage detected