From f65810a7ee9cbc71a13032866452c41f455228e9 Mon Sep 17 00:00:00 2001 From: Chao Shi Date: Sat, 13 Sep 2025 20:02:14 +0800 Subject: [PATCH] Fix issue #41 handle return statement inside closure We should never replace return statements to `break 'run` inside closures. Refer to test case `gl_issue_41` for an example. --- src/implementation/codegen.rs | 6 +++++- tests/issues.rs | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/implementation/codegen.rs b/src/implementation/codegen.rs index e94956f..34dcb1d 100644 --- a/src/implementation/codegen.rs +++ b/src/implementation/codegen.rs @@ -8,7 +8,7 @@ use syn::{ spanned::Spanned, visit::{visit_return_type, Visit}, visit_mut::{self as visitor, visit_block_mut, visit_expr_mut, VisitMut}, - Attribute, Expr, ExprCall, ReturnType, TypeImplTrait, + Attribute, Expr, ExprCall, ExprClosure, ReturnType, TypeImplTrait, }; use crate::implementation::{Contract, ContractMode, ContractType, FuncWithContracts}; @@ -379,6 +379,10 @@ impl VisitMut for ReturnReplacer { visit_expr_mut(self, node); } + + fn visit_expr_closure_mut(&mut self, _node: &mut ExprClosure) { + // Do not replace return statements inside closures. Skip calling the base visitor. + } } struct ImplDetector { diff --git a/tests/issues.rs b/tests/issues.rs index 043cac2..36449f1 100644 --- a/tests/issues.rs +++ b/tests/issues.rs @@ -96,3 +96,25 @@ fn gl_issue_18() { assert_eq!(1, euclidean(3, 4)); } + +#[allow(unused)] // compile-only test +#[test] +fn gl_issue_41() { + use contracts::requires; + + fn foo(f: impl Fn(i32) -> i32) -> i32 { + // no-op + f(-10) + } + + #[requires(true)] + fn bar() { + let y = foo(|x: i32| { + if x < 0 { + return 0; + } + x + }); + assert_eq!(y, 0); + } +}