Better support arrays in arithmetic contexts. Fixes #1074

This commit is contained in:
Vidar Holen 2018-02-25 17:58:29 -08:00
parent 8d5e3a80ae
commit d16bf41c3d
5 changed files with 34 additions and 36 deletions

View File

@ -37,8 +37,8 @@ newtype Root = Root Token
data Token =
TA_Binary Id String Token Token
| TA_Assignment Id String Token Token
| TA_Variable Id String [Token]
| TA_Expansion Id [Token]
| TA_Index Id Token
| TA_Sequence Id [Token]
| TA_Trinary Id Token Token Token
| TA_Unary Id String Token
@ -266,7 +266,7 @@ analyze f g i =
c <- round t3
return $ TA_Trinary id a b c
delve (TA_Expansion id t) = dl t $ TA_Expansion id
delve (TA_Index id t) = d1 t $ TA_Index id
delve (TA_Variable id str t) = dl t $ TA_Variable id str
delve (T_Annotation id anns t) = d1 t $ T_Annotation id anns
delve (T_CoProc id var body) = d1 body $ T_CoProc id var
delve (T_CoProcBody id t) = d1 t $ T_CoProcBody id
@ -360,7 +360,6 @@ getId t = case t of
TA_Sequence id _ -> id
TA_Trinary id _ _ _ -> id
TA_Expansion id _ -> id
TA_Index id _ -> id
T_ProcSub id _ _ -> id
T_Glob id _ -> id
T_ForArithmetic id _ _ _ _ -> id
@ -374,6 +373,7 @@ getId t = case t of
T_Include id _ _ -> id
T_UnparsedIndex id _ _ -> id
TC_Empty id _ -> id
TA_Variable id _ _ -> id
blank :: Monad m => Token -> m ()
blank = const $ return ()

View File

@ -1141,12 +1141,10 @@ checkArithmeticDeref params t@(TA_Expansion _ [b@(T_DollarBraced id _)]) =
T_Arithmetic {} -> return normalWarning
T_DollarArithmetic {} -> return normalWarning
T_ForArithmetic {} -> return normalWarning
TA_Index {} -> return indexWarning
T_SimpleCommand {} -> return noWarning
_ -> Nothing
normalWarning = style id 2004 "$/${} is unnecessary on arithmetic variables."
indexWarning = style id 2149 "Remove $/${} for numeric index, or escape it for string."
noWarning = return ()
checkArithmeticDeref _ _ = return ()
@ -1825,6 +1823,7 @@ prop_checkUnused34= verifyNotTree checkUnusedAssignments "foo=1; (( t = foo ));
prop_checkUnused35= verifyNotTree checkUnusedAssignments "a=foo; b=2; echo ${a:b}"
prop_checkUnused36= verifyNotTree checkUnusedAssignments "if [[ -v foo ]]; then true; fi"
prop_checkUnused37= verifyNotTree checkUnusedAssignments "fd=2; exec {fd}>&-"
prop_checkUnused38= verifyTree checkUnusedAssignments "(( a=42 ))"
checkUnusedAssignments params t = execWriter (mapM_ warnFor unused)
where
flow = variableFlow params
@ -1880,6 +1879,7 @@ prop_checkUnassignedReferences30= verifyNotTree checkUnassignedReferences "if [[
prop_checkUnassignedReferences31= verifyNotTree checkUnassignedReferences "X=1; if [[ -v foo[$X+42] ]]; then echo ${foo[$X+42]}; fi"
prop_checkUnassignedReferences32= verifyNotTree checkUnassignedReferences "if [[ -v \"foo[1]\" ]]; then echo ${foo[@]}; fi"
prop_checkUnassignedReferences33= verifyNotTree checkUnassignedReferences "f() { local -A foo; echo \"${foo[@]}\"; }"
prop_checkUnassignedReferences34= verifyNotTree checkUnassignedReferences "declare -A foo; (( foo[bar] ))"
checkUnassignedReferences params t = warnings
where
(readMap, writeMap) = execState (mapM tally $ variableFlow params) (Map.empty, Map.empty)
@ -2540,7 +2540,7 @@ checkLoopVariableReassignment params token =
T_ForArithmetic _
(TA_Sequence _
[TA_Assignment _ "="
(TA_Expansion _ [T_Literal _ var]) _])
(TA_Variable _ var _ ) _])
_ _ _ -> return var
_ -> fail "not loop"

View File

