amber-lang / amber-lang/bash2amber

[Detail Bug] Bash-to-Amber: local variable shadowing leaks inner alias into outer scope

Offen
#9 0 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen
bug detail
Vorherrschende Sprache
Rust
Sterne
2
Forks
0
PR-Merge-Kennzahlen
Keine gemergten PRs in 30 T.

Beschreibung

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

Beitragsleitfaden

Für dieses Repository ist kein Beitragsleitfaden indexiert

Bewertung

Dieses Issue wurde noch nicht bewertet.

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.