Resolve the target variable from a method body using the forward walker. This is the main entry point called from `resolve_variable_in_members`. It seeds the scope with parameter types and walks the method body forward to the cursor.
(
var_name: &str,
parameters: impl Iterator<Item = &'b FunctionLikeParameter<'b>>,
body_statements: impl Iterator<Item = &'b Statement<'b>>,
method_span_start: u32,
method_ctx: Opt
| 2277 | /// It seeds the scope with parameter types and walks the method body |
| 2278 | /// forward to the cursor. |
| 2279 | pub(crate) fn resolve_in_method_body<'b>( |
| 2280 | var_name: &str, |
| 2281 | parameters: impl Iterator<Item = &'b FunctionLikeParameter<'b>>, |
| 2282 | body_statements: impl Iterator<Item = &'b Statement<'b>>, |
| 2283 | method_span_start: u32, |
| 2284 | method_ctx: Option<(&str, bool)>, |
| 2285 | is_static: bool, |
| 2286 | ctx: &ForwardWalkCtx<'_>, |
| 2287 | ) -> Option<Vec<ResolvedType>> { |
| 2288 | // Collect iterators up front so they can be reused across the cache |
| 2289 | // populate path and the standard walk path without ownership issues. |
| 2290 | let params_vec: Vec<&'b FunctionLikeParameter<'b>> = parameters.collect(); |
| 2291 | let stmts_vec: Vec<&'b Statement<'b>> = body_statements.collect(); |
| 2292 | |
| 2293 | // ── Hover scope cache ──────────────────────────────────────────────── |
| 2294 | // The hover scope cache records snapshots at each statement's START |
| 2295 | // offset (before the statement is processed). This works well for |
| 2296 | // member-access resolution within a statement (which needs the scope |
| 2297 | // from before the statement), but returns the wrong type for variable |
| 2298 | // hover on the LHS of an assignment: hovering `$x` in `$x = new Foo()` |
| 2299 | // should show the post-assignment type (`Foo`), not the pre-assignment |
| 2300 | // type. Detecting all edge cases (nudged offsets, nested blocks, |
| 2301 | // closures) is fragile, so variable resolution always uses the |
| 2302 | // standard walk which processes statements up to the cursor and |
| 2303 | // returns the correct post-assignment scope. |
| 2304 | // |
| 2305 | // The cache IS still populated here (if not yet present) so that |
| 2306 | // other consumers (diagnostics member-access lookups via |
| 2307 | // `lookup_diagnostic_scope`) benefit from it. |
| 2308 | if !is_diagnostic_scope_active() |
| 2309 | && is_hover_scope_cache_active() |
| 2310 | && !hover_scope_has_method(method_span_start) |
| 2311 | { |
| 2312 | // Activate a temporary diagnostic scope so that walk_body_forward |
| 2313 | // records snapshots at every statement boundary. |
| 2314 | let _diag_guard = with_diagnostic_scope_cache(); |
| 2315 | |
| 2316 | // Build a full-walk context (cursor at u32::MAX = walk entire body). |
| 2317 | let full_ctx = ForwardWalkCtx { |
| 2318 | cursor_offset: u32::MAX, |
| 2319 | current_class: ctx.current_class, |
| 2320 | all_classes: ctx.all_classes, |
| 2321 | content: ctx.content, |
| 2322 | class_loader: ctx.class_loader, |
| 2323 | loaders: ctx.loaders, |
| 2324 | resolved_class_cache: ctx.resolved_class_cache, |
| 2325 | enclosing_return_type: ctx.enclosing_return_type.clone(), |
| 2326 | top_level_scope: ctx.top_level_scope.clone(), |
| 2327 | }; |
| 2328 | |
| 2329 | let mut scope = ScopeState::new(); |
| 2330 | if !is_static { |
| 2331 | seed_this(&mut scope, ctx.current_class); |
| 2332 | } |
| 2333 | let method_name = method_ctx.map(|(n, _)| n); |
| 2334 | let has_scope_attr = method_ctx.is_some_and(|(_, s)| s); |
| 2335 | |
| 2336 | seed_params( |
no test coverage detected