From fdd02c94c01b81b78e2dada903ea88e29a39befe Mon Sep 17 00:00:00 2001 From: Gandalf- Date: Sun, 22 Dec 2019 23:11:20 -0800 Subject: [PATCH 1/2] Issue 1759 mapfile and process substition https://github.com/koalaman/shellcheck/issues/1759 When a simple process substition is used, this tripped up the getMapfileArray function by making the last argument not a variable --- src/ShellCheck/Analytics.hs | 1 + src/ShellCheck/AnalyzerLib.hs | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/ShellCheck/Analytics.hs b/src/ShellCheck/Analytics.hs index 582b8cd..d6ea460 100644 --- a/src/ShellCheck/Analytics.hs +++ b/src/ShellCheck/Analytics.hs @@ -2190,6 +2190,7 @@ prop_checkUnassignedReferences36= verifyNotTree checkUnassignedReferences "read prop_checkUnassignedReferences37= verifyNotTree checkUnassignedReferences "var=howdy; printf -v 'array[0]' %s \"$var\"; printf %s \"${array[0]}\";" prop_checkUnassignedReferences38= verifyTree (checkUnassignedReferences' True) "echo $VAR" prop_checkUnassignedReferences39= verifyNotTree checkUnassignedReferences "builtin export var=4; echo $var" +prop_checkUnassignedReferences40= verifyNotTree checkUnassignedReferences "mapfile -t files <(cat); echo \"${files[@]}\"" checkUnassignedReferences = checkUnassignedReferences' False checkUnassignedReferences' includeGlobals params t = warnings diff --git a/src/ShellCheck/AnalyzerLib.hs b/src/ShellCheck/AnalyzerLib.hs index 590889c..1e30c7a 100644 --- a/src/ShellCheck/AnalyzerLib.hs +++ b/src/ShellCheck/AnalyzerLib.hs @@ -670,13 +670,17 @@ getModifiedVariableCommand base@(T_SimpleCommand id cmdPrefix (T_NormalWord _ (T -- 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. + -- just get the last one, if it's a variable name, and omitting process + -- substitions. getMapfileArray base arguments = do - lastArg <- listToMaybe (reverse arguments) + lastArg <- listToMaybe (filter notProcSub $ reverse arguments) name <- getLiteralString lastArg guard $ isVariableName name return (base, lastArg, name, DataArray SourceExternal) + notProcSub (T_NormalWord _ (T_ProcSub{} :_)) = False + notProcSub _ = True + -- get all the array variables used in read, e.g. read -a arr getReadArrayVariables args = map (getLiteralArray . snd) From e779aedac3355bba0c6d071c741845d62d7aa4d8 Mon Sep 17 00:00:00 2001 From: Vidar Holen Date: Fri, 7 Aug 2020 16:00:58 -0700 Subject: [PATCH 2/2] Modernize getting mapfile array name --- src/ShellCheck/Analytics.hs | 4 +++- src/ShellCheck/AnalyzerLib.hs | 35 ++++++++++++++++++++++++----------- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/ShellCheck/Analytics.hs b/src/ShellCheck/Analytics.hs index 9ad53ae..f5c1a18 100644 --- a/src/ShellCheck/Analytics.hs +++ b/src/ShellCheck/Analytics.hs @@ -2228,7 +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 <(cat); echo \"${files[@]}\"" +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 ba2c901..596340b 100644 --- a/src/ShellCheck/AnalyzerLib.hs +++ b/src/ShellCheck/AnalyzerLib.hs @@ -658,17 +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, and omitting process - -- substitions. - getMapfileArray base arguments = do - lastArg <- listToMaybe (filter notProcSub $ reverse arguments) - name <- getLiteralString lastArg - guard $ isVariableName name - return (base, lastArg, name, DataArray SourceExternal) - - notProcSub (T_NormalWord _ (T_ProcSub{} :_)) = False - notProcSub _ = True + -- 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 =