amber-lang / amber-lang/bash2amber
[Detail Bug] Bash-to-Amber: local variable shadowing leaks inner alias into outer scope
- Langage dominant
- Rust
- Étoiles
- 2
- Forks
- 0
- Métriques de merge des PR
- Aucune PR mergée en 30 j
Description
# Summary
- **Context**: The `RenderContext` manages variable aliasing and scoping during the conversion of Bash scripts to Amber.
- **Bug**: `merge_from_child` uses `HashMap::extend` to merge `var_aliases`, which causes shadowed variable aliases (created by `local` in blocks) to leak back into the parent scope.
- **Actual vs. expected**: When a variable is shadowed in a block, its new alias overwrites the parent's alias in the parent context after the block ends; it should instead preserve the parent's original alias.
- **Impact**: The generated Amber code attempts to use out-of-scope variable aliases (e.g., `x_2` instead of `x`), resulting in "Variable does not exist" errors when building the Amber output.
# Code with bug
```rust
pub(super) fn merge_from_child(&mut self, child: Self) {
self.var_aliases.extend(child.var_aliases); // <-- BUG 🔴 Overwrites parent aliases with shadowed child aliases
self.declared_vars.extend(child.declared_vars);
self.functions.extend(child.functions);
```
# Evidence
I created a reproduction Bash script `tests/bash/local_shadowing.sh`:
```bash
f() {
local x=1
if true; then
local x=2
echo "inner: $x"
fi
echo "outer: $x"
}
f
```
The converted Amber code was:
```amber
fun f() {
let x = 1
if trust $ true $ {
let x_2 = 2
echo("inner: {x_2}")
}
echo("outer: {x_2}")
}
f()
```
Attempting to build this Amber code failed with:
`ERROR Variable 'x_2' does not exist at /home/user/bash2amber/target/runtime-compare/local_shadowing_1.ab:7:19`
# Why has this bug gone undetected?
Shadowing a `local` variable with another `local` declaration within a block in the same function is relatively uncommon in Bash scripts. Furthermore, if the shadowed variable is not used again after the block ends, the leaked alias does not cause a visible error in the generated code.
# Recommended fix
Modify `merge_from_child` to only add aliases for variables that are not already present in the parent context:
```rust
pub(super) fn merge_from_child(&mut self, child: Self) {
for (raw, alias) in child.var_aliases { // <-- FIX 🟢
self.var_aliases.entry(raw).or_insert(alias);
}
self.declared_vars.extend(child.declared_vars);
```
# History
This bug was introduced in commit 9fd39e1 (@Ph0enixKM, 2026-02-07). This commit introduced `declare_local_var` to support `local` variable shadowing in Bash functions, which triggered the flawed `extend` merge logic in `RenderContext::merge_from_child` that had been present since the initial commit.
Guide de contribution
Aucun guide de contribution indexé pour ce dépôt
Évaluation
Cette issue n'a pas encore été évaluée.