File tree Expand file tree Collapse file tree 5 files changed +109
-11
lines changed
Expand file tree Collapse file tree 5 files changed +109
-11
lines changed Original file line number Diff line number Diff line change @@ -56,9 +56,11 @@ impl Builtin for Exit {
5656 . unwrap_or ( 0 )
5757 & 0xFF ;
5858
59- // For now, we just return the exit code
60- // In a full implementation, this would terminate the shell
61- Ok ( ExecResult :: err ( String :: new ( ) , exit_code) )
59+ Ok ( ExecResult {
60+ exit_code,
61+ control_flow : ControlFlow :: Exit ( exit_code) ,
62+ ..Default :: default ( )
63+ } )
6264 }
6365}
6466
Original file line number Diff line number Diff line change @@ -1408,6 +1408,16 @@ impl Interpreter {
14081408 self . last_exit_code = saved_exit;
14091409 self . aliases = saved_aliases;
14101410 self . coproc_buffers = saved_coproc;
1411+
1412+ // Consume Exit control flow at subshell boundary — exit only
1413+ // terminates the subshell, not the parent shell.
1414+ if let Ok ( ref mut res) = result
1415+ && let ControlFlow :: Exit ( code) = res. control_flow
1416+ {
1417+ res. exit_code = code;
1418+ res. control_flow = ControlFlow :: None ;
1419+ }
1420+
14111421 result
14121422 }
14131423 CompoundCommand :: BraceGroup ( commands) => self . execute_command_sequence ( commands) . await ,
@@ -1680,6 +1690,15 @@ impl Interpreter {
16801690 ..Default :: default ( )
16811691 } ) ;
16821692 }
1693+ ControlFlow :: Exit ( code) => {
1694+ return Ok ( ExecResult {
1695+ stdout,
1696+ stderr,
1697+ exit_code : code,
1698+ control_flow : ControlFlow :: Exit ( code) ,
1699+ ..Default :: default ( )
1700+ } ) ;
1701+ }
16831702 ControlFlow :: None => { }
16841703 }
16851704 }
@@ -5880,6 +5899,9 @@ impl Interpreter {
58805899 let cmd_result = self . execute_command ( cmd) . await ?;
58815900 stdout. push_str ( & cmd_result. stdout ) ;
58825901 self . last_exit_code = cmd_result. exit_code ;
5902+ if matches ! ( cmd_result. control_flow, ControlFlow :: Exit ( _) ) {
5903+ break ;
5904+ }
58835905 }
58845906 // Fire EXIT trap set inside the command substitution
58855907 if let Some ( trap_cmd) = self . traps . get ( "EXIT" ) . cloned ( )
Original file line number Diff line number Diff line change @@ -11,6 +11,8 @@ pub enum ControlFlow {
1111 Continue ( u32 ) ,
1212 /// Return from a function (with exit code)
1313 Return ( i32 ) ,
14+ /// Exit the shell (with exit code)
15+ Exit ( i32 ) ,
1416}
1517
1618/// Structured side-effect channel for builtins that need to communicate
@@ -174,6 +176,7 @@ impl LoopAccumulator {
174176 ControlFlow :: Return ( code) => {
175177 LoopAction :: Exit ( self . build_exit ( ControlFlow :: Return ( code) ) )
176178 }
179+ ControlFlow :: Exit ( code) => LoopAction :: Exit ( self . build_exit ( ControlFlow :: Exit ( code) ) ) ,
177180 ControlFlow :: None => LoopAction :: None ,
178181 }
179182 }
@@ -193,7 +196,7 @@ impl LoopAccumulator {
193196 /// Build an exit result, draining accumulated stdout/stderr.
194197 fn build_exit ( & mut self , control_flow : ControlFlow ) -> ExecResult {
195198 let exit_code = match control_flow {
196- ControlFlow :: Return ( code) => code,
199+ ControlFlow :: Return ( code) | ControlFlow :: Exit ( code ) => code,
197200 _ => self . exit_code ,
198201 } ;
199202 ExecResult {
Original file line number Diff line number Diff line change @@ -168,3 +168,74 @@ false || false; echo $?
1681681
1691690
170170# ## end
171+
172+ # ## exit_in_if_block
173+ # exit inside if block terminates script
174+ if true ; then echo before; exit 0; fi
175+ echo SHOULD_NOT_REACH
176+ # ## expect
177+ before
178+ # ## end
179+
180+ # ## exit_in_if_block_with_code
181+ # exit with non-zero code inside if block
182+ if true ; then exit 42; fi
183+ echo SHOULD_NOT_REACH
184+ # ## exit_code: 42
185+ # ## expect
186+ # ## end
187+
188+ # ## exit_in_while_loop
189+ # exit inside while loop terminates script
190+ i=0
191+ while true ; do
192+ echo " iter $i "
193+ if [ " $i " -eq 1 ]; then exit 0; fi
194+ i=$(( i + 1 ))
195+ done
196+ echo SHOULD_NOT_REACH
197+ # ## expect
198+ iter 0
199+ iter 1
200+ # ## end
201+
202+ # ## exit_in_for_loop
203+ # exit inside for loop terminates script
204+ for x in a b c; do
205+ echo " $x "
206+ if [ " $x " = " b" ]; then exit 5; fi
207+ done
208+ echo SHOULD_NOT_REACH
209+ # ## exit_code: 5
210+ # ## expect
211+ a
212+ b
213+ # ## end
214+
215+ # ## exit_in_case_block
216+ # exit inside case block terminates script
217+ case " yes" in
218+ yes) echo matched; exit 0 ;;
219+ esac
220+ echo SHOULD_NOT_REACH
221+ # ## expect
222+ matched
223+ # ## end
224+
225+ # ## exit_in_subshell_does_not_propagate
226+ # exit inside subshell only terminates the subshell
227+ (exit 7)
228+ echo " after subshell: $? "
229+ # ## expect
230+ after subshell: 7
231+ # ## end
232+
233+ # ## exit_in_function
234+ # exit inside function terminates the whole script
235+ f () { echo in_func; exit 3; }
236+ f
237+ echo SHOULD_NOT_REACH
238+ # ## exit_code: 3
239+ # ## expect
240+ in_func
241+ # ## end
Original file line number Diff line number Diff line change @@ -663,7 +663,7 @@ version = "0.1.34"
663663criteria = " safe-to-deploy"
664664
665665[[exemptions .js-sys ]]
666- version = " 0.3.92 "
666+ version = " 0.3.93 "
667667criteria = " safe-to-deploy"
668668
669669[[exemptions .leb128fmt ]]
@@ -1467,23 +1467,23 @@ version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
14671467criteria = " safe-to-run"
14681468
14691469[[exemptions .wasm-bindgen ]]
1470- version = " 0.2.115 "
1470+ version = " 0.2.116 "
14711471criteria = " safe-to-deploy"
14721472
14731473[[exemptions .wasm-bindgen-futures ]]
1474- version = " 0.4.65 "
1474+ version = " 0.4.66 "
14751475criteria = " safe-to-deploy"
14761476
14771477[[exemptions .wasm-bindgen-macro ]]
1478- version = " 0.2.115 "
1478+ version = " 0.2.116 "
14791479criteria = " safe-to-deploy"
14801480
14811481[[exemptions .wasm-bindgen-macro-support ]]
1482- version = " 0.2.115 "
1482+ version = " 0.2.116 "
14831483criteria = " safe-to-deploy"
14841484
14851485[[exemptions .wasm-bindgen-shared ]]
1486- version = " 0.2.115 "
1486+ version = " 0.2.116 "
14871487criteria = " safe-to-deploy"
14881488
14891489[[exemptions .wasm-encoder ]]
@@ -1503,7 +1503,7 @@ version = "0.244.0"
15031503criteria = " safe-to-deploy"
15041504
15051505[[exemptions .web-sys ]]
1506- version = " 0.3.92 "
1506+ version = " 0.3.93 "
15071507criteria = " safe-to-deploy"
15081508
15091509[[exemptions .web-time ]]
You can’t perform that action at this time.
0 commit comments