Skip to content

Commit

Permalink
fix: false positive when break nested inside loop
Browse files Browse the repository at this point in the history
  • Loading branch information
fcoury committed Jul 4, 2024
1 parent ce4faaf commit c39ccc7
Showing 1 changed file with 29 additions and 9 deletions.
38 changes: 29 additions & 9 deletions src/semantic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub struct SemanticAnalyzer {
structs: HashMap<String, HashMap<String, String>>, // Struct name to field name to type mapping
functions: HashMap<String, (Vec<(String, String)>, String, Span)>, // Function name to parameter types mapping
enums: HashMap<String, HashMap<String, String>>, // Enum name to variant name to type mapping
loop_depth: u32,
}

impl SemanticAnalyzer {
Expand All @@ -20,6 +21,7 @@ impl SemanticAnalyzer {
structs: HashMap::new(),
functions: HashMap::new(),
enums: HashMap::new(),
loop_depth: 0,
};
analyzer.init_standard_library();
analyzer
Expand All @@ -38,12 +40,12 @@ impl SemanticAnalyzer {

pub fn analyze(&mut self, stmts: &Vec<Stmt>) -> Result<()> {
for stmt in stmts {
self.analyze_stmt(stmt, false)?;
self.analyze_stmt(stmt)?;
}
Ok(())
}

fn analyze_stmt(&mut self, stmt: &Stmt, in_loop: bool) -> Result<()> {
fn analyze_stmt(&mut self, stmt: &Stmt) -> Result<()> {
match stmt {
Stmt::Let(name, expr, _span) => {
let expr_type = self.analyze_expr(expr)?;
Expand Down Expand Up @@ -86,7 +88,7 @@ impl SemanticAnalyzer {

// Analyze function body
for stmt in body {
self.analyze_stmt(stmt, false)?;
self.analyze_stmt(stmt)?;
}

// Check if return type matches the last expression in the body (if any)
Expand All @@ -113,10 +115,10 @@ impl SemanticAnalyzer {
Stmt::If(condition, then_block, else_block, _span) => {
self.analyze_expr(condition)?;
for stmt in then_block {
self.analyze_stmt(stmt, false)?;
self.analyze_stmt(stmt)?;
}
for stmt in else_block {
self.analyze_stmt(stmt, false)?;
self.analyze_stmt(stmt)?;
}
Ok(())
}
Expand Down Expand Up @@ -156,7 +158,7 @@ impl SemanticAnalyzer {
_ => unimplemented!(),
}
for stmt in stmts {
self.analyze_stmt(stmt, true)?;
self.analyze_stmt(stmt)?;
}
}
Ok(())
Expand Down Expand Up @@ -189,17 +191,19 @@ impl SemanticAnalyzer {
// Create a new scope for the loop body
self.symbol_table.insert(variable.clone(), element_type);

self.loop_depth += 1;
for stmt in body {
self.analyze_stmt(stmt, true)?;
self.analyze_stmt(stmt)?;
}
self.loop_depth -= 1;

// Remove the loop variable from the symbol table after analyzing the body
self.symbol_table.remove(variable);

Ok(())
}
Stmt::Break(span) => {
if !in_loop {
if self.loop_depth == 0 {
return Err(Error::new_semantic(
"'break' outside of loop".to_string(),
*span,
Expand All @@ -208,7 +212,7 @@ impl SemanticAnalyzer {
Ok(())
}
Stmt::Continue(span) => {
if !in_loop {
if self.loop_depth == 0 {
return Err(Error::new_semantic(
"'continue' outside of loop".to_string(),
*span,
Expand Down Expand Up @@ -409,6 +413,22 @@ mod tests {
.contains("'break' outside of loop"));
}

#[test]
fn test_break_outside_loop_false_positive() {
let result = analyze(
r#"
for i in 1..10 {
if i == 3 {
break;
}
println(i)
}
"#,
);

assert!(result.is_ok());
}

#[test]
fn test_continue_outside_loop() {
let result = analyze("continue;");
Expand Down

0 comments on commit c39ccc7

Please sign in to comment.