@ -444,15 +444,12 @@ getModifiedVariables t =
c@T_SimpleCommand {} ->
getModifiedVariableCommand c
TA_Unary _ "++|" var -> maybeToList $ do
name <- getLiteralString var
return (t, t, name, DataString $ SourceFrom [t])
TA_Unary _ "|++" var -> maybeToList $ do
name <- getLiteralString var
return (t, t, name, DataString $ SourceFrom [t])
TA_Assignment _ op lhs rhs -> maybeToList $ do
TA_Unary _ "++|" v@(TA_Variable _ name _) ->
[(t, v, name, DataString $ SourceFrom [v])]
TA_Unary _ "|++" v@(TA_Variable _ name _) ->
[(t, v, name, DataString $ SourceFrom [v])]
TA_Assignment _ op (TA_Variable _ name _) rhs -> maybeToList $ do
guard $ op `elem` ["=", "*=", "/=", "%=", "+=", "-=", "<<=", ">>=", "&=", "^=", "|="]
name <- getLiteralString lhs
return (t, t, name, DataString $ SourceFrom [rhs])
-- Count [[ -v foo ]] as an "assignment".
@ -634,10 +631,10 @@ getReferencedVariables parents t =
map (\x -> (l, l, x)) (
getIndexReferences str
++ getOffsetReferences (getBracedModifier str))
TA_Expansion id _ ->
TA_Variable id name _ ->
if isArithmeticAssignment t
then []
else getIfReference t t
else [(t, t, name)]
T_Assignment id mode str _ word ->
[(t, t, str) | mode == Append] ++ specialReferences str t word
@ -664,7 +661,6 @@ getReferencedVariables parents t =
else []
literalizer t = case t of
TA_Index {} -> return "" -- x[0] becomes a reference of x
T_Glob _ s -> return s -- Also when parsed as globs
_ -> Nothing

View File

@ -191,11 +191,9 @@ checkBashisms = ForShell [Sh, Dash] $ \t -> do
bashism (T_Glob id str) | "[^" `isInfixOf` str =
warnMsg id "^ in place of ! in glob bracket expressions is"
bashism t@(TA_Expansion id _) | isBashism =
warnMsg id $ fromJust str ++ " is"
where
str = getLiteralString t
isBashism = isJust str && isBashVariable (fromJust str)
bashism t@(TA_Variable id str _) | isBashVariable str =
warnMsg id $ str ++ " is"
bashism t@(T_DollarBraced id token) = do
mapM_ check expansion
when (isBashVariable var) $

View File

@ -697,31 +697,35 @@ readArithmeticContents =
spacing1
return (str, alt)
readArrayIndex = do
id <- getNextId
char '['
middle <- readArithmeticContents
pos <- getPosition
middle <- readStringForParser readArithmeticContents
char ']'
return $ TA_Index id middle
return $ T_UnparsedIndex id pos middle
literal s = do
id <- getNextId
string s
return $ T_Literal id s
readArithmeticLiteral =
readArrayIndex <|> literal "#"
readVariable = do
id <- getNextId
name <- readVariableName
indices <- many readArrayIndex
spacing
return $ TA_Variable id name indices
readExpansion = do
id <- getNextId
pieces <- many1 $ choice [
readArithmeticLiteral,
readSingleQuoted,
readDoubleQuoted,
readNormalDollar,
readBraced,
readUnquotedBackTicked,
literal "#",
readNormalLiteral "+-*/=%^,]?:"
]
spacing
@ -734,7 +738,7 @@ readArithmeticContents =
spacing
return s
readArithTerm = readGroup <|> readExpansion
readArithTerm = readGroup <|> readVariable <|> readExpansion
readSequence = do
spacing
@ -2819,10 +2823,7 @@ readScriptFile = do
readUtf8Bom = called "Byte Order Mark" $ string "\xFEFF"
readScript = do
script <- readScriptFile
reparseIndices script
readScript = readScriptFile
-- Interactively run a parser in ghci:
-- debugParse readScript "echo 'hello world'"
@ -2945,6 +2946,9 @@ reparseIndices root =
return $ T_Array id2 newWords
x -> return x
return $ T_Assignment id mode name newIndices newValue
f (TA_Variable id name indices) = do
newIndices <- mapM (fixAssignmentIndex name) indices
return $ TA_Variable id name newIndices
f t = return t
fixIndexElement name word =
@ -2952,13 +2956,13 @@ reparseIndices root =
T_IndexedElement id indices value -> do
new <- mapM (fixAssignmentIndex name) indices
return $ T_IndexedElement id new value
otherwise -> return word
_ -> return word
fixAssignmentIndex name word =
case word of
T_UnparsedIndex id pos src -> do
T_UnparsedIndex id pos src ->
parsed name pos src
otherwise -> return word
_ -> return word
parsed name pos src =
if isAssociative name