mvdan / sh

A shell parser, formatter, and interpreter with bash support; includes shfmt
https://pkg.go.dev/mvdan.cc/sh/v3
BSD 3-Clause "New" or "Revised" License
7.27k stars 345 forks source link

syntax: "= must follow a name" on assignments in expressions accepted by Bash #858

Closed Bushmills closed 1 month ago

Bushmills commented 2 years ago

It seems that shfmt won't deal with a bash technique which is used in this code fragment:

#!/bin/bash

declare -a strings
sdp=123

declare -a wordlists                                                 # list of wordlists
declare -a builtin_wordlists=("a" "b" "c")
for wordlist in "${builtin_wordlists[@]}"; do
   declare -Ai "flags_$sdp"                                          # header flags per vocabulary
   declare -A  "wordlist_$sdp"                                       # this is the name of the wordlist array, named  wordlist_$wid"
   declare -n  "$wordlist"="wordlist_$sdp"                           # allow referencing builtins by name without indirection ( ${unresolved[...]}
   ((${wordlist}wid=sdp))     # <- problematic line                  # builtins can also be refered by wid, assigned here
   wordlists+=("$sdp")
   strings[sdp++]="$wordlist"                                        # finally associate wid with name.
done
mvdan commented 2 years ago

We don't support arbitrary Bash syntax in some edge cases. Your case is quite similar to the one mentioned in the README:

Some builtins like export and let are parsed as keywords. This allows statically building their syntax tree, as opposed to keeping the arguments as a slice of words. It is also required to support declare foo=(bar). Note that this means expansions like declare {a,b}=c are not supported.

https://github.com/mvdan/sh/issues/583#issuecomment-652898693 is also a similar answer I gave to a similar request - expanding arithmetic operators is conceptually quite similar to expanding assignments. If we allow all the kinds of magic that Bash does, then our arithmetic expression parser would do little more than collecting unstructured words and leaving it to the interpreter to do the heavy lifting. Imagie expr="foo=bar baz"; (($expr)) as an example.

I could make the README clearer about this, however, as it currently seems like the restriction only applies to builtins, which is not the case.