diff --git a/src/ShellCheck/Analytics.hs b/src/ShellCheck/Analytics.hs index 7906e7a..f5c1a18 100644 --- a/src/ShellCheck/Analytics.hs +++ b/src/ShellCheck/Analytics.hs @@ -2228,6 +2228,9 @@ prop_checkUnassignedReferences37= verifyNotTree checkUnassignedReferences "var=h prop_checkUnassignedReferences38= verifyTree (checkUnassignedReferences' True) "echo $VAR" prop_checkUnassignedReferences39= verifyNotTree checkUnassignedReferences "builtin export var=4; echo $var" prop_checkUnassignedReferences40= verifyNotTree checkUnassignedReferences ": ${foo=bar}" +prop_checkUnassignedReferences41= verifyNotTree checkUnassignedReferences "mapfile -t files 123; echo \"${files[@]}\"" +prop_checkUnassignedReferences42= verifyNotTree checkUnassignedReferences "mapfile files -t; echo \"${files[@]}\"" +prop_checkUnassignedReferences43= verifyNotTree checkUnassignedReferences "mapfile --future files; echo \"${files[@]}\"" checkUnassignedReferences = checkUnassignedReferences' False checkUnassignedReferences' includeGlobals params t = warnings diff --git a/src/ShellCheck/AnalyzerLib.hs b/src/ShellCheck/AnalyzerLib.hs index 1ff50b2..596340b 100644 --- a/src/ShellCheck/AnalyzerLib.hs +++ b/src/ShellCheck/AnalyzerLib.hs @@ -658,13 +658,30 @@ getModifiedVariableCommand base@(T_SimpleCommand id cmdPrefix (T_NormalWord _ (T f [] = fail "not found" -- mapfile has some curious syntax allowing flags plus 0..n variable names - -- where only the first non-option one is used if any. Here we cheat and - -- just get the last one, if it's a variable name. - getMapfileArray base arguments = do - lastArg <- listToMaybe (reverse arguments) - name <- getLiteralString lastArg - guard $ isVariableName name - return (base, lastArg, name, DataArray SourceExternal) + -- where only the first non-option one is used if any. + getMapfileArray base rest = parseArgs `mplus` fallback + where + parseArgs :: Maybe (Token, Token, String, DataType) + parseArgs = do + args <- getGnuOpts "d:n:O:s:u:C:c:t" base + let names = map snd $ filter (\(x,y) -> null x) args + if null names + then + return (base, base, "MAPFILE", DataArray SourceExternal) + else do + first <- listToMaybe names + name <- getLiteralString first + guard $ isVariableName name + return (base, first, name, DataArray SourceExternal) + -- If arg parsing fails (due to bad or new flags), get the last variable name + fallback :: Maybe (Token, Token, String, DataType) + fallback = do + (name, token) <- listToMaybe . mapMaybe f $ reverse rest + return (base, token, name, DataArray SourceExternal) + f arg = do + name <- getLiteralString arg + guard $ isVariableName name + return (name, arg) -- get all the array variables used in read, e.g. read -a arr getReadArrayVariables args =