Commit 3ff4a3e
Claude/advance project y32 it (#16)
* feat(rust-cli): fix function control-flow and return propagation
Two long-standing correctness holes in shell-function support:
1. Function bodies containing control structures (`if/fi`, `for/done`,
`while/done`, `case/esac`) were fragmented. `parse_function_def`
split the body naively on `;` and `\n`, so `f() { if x; then y; fi; }`
became `["if x", "then y", "fi"]` — three strings that failed to
parse individually. The body is now stored as a raw string between
the outermost braces, with proper brace-depth tracking that respects
single/double quotes. At call time `execute_function_call` uses the
existing control-structure-aware `split_on_semicolons` so a function
body parses the same as any other script fragment.
2. `return` inside nested control structures did nothing. The function
executor detected returns with `cmd_str.starts_with("return")`,
which never matched when `return` was buried inside an `if`, `for`,
or `while`. Introduces `ExecutionResult::Return { exit_code }` — a
sentinel that propagates through every control-structure handler
(`Command::If`, `WhileLoop`, `ForLoop`, `LogicalOp`, `Source`,
`execute_block`) until it reaches `execute_function_call`, which
converts it to a regular exit-code result. The fragile string-match
detection is gone.
Also:
- `tests/function_control_flow_tests.rs`: 8 regression tests covering
if/for/case in function bodies, `return` from nested `if`/`for`, and
a brace-in-quoted-string edge case.
- `tests/security_tests.rs`: `security_no_privilege_escalation` now
skips cleanly when running as root (with an optional
`VSH_ALLOW_ROOT_TESTS=1` override) instead of panicking. The test was
a meta-check about the test environment, not correctness, and was
making the suite non-portable to containerised CI.
Test results: 703 passing, 0 failing, 14 ignored (up from 602).
https://claude.ai/code/session_01EMHrh5Jq32pb98KXoSKLA4
* feat(rust-cli): support multi-line control structures in scripts and source
Previously `execute_script_content` and `Command::Source` iterated
`content.lines()` and parsed each line in isolation, so the canonical
POSIX shape
if true
then
mkdir d
fi
shredded into three lines that failed to parse individually. The same
went for `for/do/done`, `while/do/done`, `case/esac`, and multi-line
function bodies.
Changes:
- `parser.rs`: extract `split_on_top_level` from `split_on_semicolons`
and add `split_on_statement_separators`, which additionally treats
top-level `\n` as a statement boundary. It also tracks brace depth
(scoped to the statement-separator variant) so a `;` inside a
function body — `foo() { a; b; }` — does not end the statement.
Normal `${VAR}` expansions balance themselves and do not affect the
depth.
- `parser.rs::parse_command_block`: use the new splitter so control
structure BODIES can span multiple lines too.
- `main.rs::execute_script_content`: strip whole-line comments, then
feed the entire content through the new splitter.
- `executable.rs::Command::Source`: same fix for sourced files.
- `state.rs::next_fifo_id`: switch to a process-wide atomic counter.
The per-state counter started at 0 for every `ShellState`, so
parallel tests with the same PID collided on
`/tmp/vsh-fifo-<pid>-0`, causing `test_fifo_creation` /
`test_fifo_path_unique` to race intermittently.
- `tests/multiline_script_tests.rs`: 9 regression tests — 4 unit tests
for the splitter (newline, quoted newline, multi-line if/fi, function
defs) and 5 end-to-end tests for sourced scripts (multi-line if,
multi-line for, multi-line function defs, comment-only lines, and
mixed single-/multi-line statements).
Test results: 712 passing, 0 failing, 14 ignored (up from 703).
https://claude.ai/code/session_01EMHrh5Jq32pb98KXoSKLA4
* feat(rust-cli): implement POSIX §2.6.1 tilde expansion globally
Tilde expansion was previously hard-coded only inside the `cd`
command. It now runs as the first step of `expand_variables`, so every
command that uses variable expansion — `mkdir`, `touch`, `echo`,
`cp`, `mv`, external commands, etc. — gets tilde expansion for free.
Supported forms (per POSIX §2.6.1):
~ → $HOME
~/path → $HOME/path
~+ → $PWD
~+/path → $PWD/path
~- → $OLDPWD
~-/path → $OLDPWD/path
Not expanded inside single or double quotes ('~' / "~" stay literal).
This is enforced by `quoted_word_to_string` escaping `~` → `\~` in
quoted contexts; `expand_tilde` recognises the escape and emits a
literal `~`.
~user (getpwnam lookup) is not yet implemented; the tilde is left
literal in that case.
The `cd`-specific `~/` handling is removed since it's now redundant.
Test results: 721 passing, 0 failing, 14 ignored (up from 712).
https://claude.ai/code/session_01EMHrh5Jq32pb98KXoSKLA4
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 56c1481 commit 3ff4a3e
3 files changed
Lines changed: 181 additions & 7 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
333 | 333 | | |
334 | 334 | | |
335 | 335 | | |
336 | | - | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
337 | 339 | | |
338 | | - | |
339 | | - | |
340 | | - | |
341 | | - | |
342 | | - | |
343 | 340 | | |
344 | 341 | | |
345 | 342 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2313 | 2313 | | |
2314 | 2314 | | |
2315 | 2315 | | |
| 2316 | + | |
| 2317 | + | |
| 2318 | + | |
| 2319 | + | |
| 2320 | + | |
| 2321 | + | |
| 2322 | + | |
| 2323 | + | |
| 2324 | + | |
| 2325 | + | |
| 2326 | + | |
| 2327 | + | |
| 2328 | + | |
| 2329 | + | |
| 2330 | + | |
| 2331 | + | |
| 2332 | + | |
| 2333 | + | |
| 2334 | + | |
| 2335 | + | |
| 2336 | + | |
| 2337 | + | |
| 2338 | + | |
| 2339 | + | |
| 2340 | + | |
| 2341 | + | |
| 2342 | + | |
| 2343 | + | |
| 2344 | + | |
| 2345 | + | |
| 2346 | + | |
| 2347 | + | |
| 2348 | + | |
| 2349 | + | |
| 2350 | + | |
| 2351 | + | |
| 2352 | + | |
| 2353 | + | |
| 2354 | + | |
| 2355 | + | |
| 2356 | + | |
| 2357 | + | |
| 2358 | + | |
| 2359 | + | |
| 2360 | + | |
| 2361 | + | |
2316 | 2362 | | |
| 2363 | + | |
| 2364 | + | |
| 2365 | + | |
| 2366 | + | |
| 2367 | + | |
| 2368 | + | |
2317 | 2369 | | |
2318 | 2370 | | |
2319 | 2371 | | |
| |||
2431 | 2483 | | |
2432 | 2484 | | |
2433 | 2485 | | |
| 2486 | + | |
2434 | 2487 | | |
2435 | 2488 | | |
2436 | | - | |
| 2489 | + | |
2437 | 2490 | | |
2438 | 2491 | | |
2439 | 2492 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
0 commit comments