amber-lang / amber-lang/bash2amber

[Detail Bug] bash2amber: Variables first assigned in control-flow blocks compile as block-scoped `let`, breaking later references

Open
#4 0 comments 0 reactions 0 assignees View on GitHub
bug detail
Dominant language
Rust
Stars
2
Forks
0
PR merge metrics
No merged PRs in 30d

Description

# Summary
- **Context**: The `bash2amber` tool converts Bash scripts to Amber code. In `src/amber/render/mod.rs`, it handles the conversion of control flow structures like `if`, `while`, and `for` by creating child rendering contexts and merging them back into the parent context after rendering the block's body.
- **Bug**: Variables first assigned inside a control flow block (like an `if` statement) are rendered with `let` inside that block in Amber. However, because Amber variables are block-scoped while Bash variables are not, these variables are inaccessible after the block, even though the parent rendering context correctly identifies them as "declared" and allows subsequent references to them.
- **Actual vs. expected**: The tool generates Amber code where a variable is declared with `let` inside a block but then referenced outside of it, which is a compilation error in Amber. It should instead pre-declare such variables at a higher scope (with a default value) before the block, or ensure they are properly hoisted.
- **Impact**: Any Bash script where a variable is first introduced inside a conditional or loop block and used afterwards will fail to compile in Amber.

# Code with bug
```rust
// src/amber/render/mod.rs

// Inside render_command for Command::If
00132| let mut then_ctx = ctx.with_child_scope();
00133| let then_body = BlockFragment::new(
00134| render_commands(&if_cmd.then_body, &mut then_ctx, tail_return),
00135| true,
00136| );
...
00150| ctx.merge_from_child(then_ctx); // <-- BUG 🔴 Variables declared in then_ctx are merged into parent, but were only 'let' inside a block in the output
```

# Evidence
### Reproduction Test Case
Created a test script `tests/bash/if_scope_bug.sh`:
```bash
if [ "true" == "true" ]; then
x=1
fi
echo "$x"
```

Running `bash2amber` on this script produces:
```amber
if "true" == "true" {
let x = 1
}
echo(x)
```

### Verification of Failure
Attempting to build the generated Amber code results in a compilation error:
```bash
$ echo 'if "true" == "true" {
let x = 1
}
echo(x)' > test_bug.ab && amber build test_bug.ab test_bug.sh
ERROR Variable 'x' does not exist
at test_bug.ab:4:6

3| }
4| echo(x)
```

# Why has this bug gone undetected?
This bug likely went undetected because existing tests might favor scripts where variables are either declared before being used in blocks, or are only used within the same block where they were assigned. Scripts following common Bash patterns of "initializing" a variable inside a conditional branch without prior declaration are correctly analyzed by the rendering context (thanks to `merge_from_child`), but the generated Amber output fails to account for Amber's stricter block-level scoping.

# Recommended fix
The `render_commands` function should perform a pre-pass (similar to how it handles global variables from function signatures) to identify variables that are assigned within nested blocks but need to be accessible in the current scope. These variables should be pre-declared at the beginning of the `render_commands` block with default values.

In `src/amber/render/mod.rs`, `render_commands` should be updated to:
1. Use `analysis::collect_non_local_assignments` to find all variables assigned in the current list of commands.
2. For any variable that is assigned but not yet declared in the current context, declare it and emit a `let var = default_value` fragment at the start of the block.
3. This will ensure that when the assignment inside the block is rendered, it is treated as a reassignment (`x = 1`) rather than a new declaration (`let x = 1`).

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.