amber-lang / amber-lang/bash2amber

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

未关闭
#4 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看
bug detail
主要语言
Rust
星标
2
派生
0
PR 合并指标
30 天内没有已合并 PR

描述

# 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`).

贡献指南

这个仓库没有索引到贡献指南

评估

这个 Issue 还没有评估数据。

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。