amber-lang / amber-lang/bash2amber

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

Ouverte
#9 0 commentaires 0 réactions 0 personnes assignées Voir sur GitHub
bug detail
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.

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.