diff --git a/src/analyzer/scope/mod.rs b/src/analyzer/scope/mod.rs index 17ab50f1..2af178ff 100644 --- a/src/analyzer/scope/mod.rs +++ b/src/analyzer/scope/mod.rs @@ -160,6 +160,8 @@ pub struct BlockContext { pub inside_async: bool, + pub loop_bounds: (u32, u32), + pub for_loop_init_bounds: (u32, u32), /* Effects for pipe var, if applicable */ @@ -205,6 +207,7 @@ impl BlockContext { parent_conflicting_clause_vars: FxHashSet::default(), allow_taints: true, inside_async: false, + loop_bounds: (0, 0), for_loop_init_bounds: (0, 0), pipe_var_effects: EFFECT_PURE, diff --git a/src/analyzer/stmt/do_analyzer.rs b/src/analyzer/stmt/do_analyzer.rs index bdb69ee1..b7168a04 100644 --- a/src/analyzer/stmt/do_analyzer.rs +++ b/src/analyzer/stmt/do_analyzer.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use hakana_algebra::Clause; -use oxidized::aast; +use oxidized::{aast, pos::Pos}; use rustc_hash::{FxHashMap, FxHashSet}; use crate::{ @@ -21,6 +21,7 @@ use super::{ pub(crate) fn analyze( statements_analyzer: &StatementsAnalyzer, stmt: (&aast::Block<(), ()>, &aast::Expr<(), ()>), + pos: &Pos, analysis_data: &mut FunctionAnalysisData, context: &mut BlockContext, ) -> Result<(), AnalysisError> { @@ -72,6 +73,10 @@ pub(crate) fn analyze( )); } + let prev_loop_bounds = do_context.loop_bounds; + + do_context.loop_bounds = (pos.start_offset() as u32, pos.end_offset() as u32); + let mut inner_loop_context = loop_analyzer::analyze( statements_analyzer, &stmt.0 .0, @@ -85,6 +90,8 @@ pub(crate) fn analyze( true, )?; + do_context.loop_bounds = prev_loop_bounds; + let clauses_to_simplify = { let mut c = context .clauses @@ -120,9 +127,7 @@ pub(crate) fn analyze( } for (var_id, var_type) in inner_loop_context.locals { - context - .locals - .insert(var_id.clone(), var_type.clone()); + context.locals.insert(var_id.clone(), var_type.clone()); } Ok(()) diff --git a/src/analyzer/stmt/for_analyzer.rs b/src/analyzer/stmt/for_analyzer.rs index 67d0f066..ef425060 100644 --- a/src/analyzer/stmt/for_analyzer.rs +++ b/src/analyzer/stmt/for_analyzer.rs @@ -46,6 +46,9 @@ pub(crate) fn analyze( for_context.inside_loop = true; for_context.break_types.push(BreakContext::Loop); + let prev_loop_bounds = for_context.loop_bounds; + for_context.loop_bounds = (pos.start_offset() as u32, pos.end_offset() as u32); + loop_analyzer::analyze( statements_analyzer, &stmt.3 .0, @@ -63,6 +66,8 @@ pub(crate) fn analyze( while_true, )?; + for_context.loop_bounds = prev_loop_bounds; + // theoretically we could also port over always_enters_loop logic from Psalm here // but I'm not sure that would be massively useful diff --git a/src/analyzer/stmt/foreach_analyzer.rs b/src/analyzer/stmt/foreach_analyzer.rs index 9b1b1714..593d1b3b 100644 --- a/src/analyzer/stmt/foreach_analyzer.rs +++ b/src/analyzer/stmt/foreach_analyzer.rs @@ -142,6 +142,9 @@ pub(crate) fn analyze( foreach_context.for_loop_init_bounds = (0, 0); foreach_context.inside_loop_exprs = false; + let prev_loop_bounds = foreach_context.loop_bounds; + foreach_context.loop_bounds = (pos.start_offset() as u32, pos.end_offset() as u32); + loop_analyzer::analyze( statements_analyzer, &stmt.2 .0, @@ -155,6 +158,8 @@ pub(crate) fn analyze( always_non_empty_array, )?; + foreach_context.loop_bounds = prev_loop_bounds; + // todo do we need to remove the loop scope from analysis_data here? unsure Ok(()) diff --git a/src/analyzer/stmt/while_analyzer.rs b/src/analyzer/stmt/while_analyzer.rs index 36d3cf52..145222c4 100644 --- a/src/analyzer/stmt/while_analyzer.rs +++ b/src/analyzer/stmt/while_analyzer.rs @@ -1,17 +1,18 @@ use super::{control_analyzer::BreakContext, loop_analyzer}; use crate::{ function_analysis_data::FunctionAnalysisData, - scope_analyzer::ScopeAnalyzer, scope::{control_action::ControlAction, loop_scope::LoopScope, BlockContext}, + scope_analyzer::ScopeAnalyzer, statements_analyzer::StatementsAnalyzer, stmt_analyzer::AnalysisError, }; -use oxidized::{aast, ast_defs}; +use oxidized::{aast, ast_defs, pos::Pos}; use std::rc::Rc; pub(crate) fn analyze( statements_analyzer: &StatementsAnalyzer, stmt: (&aast::Expr<(), ()>, &aast::Block<(), ()>), + pos: &Pos, analysis_data: &mut FunctionAnalysisData, context: &mut BlockContext, ) -> Result<(), AnalysisError> { @@ -38,6 +39,9 @@ pub(crate) fn analyze( false }; + let prev_loop_bounds = while_context.loop_bounds; + while_context.loop_bounds = (pos.start_offset() as u32, pos.end_offset() as u32); + let inner_loop_context = loop_analyzer::analyze( statements_analyzer, &stmt.1 .0, @@ -51,6 +55,8 @@ pub(crate) fn analyze( always_enters_loop, )?; + while_context.loop_bounds = prev_loop_bounds; + let can_leave_loop = !while_true || loop_scope.final_actions.contains(&ControlAction::Break); if always_enters_loop { @@ -79,9 +85,7 @@ pub(crate) fn analyze( ); } } else { - context - .locals - .insert(var_id.clone(), var_type.clone()); + context.locals.insert(var_id.clone(), var_type.clone()); } } } else { diff --git a/src/analyzer/stmt_analyzer.rs b/src/analyzer/stmt_analyzer.rs index 4f99d588..999bd8d5 100644 --- a/src/analyzer/stmt_analyzer.rs +++ b/src/analyzer/stmt_analyzer.rs @@ -100,6 +100,7 @@ pub(crate) fn analyze( while_analyzer::analyze( statements_analyzer, (&boxed.0, &boxed.1), + &stmt.0, analysis_data, context, )?; @@ -108,6 +109,7 @@ pub(crate) fn analyze( do_analyzer::analyze( statements_analyzer, (&boxed.0, &boxed.1), + &stmt.0, analysis_data, context, )?;