From c6e0c0bb1c4deec7cff07f664b92269813ce003b Mon Sep 17 00:00:00 2001 From: Mykhailo Chalyi Date: Thu, 26 Mar 2026 22:17:55 -0500 Subject: [PATCH 1/2] test(interpreter): add failing tests for assoc array literal key lookup Issue #861: associative array subscripts are evaluated as arithmetic instead of literal strings. When a variable with the same name as a key exists, ${assoc[key]} looks up the variable's value instead of the literal string "key". --- .../spec_cases/bash/assoc-arrays.test.sh | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/crates/bashkit/tests/spec_cases/bash/assoc-arrays.test.sh b/crates/bashkit/tests/spec_cases/bash/assoc-arrays.test.sh index 7f18c9f1..fc0829a9 100644 --- a/crates/bashkit/tests/spec_cases/bash/assoc-arrays.test.sh +++ b/crates/bashkit/tests/spec_cases/bash/assoc-arrays.test.sh @@ -132,6 +132,47 @@ echo "${m[key]}" hello world ### end +### assoc_literal_key_not_variable +# Issue #861: subscripts should be literal strings, not variable lookups +x="outside" +declare -A map=([x]=found [outside]=wrong) +echo "${map[x]}" +### expect +found +### end + +### assoc_literal_key_numeric_variable +i=999 +declare -A assoc=([i]=correct [999]=incorrect) +echo "${assoc[i]}" +### expect +correct +### end + +### assoc_assignment_literal_key +x="other" +declare -A m6 +m6[x]="at-x" +m6[other]="at-other" +echo "${m6[x]} ${m6[other]}" +### expect +at-x at-other +### end + +### assoc_loop_key_variable_does_not_interfere +a="wrong_a" +b="wrong_b" +c="wrong_c" +declare -A data=([a]=1 [b]=2 [c]=3) +for k in "${!data[@]}"; do + echo "${k}=${data[$k]}" +done | sort +### expect +a=1 +b=2 +c=3 +### end + ### assoc_iteration declare -A m m[a]="1" From 5f8c5ece96a0b8cf6c7d0ec8f93f88f5f00da0e8 Mon Sep 17 00:00:00 2001 From: Mykhailo Chalyi Date: Fri, 27 Mar 2026 03:32:25 +0000 Subject: [PATCH 2/2] fix(interpreter): treat assoc array subscripts as literal strings In real bash, associative array subscripts are always treated as literal strings. A bare name in ${assoc[key]} is the string "key", not the value of variable $key. The expand_variable_or_literal() function incorrectly looked up bare names as variable references, causing wrong values when a variable with the same name as the key existed in scope. Closes #861 --- crates/bashkit/src/interpreter/mod.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/bashkit/src/interpreter/mod.rs b/crates/bashkit/src/interpreter/mod.rs index cf1140f6..2c683556 100644 --- a/crates/bashkit/src/interpreter/mod.rs +++ b/crates/bashkit/src/interpreter/mod.rs @@ -7752,9 +7752,13 @@ impl Interpreter { result } - /// Expand a variable by name, checking local scope, positional params, shell vars, then env /// Expand a string as a variable reference, or return as literal. /// Used for associative array keys which may be variable refs or literals. + /// + /// In real bash, associative array subscripts are treated as literal strings + /// unless they contain explicit `$var` or `${var}` references. A bare name + /// like `key` in `${assoc[key]}` is the string "key", NOT the value of + /// variable `$key`. (Issue #861) fn expand_variable_or_literal(&self, s: &str) -> String { // Handle $var and ${var} references in assoc array keys let trimmed = s.trim(); @@ -7762,9 +7766,7 @@ impl Interpreter { let var_name = var_name.trim_start_matches('{').trim_end_matches('}'); return self.expand_variable(var_name); } - if let Some(val) = self.variables.get(s) { - return val.clone(); - } + // Bare names are literal string keys — do NOT look up as variables. s.to_string() }