"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "src/ShellCheck/Analytics.hs" between
shellcheck-0.8.0.tar.gz and shellcheck-0.9.0.tar.gz

About: ShellCheck is a static analysis and linting tool for sh/bash scripts (written in Haskell).

Analytics.hs  (shellcheck-0.8.0):Analytics.hs  (shellcheck-0.9.0)
{- {-
Copyright 2012-2021 Vidar Holen Copyright 2012-2022 Vidar Holen
This file is part of ShellCheck. This file is part of ShellCheck.
https://www.shellcheck.net https://www.shellcheck.net
ShellCheck is free software: you can redistribute it and/or modify ShellCheck is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
ShellCheck is distributed in the hope that it will be useful, ShellCheck is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
-} -}
{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
module ShellCheck.Analytics (runAnalytics, optionalChecks, ShellCheck.Analytics. runTests) where module ShellCheck.Analytics (checker, optionalChecks, ShellCheck.Analytics.runTe sts) where
import ShellCheck.AST import ShellCheck.AST
import ShellCheck.ASTLib import ShellCheck.ASTLib
import ShellCheck.AnalyzerLib hiding (producesComments) import ShellCheck.AnalyzerLib hiding (producesComments)
import ShellCheck.CFG
import qualified ShellCheck.CFGAnalysis as CF
import ShellCheck.Data import ShellCheck.Data
import ShellCheck.Parser import ShellCheck.Parser
import ShellCheck.Prelude
import ShellCheck.Interface import ShellCheck.Interface
import ShellCheck.Regex import ShellCheck.Regex
import Control.Arrow (first) import Control.Arrow (first)
import Control.Monad import Control.Monad
import Control.Monad.Identity import Control.Monad.Identity
import Control.Monad.State import Control.Monad.State
import Control.Monad.Writer hiding ((<>)) import Control.Monad.Writer hiding ((<>))
import Control.Monad.Reader import Control.Monad.Reader
import Data.Char import Data.Char
import Data.Functor import Data.Functor
import Data.Function (on) import Data.Function (on)
import Data.List import Data.List
import Data.Maybe import Data.Maybe
import Data.Ord import Data.Ord
import Data.Semigroup import Data.Semigroup
import Debug.Trace -- STRIP import Debug.Trace -- STRIP
import qualified Data.Map.Strict as Map import qualified Data.Map.Strict as Map
import qualified Data.Set as S
import Test.QuickCheck.All (forAllProperties) import Test.QuickCheck.All (forAllProperties)
import Test.QuickCheck.Test (quickCheckWithResult, stdArgs, maxSuccess) import Test.QuickCheck.Test (quickCheckWithResult, stdArgs, maxSuccess)
-- Checks that are run on the AST root -- Checks that are run on the AST root
treeChecks :: [Parameters -> Token -> [TokenComment]] treeChecks :: [Parameters -> Token -> [TokenComment]]
treeChecks = [ treeChecks = [
nodeChecksToTreeCheck nodeChecks nodeChecksToTreeCheck nodeChecks
,subshellAssignmentCheck ,subshellAssignmentCheck
,checkSpacefulness
,checkQuotesInLiterals ,checkQuotesInLiterals
,checkShebangParameters ,checkShebangParameters
,checkFunctionsUsedExternally ,checkFunctionsUsedExternally
,checkUnusedAssignments ,checkUnusedAssignments
,checkUnpassedInFunctions ,checkUnpassedInFunctions
,checkArrayWithoutIndex ,checkArrayWithoutIndex
,checkShebang ,checkShebang
,checkUnassignedReferences ,checkUnassignedReferences
,checkUncheckedCdPushdPopd ,checkUncheckedCdPushdPopd
,checkArrayAssignmentIndices ,checkArrayAssignmentIndices
,checkUseBeforeDefinition ,checkUseBeforeDefinition
,checkAliasUsedInSameParsingUnit ,checkAliasUsedInSameParsingUnit
,checkArrayValueUsedAsIndex ,checkArrayValueUsedAsIndex
] ]
runAnalytics :: AnalysisSpec -> [TokenComment] checker spec params = mkChecker spec params treeChecks
runAnalytics options =
runList options treeChecks ++ runList options optionalChecks
where
root = asScript options
optionals = getEnableDirectives root ++ asOptionalChecks options
optionalChecks =
if "all" `elem` optionals
then map snd optionalTreeChecks
else mapMaybe (\c -> Map.lookup c optionalCheckMap) optionals
runList :: AnalysisSpec -> [Parameters -> Token -> [TokenComment]] mkChecker spec params checks =
-> [TokenComment] Checker {
runList spec list = notes perScript = \(Root root) -> do
where tell $ concatMap (\f -> f params root) all,
root = asScript spec perToken = const $ return ()
params = makeParameters spec }
notes = concatMap (\f -> f params root) list where
all = checks ++ optionals
getEnableDirectives root = optionalKeys = asOptionalChecks spec
case root of optionals =
T_Annotation _ list _ -> [s | EnableComment s <- list] if "all" `elem` optionalKeys
_ -> [] then map snd optionalTreeChecks
else mapMaybe (\c -> Map.lookup c optionalCheckMap) optionalKeys
checkList l t = concatMap (\f -> f t) l checkList l t = concatMap (\f -> f t) l
-- Checks that are run on each node in the AST -- Checks that are run on each node in the AST
runNodeAnalysis f p t = execWriter (doAnalysis (f p) t) runNodeAnalysis f p t = execWriter (doAnalysis (f p) t)
-- Perform multiple node checks in a single iteration over the tree -- Perform multiple node checks in a single iteration over the tree
nodeChecksToTreeCheck checkList = nodeChecksToTreeCheck checkList =
runNodeAnalysis runNodeAnalysis
(\p t -> (mapM_ ((\ f -> f t) . (\ f -> f p)) (\p t -> (mapM_ ((\ f -> f t) . (\ f -> f p))
skipping to change at line 202 skipping to change at line 197
,checkTranslatedStringVariable ,checkTranslatedStringVariable
,checkModifiedArithmeticInRedirection ,checkModifiedArithmeticInRedirection
,checkBlatantRecursion ,checkBlatantRecursion
,checkBadTestAndOr ,checkBadTestAndOr
,checkAssignToSelf ,checkAssignToSelf
,checkEqualsInCommand ,checkEqualsInCommand
,checkSecondArgIsComparison ,checkSecondArgIsComparison
,checkComparisonWithLeadingX ,checkComparisonWithLeadingX
,checkCommandWithTrailingSymbol ,checkCommandWithTrailingSymbol
,checkUnquotedParameterExpansionPattern ,checkUnquotedParameterExpansionPattern
,checkBatsTestDoesNotUseNegation
,checkCommandIsUnreachable
,checkSpacefulnessCfg
,checkOverwrittenExitCode
,checkUnnecessaryArithmeticExpansionIndex
,checkUnnecessaryParens
] ]
optionalChecks = map fst optionalTreeChecks optionalChecks = map fst optionalTreeChecks
prop_verifyOptionalExamples = all check optionalTreeChecks prop_verifyOptionalExamples = all check optionalTreeChecks
where where
check (desc, check) = check (desc, check) =
verifyTree check (cdPositive desc) verifyTree check (cdPositive desc)
&& verifyNotTree check (cdNegative desc) && verifyNotTree check (cdNegative desc)
optionalTreeChecks :: [(CheckDescription, (Parameters -> Token -> [TokenComment] ))] optionalTreeChecks :: [(CheckDescription, (Parameters -> Token -> [TokenComment] ))]
optionalTreeChecks = [ optionalTreeChecks = [
(newCheckDescription { (newCheckDescription {
cdName = "quote-safe-variables", cdName = "quote-safe-variables",
cdDescription = "Suggest quoting variables without metacharacters", cdDescription = "Suggest quoting variables without metacharacters",
cdPositive = "var=hello; echo $var", cdPositive = "var=hello; echo $var",
cdNegative = "var=hello; echo \"$var\"" cdNegative = "var=hello; echo \"$var\""
}, checkVerboseSpacefulness) }, nodeChecksToTreeCheck [checkVerboseSpacefulnessCfg])
,(newCheckDescription { ,(newCheckDescription {
cdName = "avoid-nullary-conditions", cdName = "avoid-nullary-conditions",
cdDescription = "Suggest explicitly using -n in `[ $var ]`", cdDescription = "Suggest explicitly using -n in `[ $var ]`",
cdPositive = "[ \"$var\" ]", cdPositive = "[ \"$var\" ]",
cdNegative = "[ -n \"$var\" ]" cdNegative = "[ -n \"$var\" ]"
}, nodeChecksToTreeCheck [checkNullaryExpansionTest]) }, nodeChecksToTreeCheck [checkNullaryExpansionTest])
,(newCheckDescription { ,(newCheckDescription {
cdName = "add-default-case", cdName = "add-default-case",
skipping to change at line 311 skipping to change at line 312
treeCheck = runNodeAnalysis f treeCheck = runNodeAnalysis f
comments = runAndGetComments treeCheck s comments = runAndGetComments treeCheck s
codes = map (cCode . tcComment) <$> comments codes = map (cCode . tcComment) <$> comments
checkNode f = producesComments (runNodeAnalysis f) checkNode f = producesComments (runNodeAnalysis f)
producesComments :: (Parameters -> Token -> [TokenComment]) -> String -> Maybe B ool producesComments :: (Parameters -> Token -> [TokenComment]) -> String -> Maybe B ool
producesComments f s = not . null <$> runAndGetComments f s producesComments f s = not . null <$> runAndGetComments f s
runAndGetComments f s = do runAndGetComments f s = do
let pr = pScript s let pr = pScript s
prRoot pr root <- prRoot pr
let spec = defaultSpec pr let spec = defaultSpec pr
let params = makeParameters spec let params = makeParameters spec
return $ return $
filterByAnnotation spec params $ filterByAnnotation spec params $
runList spec [f] f params root
-- Copied from https://wiki.haskell.org/Edit_distance -- Copied from https://wiki.haskell.org/Edit_distance
dist :: Eq a => [a] -> [a] -> Int dist :: Eq a => [a] -> [a] -> Int
dist a b dist a b
= last (if lab == 0 then mainDiag = last (if lab == 0 then mainDiag
else if lab > 0 then lowers !! (lab - 1) else if lab > 0 then lowers !! (lab - 1)
else{- < 0 -} uppers !! (-1 - lab)) else{- < 0 -} uppers !! (-1 - lab))
where mainDiag = oneDiag a b (head uppers) (-1 : head lowers) where mainDiag = oneDiag a b (head uppers) (-1 : head lowers)
uppers = eachDiag a b (mainDiag : uppers) -- upper diagonals uppers = eachDiag a b (mainDiag : uppers) -- upper diagonals
lowers = eachDiag b a (mainDiag : lowers) -- lower diagonals lowers = eachDiag b a (mainDiag : lowers) -- lower diagonals
skipping to change at line 545 skipping to change at line 546
prop_checkPipePitfalls9 = verifyNot checkPipePitfalls "foo | grep -o bar | wc -l " prop_checkPipePitfalls9 = verifyNot checkPipePitfalls "foo | grep -o bar | wc -l "
prop_checkPipePitfalls10 = verifyNot checkPipePitfalls "foo | grep -o bar | wc" prop_checkPipePitfalls10 = verifyNot checkPipePitfalls "foo | grep -o bar | wc"
prop_checkPipePitfalls11 = verifyNot checkPipePitfalls "foo | grep bar | wc" prop_checkPipePitfalls11 = verifyNot checkPipePitfalls "foo | grep bar | wc"
prop_checkPipePitfalls12 = verifyNot checkPipePitfalls "foo | grep -o bar | wc - c" prop_checkPipePitfalls12 = verifyNot checkPipePitfalls "foo | grep -o bar | wc - c"
prop_checkPipePitfalls13 = verifyNot checkPipePitfalls "foo | grep bar | wc -c" prop_checkPipePitfalls13 = verifyNot checkPipePitfalls "foo | grep bar | wc -c"
prop_checkPipePitfalls14 = verifyNot checkPipePitfalls "foo | grep -o bar | wc - cmwL" prop_checkPipePitfalls14 = verifyNot checkPipePitfalls "foo | grep -o bar | wc - cmwL"
prop_checkPipePitfalls15 = verifyNot checkPipePitfalls "foo | grep bar | wc -cmw L" prop_checkPipePitfalls15 = verifyNot checkPipePitfalls "foo | grep bar | wc -cmw L"
prop_checkPipePitfalls16 = verifyNot checkPipePitfalls "foo | grep -r bar | wc - l" prop_checkPipePitfalls16 = verifyNot checkPipePitfalls "foo | grep -r bar | wc - l"
prop_checkPipePitfalls17 = verifyNot checkPipePitfalls "foo | grep -l bar | wc - l" prop_checkPipePitfalls17 = verifyNot checkPipePitfalls "foo | grep -l bar | wc - l"
prop_checkPipePitfalls18 = verifyNot checkPipePitfalls "foo | grep -L bar | wc - l" prop_checkPipePitfalls18 = verifyNot checkPipePitfalls "foo | grep -L bar | wc - l"
prop_checkPipePitfalls19 = verifyNot checkPipePitfalls "foo | grep -A2 bar | wc
-l"
prop_checkPipePitfalls20 = verifyNot checkPipePitfalls "foo | grep -B999 bar | w
c -l"
prop_checkPipePitfalls21 = verifyNot checkPipePitfalls "foo | grep --after-conte
xt 999 bar | wc -l"
prop_checkPipePitfalls22 = verifyNot checkPipePitfalls "foo | grep -B 1 --after-
context 999 bar | wc -l"
prop_checkPipePitfalls23 = verifyNot checkPipePitfalls "ps -o pid,args -p $(pgre
p java) | grep -F net.shellcheck.Test"
checkPipePitfalls _ (T_Pipeline id _ commands) = do checkPipePitfalls _ (T_Pipeline id _ commands) = do
for ["find", "xargs"] $ for ["find", "xargs"] $
\(find:xargs:_) -> \(find:xargs:_) ->
let args = oversimplify xargs ++ oversimplify find let args = oversimplify xargs ++ oversimplify find
in in
unless (any ($ args) [ unless (any ($ args) [
hasShortParameter '0', hasShortParameter '0',
hasParameter "null", hasParameter "null",
hasParameter "print0", hasParameter "print0",
hasParameter "printf" hasParameter "printf"
]) $ warn (getId find) 2038 ]) $ warn (getId find) 2038
"Use -print0/-0 or -exec + to allow for non-alphanumeric f ilenames." "Use -print0/-0 or -exec + to allow for non-alphanumeric f ilenames."
for' ["ps", "grep"] $ for ["ps", "grep"] $
\x -> info x 2009 "Consider using pgrep instead of grepping ps output." \(ps:grep:_) ->
let
psFlags = maybe [] (map snd . getAllFlags) $ getCommand ps
in
-- There are many ways to specify a pid: 1, -1, p 1, wup 1, -q 1
, -p 1, --pid 1.
-- For simplicity we only deal with the most canonical looking f
lags:
unless (any (`elem` ["p", "pid", "q", "quick-pid"]) psFlags) $
info (getId ps) 2009 "Consider using pgrep instead of greppi
ng ps output."
for ["grep", "wc"] $ for ["grep", "wc"] $
\(grep:wc:_) -> \(grep:wc:_) ->
let flagsGrep = maybe [] (map snd . getAllFlags) $ getCommand grep let flagsGrep = maybe [] (map snd . getAllFlags) $ getCommand grep
flagsWc = maybe [] (map snd . getAllFlags) $ getCommand wc flagsWc = maybe [] (map snd . getAllFlags) $ getCommand wc
in in
unless (any (`elem` ["l", "files-with-matches", "L", "files-with out-matches", "o", "only-matching", "r", "R", "recursive"]) flagsGrep unless (any (`elem` ["l", "files-with-matches", "L", "files-with out-matches", "o", "only-matching", "r", "R", "recursive", "A", "after-context", "B", "before-context"]) flagsGrep
|| any (`elem` ["m", "chars", "w", "words", "c", "bytes" , "L", "max-line-length"]) flagsWc || any (`elem` ["m", "chars", "w", "words", "c", "bytes" , "L", "max-line-length"]) flagsWc
|| null flagsWc) $ || null flagsWc) $
style (getId grep) 2126 "Consider using grep -c instead of g rep|wc -l." style (getId grep) 2126 "Consider using 'grep -c' instead of 'grep|wc -l'."
didLs <- fmap or . sequence $ [ didLs <- fmap or . sequence $ [
for' ["ls", "grep"] $ for' ["ls", "grep"] $
\x -> warn x 2010 "Don't use ls | grep. Use a glob or a for loop wit h a condition to allow non-alphanumeric filenames.", \x -> warn x 2010 "Don't use ls | grep. Use a glob or a for loop wit h a condition to allow non-alphanumeric filenames.",
for' ["ls", "xargs"] $ for' ["ls", "xargs"] $
\x -> warn x 2011 "Use 'find .. -print0 | xargs -0 ..' or 'find .. - exec .. +' to allow non-alphanumeric filenames." \x -> warn x 2011 "Use 'find .. -print0 | xargs -0 ..' or 'find .. - exec .. +' to allow non-alphanumeric filenames."
] ]
unless didLs $ void $ unless didLs $ void $
for ["ls", "?"] $ for ["ls", "?"] $
\(ls:_) -> unless (hasShortParameter 'N' (oversimplify ls)) $ \(ls:_) -> unless (hasShortParameter 'N' (oversimplify ls)) $
skipping to change at line 628 skipping to change at line 641
prop_checkShebang1 = verifyNotTree checkShebang "#!/usr/bin/env bash -x\necho co w" prop_checkShebang1 = verifyNotTree checkShebang "#!/usr/bin/env bash -x\necho co w"
prop_checkShebang2 = verifyNotTree checkShebang "#! /bin/sh -l " prop_checkShebang2 = verifyNotTree checkShebang "#! /bin/sh -l "
prop_checkShebang3 = verifyTree checkShebang "ls -l" prop_checkShebang3 = verifyTree checkShebang "ls -l"
prop_checkShebang4 = verifyNotTree checkShebang "#shellcheck shell=sh\nfoo" prop_checkShebang4 = verifyNotTree checkShebang "#shellcheck shell=sh\nfoo"
prop_checkShebang5 = verifyTree checkShebang "#!/usr/bin/env ash" prop_checkShebang5 = verifyTree checkShebang "#!/usr/bin/env ash"
prop_checkShebang6 = verifyNotTree checkShebang "#!/usr/bin/env ash\n# shellchec k shell=dash\n" prop_checkShebang6 = verifyNotTree checkShebang "#!/usr/bin/env ash\n# shellchec k shell=dash\n"
prop_checkShebang7 = verifyNotTree checkShebang "#!/usr/bin/env ash\n# shellchec k shell=sh\n" prop_checkShebang7 = verifyNotTree checkShebang "#!/usr/bin/env ash\n# shellchec k shell=sh\n"
prop_checkShebang8 = verifyTree checkShebang "#!bin/sh\ntrue" prop_checkShebang8 = verifyTree checkShebang "#!bin/sh\ntrue"
prop_checkShebang9 = verifyNotTree checkShebang "# shellcheck shell=sh\ntrue" prop_checkShebang9 = verifyNotTree checkShebang "# shellcheck shell=sh\ntrue"
prop_checkShebang10= verifyNotTree checkShebang "#!foo\n# shellcheck shell=sh ig prop_checkShebang10 = verifyNotTree checkShebang "#!foo\n# shellcheck shell=sh i
nore=SC2239\ntrue" gnore=SC2239\ntrue"
prop_checkShebang11= verifyTree checkShebang "#!/bin/sh/\ntrue" prop_checkShebang11 = verifyTree checkShebang "#!/bin/sh/\ntrue"
prop_checkShebang12= verifyTree checkShebang "#!/bin/sh/ -xe\ntrue" prop_checkShebang12 = verifyTree checkShebang "#!/bin/sh/ -xe\ntrue"
prop_checkShebang13= verifyTree checkShebang "#!/bin/busybox sh" prop_checkShebang13 = verifyTree checkShebang "#!/bin/busybox sh"
prop_checkShebang14= verifyNotTree checkShebang "#!/bin/busybox sh\n# shellcheck prop_checkShebang14 = verifyNotTree checkShebang "#!/bin/busybox sh\n# shellchec
shell=sh\n" k shell=sh\n"
prop_checkShebang15= verifyNotTree checkShebang "#!/bin/busybox sh\n# shellcheck prop_checkShebang15 = verifyNotTree checkShebang "#!/bin/busybox sh\n# shellchec
shell=dash\n" k shell=dash\n"
prop_checkShebang16= verifyTree checkShebang "#!/bin/busybox ash" prop_checkShebang16 = verifyTree checkShebang "#!/bin/busybox ash"
prop_checkShebang17= verifyNotTree checkShebang "#!/bin/busybox ash\n# shellchec prop_checkShebang17 = verifyNotTree checkShebang "#!/bin/busybox ash\n# shellche
k shell=dash\n" ck shell=dash\n"
prop_checkShebang18= verifyNotTree checkShebang "#!/bin/busybox ash\n# shellchec prop_checkShebang18 = verifyNotTree checkShebang "#!/bin/busybox ash\n# shellche
k shell=sh\n" ck shell=sh\n"
checkShebang params (T_Annotation _ list t) = checkShebang params (T_Annotation _ list t) =
if any isOverride list then [] else checkShebang params t if any isOverride list then [] else checkShebang params t
where where
isOverride (ShellOverride _) = True isOverride (ShellOverride _) = True
isOverride _ = False isOverride _ = False
checkShebang params (T_Script _ (T_Literal id sb) _) = execWriter $ do checkShebang params (T_Script _ (T_Literal id sb) _) = execWriter $ do
unless (shellTypeSpecified params) $ do unless (shellTypeSpecified params) $ do
when (null sb) $ when (null sb) $
err id 2148 "Tips depend on target shell and yours is unknown. Add a shebang or a 'shell' directive." err id 2148 "Tips depend on target shell and yours is unknown. Add a shebang or a 'shell' directive."
when (executableFromShebang sb == "ash") $ when (executableFromShebang sb == "ash") $
skipping to change at line 689 skipping to change at line 702
suffix <- getTrailingUnquotedLiteral arg suffix <- getTrailingUnquotedLiteral arg
string <- getLiteralString suffix string <- getLiteralString suffix
guard $ "," `isSuffixOf` string guard $ "," `isSuffixOf` string
return $ return $
warnWithFix (getId arg) 2258 warnWithFix (getId arg) 2258
"The trailing comma is part of the value, not a separator. Delet e or quote it." "The trailing comma is part of the value, not a separator. Delet e or quote it."
(fixWith [replaceEnd (getId suffix) params 1 ""]) (fixWith [replaceEnd (getId suffix) params 1 ""])
checkForInQuoted _ _ = return () checkForInQuoted _ _ = return ()
prop_checkForInCat1 = verify checkForInCat "for f in $(cat foo); do stuff; done" prop_checkForInCat1 = verify checkForInCat "for f in $(cat foo); do stuff; done"
prop_checkForInCat1a= verify checkForInCat "for f in `cat foo`; do stuff; done" prop_checkForInCat1a = verify checkForInCat "for f in `cat foo`; do stuff; done"
prop_checkForInCat2 = verify checkForInCat "for f in $(cat foo | grep lol); do s tuff; done" prop_checkForInCat2 = verify checkForInCat "for f in $(cat foo | grep lol); do s tuff; done"
prop_checkForInCat2a= verify checkForInCat "for f in `cat foo | grep lol`; do st uff; done" prop_checkForInCat2a = verify checkForInCat "for f in `cat foo | grep lol`; do s tuff; done"
prop_checkForInCat3 = verifyNot checkForInCat "for f in $(cat foo | grep bar | w c -l); do stuff; done" prop_checkForInCat3 = verifyNot checkForInCat "for f in $(cat foo | grep bar | w c -l); do stuff; done"
checkForInCat _ (T_ForIn _ f [T_NormalWord _ w] _) = mapM_ checkF w checkForInCat _ (T_ForIn _ f [T_NormalWord _ w] _) = mapM_ checkF w
where where
checkF (T_DollarExpansion id [T_Pipeline _ _ r]) checkF (T_DollarExpansion id [T_Pipeline _ _ r])
| all isLineBased r = | all isLineBased r =
info id 2013 "To read lines rather than words, pipe/redirect to a 'w hile read' loop." info id 2013 "To read lines rather than words, pipe/redirect to a 'w hile read' loop."
checkF (T_Backticked id cmds) = checkF (T_DollarExpansion id cmds) checkF (T_Backticked id cmds) = checkF (T_DollarExpansion id cmds)
checkF _ = return () checkF _ = return ()
isLineBased cmd = any (cmd `isCommand`) isLineBased cmd = any (cmd `isCommand`)
["grep", "fgrep", "egrep", "sed", "cat", "awk", "cut", " sort"] ["grep", "fgrep", "egrep", "sed", "cat", "awk", "cut", " sort"]
skipping to change at line 762 skipping to change at line 775
warnFor x = warnFor x =
when(shouldWarn x) $ when(shouldWarn x) $
info (getId x) 2014 "This will expand once before find runs, not per file found." info (getId x) 2014 "This will expand once before find runs, not per file found."
fromWord (T_NormalWord _ l) = l fromWord (T_NormalWord _ l) = l
fromWord _ = [] fromWord _ = []
checkFindExec _ _ = return () checkFindExec _ _ = return ()
prop_checkUnquotedExpansions1 = verify checkUnquotedExpansions "rm $(ls)" prop_checkUnquotedExpansions1 = verify checkUnquotedExpansions "rm $(ls)"
prop_checkUnquotedExpansions1a= verify checkUnquotedExpansions "rm `ls`" prop_checkUnquotedExpansions1a = verify checkUnquotedExpansions "rm `ls`"
prop_checkUnquotedExpansions2 = verify checkUnquotedExpansions "rm foo$(date)" prop_checkUnquotedExpansions2 = verify checkUnquotedExpansions "rm foo$(date)"
prop_checkUnquotedExpansions3 = verify checkUnquotedExpansions "[ $(foo) == cow ]" prop_checkUnquotedExpansions3 = verify checkUnquotedExpansions "[ $(foo) == cow ]"
prop_checkUnquotedExpansions3a= verify checkUnquotedExpansions "[ ! $(foo) ]" prop_checkUnquotedExpansions3a = verify checkUnquotedExpansions "[ ! $(foo) ]"
prop_checkUnquotedExpansions4 = verifyNot checkUnquotedExpansions "[[ $(foo) == cow ]]" prop_checkUnquotedExpansions4 = verifyNot checkUnquotedExpansions "[[ $(foo) == cow ]]"
prop_checkUnquotedExpansions5 = verifyNot checkUnquotedExpansions "for f in $(cm d); do echo $f; done" prop_checkUnquotedExpansions5 = verifyNot checkUnquotedExpansions "for f in $(cm d); do echo $f; done"
prop_checkUnquotedExpansions6 = verifyNot checkUnquotedExpansions "$(cmd)" prop_checkUnquotedExpansions6 = verifyNot checkUnquotedExpansions "$(cmd)"
prop_checkUnquotedExpansions7 = verifyNot checkUnquotedExpansions "cat << foo\n$ (ls)\nfoo" prop_checkUnquotedExpansions7 = verifyNot checkUnquotedExpansions "cat << foo\n$ (ls)\nfoo"
prop_checkUnquotedExpansions8 = verifyNot checkUnquotedExpansions "set -- $(seq 1 4)" prop_checkUnquotedExpansions8 = verifyNot checkUnquotedExpansions "set -- $(seq 1 4)"
prop_checkUnquotedExpansions9 = verifyNot checkUnquotedExpansions "echo foo `# i nline comment`" prop_checkUnquotedExpansions9 = verifyNot checkUnquotedExpansions "echo foo `# i nline comment`"
prop_checkUnquotedExpansions10 = verify checkUnquotedExpansions "#!/bin/sh\nexpo rt var=$(val)" prop_checkUnquotedExpansions10 = verify checkUnquotedExpansions "#!/bin/sh\nexpo rt var=$(val)"
prop_checkUnquotedExpansions11 = verifyNot checkUnquotedExpansions "ps -p $(pgre p foo)"
checkUnquotedExpansions params = checkUnquotedExpansions params =
check check
where where
check t@(T_DollarExpansion _ c) = examine t c check t@(T_DollarExpansion _ c) = examine t c
check t@(T_Backticked _ c) = examine t c check t@(T_Backticked _ c) = examine t c
check t@(T_DollarBraceCommandExpansion _ c) = examine t c check t@(T_DollarBraceCommandExpansion _ c) = examine t c
check _ = return () check _ = return ()
tree = parentMap params tree = parentMap params
examine t contents = examine t contents =
unless (null contents || shouldBeSplit t || isQuoteFree (shellType param s) tree t || usedAsCommandName tree t) $ unless (null contents || shouldBeSplit t || isQuoteFree (shellType param s) tree t || usedAsCommandName tree t) $
warn (getId t) 2046 "Quote this to prevent word splitting." warn (getId t) 2046 "Quote this to prevent word splitting."
shouldBeSplit t = shouldBeSplit t =
getCommandNameFromExpansion t == Just "seq" getCommandNameFromExpansion t `elem` [Just "seq", Just "pgrep"]
prop_checkRedirectToSame = verify checkRedirectToSame "cat foo > foo" prop_checkRedirectToSame = verify checkRedirectToSame "cat foo > foo"
prop_checkRedirectToSame2 = verify checkRedirectToSame "cat lol | sed -e 's/a/b/ g' > lol" prop_checkRedirectToSame2 = verify checkRedirectToSame "cat lol | sed -e 's/a/b/ g' > lol"
prop_checkRedirectToSame3 = verifyNot checkRedirectToSame "cat lol | sed -e 's/a /b/g' > foo.bar && mv foo.bar lol" prop_checkRedirectToSame3 = verifyNot checkRedirectToSame "cat lol | sed -e 's/a /b/g' > foo.bar && mv foo.bar lol"
prop_checkRedirectToSame4 = verifyNot checkRedirectToSame "foo /dev/null > /dev/ null" prop_checkRedirectToSame4 = verifyNot checkRedirectToSame "foo /dev/null > /dev/ null"
prop_checkRedirectToSame5 = verifyNot checkRedirectToSame "foo > bar 2> bar" prop_checkRedirectToSame5 = verifyNot checkRedirectToSame "foo > bar 2> bar"
prop_checkRedirectToSame6 = verifyNot checkRedirectToSame "echo foo > foo" prop_checkRedirectToSame6 = verifyNot checkRedirectToSame "echo foo > foo"
prop_checkRedirectToSame7 = verifyNot checkRedirectToSame "sed 's/foo/bar/g' fil e | sponge file" prop_checkRedirectToSame7 = verifyNot checkRedirectToSame "sed 's/foo/bar/g' fil e | sponge file"
prop_checkRedirectToSame8 = verifyNot checkRedirectToSame "while read -r line; d o _=\"$fname\"; done <\"$fname\"" prop_checkRedirectToSame8 = verifyNot checkRedirectToSame "while read -r line; d o _=\"$fname\"; done <\"$fname\""
prop_checkRedirectToSame9 = verifyNot checkRedirectToSame "while read -r line; d o cat < \"$fname\"; done <\"$fname\"" prop_checkRedirectToSame9 = verifyNot checkRedirectToSame "while read -r line; d o cat < \"$fname\"; done <\"$fname\""
prop_checkRedirectToSame10 = verifyNot checkRedirectToSame "mapfile -t foo <foo"
checkRedirectToSame params s@(T_Pipeline _ _ list) = checkRedirectToSame params s@(T_Pipeline _ _ list) =
mapM_ (\l -> (mapM_ (\x -> doAnalysis (checkOccurrences x) l) (getAllRedirs list))) list mapM_ (\l -> (mapM_ (\x -> doAnalysis (checkOccurrences x) l) (getAllRedirs list))) list
where where
note x = makeComment InfoC x 2094 note x = makeComment InfoC x 2094
"Make sure not to read and write the same file in the same pipel ine." "Make sure not to read and write the same file in the same pipel ine."
checkOccurrences t@(T_NormalWord exceptId x) u@(T_NormalWord newId y) | checkOccurrences t@(T_NormalWord exceptId x) u@(T_NormalWord newId y) |
exceptId /= newId exceptId /= newId
&& x == y && x == y
&& not (isInput t && isInput u) && not (isInput t && isInput u)
&& not (isOutput t && isOutput u) && not (isOutput t && isOutput u)
skipping to change at line 842 skipping to change at line 857
case drop 1 $ getPath (parentMap params) t of case drop 1 $ getPath (parentMap params) t of
T_IoFile _ op _:_ -> T_IoFile _ op _:_ ->
case op of case op of
T_Greater _ -> True T_Greater _ -> True
T_DGREAT _ -> True T_DGREAT _ -> True
_ -> False _ -> False
_ -> False _ -> False
isHarmlessCommand arg = fromMaybe False $ do isHarmlessCommand arg = fromMaybe False $ do
cmd <- getClosestCommand (parentMap params) arg cmd <- getClosestCommand (parentMap params) arg
name <- getCommandBasename cmd name <- getCommandBasename cmd
return $ name `elem` ["echo", "printf", "sponge"] return $ name `elem` ["echo", "mapfile", "printf", "sponge"]
containsAssignment arg = fromMaybe False $ do containsAssignment arg = fromMaybe False $ do
cmd <- getClosestCommand (parentMap params) arg cmd <- getClosestCommand (parentMap params) arg
return $ isAssignment cmd return $ isAssignment cmd
checkRedirectToSame _ _ = return () checkRedirectToSame _ _ = return ()
prop_checkShorthandIf = verify checkShorthandIf "[[ ! -z file ]] && scp file ho st || rm file" prop_checkShorthandIf = verify checkShorthandIf "[[ ! -z file ]] && scp file ho st || rm file"
prop_checkShorthandIf2 = verifyNot checkShorthandIf "[[ ! -z file ]] && { scp fi le host || echo 'Eek'; }" prop_checkShorthandIf2 = verifyNot checkShorthandIf "[[ ! -z file ]] && { scp fi le host || echo 'Eek'; }"
prop_checkShorthandIf3 = verifyNot checkShorthandIf "foo && bar || echo baz" prop_checkShorthandIf3 = verifyNot checkShorthandIf "foo && bar || echo baz"
prop_checkShorthandIf4 = verifyNot checkShorthandIf "foo && a=b || a=c" prop_checkShorthandIf4 = verifyNot checkShorthandIf "foo && a=b || a=c"
prop_checkShorthandIf5 = verifyNot checkShorthandIf "foo && rm || printf b" prop_checkShorthandIf5 = verifyNot checkShorthandIf "foo && rm || printf b"
prop_checkShorthandIf6 = verifyNot checkShorthandIf "if foo && bar || baz; then true; fi" prop_checkShorthandIf6 = verifyNot checkShorthandIf "if foo && bar || baz; then true; fi"
prop_checkShorthandIf7 = verifyNot checkShorthandIf "while foo && bar || baz; do true; done" prop_checkShorthandIf7 = verifyNot checkShorthandIf "while foo && bar || baz; do true; done"
prop_checkShorthandIf8 = verify checkShorthandIf "if true; then foo && bar || ba z; fi" prop_checkShorthandIf8 = verify checkShorthandIf "if true; then foo && bar || ba z; fi"
checkShorthandIf params x@(T_AndIf id _ (T_OrIf _ _ (T_Pipeline _ _ t))) checkShorthandIf params x@(T_OrIf _ (T_AndIf id _ _) (T_Pipeline _ _ t))
| not (isOk t || inCondition) = | not (isOk t || inCondition) =
info id 2015 "Note that A && B || C is not if-then-else. C may run when A is true." info id 2015 "Note that A && B || C is not if-then-else. C may run when A is true."
where where
isOk [t] = isAssignment t || fromMaybe False (do isOk [t] = isAssignment t || fromMaybe False (do
name <- getCommandBasename t name <- getCommandBasename t
return $ name `elem` ["echo", "exit", "return", "printf"]) return $ name `elem` ["echo", "exit", "return", "printf"])
isOk _ = False isOk _ = False
inCondition = isCondition $ getPath (parentMap params) x inCondition = isCondition $ getPath (parentMap params) x
checkShorthandIf _ _ = return () checkShorthandIf _ _ = return ()
skipping to change at line 889 skipping to change at line 904
| not (isStrictlyQuoteFree (shellType p) (parentMap p) t) = do | not (isStrictlyQuoteFree (shellType p) (parentMap p) t) = do
let str = concat (oversimplify l) let str = concat (oversimplify l)
when ("*" `isPrefixOf` str) $ when ("*" `isPrefixOf` str) $
warn id 2048 "Use \"$@\" (with quotes) to prevent whitespace problem s." warn id 2048 "Use \"$@\" (with quotes) to prevent whitespace problem s."
when ("[*]" `isPrefixOf` (getBracedModifier str) && isVariableChar (headOr Default '!' str)) $ when ("[*]" `isPrefixOf` (getBracedModifier str) && isVariableChar (headOr Default '!' str)) $
warn id 2048 "Use \"${array[@]}\" (with quotes) to prevent whitespac e problems." warn id 2048 "Use \"${array[@]}\" (with quotes) to prevent whitespac e problems."
checkDollarStar _ _ = return () checkDollarStar _ _ = return ()
prop_checkUnquotedDollarAt = verify checkUnquotedDollarAt "ls $@" prop_checkUnquotedDollarAt = verify checkUnquotedDollarAt "ls $@"
prop_checkUnquotedDollarAt1= verifyNot checkUnquotedDollarAt "ls ${#@}" prop_checkUnquotedDollarAt1 = verifyNot checkUnquotedDollarAt "ls ${#@}"
prop_checkUnquotedDollarAt2 = verify checkUnquotedDollarAt "ls ${foo[@]}" prop_checkUnquotedDollarAt2 = verify checkUnquotedDollarAt "ls ${foo[@]}"
prop_checkUnquotedDollarAt3 = verifyNot checkUnquotedDollarAt "ls ${#foo[@]}" prop_checkUnquotedDollarAt3 = verifyNot checkUnquotedDollarAt "ls ${#foo[@]}"
prop_checkUnquotedDollarAt4 = verifyNot checkUnquotedDollarAt "ls \"$@\"" prop_checkUnquotedDollarAt4 = verifyNot checkUnquotedDollarAt "ls \"$@\""
prop_checkUnquotedDollarAt5 = verifyNot checkUnquotedDollarAt "ls ${foo/@/ at }" prop_checkUnquotedDollarAt5 = verifyNot checkUnquotedDollarAt "ls ${foo/@/ at }"
prop_checkUnquotedDollarAt6 = verifyNot checkUnquotedDollarAt "a=$@" prop_checkUnquotedDollarAt6 = verifyNot checkUnquotedDollarAt "a=$@"
prop_checkUnquotedDollarAt7 = verify checkUnquotedDollarAt "for f in ${var[@]}; do true; done" prop_checkUnquotedDollarAt7 = verify checkUnquotedDollarAt "for f in ${var[@]}; do true; done"
prop_checkUnquotedDollarAt8 = verifyNot checkUnquotedDollarAt "echo \"${args[@]: +${args[@]}}\"" prop_checkUnquotedDollarAt8 = verifyNot checkUnquotedDollarAt "echo \"${args[@]: +${args[@]}}\""
prop_checkUnquotedDollarAt9 = verifyNot checkUnquotedDollarAt "echo ${args[@]:+\ "${args[@]}\"}" prop_checkUnquotedDollarAt9 = verifyNot checkUnquotedDollarAt "echo ${args[@]:+\ "${args[@]}\"}"
prop_checkUnquotedDollarAt10 = verifyNot checkUnquotedDollarAt "echo ${@+\"$@\"} " prop_checkUnquotedDollarAt10 = verifyNot checkUnquotedDollarAt "echo ${@+\"$@\"} "
checkUnquotedDollarAt p word@(T_NormalWord _ parts) | not $ isStrictlyQuoteFree (shellType p) (parentMap p) word = checkUnquotedDollarAt p word@(T_NormalWord _ parts) | not $ isStrictlyQuoteFree (shellType p) (parentMap p) word =
skipping to change at line 1019 skipping to change at line 1034
warn id 2069 "To redirect stdout+stderr, 2>&1 must be last (or use '{ cm d > file; } 2>&1' to clarify)." warn id 2069 "To redirect stdout+stderr, 2>&1 must be last (or use '{ cm d > file; } 2>&1' to clarify)."
checkStderrRedirect _ _ = return () checkStderrRedirect _ _ = return ()
lt x = trace ("Tracing " ++ show x) x -- STRIP lt x = trace ("Tracing " ++ show x) x -- STRIP
ltt t = trace ("Tracing " ++ show t) -- STRIP ltt t = trace ("Tracing " ++ show t) -- STRIP
prop_checkSingleQuotedVariables = verify checkSingleQuotedVariables "echo '$foo '" prop_checkSingleQuotedVariables = verify checkSingleQuotedVariables "echo '$foo '"
prop_checkSingleQuotedVariables2 = verify checkSingleQuotedVariables "echo 'lol$ 1.jpg'" prop_checkSingleQuotedVariables2 = verify checkSingleQuotedVariables "echo 'lol$ 1.jpg'"
prop_checkSingleQuotedVariables3 = verifyNot checkSingleQuotedVariables "sed 's/ foo$/bar/'" prop_checkSingleQuotedVariables3 = verifyNot checkSingleQuotedVariables "sed 's/ foo$/bar/'"
prop_checkSingleQuotedVariables3a= verify checkSingleQuotedVariables "sed 's/${f prop_checkSingleQuotedVariables3a = verify checkSingleQuotedVariables "sed 's/${
oo}/bar/'" foo}/bar/'"
prop_checkSingleQuotedVariables3b= verify checkSingleQuotedVariables "sed 's/$(e prop_checkSingleQuotedVariables3b = verify checkSingleQuotedVariables "sed 's/$(
cho cow)/bar/'" echo cow)/bar/'"
prop_checkSingleQuotedVariables3c= verify checkSingleQuotedVariables "sed 's/$(( prop_checkSingleQuotedVariables3c = verify checkSingleQuotedVariables "sed 's/$(
1+foo))/bar/'" (1+foo))/bar/'"
prop_checkSingleQuotedVariables4 = verifyNot checkSingleQuotedVariables "awk '{p rint $1}'" prop_checkSingleQuotedVariables4 = verifyNot checkSingleQuotedVariables "awk '{p rint $1}'"
prop_checkSingleQuotedVariables5 = verifyNot checkSingleQuotedVariables "trap 'e cho $SECONDS' EXIT" prop_checkSingleQuotedVariables5 = verifyNot checkSingleQuotedVariables "trap 'e cho $SECONDS' EXIT"
prop_checkSingleQuotedVariables6 = verifyNot checkSingleQuotedVariables "sed -n '$p'" prop_checkSingleQuotedVariables6 = verifyNot checkSingleQuotedVariables "sed -n '$p'"
prop_checkSingleQuotedVariables6a= verify checkSingleQuotedVariables "sed -n '$p attern'" prop_checkSingleQuotedVariables6a = verify checkSingleQuotedVariables "sed -n '$ pattern'"
prop_checkSingleQuotedVariables7 = verifyNot checkSingleQuotedVariables "PS1='$P WD \\$ '" prop_checkSingleQuotedVariables7 = verifyNot checkSingleQuotedVariables "PS1='$P WD \\$ '"
prop_checkSingleQuotedVariables8 = verify checkSingleQuotedVariables "find . -ex ec echo '$1' {} +" prop_checkSingleQuotedVariables8 = verify checkSingleQuotedVariables "find . -ex ec echo '$1' {} +"
prop_checkSingleQuotedVariables9 = verifyNot checkSingleQuotedVariables "find . -exec awk '{print $1}' {} \\;" prop_checkSingleQuotedVariables9 = verifyNot checkSingleQuotedVariables "find . -exec awk '{print $1}' {} \\;"
prop_checkSingleQuotedVariables10= verify checkSingleQuotedVariables "echo '`pwd prop_checkSingleQuotedVariables10 = verify checkSingleQuotedVariables "echo '`pw
`'" d`'"
prop_checkSingleQuotedVariables11= verifyNot checkSingleQuotedVariables "sed '${ prop_checkSingleQuotedVariables11 = verifyNot checkSingleQuotedVariables "sed '$
/lol/d}'" {/lol/d}'"
prop_checkSingleQuotedVariables12= verifyNot checkSingleQuotedVariables "eval 'e prop_checkSingleQuotedVariables12 = verifyNot checkSingleQuotedVariables "eval '
cho $1'" echo $1'"
prop_checkSingleQuotedVariables13= verifyNot checkSingleQuotedVariables "busybox prop_checkSingleQuotedVariables13 = verifyNot checkSingleQuotedVariables "busybo
awk '{print $1}'" x awk '{print $1}'"
prop_checkSingleQuotedVariables14= verifyNot checkSingleQuotedVariables "[ -v 'b prop_checkSingleQuotedVariables14 = verifyNot checkSingleQuotedVariables "[ -v '
ar[$foo]' ]" bar[$foo]' ]"
prop_checkSingleQuotedVariables15= verifyNot checkSingleQuotedVariables "git fil prop_checkSingleQuotedVariables15 = verifyNot checkSingleQuotedVariables "git fi
ter-branch 'test $GIT_COMMIT'" lter-branch 'test $GIT_COMMIT'"
prop_checkSingleQuotedVariables16= verify checkSingleQuotedVariables "git '$a'" prop_checkSingleQuotedVariables16 = verify checkSingleQuotedVariables "git '$a'"
prop_checkSingleQuotedVariables17= verifyNot checkSingleQuotedVariables "rename prop_checkSingleQuotedVariables17 = verifyNot checkSingleQuotedVariables "rename
's/(.)a/$1/g' *" 's/(.)a/$1/g' *"
prop_checkSingleQuotedVariables18= verifyNot checkSingleQuotedVariables "echo '` prop_checkSingleQuotedVariables18 = verifyNot checkSingleQuotedVariables "echo '
`'" ``'"
prop_checkSingleQuotedVariables19= verifyNot checkSingleQuotedVariables "echo '` prop_checkSingleQuotedVariables19 = verifyNot checkSingleQuotedVariables "echo '
``'" ```'"
prop_checkSingleQuotedVariables20= verifyNot checkSingleQuotedVariables "mumps - prop_checkSingleQuotedVariables20 = verifyNot checkSingleQuotedVariables "mumps
run %XCMD 'W $O(^GLOBAL(5))'" -run %XCMD 'W $O(^GLOBAL(5))'"
prop_checkSingleQuotedVariables21= verifyNot checkSingleQuotedVariables "mumps - prop_checkSingleQuotedVariables21 = verifyNot checkSingleQuotedVariables "mumps
run LOOP%XCMD --xec 'W $O(^GLOBAL(6))'" -run LOOP%XCMD --xec 'W $O(^GLOBAL(6))'"
prop_checkSingleQuotedVariables22= verifyNot checkSingleQuotedVariables "jq '$__ prop_checkSingleQuotedVariables22 = verifyNot checkSingleQuotedVariables "jq '$_
loc__'" _loc__'"
prop_checkSingleQuotedVariables23= verifyNot checkSingleQuotedVariables "command prop_checkSingleQuotedVariables23 = verifyNot checkSingleQuotedVariables "comman
jq '$__loc__'" d jq '$__loc__'"
prop_checkSingleQuotedVariables24= verifyNot checkSingleQuotedVariables "exec jq prop_checkSingleQuotedVariables24 = verifyNot checkSingleQuotedVariables "exec j
'$__loc__'" q '$__loc__'"
prop_checkSingleQuotedVariables25= verifyNot checkSingleQuotedVariables "exec -c prop_checkSingleQuotedVariables25 = verifyNot checkSingleQuotedVariables "exec -
-a foo jq '$__loc__'" c -a foo jq '$__loc__'"
checkSingleQuotedVariables params t@(T_SingleQuoted id s) = checkSingleQuotedVariables params t@(T_SingleQuoted id s) =
when (s `matches` re) $ when (s `matches` re) $
if "sed" == commandName if "sed" == commandName
then unless (s `matches` sedContra) showMessage then unless (s `matches` sedContra) showMessage
else unless isProbablyOk showMessage else unless isProbablyOk showMessage
where where
parents = parentMap params parents = parentMap params
showMessage = info id 2016 showMessage = info id 2016
"Expressions don't expand in single quotes, use double quotes for that." "Expressions don't expand in single quotes, use double quotes for that."
skipping to change at line 1151 skipping to change at line 1166
prop_checkNumberComparisons12 = verify checkNumberComparisons "[ x$foo -gt x${N} ]" prop_checkNumberComparisons12 = verify checkNumberComparisons "[ x$foo -gt x${N} ]"
prop_checkNumberComparisons13 = verify checkNumberComparisons "[ $foo > $bar ]" prop_checkNumberComparisons13 = verify checkNumberComparisons "[ $foo > $bar ]"
prop_checkNumberComparisons14 = verifyNot checkNumberComparisons "[[ foo < bar ] ]" prop_checkNumberComparisons14 = verifyNot checkNumberComparisons "[[ foo < bar ] ]"
prop_checkNumberComparisons15 = verifyNot checkNumberComparisons "[ $foo '>' $ba r ]" prop_checkNumberComparisons15 = verifyNot checkNumberComparisons "[ $foo '>' $ba r ]"
prop_checkNumberComparisons16 = verify checkNumberComparisons "[ foo -eq 'y' ]" prop_checkNumberComparisons16 = verify checkNumberComparisons "[ foo -eq 'y' ]"
prop_checkNumberComparisons17 = verify checkNumberComparisons "[[ 'foo' -eq 2 ]] " prop_checkNumberComparisons17 = verify checkNumberComparisons "[[ 'foo' -eq 2 ]] "
prop_checkNumberComparisons18 = verify checkNumberComparisons "[[ foo -eq 2 ]]" prop_checkNumberComparisons18 = verify checkNumberComparisons "[[ foo -eq 2 ]]"
prop_checkNumberComparisons19 = verifyNot checkNumberComparisons "foo=1; [[ foo -eq 2 ]]" prop_checkNumberComparisons19 = verifyNot checkNumberComparisons "foo=1; [[ foo -eq 2 ]]"
prop_checkNumberComparisons20 = verify checkNumberComparisons "[[ 2 -eq / ]]" prop_checkNumberComparisons20 = verify checkNumberComparisons "[[ 2 -eq / ]]"
prop_checkNumberComparisons21 = verify checkNumberComparisons "[[ foo -eq foo ]] " prop_checkNumberComparisons21 = verify checkNumberComparisons "[[ foo -eq foo ]] "
prop_checkNumberComparisons22 = verify checkNumberComparisons "x=10; [[ $x > $z
]]"
prop_checkNumberComparisons23 = verify checkNumberComparisons "x=0; if [[ -n $de
f ]]; then x=$def; fi; while [ $x > $z ]; do lol; done"
prop_checkNumberComparisons24 = verify checkNumberComparisons "x=$RANDOM; [ $x >
$z ]"
prop_checkNumberComparisons25 = verify checkNumberComparisons "[[ $((n++)) > $x
]]"
checkNumberComparisons params (TC_Binary id typ op lhs rhs) = do checkNumberComparisons params (TC_Binary id typ op lhs rhs) = do
if isNum lhs || isNum rhs if isNum lhs || isNum rhs
then do then do
when (isLtGt op) $ when (isLtGt op) $
err id 2071 $ err id 2071 $
op ++ " is for string comparisons. Use " ++ eqv op ++ " instead." op ++ " is for string comparisons. Use " ++ eqv op ++ " instead."
when (isLeGe op && hasStringComparison) $ when (isLeGe op && hasStringComparison) $
err id 2071 $ op ++ " is not a valid operator. " ++ err id 2071 $ op ++ " is not a valid operator. " ++
"Use " ++ eqv op ++ " ." "Use " ++ eqv op ++ " ."
skipping to change at line 1225 skipping to change at line 1244
assignedVariables = mapMaybe f (variableFlow params) assignedVariables = mapMaybe f (variableFlow params)
where where
f t = do f t = do
Assignment (_, _, name, _) <- return t Assignment (_, _, name, _) <- return t
return name return name
isNonNum t = not . all numChar $ onlyLiteralString t isNonNum t = not . all numChar $ onlyLiteralString t
numChar x = isDigit x || x `elem` "+-. " numChar x = isDigit x || x `elem` "+-. "
isNum t = isNum t =
case oversimplify t of case getWordParts t of
[v] -> all isDigit v [T_DollarArithmetic {}] -> True
_ -> False [b@(T_DollarBraced id _ c)] ->
let
str = concat $ oversimplify c
var = getBracedReference str
in fromMaybe False $ do
state <- CF.getIncomingState (cfgAnalysis params) id
value <- Map.lookup var $ CF.variablesInScope state
return $ CF.numericalStatus (CF.variableValue value) >= CF.N
umericalStatusMaybe
_ ->
case oversimplify t of
[v] -> all isDigit v
_ -> False
isFraction t = isFraction t =
case oversimplify t of case oversimplify t of
[v] -> isJust $ matchRegex floatRegex v [v] -> isJust $ matchRegex floatRegex v
_ -> False _ -> False
eqv ('\\':s) = eqv s eqv ('\\':s) = eqv s
eqv "<" = "-lt" eqv "<" = "-lt"
eqv ">" = "-gt" eqv ">" = "-gt"
eqv "<=" = "-le" eqv "<=" = "-le"
skipping to change at line 1331 skipping to change at line 1361
prop_checkGlobbedRegex6 = verifyNot checkGlobbedRegex "[[ $foo =~ (o*) ]]" prop_checkGlobbedRegex6 = verifyNot checkGlobbedRegex "[[ $foo =~ (o*) ]]"
prop_checkGlobbedRegex7 = verifyNot checkGlobbedRegex "[[ $foo =~ \\*foo ]]" prop_checkGlobbedRegex7 = verifyNot checkGlobbedRegex "[[ $foo =~ \\*foo ]]"
prop_checkGlobbedRegex8 = verifyNot checkGlobbedRegex "[[ $foo =~ x\\* ]]" prop_checkGlobbedRegex8 = verifyNot checkGlobbedRegex "[[ $foo =~ x\\* ]]"
checkGlobbedRegex _ (TC_Binary _ DoubleBracket "=~" _ rhs) checkGlobbedRegex _ (TC_Binary _ DoubleBracket "=~" _ rhs)
| isConfusedGlobRegex s = | isConfusedGlobRegex s =
warn (getId rhs) 2049 "=~ is for regex, but this looks like a glob. Use = instead." warn (getId rhs) 2049 "=~ is for regex, but this looks like a glob. Use = instead."
where s = concat $ oversimplify rhs where s = concat $ oversimplify rhs
checkGlobbedRegex _ _ = return () checkGlobbedRegex _ _ = return ()
prop_checkConstantIfs1 = verify checkConstantIfs "[[ foo != bar ]]" prop_checkConstantIfs1 = verify checkConstantIfs "[[ foo != bar ]]"
prop_checkConstantIfs2a= verify checkConstantIfs "[ n -le 4 ]" prop_checkConstantIfs2a = verify checkConstantIfs "[ n -le 4 ]"
prop_checkConstantIfs2b= verifyNot checkConstantIfs "[[ n -le 4 ]]" prop_checkConstantIfs2b = verifyNot checkConstantIfs "[[ n -le 4 ]]"
prop_checkConstantIfs3 = verify checkConstantIfs "[[ $n -le 4 && n != 2 ]]" prop_checkConstantIfs3 = verify checkConstantIfs "[[ $n -le 4 && n != 2 ]]"
prop_checkConstantIfs4 = verifyNot checkConstantIfs "[[ $n -le 3 ]]" prop_checkConstantIfs4 = verifyNot checkConstantIfs "[[ $n -le 3 ]]"
prop_checkConstantIfs5 = verifyNot checkConstantIfs "[[ $n -le $n ]]" prop_checkConstantIfs5 = verifyNot checkConstantIfs "[[ $n -le $n ]]"
prop_checkConstantIfs6 = verifyNot checkConstantIfs "[[ a -ot b ]]" prop_checkConstantIfs6 = verifyNot checkConstantIfs "[[ a -ot b ]]"
prop_checkConstantIfs7 = verifyNot checkConstantIfs "[ a -nt b ]" prop_checkConstantIfs7 = verifyNot checkConstantIfs "[ a -nt b ]"
prop_checkConstantIfs8 = verifyNot checkConstantIfs "[[ ~foo == '~foo' ]]" prop_checkConstantIfs8 = verifyNot checkConstantIfs "[[ ~foo == '~foo' ]]"
prop_checkConstantIfs9 = verify checkConstantIfs "[[ *.png == [a-z] ]]" prop_checkConstantIfs9 = verify checkConstantIfs "[[ *.png == [a-z] ]]"
prop_checkConstantIfs10 = verifyNot checkConstantIfs "[[ ~me == ~+ ]]" prop_checkConstantIfs10 = verifyNot checkConstantIfs "[[ ~me == ~+ ]]"
prop_checkConstantIfs11 = verifyNot checkConstantIfs "[[ ~ == ~+ ]]" prop_checkConstantIfs11 = verifyNot checkConstantIfs "[[ ~ == ~+ ]]"
prop_checkConstantIfs12 = verify checkConstantIfs "[[ '~' == x ]]" prop_checkConstantIfs12 = verify checkConstantIfs "[[ '~' == x ]]"
skipping to change at line 1437 skipping to change at line 1467
prop_checkArithmeticDeref = verify checkArithmeticDeref "echo $((3+$foo))" prop_checkArithmeticDeref = verify checkArithmeticDeref "echo $((3+$foo))"
prop_checkArithmeticDeref2 = verify checkArithmeticDeref "cow=14; (( s+= $cow )) " prop_checkArithmeticDeref2 = verify checkArithmeticDeref "cow=14; (( s+= $cow )) "
prop_checkArithmeticDeref3 = verifyNot checkArithmeticDeref "cow=1/40; (( s+= ${ cow%%/*} ))" prop_checkArithmeticDeref3 = verifyNot checkArithmeticDeref "cow=1/40; (( s+= ${ cow%%/*} ))"
prop_checkArithmeticDeref4 = verifyNot checkArithmeticDeref "(( ! $? ))" prop_checkArithmeticDeref4 = verifyNot checkArithmeticDeref "(( ! $? ))"
prop_checkArithmeticDeref5 = verifyNot checkArithmeticDeref "(($1))" prop_checkArithmeticDeref5 = verifyNot checkArithmeticDeref "(($1))"
prop_checkArithmeticDeref6 = verify checkArithmeticDeref "(( a[$i] ))" prop_checkArithmeticDeref6 = verify checkArithmeticDeref "(( a[$i] ))"
prop_checkArithmeticDeref7 = verifyNot checkArithmeticDeref "(( 10#$n ))" prop_checkArithmeticDeref7 = verifyNot checkArithmeticDeref "(( 10#$n ))"
prop_checkArithmeticDeref8 = verifyNot checkArithmeticDeref "let i=$i+1" prop_checkArithmeticDeref8 = verifyNot checkArithmeticDeref "let i=$i+1"
prop_checkArithmeticDeref9 = verifyNot checkArithmeticDeref "(( a[foo] ))" prop_checkArithmeticDeref9 = verifyNot checkArithmeticDeref "(( a[foo] ))"
prop_checkArithmeticDeref10= verifyNot checkArithmeticDeref "(( a[\\$foo] ))" prop_checkArithmeticDeref10 = verifyNot checkArithmeticDeref "(( a[\\$foo] ))"
prop_checkArithmeticDeref11= verifyNot checkArithmeticDeref "a[$foo]=wee" prop_checkArithmeticDeref11 = verify checkArithmeticDeref "a[$foo]=wee"
prop_checkArithmeticDeref12= verify checkArithmeticDeref "for ((i=0; $i < 3; i)) prop_checkArithmeticDeref11b = verifyNot checkArithmeticDeref "declare -A a; a[$
; do true; done" foo]=wee"
prop_checkArithmeticDeref13= verifyNot checkArithmeticDeref "(( $$ ))" prop_checkArithmeticDeref12 = verify checkArithmeticDeref "for ((i=0; $i < 3; i)
prop_checkArithmeticDeref14= verifyNot checkArithmeticDeref "(( $! ))" ); do true; done"
prop_checkArithmeticDeref15= verifyNot checkArithmeticDeref "(( ${!var} ))" prop_checkArithmeticDeref13 = verifyNot checkArithmeticDeref "(( $$ ))"
prop_checkArithmeticDeref16= verifyNot checkArithmeticDeref "(( ${x+1} + ${x=42} prop_checkArithmeticDeref14 = verifyNot checkArithmeticDeref "(( $! ))"
))" prop_checkArithmeticDeref15 = verifyNot checkArithmeticDeref "(( ${!var} ))"
prop_checkArithmeticDeref16 = verifyNot checkArithmeticDeref "(( ${x+1} + ${x=42
} ))"
checkArithmeticDeref params t@(TA_Expansion _ [T_DollarBraced id _ l]) = checkArithmeticDeref params t@(TA_Expansion _ [T_DollarBraced id _ l]) =
unless (isException $ concat $ oversimplify l) getWarning unless (isException $ concat $ oversimplify l) getWarning
where where
isException [] = True isException [] = True
isException s@(h:_) = any (`elem` "/.:#%?*@$-!+=^,") s || isDigit h isException s@(h:_) = any (`elem` "/.:#%?*@$-!+=^,") s || isDigit h
getWarning = fromMaybe noWarning . msum . map warningFor $ parents params t getWarning = fromMaybe noWarning . msum . map warningFor $ parents params t
warningFor t = warningFor t =
case t of case t of
T_Arithmetic {} -> return normalWarning T_Arithmetic {} -> return normalWarning
T_DollarArithmetic {} -> return normalWarning T_DollarArithmetic {} -> return normalWarning
T_ForArithmetic {} -> return normalWarning T_ForArithmetic {} -> return normalWarning
T_Assignment {} -> return normalWarning
T_SimpleCommand {} -> return noWarning T_SimpleCommand {} -> return noWarning
_ -> Nothing _ -> Nothing
normalWarning = style id 2004 "$/${} is unnecessary on arithmetic variables. " normalWarning = style id 2004 "$/${} is unnecessary on arithmetic variables. "
noWarning = return () noWarning = return ()
checkArithmeticDeref _ _ = return () checkArithmeticDeref _ _ = return ()
prop_checkArithmeticBadOctal1 = verify checkArithmeticBadOctal "(( 0192 ))" prop_checkArithmeticBadOctal1 = verify checkArithmeticBadOctal "(( 0192 ))"
prop_checkArithmeticBadOctal2 = verifyNot checkArithmeticBadOctal "(( 0x192 ))" prop_checkArithmeticBadOctal2 = verifyNot checkArithmeticBadOctal "(( 0x192 ))"
prop_checkArithmeticBadOctal3 = verifyNot checkArithmeticBadOctal "(( 1 ^ 0777 ) )" prop_checkArithmeticBadOctal3 = verifyNot checkArithmeticBadOctal "(( 1 ^ 0777 ) )"
skipping to change at line 1576 skipping to change at line 1608
orient (lhs, op, rhs) = orient (lhs, op, rhs) =
case (isConstant lhs, isConstant rhs) of case (isConstant lhs, isConstant rhs) of
(True, False) -> return (rhs, op, lhs) (True, False) -> return (rhs, op, lhs)
(False, True) -> return (lhs, op, rhs) (False, True) -> return (lhs, op, rhs)
_ -> Nothing _ -> Nothing
checkOrNeq _ _ = return () checkOrNeq _ _ = return ()
prop_checkValidCondOps1 = verify checkValidCondOps "[[ a -xz b ]]" prop_checkValidCondOps1 = verify checkValidCondOps "[[ a -xz b ]]"
prop_checkValidCondOps2 = verify checkValidCondOps "[ -M a ]" prop_checkValidCondOps2 = verify checkValidCondOps "[ -M a ]"
prop_checkValidCondOps2a= verifyNot checkValidCondOps "[ 3 \\> 2 ]" prop_checkValidCondOps2a = verifyNot checkValidCondOps "[ 3 \\> 2 ]"
prop_checkValidCondOps3 = verifyNot checkValidCondOps "[ 1 = 2 -a 3 -ge 4 ]" prop_checkValidCondOps3 = verifyNot checkValidCondOps "[ 1 = 2 -a 3 -ge 4 ]"
prop_checkValidCondOps4 = verifyNot checkValidCondOps "[[ ! -v foo ]]" prop_checkValidCondOps4 = verifyNot checkValidCondOps "[[ ! -v foo ]]"
checkValidCondOps _ (TC_Binary id _ s _ _) checkValidCondOps _ (TC_Binary id _ s _ _)
| s `notElem` binaryTestOps = | s `notElem` binaryTestOps =
warn id 2057 "Unknown binary operator." warn id 2057 "Unknown binary operator."
checkValidCondOps _ (TC_Unary id _ s _) checkValidCondOps _ (TC_Unary id _ s _)
| s `notElem` unaryTestOps = | s `notElem` unaryTestOps =
warn id 2058 "Unknown unary operator." warn id 2058 "Unknown unary operator."
checkValidCondOps _ _ = return () checkValidCondOps _ _ = return ()
skipping to change at line 1643 skipping to change at line 1675
T_FdRedirect _ fd (T_IoFile _ op _) -> fd /= "2" && isComparison op T_FdRedirect _ fd (T_IoFile _ op _) -> fd /= "2" && isComparison op
_ -> False _ -> False
isComparison t = isComparison t =
case t of case t of
T_Greater _ -> True T_Greater _ -> True
T_Less _ -> True T_Less _ -> True
_ -> False _ -> False
checkTestRedirects _ _ = return () checkTestRedirects _ _ = return ()
prop_checkPS11 = verify checkPS1Assignments "PS1='\\033[1;35m\\$ '" prop_checkPS11 = verify checkPS1Assignments "PS1='\\033[1;35m\\$ '"
prop_checkPS11a= verify checkPS1Assignments "export PS1='\\033[1;35m\\$ '" prop_checkPS11a = verify checkPS1Assignments "export PS1='\\033[1;35m\\$ '"
prop_checkPSf2 = verify checkPS1Assignments "PS1='\\h \\e[0m\\$ '" prop_checkPSf2 = verify checkPS1Assignments "PS1='\\h \\e[0m\\$ '"
prop_checkPS13 = verify checkPS1Assignments "PS1=$'\\x1b[c '" prop_checkPS13 = verify checkPS1Assignments "PS1=$'\\x1b[c '"
prop_checkPS14 = verify checkPS1Assignments "PS1=$'\\e[3m; '" prop_checkPS14 = verify checkPS1Assignments "PS1=$'\\e[3m; '"
prop_checkPS14a= verify checkPS1Assignments "export PS1=$'\\e[3m; '" prop_checkPS14a = verify checkPS1Assignments "export PS1=$'\\e[3m; '"
prop_checkPS15 = verifyNot checkPS1Assignments "PS1='\\[\\033[1;35m\\]\\$ '" prop_checkPS15 = verifyNot checkPS1Assignments "PS1='\\[\\033[1;35m\\]\\$ '"
prop_checkPS16 = verifyNot checkPS1Assignments "PS1='\\[\\e1m\\e[1m\\]\\$ '" prop_checkPS16 = verifyNot checkPS1Assignments "PS1='\\[\\e1m\\e[1m\\]\\$ '"
prop_checkPS17 = verifyNot checkPS1Assignments "PS1='e033x1B'" prop_checkPS17 = verifyNot checkPS1Assignments "PS1='e033x1B'"
prop_checkPS18 = verifyNot checkPS1Assignments "PS1='\\[\\e\\]'" prop_checkPS18 = verifyNot checkPS1Assignments "PS1='\\[\\e\\]'"
checkPS1Assignments _ (T_Assignment _ _ "PS1" _ word) = warnFor word checkPS1Assignments _ (T_Assignment _ _ "PS1" _ word) = warnFor word
where where
warnFor word = warnFor word =
let contents = concat $ oversimplify word in let contents = concat $ oversimplify word in
when (containsUnescaped contents) $ when (containsUnescaped contents) $
info (getId word) 2025 "Make sure all escape sequences are enclo sed in \\[..\\] to prevent line wrapping issues" info (getId word) 2025 "Make sure all escape sequences are enclo sed in \\[..\\] to prevent line wrapping issues"
skipping to change at line 1834 skipping to change at line 1866
prop_checkSpuriousExec1 = verify checkSpuriousExec "exec foo; true" prop_checkSpuriousExec1 = verify checkSpuriousExec "exec foo; true"
prop_checkSpuriousExec2 = verify checkSpuriousExec "if a; then exec b; exec c; f i" prop_checkSpuriousExec2 = verify checkSpuriousExec "if a; then exec b; exec c; f i"
prop_checkSpuriousExec3 = verifyNot checkSpuriousExec "echo cow; exec foo" prop_checkSpuriousExec3 = verifyNot checkSpuriousExec "echo cow; exec foo"
prop_checkSpuriousExec4 = verifyNot checkSpuriousExec "if a; then exec b; fi" prop_checkSpuriousExec4 = verifyNot checkSpuriousExec "if a; then exec b; fi"
prop_checkSpuriousExec5 = verifyNot checkSpuriousExec "exec > file; cmd" prop_checkSpuriousExec5 = verifyNot checkSpuriousExec "exec > file; cmd"
prop_checkSpuriousExec6 = verify checkSpuriousExec "exec foo > file; cmd" prop_checkSpuriousExec6 = verify checkSpuriousExec "exec foo > file; cmd"
prop_checkSpuriousExec7 = verifyNot checkSpuriousExec "exec file; echo failed; e xit 3" prop_checkSpuriousExec7 = verifyNot checkSpuriousExec "exec file; echo failed; e xit 3"
prop_checkSpuriousExec8 = verifyNot checkSpuriousExec "exec {origout}>&1- >tmp.l og 2>&1; bar" prop_checkSpuriousExec8 = verifyNot checkSpuriousExec "exec {origout}>&1- >tmp.l og 2>&1; bar"
prop_checkSpuriousExec9 = verify checkSpuriousExec "for file in rc.d/*; do exec \"$file\"; done" prop_checkSpuriousExec9 = verify checkSpuriousExec "for file in rc.d/*; do exec \"$file\"; done"
prop_checkSpuriousExec10 = verifyNot checkSpuriousExec "exec file; r=$?; printf >&2 'failed\n'; return $r" prop_checkSpuriousExec10 = verifyNot checkSpuriousExec "exec file; r=$?; printf >&2 'failed\n'; return $r"
prop_checkSpuriousExec11 = verifyNot checkSpuriousExec "exec file; :"
checkSpuriousExec _ = doLists checkSpuriousExec _ = doLists
where where
doLists (T_Script _ _ cmds) = doList cmds False doLists (T_Script _ _ cmds) = doList cmds False
doLists (T_BraceGroup _ cmds) = doList cmds False doLists (T_BraceGroup _ cmds) = doList cmds False
doLists (T_WhileExpression _ _ cmds) = doList cmds True doLists (T_WhileExpression _ _ cmds) = doList cmds True
doLists (T_UntilExpression _ _ cmds) = doList cmds True doLists (T_UntilExpression _ _ cmds) = doList cmds True
doLists (T_ForIn _ _ _ cmds) = doList cmds True doLists (T_ForIn _ _ _ cmds) = doList cmds True
doLists (T_ForArithmetic _ _ _ _ cmds) = doList cmds True doLists (T_ForArithmetic _ _ _ _ cmds) = doList cmds True
doLists (T_IfExpression _ thens elses) = do doLists (T_IfExpression _ thens elses) = do
mapM_ (\(_, l) -> doList l False) thens mapM_ (\(_, l) -> doList l False) thens
doList elses False doList elses False
doLists _ = return () doLists _ = return ()
stripCleanup = reverse . dropWhile cleanup . reverse stripCleanup = reverse . dropWhile cleanup . reverse
cleanup (T_Pipeline _ _ [cmd]) = cleanup (T_Pipeline _ _ [cmd]) =
isCommandMatch cmd (`elem` ["echo", "exit", "printf", "return"]) isCommandMatch cmd (`elem` [":", "echo", "exit", "printf", "return"])
|| isAssignment cmd || isAssignment cmd
cleanup _ = False cleanup _ = False
doList = doList' . stripCleanup doList = doList' . stripCleanup
-- The second parameter is True if we are in a loop -- The second parameter is True if we are in a loop
-- In that case we should emit the warning also if `exec' is the last statem ent -- In that case we should emit the warning also if `exec' is the last statem ent
doList' (current:t@(following:_)) False = do doList' (current:t@(following:_)) False = do
commentIfExec current commentIfExec current
doList t False doList t False
doList' (current:tail) True = do doList' (current:tail) True = do
skipping to change at line 1911 skipping to change at line 1944
checkHereDoc _ = return () checkHereDoc _ = return ()
checkSshHereDoc _ _ = return () checkSshHereDoc _ _ = return ()
--- Subshell detection --- Subshell detection
prop_subshellAssignmentCheck = verifyTree subshellAssignmentCheck "cat foo | while read bar; do a=$bar; done; echo \"$a\"" prop_subshellAssignmentCheck = verifyTree subshellAssignmentCheck "cat foo | while read bar; do a=$bar; done; echo \"$a\""
prop_subshellAssignmentCheck2 = verifyNotTree subshellAssignmentCheck "while rea d bar; do a=$bar; done < file; echo \"$a\"" prop_subshellAssignmentCheck2 = verifyNotTree subshellAssignmentCheck "while rea d bar; do a=$bar; done < file; echo \"$a\""
prop_subshellAssignmentCheck3 = verifyTree subshellAssignmentCheck "( A=foo; ); rm $A" prop_subshellAssignmentCheck3 = verifyTree subshellAssignmentCheck "( A=foo; ); rm $A"
prop_subshellAssignmentCheck4 = verifyNotTree subshellAssignmentCheck "( A=foo; rm $A; )" prop_subshellAssignmentCheck4 = verifyNotTree subshellAssignmentCheck "( A=foo; rm $A; )"
prop_subshellAssignmentCheck5 = verifyTree subshellAssignmentCheck "cat foo | while read cow; do true; done; echo $cow;" prop_subshellAssignmentCheck5 = verifyTree subshellAssignmentCheck "cat foo | while read cow; do true; done; echo $cow;"
prop_subshellAssignmentCheck6 = verifyTree subshellAssignmentCheck "( export lol=$(ls); ); echo $lol;" prop_subshellAssignmentCheck6 = verifyTree subshellAssignmentCheck "( export lol=$(ls); ); echo $lol;"
prop_subshellAssignmentCheck6a= verifyTree subshellAssignmentCheck "( typeset -a lol=a; ); echo $lol;" prop_subshellAssignmentCheck6a = verifyTree subshellAssignmentCheck "( typese t -a lol=a; ); echo $lol;"
prop_subshellAssignmentCheck7 = verifyTree subshellAssignmentCheck "cmd | whi le read foo; do (( n++ )); done; echo \"$n lines\"" prop_subshellAssignmentCheck7 = verifyTree subshellAssignmentCheck "cmd | whi le read foo; do (( n++ )); done; echo \"$n lines\""
prop_subshellAssignmentCheck8 = verifyTree subshellAssignmentCheck "n=3 & ech o $((n++))" prop_subshellAssignmentCheck8 = verifyTree subshellAssignmentCheck "n=3 & ech o $((n++))"
prop_subshellAssignmentCheck9 = verifyTree subshellAssignmentCheck "read n & n=foo$n" prop_subshellAssignmentCheck9 = verifyTree subshellAssignmentCheck "read n & n=foo$n"
prop_subshellAssignmentCheck10 = verifyTree subshellAssignmentCheck "(( n <<= 3 )) & (( n |= 4 )) &" prop_subshellAssignmentCheck10 = verifyTree subshellAssignmentCheck "(( n <<= 3 )) & (( n |= 4 )) &"
prop_subshellAssignmentCheck11 = verifyTree subshellAssignmentCheck "cat /etc/pa sswd | while read line; do let n=n+1; done\necho $n" prop_subshellAssignmentCheck11 = verifyTree subshellAssignmentCheck "cat /etc/pa sswd | while read line; do let n=n+1; done\necho $n"
prop_subshellAssignmentCheck12 = verifyTree subshellAssignmentCheck "cat /etc/pa sswd | while read line; do let ++n; done\necho $n" prop_subshellAssignmentCheck12 = verifyTree subshellAssignmentCheck "cat /etc/pa sswd | while read line; do let ++n; done\necho $n"
prop_subshellAssignmentCheck13 = verifyTree subshellAssignmentCheck "#!/bin/bash \necho foo | read bar; echo $bar" prop_subshellAssignmentCheck13 = verifyTree subshellAssignmentCheck "#!/bin/bash \necho foo | read bar; echo $bar"
prop_subshellAssignmentCheck14 = verifyNotTree subshellAssignmentCheck "#!/bin/k sh93\necho foo | read bar; echo $bar" prop_subshellAssignmentCheck14 = verifyNotTree subshellAssignmentCheck "#!/bin/k sh93\necho foo | read bar; echo $bar"
prop_subshellAssignmentCheck15 = verifyNotTree subshellAssignmentCheck "#!/bin/k sh\ncat foo | while read bar; do a=$bar; done\necho \"$a\"" prop_subshellAssignmentCheck15 = verifyNotTree subshellAssignmentCheck "#!/bin/k sh\ncat foo | while read bar; do a=$bar; done\necho \"$a\""
prop_subshellAssignmentCheck16 = verifyNotTree subshellAssignmentCheck "(set -e) ; echo $@" prop_subshellAssignmentCheck16 = verifyNotTree subshellAssignmentCheck "(set -e) ; echo $@"
skipping to change at line 1979 skipping to change at line 2012
doVariableFlowAnalysis readFunc writeFunc empty flow = evalState ( doVariableFlowAnalysis readFunc writeFunc empty flow = evalState (
foldM (\list x -> do { l <- doFlow x; return $ l ++ list; }) [] flow foldM (\list x -> do { l <- doFlow x; return $ l ++ list; }) [] flow
) empty ) empty
where where
doFlow (Reference (base, token, name)) = doFlow (Reference (base, token, name)) =
readFunc base token name readFunc base token name
doFlow (Assignment (base, token, name, values)) = doFlow (Assignment (base, token, name, values)) =
writeFunc base token name values writeFunc base token name values
doFlow _ = return [] doFlow _ = return []
prop_checkSpacefulness1 = verifyTree checkSpacefulness "a='cow moo'; echo $a"
prop_checkSpacefulness2 = verifyNotTree checkSpacefulness "a='cow moo'; [[ $a ]]
"
prop_checkSpacefulness3 = verifyNotTree checkSpacefulness "a='cow*.mp3'; echo \"
$a\""
prop_checkSpacefulness4 = verifyTree checkSpacefulness "for f in *.mp3; do echo
$f; done"
prop_checkSpacefulness4a= verifyNotTree checkSpacefulness "foo=3; foo=$(echo $fo
o)"
prop_checkSpacefulness5 = verifyTree checkSpacefulness "a='*'; b=$a; c=lol${b//f
oo/bar}; echo $c"
prop_checkSpacefulness6 = verifyTree checkSpacefulness "a=foo$(lol); echo $a"
prop_checkSpacefulness7 = verifyTree checkSpacefulness "a=foo\\ bar; rm $a"
prop_checkSpacefulness8 = verifyNotTree checkSpacefulness "a=foo\\ bar; a=foo; r
m $a"
prop_checkSpacefulness10= verifyTree checkSpacefulness "rm $1"
prop_checkSpacefulness11= verifyTree checkSpacefulness "rm ${10//foo/bar}"
prop_checkSpacefulness12= verifyNotTree checkSpacefulness "(( $1 + 3 ))"
prop_checkSpacefulness13= verifyNotTree checkSpacefulness "if [[ $2 -gt 14 ]]; t
hen true; fi"
prop_checkSpacefulness14= verifyNotTree checkSpacefulness "foo=$3 env"
prop_checkSpacefulness15= verifyNotTree checkSpacefulness "local foo=$1"
prop_checkSpacefulness16= verifyNotTree checkSpacefulness "declare foo=$1"
prop_checkSpacefulness17= verifyTree checkSpacefulness "echo foo=$1"
prop_checkSpacefulness18= verifyNotTree checkSpacefulness "$1 --flags"
prop_checkSpacefulness19= verifyTree checkSpacefulness "echo $PWD"
prop_checkSpacefulness20= verifyNotTree checkSpacefulness "n+='foo bar'"
prop_checkSpacefulness21= verifyNotTree checkSpacefulness "select foo in $bar; d
o true; done"
prop_checkSpacefulness22= verifyNotTree checkSpacefulness "echo $\"$1\""
prop_checkSpacefulness23= verifyNotTree checkSpacefulness "a=(1); echo ${a[@]}"
prop_checkSpacefulness24= verifyTree checkSpacefulness "a='a b'; cat <<< $a"
prop_checkSpacefulness25= verifyTree checkSpacefulness "a='s/[0-9]//g'; sed $a"
prop_checkSpacefulness26= verifyTree checkSpacefulness "a='foo bar'; echo {1,2,$
a}"
prop_checkSpacefulness27= verifyNotTree checkSpacefulness "echo ${a:+'foo'}"
prop_checkSpacefulness28= verifyNotTree checkSpacefulness "exec {n}>&1; echo $n"
prop_checkSpacefulness29= verifyNotTree checkSpacefulness "n=$(stuff); exec {n}>
&-;"
prop_checkSpacefulness30= verifyTree checkSpacefulness "file='foo bar'; echo foo
> $file;"
prop_checkSpacefulness31= verifyNotTree checkSpacefulness "echo \"`echo \\\"$1\\
\"`\""
prop_checkSpacefulness32= verifyNotTree checkSpacefulness "var=$1; [ -v var ]"
prop_checkSpacefulness33= verifyTree checkSpacefulness "for file; do echo $file;
done"
prop_checkSpacefulness34= verifyTree checkSpacefulness "declare foo$n=$1"
prop_checkSpacefulness35= verifyNotTree checkSpacefulness "echo ${1+\"$1\"}"
prop_checkSpacefulness36= verifyNotTree checkSpacefulness "arg=$#; echo $arg"
prop_checkSpacefulness37= verifyNotTree checkSpacefulness "@test 'status' {\n [
$status -eq 0 ]\n}"
prop_checkSpacefulness37v = verifyTree checkVerboseSpacefulness "@test 'status'
{\n [ $status -eq 0 ]\n}"
prop_checkSpacefulness38= verifyTree checkSpacefulness "a=; echo $a"
prop_checkSpacefulness39= verifyNotTree checkSpacefulness "a=''\"\"''; b=x$a; ec
ho $b"
prop_checkSpacefulness40= verifyNotTree checkSpacefulness "a=$((x+1)); echo $a"
prop_checkSpacefulness41= verifyNotTree checkSpacefulness "exec $1 --flags"
prop_checkSpacefulness42= verifyNotTree checkSpacefulness "run $1 --flags"
prop_checkSpacefulness43= verifyNotTree checkSpacefulness "$foo=42"
prop_checkSpacefulness44= verifyTree checkSpacefulness "#!/bin/sh\nexport var=$v
alue"
prop_checkSpacefulness45= verifyNotTree checkSpacefulness "wait -zzx -p foo; ech
o $foo"
prop_checkSpacefulness46= verifyNotTree checkSpacefulness "x=0; (( x += 1 )); ec
ho $x"
data SpaceStatus = SpaceSome | SpaceNone | SpaceEmpty deriving (Eq)
instance Semigroup SpaceStatus where
SpaceNone <> SpaceNone = SpaceNone
SpaceSome <> _ = SpaceSome
_ <> SpaceSome = SpaceSome
SpaceEmpty <> x = x
x <> SpaceEmpty = x
instance Monoid SpaceStatus where
mempty = SpaceEmpty
mappend = (<>)
checkSpacefulness params = checkSpacefulness' onFind params
where
emit x = tell [x]
onFind spaces token _ =
when (spaces /= SpaceNone) $
if isDefaultAssignment (parentMap params) token
then
emit $ makeComment InfoC (getId token) 2223
"This default assignment may cause DoS due to globbing.
Quote it."
else
unless (quotesMayConflictWithSC2281 params token) $
emit $ makeCommentWithFix InfoC (getId token) 2086
"Double quote to prevent globbing and word splittin
g."
(addDoubleQuotesAround params token)
isDefaultAssignment parents token =
let modifier = getBracedModifier $ bracedString token in
any (`isPrefixOf` modifier) ["=", ":="]
&& isParamTo parents ":" token
-- Given a T_DollarBraced, return a simplified version of the string content
s.
bracedString (T_DollarBraced _ _ l) = concat $ oversimplify l
bracedString _ = error "Internal shellcheck error, please report! (bracedStr
ing on non-variable)"
prop_checkSpacefulness4v= verifyTree checkVerboseSpacefulness "foo=3; foo=$(echo
$foo)"
prop_checkSpacefulness8v= verifyTree checkVerboseSpacefulness "a=foo\\ bar; a=fo
o; rm $a"
prop_checkSpacefulness28v = verifyTree checkVerboseSpacefulness "exec {n}>&1; ec
ho $n"
prop_checkSpacefulness36v = verifyTree checkVerboseSpacefulness "arg=$#; echo $a
rg"
prop_checkSpacefulness44v = verifyNotTree checkVerboseSpacefulness "foo=3; $foo=
4"
checkVerboseSpacefulness params = checkSpacefulness' onFind params
where
onFind spaces token name =
when (spaces == SpaceNone
&& name `notElem` specialVariablesWithoutSpaces
&& not (quotesMayConflictWithSC2281 params token)) $
tell [makeCommentWithFix StyleC (getId token) 2248
"Prefer double quoting even when variables don't contain spe
cial characters."
(addDoubleQuotesAround params token)]
-- Don't suggest quotes if this will instead be autocorrected -- Don't suggest quotes if this will instead be autocorrected
-- from $foo=bar to foo=bar. This is not pretty but ok. -- from $foo=bar to foo=bar. This is not pretty but ok.
quotesMayConflictWithSC2281 params t = quotesMayConflictWithSC2281 params t =
case getPath (parentMap params) t of case getPath (parentMap params) t of
_ : T_NormalWord parentId (me:T_Literal _ ('=':_):_) : T_SimpleCommand _ _ (cmd:_) : _ -> _ : T_NormalWord parentId (me:T_Literal _ ('=':_):_) : T_SimpleCommand _ _ (cmd:_) : _ ->
(getId t) == (getId me) && (parentId == getId cmd) (getId t) == (getId me) && (parentId == getId cmd)
_ -> False _ -> False
addDoubleQuotesAround params token = (surroundWith (getId token) params "\"") addDoubleQuotesAround params token = (surroundWith (getId token) params "\"")
checkSpacefulness'
:: (SpaceStatus -> Token -> String -> Writer [TokenComment] ()) ->
Parameters -> Token -> [TokenComment]
checkSpacefulness' onFind params t =
doVariableFlowAnalysis readF writeF (Map.fromList defaults) (variableFlow pa
rams)
where
defaults = zip variablesWithoutSpaces (repeat SpaceNone)
hasSpaces name = gets (Map.findWithDefault SpaceSome name)
setSpaces name status =
modify $ Map.insert name status
readF _ token name = do
spaces <- hasSpaces name
let needsQuoting =
isExpansion token
&& not (isArrayExpansion token) -- There's another warning for
this
&& not (isCountingReference token)
&& not (isQuoteFree (shellType params) parents token)
&& not (isQuotedAlternativeReference token)
&& not (usedAsCommandName parents token)
return . execWriter $ when needsQuoting $ onFind spaces token name
where prop_checkSpacefulnessCfg1 = verify checkSpacefulnessCfg "a='cow moo'; echo $a"
emit x = tell [x] prop_checkSpacefulnessCfg2 = verifyNot checkSpacefulnessCfg "a='cow moo'; [[ $a
]]"
writeF _ (TA_Assignment {}) name _ = setSpaces name SpaceNone >> return [] prop_checkSpacefulnessCfg3 = verifyNot checkSpacefulnessCfg "a='cow*.mp3'; echo
writeF _ _ name (DataString SourceExternal) = setSpaces name SpaceSome >> re \"$a\""
turn [] prop_checkSpacefulnessCfg4 = verify checkSpacefulnessCfg "for f in *.mp3; do ech
writeF _ _ name (DataString SourceInteger) = setSpaces name SpaceNone >> ret o $f; done"
urn [] prop_checkSpacefulnessCfg4a = verifyNot checkSpacefulnessCfg "foo=3; foo=$(echo
$foo)"
writeF _ _ name (DataString (SourceFrom vals)) = do prop_checkSpacefulnessCfg5 = verify checkSpacefulnessCfg "a='*'; b=$a; c=lol${b/
map <- get /foo/bar}; echo $c"
setSpaces name prop_checkSpacefulnessCfg6 = verify checkSpacefulnessCfg "a=foo$(lol); echo $a"
(isSpacefulWord (\x -> Map.findWithDefault SpaceSome x map) vals) prop_checkSpacefulnessCfg7 = verify checkSpacefulnessCfg "a=foo\\ bar; rm $a"
return [] prop_checkSpacefulnessCfg8 = verifyNot checkSpacefulnessCfg "a=foo\\ bar; a=foo;
rm $a"
writeF _ _ _ _ = return [] prop_checkSpacefulnessCfg10 = verify checkSpacefulnessCfg "rm $1"
prop_checkSpacefulnessCfg11 = verify checkSpacefulnessCfg "rm ${10//foo/bar}"
prop_checkSpacefulnessCfg12 = verifyNot checkSpacefulnessCfg "(( $1 + 3 ))"
prop_checkSpacefulnessCfg13 = verifyNot checkSpacefulnessCfg "if [[ $2 -gt 14 ]]
; then true; fi"
prop_checkSpacefulnessCfg14 = verifyNot checkSpacefulnessCfg "foo=$3 env"
prop_checkSpacefulnessCfg15 = verifyNot checkSpacefulnessCfg "local foo=$1"
prop_checkSpacefulnessCfg16 = verifyNot checkSpacefulnessCfg "declare foo=$1"
prop_checkSpacefulnessCfg17 = verify checkSpacefulnessCfg "echo foo=$1"
prop_checkSpacefulnessCfg18 = verifyNot checkSpacefulnessCfg "$1 --flags"
prop_checkSpacefulnessCfg19 = verify checkSpacefulnessCfg "echo $PWD"
prop_checkSpacefulnessCfg20 = verifyNot checkSpacefulnessCfg "n+='foo bar'"
prop_checkSpacefulnessCfg21 = verifyNot checkSpacefulnessCfg "select foo in $bar
; do true; done"
prop_checkSpacefulnessCfg22 = verifyNot checkSpacefulnessCfg "echo $\"$1\""
prop_checkSpacefulnessCfg23 = verifyNot checkSpacefulnessCfg "a=(1); echo ${a[@]
}"
prop_checkSpacefulnessCfg24 = verify checkSpacefulnessCfg "a='a b'; cat <<< $
a"
prop_checkSpacefulnessCfg25 = verify checkSpacefulnessCfg "a='s/[0-9]//g'; sed $
a"
prop_checkSpacefulnessCfg26 = verify checkSpacefulnessCfg "a='foo bar'; echo {1,
2,$a}"
prop_checkSpacefulnessCfg27 = verifyNot checkSpacefulnessCfg "echo ${a:+'foo'}"
prop_checkSpacefulnessCfg28 = verifyNot checkSpacefulnessCfg "exec {n}>&1; echo
$n"
prop_checkSpacefulnessCfg29 = verifyNot checkSpacefulnessCfg "n=$(stuff); exec {
n}>&-;"
prop_checkSpacefulnessCfg30 = verify checkSpacefulnessCfg "file='foo bar'; echo
foo > $file;"
prop_checkSpacefulnessCfg31 = verifyNot checkSpacefulnessCfg "echo \"`echo \\\"$
1\\\"`\""
prop_checkSpacefulnessCfg32 = verifyNot checkSpacefulnessCfg "var=$1; [ -v var ]
"
prop_checkSpacefulnessCfg33 = verify checkSpacefulnessCfg "for file; do echo $fi
le; done"
prop_checkSpacefulnessCfg34 = verify checkSpacefulnessCfg "declare foo$n=$1"
prop_checkSpacefulnessCfg35 = verifyNot checkSpacefulnessCfg "echo ${1+\"$1\"}"
prop_checkSpacefulnessCfg36 = verifyNot checkSpacefulnessCfg "arg=$#; echo $arg"
prop_checkSpacefulnessCfg37 = verifyNot checkSpacefulnessCfg "@test 'status' {\n
[ $status -eq 0 ]\n}"
prop_checkSpacefulnessCfg37v = verify checkVerboseSpacefulnessCfg "@test 'status
' {\n [ $status -eq 0 ]\n}"
prop_checkSpacefulnessCfg38 = verify checkSpacefulnessCfg "a=; echo $a"
prop_checkSpacefulnessCfg39 = verifyNot checkSpacefulnessCfg "a=''\"\"''; b=x$a;
echo $b"
prop_checkSpacefulnessCfg40 = verifyNot checkSpacefulnessCfg "a=$((x+1)); echo $
a"
prop_checkSpacefulnessCfg41 = verifyNot checkSpacefulnessCfg "exec $1 --flags"
prop_checkSpacefulnessCfg42 = verifyNot checkSpacefulnessCfg "run $1 --flags"
prop_checkSpacefulnessCfg43 = verifyNot checkSpacefulnessCfg "$foo=42"
prop_checkSpacefulnessCfg44 = verify checkSpacefulnessCfg "#!/bin/sh\nexport var
=$value"
prop_checkSpacefulnessCfg45 = verifyNot checkSpacefulnessCfg "wait -zzx -p foo;
echo $foo"
prop_checkSpacefulnessCfg46 = verifyNot checkSpacefulnessCfg "x=0; (( x += 1 ));
echo $x"
prop_checkSpacefulnessCfg47 = verifyNot checkSpacefulnessCfg "x=0; (( x-- )); ec
ho $x"
prop_checkSpacefulnessCfg48 = verifyNot checkSpacefulnessCfg "x=0; (( ++x )); ec
ho $x"
prop_checkSpacefulnessCfg49 = verifyNot checkSpacefulnessCfg "for i in 1 2 3; do
echo $i; done"
prop_checkSpacefulnessCfg50 = verify checkSpacefulnessCfg "for i in 1 2 *; do ec
ho $i; done"
prop_checkSpacefulnessCfg51 = verify checkSpacefulnessCfg "x='foo bar'; x && x=1
; echo $x"
prop_checkSpacefulnessCfg52 = verifyNot checkSpacefulnessCfg "x=1; if f; then x=
'foo bar'; exit; fi; echo $x"
prop_checkSpacefulnessCfg53 = verifyNot checkSpacefulnessCfg "s=1; f() { local s
='a b'; }; f; echo $s"
prop_checkSpacefulnessCfg54 = verifyNot checkSpacefulnessCfg "s='a b'; f() { s=1
; }; f; echo $s"
prop_checkSpacefulnessCfg55 = verify checkSpacefulnessCfg "s='a b'; x && f() { s
=1; }; f; echo $s"
prop_checkSpacefulnessCfg56 = verifyNot checkSpacefulnessCfg "s=1; cat <(s='a b'
); echo $s"
prop_checkSpacefulnessCfg57 = verifyNot checkSpacefulnessCfg "declare -i s=0; s=
$(f); echo $s"
prop_checkSpacefulnessCfg58 = verify checkSpacefulnessCfg "f() { declare -i s; }
; f; s=$(var); echo $s"
prop_checkSpacefulnessCfg59 = verifyNot checkSpacefulnessCfg "f() { declare -gi
s; }; f; s=$(var); echo $s"
prop_checkSpacefulnessCfg60 = verify checkSpacefulnessCfg "declare -i s; declare
+i s; s=$(foo); echo $s"
prop_checkSpacefulnessCfg61 = verify checkSpacefulnessCfg "declare -x X; y=foo$X
; echo $y;"
prop_checkSpacefulnessCfg62 = verifyNot checkSpacefulnessCfg "f() { declare -x X
; y=foo$X; echo $y; }"
prop_checkSpacefulnessCfg63 = verify checkSpacefulnessCfg "f && declare -i s; s=
'x + y'; echo $s"
prop_checkSpacefulnessCfg64 = verifyNot checkSpacefulnessCfg "declare -i s; s='x
+ y'; x=$s; echo $x"
prop_checkSpacefulnessCfg65 = verifyNot checkSpacefulnessCfg "f() { s=$?; echo $
s; }; f"
prop_checkSpacefulnessCfg66 = verifyNot checkSpacefulnessCfg "f() { s=$?; echo $
s; }"
checkSpacefulnessCfg = checkSpacefulnessCfg' True
checkVerboseSpacefulnessCfg = checkSpacefulnessCfg' False
checkSpacefulnessCfg' :: Bool -> (Parameters -> Token -> Writer [TokenComment] (
))
checkSpacefulnessCfg' dirtyPass params token@(T_DollarBraced id _ list) =
when (needsQuoting && (dirtyPass == not isClean)) $
unless (name `elem` specialVariablesWithoutSpaces || quotesMayConflictWi
thSC2281 params token) $
if dirtyPass
then
if isDefaultAssignment (parentMap params) token
then
info (getId token) 2223
"This default assignment may cause DoS due to globb
ing. Quote it."
else
infoWithFix id 2086 "Double quote to prevent globbing and wo
rd splitting." $
addDoubleQuotesAround params token
else
styleWithFix id 2248 "Prefer double quoting even when variables
don't contain special characters." $
addDoubleQuotesAround params token
where
name = getBracedReference $ concat $ oversimplify list
parents = parentMap params parents = parentMap params
needsQuoting =
not (isArrayExpansion token) -- There's another warning for this
&& not (isCountingReference token)
&& not (isQuoteFree (shellType params) parents token)
&& not (isQuotedAlternativeReference token)
&& not (usedAsCommandName parents token)
isClean = fromMaybe False $ do
state <- CF.getIncomingState (cfgAnalysis params) id
value <- Map.lookup name $ CF.variablesInScope state
return $ isCleanState value
isCleanState state =
(all (S.member CFVPInteger) $ CF.variableProperties state)
|| CF.spaceStatus (CF.variableValue state) == CF.SpaceStatusClean
isExpansion t = isDefaultAssignment parents token =
case t of let modifier = getBracedModifier $ bracedString token in
(T_DollarBraced _ _ _ ) -> True any (`isPrefixOf` modifier) ["=", ":="]
_ -> False && isParamTo parents ":" token
isSpacefulWord :: (String -> SpaceStatus) -> [Token] -> SpaceStatus -- Given a T_DollarBraced, return a simplified version of the string content
isSpacefulWord f = mconcat . map (isSpaceful f) s.
isSpaceful :: (String -> SpaceStatus) -> Token -> SpaceStatus bracedString (T_DollarBraced _ _ l) = concat $ oversimplify l
isSpaceful spacefulF x = bracedString _ = error $ pleaseReport "bracedString on non-variable"
case x of
T_DollarExpansion _ _ -> SpaceSome checkSpacefulnessCfg' _ _ _ = return ()
T_Backticked _ _ -> SpaceSome
T_Glob _ _ -> SpaceSome
T_Extglob {} -> SpaceSome
T_DollarArithmetic _ _ -> SpaceNone
T_Literal _ s -> fromLiteral s
T_SingleQuoted _ s -> fromLiteral s
T_DollarBraced _ _ l -> spacefulF $ getBracedReference $ concat $ over
simplify l
T_NormalWord _ w -> isSpacefulWord spacefulF w
T_DoubleQuoted _ w -> isSpacefulWord spacefulF w
_ -> SpaceEmpty
where
globspace = "*?[] \t\n"
containsAny s = any (`elem` s)
fromLiteral "" = SpaceEmpty
fromLiteral s | s `containsAny` globspace = SpaceSome
fromLiteral _ = SpaceNone
prop_CheckVariableBraces1 = verify checkVariableBraces "a='123'; echo $a" prop_CheckVariableBraces1 = verify checkVariableBraces "a='123'; echo $a"
prop_CheckVariableBraces2 = verifyNot checkVariableBraces "a='123'; echo ${a}" prop_CheckVariableBraces2 = verifyNot checkVariableBraces "a='123'; echo ${a}"
prop_CheckVariableBraces3 = verifyNot checkVariableBraces "#shellcheck disable=S C2016\necho '$a'" prop_CheckVariableBraces3 = verifyNot checkVariableBraces "#shellcheck disable=S C2016\necho '$a'"
prop_CheckVariableBraces4 = verifyNot checkVariableBraces "echo $* $1" prop_CheckVariableBraces4 = verifyNot checkVariableBraces "echo $* $1"
prop_CheckVariableBraces5 = verifyNot checkVariableBraces "$foo=42" prop_CheckVariableBraces5 = verifyNot checkVariableBraces "$foo=42"
checkVariableBraces params t@(T_DollarBraced id False l) checkVariableBraces params t@(T_DollarBraced id False l)
| name `notElem` unbracedVariables && not (quotesMayConflictWithSC2281 param s t) = | name `notElem` unbracedVariables && not (quotesMayConflictWithSC2281 param s t) =
styleWithFix id 2250 styleWithFix id 2250
"Prefer putting braces around variable references even when not stri ctly required." "Prefer putting braces around variable references even when not stri ctly required."
(fixFor t) (fixFor t)
where where
name = getBracedReference $ concat $ oversimplify l name = getBracedReference $ concat $ oversimplify l
fixFor token = fixWith [replaceStart (getId token) params 1 "${" fixFor token = fixWith [replaceStart (getId token) params 1 "${"
,replaceEnd (getId token) params 0 "}"] ,replaceEnd (getId token) params 0 "}"]
checkVariableBraces _ _ = return () checkVariableBraces _ _ = return ()
prop_checkQuotesInLiterals1 = verifyTree checkQuotesInLiterals "param='--foo=\"b ar\"'; app $param" prop_checkQuotesInLiterals1 = verifyTree checkQuotesInLiterals "param='--foo=\"b ar\"'; app $param"
prop_checkQuotesInLiterals1a= verifyTree checkQuotesInLiterals "param=\"--foo='l olbar'\"; app $param" prop_checkQuotesInLiterals1a = verifyTree checkQuotesInLiterals "param=\"--foo=' lolbar'\"; app $param"
prop_checkQuotesInLiterals2 = verifyNotTree checkQuotesInLiterals "param='--foo= \"bar\"'; app \"$param\"" prop_checkQuotesInLiterals2 = verifyNotTree checkQuotesInLiterals "param='--foo= \"bar\"'; app \"$param\""
prop_checkQuotesInLiterals3 =verifyNotTree checkQuotesInLiterals "param=('--foo= '); app \"${param[@]}\"" prop_checkQuotesInLiterals3 =verifyNotTree checkQuotesInLiterals "param=('--foo= '); app \"${param[@]}\""
prop_checkQuotesInLiterals4 = verifyNotTree checkQuotesInLiterals "param=\"don't bother with this one\"; app $param" prop_checkQuotesInLiterals4 = verifyNotTree checkQuotesInLiterals "param=\"don't bother with this one\"; app $param"
prop_checkQuotesInLiterals5 = verifyNotTree checkQuotesInLiterals "param=\"--foo ='lolbar'\"; eval app $param" prop_checkQuotesInLiterals5 = verifyNotTree checkQuotesInLiterals "param=\"--foo ='lolbar'\"; eval app $param"
prop_checkQuotesInLiterals6 = verifyTree checkQuotesInLiterals "param='my\\ file '; cmd=\"rm $param\"; $cmd" prop_checkQuotesInLiterals6 = verifyTree checkQuotesInLiterals "param='my\\ file '; cmd=\"rm $param\"; $cmd"
prop_checkQuotesInLiterals6a= verifyNotTree checkQuotesInLiterals "param='my\\ f ile'; cmd=\"rm ${#param}\"; $cmd" prop_checkQuotesInLiterals6a = verifyNotTree checkQuotesInLiterals "param='my\\ file'; cmd=\"rm ${#param}\"; $cmd"
prop_checkQuotesInLiterals7 = verifyTree checkQuotesInLiterals "param='my\\ file '; rm $param" prop_checkQuotesInLiterals7 = verifyTree checkQuotesInLiterals "param='my\\ file '; rm $param"
prop_checkQuotesInLiterals8 = verifyTree checkQuotesInLiterals "param=\"/foo/'ba r baz'/etc\"; rm $param" prop_checkQuotesInLiterals8 = verifyTree checkQuotesInLiterals "param=\"/foo/'ba r baz'/etc\"; rm $param"
prop_checkQuotesInLiterals9 = verifyNotTree checkQuotesInLiterals "param=\"/foo/ 'bar baz'/etc\"; rm ${#param}" prop_checkQuotesInLiterals9 = verifyNotTree checkQuotesInLiterals "param=\"/foo/ 'bar baz'/etc\"; rm ${#param}"
checkQuotesInLiterals params t = checkQuotesInLiterals params t =
doVariableFlowAnalysis readF writeF Map.empty (variableFlow params) doVariableFlowAnalysis readF writeF Map.empty (variableFlow params)
where where
getQuotes name = gets (Map.lookup name) getQuotes name = gets (Map.lookup name)
setQuotes name ref = modify $ Map.insert name ref setQuotes name ref = modify $ Map.insert name ref
deleteQuotes = modify . Map.delete deleteQuotes = modify . Map.delete
parents = parentMap params parents = parentMap params
skipping to change at line 2245 skipping to change at line 2226
_ -> [] _ -> []
suggestion = suggestion =
if supportsArrays (shellType params) if supportsArrays (shellType params)
then "Use an array." then "Use an array."
else "Rewrite using set/\"$@\" or functions." else "Rewrite using set/\"$@\" or functions."
prop_checkFunctionsUsedExternally1 = prop_checkFunctionsUsedExternally1 =
verifyTree checkFunctionsUsedExternally "foo() { :; }; sudo foo" verifyTree checkFunctionsUsedExternally "foo() { :; }; sudo foo"
prop_checkFunctionsUsedExternally2 = prop_checkFunctionsUsedExternally2 =
verifyTree checkFunctionsUsedExternally "alias f='a'; xargs -0 f" verifyTree checkFunctionsUsedExternally "alias f='a'; xargs -0 f"
prop_checkFunctionsUsedExternally2b= prop_checkFunctionsUsedExternally2b =
verifyNotTree checkFunctionsUsedExternally "alias f='a'; find . -type f" verifyNotTree checkFunctionsUsedExternally "alias f='a'; find . -type f"
prop_checkFunctionsUsedExternally2c= prop_checkFunctionsUsedExternally2c =
verifyTree checkFunctionsUsedExternally "alias f='a'; find . -type f -exec f + " verifyTree checkFunctionsUsedExternally "alias f='a'; find . -type f -exec f + "
prop_checkFunctionsUsedExternally3 = prop_checkFunctionsUsedExternally3 =
verifyNotTree checkFunctionsUsedExternally "f() { :; }; echo f" verifyNotTree checkFunctionsUsedExternally "f() { :; }; echo f"
prop_checkFunctionsUsedExternally4 = prop_checkFunctionsUsedExternally4 =
verifyNotTree checkFunctionsUsedExternally "foo() { :; }; sudo \"foo\"" verifyNotTree checkFunctionsUsedExternally "foo() { :; }; sudo \"foo\""
prop_checkFunctionsUsedExternally5 = prop_checkFunctionsUsedExternally5 =
verifyTree checkFunctionsUsedExternally "foo() { :; }; ssh host foo" verifyTree checkFunctionsUsedExternally "foo() { :; }; ssh host foo"
prop_checkFunctionsUsedExternally6 = prop_checkFunctionsUsedExternally6 =
verifyNotTree checkFunctionsUsedExternally "foo() { :; }; ssh host echo foo" verifyNotTree checkFunctionsUsedExternally "foo() { :; }; ssh host echo foo"
prop_checkFunctionsUsedExternally7 = prop_checkFunctionsUsedExternally7 =
skipping to change at line 2273 skipping to change at line 2254
checkFunctionsUsedExternally params t = checkFunctionsUsedExternally params t =
runNodeAnalysis checkCommand params t runNodeAnalysis checkCommand params t
where where
checkCommand _ t@(T_SimpleCommand _ _ argv) = checkCommand _ t@(T_SimpleCommand _ _ argv) =
case getCommandNameAndToken False t of case getCommandNameAndToken False t of
(Just str, t) -> do (Just str, t) -> do
let name = basename str let name = basename str
let args = skipOver t argv let args = skipOver t argv
let argStrings = map (\x -> (fromMaybe "" $ getLiteralString x, x)) args let argStrings = map (\x -> (fromMaybe "" $ getLiteralString x, x)) args
let candidates = getPotentialCommands name argStrings let candidates = getPotentialCommands name argStrings
mapM_ (checkArg name) candidates mapM_ (checkArg name (getId t)) candidates
_ -> return () _ -> return ()
checkCommand _ _ = return () checkCommand _ _ = return ()
skipOver t list = drop 1 $ dropWhile (\c -> getId c /= id) $ list skipOver t list = drop 1 $ dropWhile (\c -> getId c /= id) $ list
where id = getId t where id = getId t
-- Try to pick out the argument[s] that may be commands -- Try to pick out the argument[s] that may be commands
getPotentialCommands name argAndString = getPotentialCommands name argAndString =
case name of case name of
"chroot" -> firstNonFlag "chroot" -> firstNonFlag
skipping to change at line 2299 skipping to change at line 2280
"find" -> take 1 $ drop 1 $ "find" -> take 1 $ drop 1 $
dropWhile (\x -> fst x `notElem` findExecFlags) argAndString dropWhile (\x -> fst x `notElem` findExecFlags) argAndString
_ -> [] _ -> []
where where
firstNonFlag = take 1 $ dropFlags argAndString firstNonFlag = take 1 $ dropFlags argAndString
findExecFlags = ["-exec", "-execdir", "-ok"] findExecFlags = ["-exec", "-execdir", "-ok"]
dropFlags = dropWhile (\x -> "-" `isPrefixOf` fst x) dropFlags = dropWhile (\x -> "-" `isPrefixOf` fst x)
functionsAndAliases = Map.union (functions t) (aliases t) functionsAndAliases = Map.union (functions t) (aliases t)
checkArg cmd (_, arg) = sequence_ $ do patternContext id =
case posLine . fst <$> Map.lookup id (tokenPositions params) of
Just l -> " on line " <> show l <> "."
_ -> "."
checkArg cmd cmdId (_, arg) = sequence_ $ do
literalArg <- getUnquotedLiteral arg -- only consider unquoted literals literalArg <- getUnquotedLiteral arg -- only consider unquoted literals
definitionId <- Map.lookup literalArg functionsAndAliases definitionId <- Map.lookup literalArg functionsAndAliases
return $ do return $ do
warn (getId arg) 2033 warn (getId arg) 2033
"Shell functions can't be passed to external commands." "Shell functions can't be passed to external commands. Use separat e script or sh -c."
info definitionId 2032 $ info definitionId 2032 $
"Use own script or sh -c '..' to run this from " ++ cmd ++ "." "This function can't be invoked via " ++ cmd ++ patternContext cmd Id
prop_checkUnused0 = verifyNotTree checkUnusedAssignments "var=foo; echo $var" prop_checkUnused0 = verifyNotTree checkUnusedAssignments "var=foo; echo $var"
prop_checkUnused1 = verifyTree checkUnusedAssignments "var=foo; echo $bar" prop_checkUnused1 = verifyTree checkUnusedAssignments "var=foo; echo $bar"
prop_checkUnused2 = verifyNotTree checkUnusedAssignments "var=foo; export var;" prop_checkUnused2 = verifyNotTree checkUnusedAssignments "var=foo; export var;"
prop_checkUnused3 = verifyTree checkUnusedAssignments "for f in *; do echo '$f'; done" prop_checkUnused3 = verifyTree checkUnusedAssignments "for f in *; do echo '$f'; done"
prop_checkUnused4 = verifyTree checkUnusedAssignments "local i=0" prop_checkUnused4 = verifyTree checkUnusedAssignments "local i=0"
prop_checkUnused5 = verifyNotTree checkUnusedAssignments "read lol; echo $lol" prop_checkUnused5 = verifyNotTree checkUnusedAssignments "read lol; echo $lol"
prop_checkUnused6 = verifyNotTree checkUnusedAssignments "var=4; (( var++ ))" prop_checkUnused6 = verifyNotTree checkUnusedAssignments "var=4; (( var++ ))"
prop_checkUnused7 = verifyNotTree checkUnusedAssignments "var=2; $((var))" prop_checkUnused7 = verifyNotTree checkUnusedAssignments "var=2; $((var))"
prop_checkUnused8 = verifyTree checkUnusedAssignments "var=2; var=3;" prop_checkUnused8 = verifyTree checkUnusedAssignments "var=2; var=3;"
prop_checkUnused9 = verifyNotTree checkUnusedAssignments "read ''" prop_checkUnused9 = verifyNotTree checkUnusedAssignments "read ''"
prop_checkUnused10= verifyNotTree checkUnusedAssignments "read -p 'test: '" prop_checkUnused10 = verifyNotTree checkUnusedAssignments "read -p 'test: '"
prop_checkUnused11= verifyNotTree checkUnusedAssignments "bar=5; export foo[$bar prop_checkUnused11 = verifyNotTree checkUnusedAssignments "bar=5; export foo[$ba
]=3" r]=3"
prop_checkUnused12= verifyNotTree checkUnusedAssignments "read foo; echo ${!foo} prop_checkUnused12 = verifyNotTree checkUnusedAssignments "read foo; echo ${!foo
" }"
prop_checkUnused13= verifyNotTree checkUnusedAssignments "x=(1); (( x[0] ))" prop_checkUnused13 = verifyNotTree checkUnusedAssignments "x=(1); (( x[0] ))"
prop_checkUnused14= verifyNotTree checkUnusedAssignments "x=(1); n=0; echo ${x[n prop_checkUnused14 = verifyNotTree checkUnusedAssignments "x=(1); n=0; echo ${x[
]}" n]}"
prop_checkUnused15= verifyNotTree checkUnusedAssignments "x=(1); n=0; (( x[n] )) prop_checkUnused15 = verifyNotTree checkUnusedAssignments "x=(1); n=0; (( x[n] )
" )"
prop_checkUnused16= verifyNotTree checkUnusedAssignments "foo=5; declare -x foo" prop_checkUnused16 = verifyNotTree checkUnusedAssignments "foo=5; declare -x foo
prop_checkUnused16b= verifyNotTree checkUnusedAssignments "f() { local -x foo; f "
oo=42; bar; }; f" prop_checkUnused16b = verifyNotTree checkUnusedAssignments "f() { local -x foo;
prop_checkUnused17= verifyNotTree checkUnusedAssignments "read -i 'foo' -e -p 'I foo=42; bar; }; f"
nput: ' bar; $bar;" prop_checkUnused17 = verifyNotTree checkUnusedAssignments "read -i 'foo' -e -p '
prop_checkUnused18= verifyNotTree checkUnusedAssignments "a=1; arr=( [$a]=42 ); Input: ' bar; $bar;"
echo \"${arr[@]}\"" prop_checkUnused18 = verifyNotTree checkUnusedAssignments "a=1; arr=( [$a]=42 );
prop_checkUnused19= verifyNotTree checkUnusedAssignments "a=1; let b=a+1; echo $ echo \"${arr[@]}\""
b" prop_checkUnused19 = verifyNotTree checkUnusedAssignments "a=1; let b=a+1; echo
prop_checkUnused20= verifyNotTree checkUnusedAssignments "a=1; PS1='$a'" $b"
prop_checkUnused21= verifyNotTree checkUnusedAssignments "a=1; trap 'echo $a' IN prop_checkUnused20 = verifyNotTree checkUnusedAssignments "a=1; PS1='$a'"
T" prop_checkUnused21 = verifyNotTree checkUnusedAssignments "a=1; trap 'echo $a' I
prop_checkUnused22= verifyNotTree checkUnusedAssignments "a=1; [ -v a ]" NT"
prop_checkUnused23= verifyNotTree checkUnusedAssignments "a=1; [ -R a ]" prop_checkUnused22 = verifyNotTree checkUnusedAssignments "a=1; [ -v a ]"
prop_checkUnused24= verifyNotTree checkUnusedAssignments "mapfile -C a b; echo $ prop_checkUnused23 = verifyNotTree checkUnusedAssignments "a=1; [ -R a ]"
{b[@]}" prop_checkUnused24 = verifyNotTree checkUnusedAssignments "mapfile -C a b; echo
prop_checkUnused25= verifyNotTree checkUnusedAssignments "readarray foo; echo ${ ${b[@]}"
foo[@]}" prop_checkUnused25 = verifyNotTree checkUnusedAssignments "readarray foo; echo $
prop_checkUnused26= verifyNotTree checkUnusedAssignments "declare -F foo" {foo[@]}"
prop_checkUnused27= verifyTree checkUnusedAssignments "var=3; [ var -eq 3 ]" prop_checkUnused26 = verifyNotTree checkUnusedAssignments "declare -F foo"
prop_checkUnused28= verifyNotTree checkUnusedAssignments "var=3; [[ var -eq 3 ]] prop_checkUnused27 = verifyTree checkUnusedAssignments "var=3; [ var -eq 3 ]"
" prop_checkUnused28 = verifyNotTree checkUnusedAssignments "var=3; [[ var -eq 3 ]
prop_checkUnused29= verifyNotTree checkUnusedAssignments "var=(a b); declare -p ]"
var" prop_checkUnused29 = verifyNotTree checkUnusedAssignments "var=(a b); declare -p
prop_checkUnused30= verifyTree checkUnusedAssignments "let a=1" var"
prop_checkUnused31= verifyTree checkUnusedAssignments "let 'a=1'" prop_checkUnused30 = verifyTree checkUnusedAssignments "let a=1"
prop_checkUnused32= verifyTree checkUnusedAssignments "let a=b=c; echo $a" prop_checkUnused31 = verifyTree checkUnusedAssignments "let 'a=1'"
prop_checkUnused33= verifyNotTree checkUnusedAssignments "a=foo; [[ foo =~ ^{$a} prop_checkUnused32 = verifyTree checkUnusedAssignments "let a=b=c; echo $a"
$ ]]" prop_checkUnused33 = verifyNotTree checkUnusedAssignments "a=foo; [[ foo =~ ^{$a
prop_checkUnused34= verifyNotTree checkUnusedAssignments "foo=1; (( t = foo )); }$ ]]"
echo $t" prop_checkUnused34 = verifyNotTree checkUnusedAssignments "foo=1; (( t = foo ));
prop_checkUnused35= verifyNotTree checkUnusedAssignments "a=foo; b=2; echo ${a:b echo $t"
}" prop_checkUnused35 = verifyNotTree checkUnusedAssignments "a=foo; b=2; echo ${a:
prop_checkUnused36= verifyNotTree checkUnusedAssignments "if [[ -v foo ]]; then b}"
true; fi" prop_checkUnused36 = verifyNotTree checkUnusedAssignments "if [[ -v foo ]]; then
prop_checkUnused37= verifyNotTree checkUnusedAssignments "fd=2; exec {fd}>&-" true; fi"
prop_checkUnused38= verifyTree checkUnusedAssignments "(( a=42 ))" prop_checkUnused37 = verifyNotTree checkUnusedAssignments "fd=2; exec {fd}>&-"
prop_checkUnused39= verifyNotTree checkUnusedAssignments "declare -x -f foo" prop_checkUnused38 = verifyTree checkUnusedAssignments "(( a=42 ))"
prop_checkUnused40= verifyNotTree checkUnusedAssignments "arr=(1 2); num=2; echo prop_checkUnused39 = verifyNotTree checkUnusedAssignments "declare -x -f foo"
\"${arr[@]:num}\"" prop_checkUnused40 = verifyNotTree checkUnusedAssignments "arr=(1 2); num=2; ech
prop_checkUnused41= verifyNotTree checkUnusedAssignments "@test 'foo' {\ntrue\n} o \"${arr[@]:num}\""
\n" prop_checkUnused41 = verifyNotTree checkUnusedAssignments "@test 'foo' {\ntrue\n
prop_checkUnused42= verifyNotTree checkUnusedAssignments "DEFINE_string foo '' ' }\n"
'; echo \"${FLAGS_foo}\"" prop_checkUnused42 = verifyNotTree checkUnusedAssignments "DEFINE_string foo ''
prop_checkUnused43= verifyTree checkUnusedAssignments "DEFINE_string foo '' ''" ''; echo \"${FLAGS_foo}\""
prop_checkUnused44= verifyNotTree checkUnusedAssignments "DEFINE_string \"foo$ib prop_checkUnused43 = verifyTree checkUnusedAssignments "DEFINE_string foo '' ''"
ar\" x y" prop_checkUnused44 = verifyNotTree checkUnusedAssignments "DEFINE_string \"foo$i
prop_checkUnused45= verifyTree checkUnusedAssignments "readonly foo=bar" bar\" x y"
prop_checkUnused46= verifyTree checkUnusedAssignments "readonly foo=(bar)" prop_checkUnused45 = verifyTree checkUnusedAssignments "readonly foo=bar"
prop_checkUnused47= verifyNotTree checkUnusedAssignments "a=1; alias hello='echo prop_checkUnused46 = verifyTree checkUnusedAssignments "readonly foo=(bar)"
$a'" prop_checkUnused47 = verifyNotTree checkUnusedAssignments "a=1; alias hello='ech
prop_checkUnused48= verifyNotTree checkUnusedAssignments "_a=1" o $a'"
prop_checkUnused49= verifyNotTree checkUnusedAssignments "declare -A array; key= prop_checkUnused48 = verifyNotTree checkUnusedAssignments "_a=1"
a; [[ -v array[$key] ]]" prop_checkUnused49 = verifyNotTree checkUnusedAssignments "declare -A array; key
prop_checkUnused50= verifyNotTree checkUnusedAssignments "foofunc() { :; }; type =a; [[ -v array[$key] ]]"
set -fx foofunc" prop_checkUnused50 = verifyNotTree checkUnusedAssignments "foofunc() { :; }; typ
eset -fx foofunc"
prop_checkUnused51 = verifyTree checkUnusedAssignments "x[y[z=1]]=1; echo ${x[@]
}"
checkUnusedAssignments params t = execWriter (mapM_ warnFor unused) checkUnusedAssignments params t = execWriter (mapM_ warnFor unused)
where where
flow = variableFlow params flow = variableFlow params
references = foldl (flip ($)) defaultMap (map insertRef flow) references = foldl (flip ($)) defaultMap (map insertRef flow)
insertRef (Reference (base, token, name)) = insertRef (Reference (base, token, name)) =
Map.insert (stripSuffix name) () Map.insert (stripSuffix name) ()
insertRef _ = id insertRef _ = id
assignments = foldl (flip ($)) Map.empty (map insertAssignment flow) assignments = foldl (flip ($)) Map.empty (map insertAssignment flow)
skipping to change at line 2393 skipping to change at line 2380
prop_checkUnassignedReferences1 = verifyTree checkUnassignedReferences "echo $fo o" prop_checkUnassignedReferences1 = verifyTree checkUnassignedReferences "echo $fo o"
prop_checkUnassignedReferences2 = verifyNotTree checkUnassignedReferences "foo=h ello; echo $foo" prop_checkUnassignedReferences2 = verifyNotTree checkUnassignedReferences "foo=h ello; echo $foo"
prop_checkUnassignedReferences3 = verifyTree checkUnassignedReferences "MY_VALUE =3; echo $MYVALUE" prop_checkUnassignedReferences3 = verifyTree checkUnassignedReferences "MY_VALUE =3; echo $MYVALUE"
prop_checkUnassignedReferences4 = verifyNotTree checkUnassignedReferences "RANDO M2=foo; echo $RANDOM" prop_checkUnassignedReferences4 = verifyNotTree checkUnassignedReferences "RANDO M2=foo; echo $RANDOM"
prop_checkUnassignedReferences5 = verifyNotTree checkUnassignedReferences "decla re -A foo=([bar]=baz); echo ${foo[bar]}" prop_checkUnassignedReferences5 = verifyNotTree checkUnassignedReferences "decla re -A foo=([bar]=baz); echo ${foo[bar]}"
prop_checkUnassignedReferences6 = verifyNotTree checkUnassignedReferences "foo=. .; echo ${foo-bar}" prop_checkUnassignedReferences6 = verifyNotTree checkUnassignedReferences "foo=. .; echo ${foo-bar}"
prop_checkUnassignedReferences7 = verifyNotTree checkUnassignedReferences "getop ts ':h' foo; echo $foo" prop_checkUnassignedReferences7 = verifyNotTree checkUnassignedReferences "getop ts ':h' foo; echo $foo"
prop_checkUnassignedReferences8 = verifyNotTree checkUnassignedReferences "let ' foo = 1'; echo $foo" prop_checkUnassignedReferences8 = verifyNotTree checkUnassignedReferences "let ' foo = 1'; echo $foo"
prop_checkUnassignedReferences9 = verifyNotTree checkUnassignedReferences "echo ${foo-bar}" prop_checkUnassignedReferences9 = verifyNotTree checkUnassignedReferences "echo ${foo-bar}"
prop_checkUnassignedReferences10= verifyNotTree checkUnassignedReferences "echo prop_checkUnassignedReferences10 = verifyNotTree checkUnassignedReferences "echo
${foo:?}" ${foo:?}"
prop_checkUnassignedReferences11= verifyNotTree checkUnassignedReferences "decla prop_checkUnassignedReferences11 = verifyNotTree checkUnassignedReferences "decl
re -A foo; echo \"${foo[@]}\"" are -A foo; echo \"${foo[@]}\""
prop_checkUnassignedReferences12= verifyNotTree checkUnassignedReferences "types prop_checkUnassignedReferences12 = verifyNotTree checkUnassignedReferences "type
et -a foo; echo \"${foo[@]}\"" set -a foo; echo \"${foo[@]}\""
prop_checkUnassignedReferences13= verifyNotTree checkUnassignedReferences "f() { prop_checkUnassignedReferences13 = verifyNotTree checkUnassignedReferences "f()
local foo; echo $foo; }" { local foo; echo $foo; }"
prop_checkUnassignedReferences14= verifyNotTree checkUnassignedReferences "foo=; prop_checkUnassignedReferences14 = verifyNotTree checkUnassignedReferences "foo=
echo $foo" ; echo $foo"
prop_checkUnassignedReferences15= verifyNotTree checkUnassignedReferences "f() { prop_checkUnassignedReferences15 = verifyNotTree checkUnassignedReferences "f()
true; }; export -f f" { true; }; export -f f"
prop_checkUnassignedReferences16= verifyNotTree checkUnassignedReferences "decla prop_checkUnassignedReferences16 = verifyNotTree checkUnassignedReferences "decl
re -A foo=( [a b]=bar ); echo ${foo[a b]}" are -A foo=( [a b]=bar ); echo ${foo[a b]}"
prop_checkUnassignedReferences17= verifyNotTree checkUnassignedReferences "USERS prop_checkUnassignedReferences17 = verifyNotTree checkUnassignedReferences "USER
=foo; echo $USER" S=foo; echo $USER"
prop_checkUnassignedReferences18= verifyNotTree checkUnassignedReferences "FOOBA prop_checkUnassignedReferences18 = verifyNotTree checkUnassignedReferences "FOOB
R=42; export FOOBAR=" AR=42; export FOOBAR="
prop_checkUnassignedReferences19= verifyNotTree checkUnassignedReferences "reado prop_checkUnassignedReferences19 = verifyNotTree checkUnassignedReferences "read
nly foo=bar; echo $foo" only foo=bar; echo $foo"
prop_checkUnassignedReferences20= verifyNotTree checkUnassignedReferences "print prop_checkUnassignedReferences20 = verifyNotTree checkUnassignedReferences "prin
f -v foo bar; echo $foo" tf -v foo bar; echo $foo"
prop_checkUnassignedReferences21= verifyTree checkUnassignedReferences "echo ${# prop_checkUnassignedReferences21 = verifyTree checkUnassignedReferences "echo ${
foo}" #foo}"
prop_checkUnassignedReferences22= verifyNotTree checkUnassignedReferences "echo prop_checkUnassignedReferences22 = verifyNotTree checkUnassignedReferences "echo
${!os*}" ${!os*}"
prop_checkUnassignedReferences23= verifyTree checkUnassignedReferences "declare prop_checkUnassignedReferences23 = verifyTree checkUnassignedReferences "declare
-a foo; foo[bar]=42;" -a foo; foo[bar]=42;"
prop_checkUnassignedReferences24= verifyNotTree checkUnassignedReferences "decla prop_checkUnassignedReferences24 = verifyNotTree checkUnassignedReferences "decl
re -A foo; foo[bar]=42;" are -A foo; foo[bar]=42;"
prop_checkUnassignedReferences25= verifyNotTree checkUnassignedReferences "decla prop_checkUnassignedReferences25 = verifyNotTree checkUnassignedReferences "decl
re -A foo=(); foo[bar]=42;" are -A foo=(); foo[bar]=42;"
prop_checkUnassignedReferences26= verifyNotTree checkUnassignedReferences "a::b( prop_checkUnassignedReferences26 = verifyNotTree checkUnassignedReferences "a::b
) { foo; }; readonly -f a::b" () { foo; }; readonly -f a::b"
prop_checkUnassignedReferences27= verifyNotTree checkUnassignedReferences ": ${f prop_checkUnassignedReferences27 = verifyNotTree checkUnassignedReferences ": ${
oo:=bar}" foo:=bar}"
prop_checkUnassignedReferences28= verifyNotTree checkUnassignedReferences "#!/bi prop_checkUnassignedReferences28 = verifyNotTree checkUnassignedReferences "#!/b
n/ksh\necho \"${.sh.version}\"\n" in/ksh\necho \"${.sh.version}\"\n"
prop_checkUnassignedReferences29= verifyNotTree checkUnassignedReferences "if [[ prop_checkUnassignedReferences29 = verifyNotTree checkUnassignedReferences "if [
-v foo ]]; then echo $foo; fi" [ -v foo ]]; then echo $foo; fi"
prop_checkUnassignedReferences30= verifyNotTree checkUnassignedReferences "if [[ prop_checkUnassignedReferences30 = verifyNotTree checkUnassignedReferences "if [
-v foo[3] ]]; then echo ${foo[3]}; fi" [ -v foo[3] ]]; then echo ${foo[3]}; fi"
prop_checkUnassignedReferences31= verifyNotTree checkUnassignedReferences "X=1; prop_checkUnassignedReferences31 = verifyNotTree checkUnassignedReferences "X=1;
if [[ -v foo[$X+42] ]]; then echo ${foo[$X+42]}; fi" if [[ -v foo[$X+42] ]]; then echo ${foo[$X+42]}; fi"
prop_checkUnassignedReferences32= verifyNotTree checkUnassignedReferences "if [[ prop_checkUnassignedReferences32 = verifyNotTree checkUnassignedReferences "if [
-v \"foo[1]\" ]]; then echo ${foo[@]}; fi" [ -v \"foo[1]\" ]]; then echo ${foo[@]}; fi"
prop_checkUnassignedReferences33= verifyNotTree checkUnassignedReferences "f() { prop_checkUnassignedReferences33 = verifyNotTree checkUnassignedReferences "f()
local -A foo; echo \"${foo[@]}\"; }" { local -A foo; echo \"${foo[@]}\"; }"
prop_checkUnassignedReferences34= verifyNotTree checkUnassignedReferences "decla prop_checkUnassignedReferences34 = verifyNotTree checkUnassignedReferences "decl
re -A foo; (( foo[bar] ))" are -A foo; (( foo[bar] ))"
prop_checkUnassignedReferences35= verifyNotTree checkUnassignedReferences "echo prop_checkUnassignedReferences35 = verifyNotTree checkUnassignedReferences "echo
${arr[foo-bar]:?fail}" ${arr[foo-bar]:?fail}"
prop_checkUnassignedReferences36= verifyNotTree checkUnassignedReferences "read prop_checkUnassignedReferences36 = verifyNotTree checkUnassignedReferences "read
-a foo -r <<<\"foo bar\"; echo \"$foo\"" -a foo -r <<<\"foo bar\"; echo \"$foo\""
prop_checkUnassignedReferences37= verifyNotTree checkUnassignedReferences "var=h prop_checkUnassignedReferences37 = verifyNotTree checkUnassignedReferences "var=
owdy; printf -v 'array[0]' %s \"$var\"; printf %s \"${array[0]}\";" howdy; printf -v 'array[0]' %s \"$var\"; printf %s \"${array[0]}\";"
prop_checkUnassignedReferences38= verifyTree (checkUnassignedReferences' True) " prop_checkUnassignedReferences38 = verifyTree (checkUnassignedReferences' True)
echo $VAR" "echo $VAR"
prop_checkUnassignedReferences39= verifyNotTree checkUnassignedReferences "built prop_checkUnassignedReferences39 = verifyNotTree checkUnassignedReferences "buil
in export var=4; echo $var" tin export var=4; echo $var"
prop_checkUnassignedReferences40= verifyNotTree checkUnassignedReferences ": ${f prop_checkUnassignedReferences40 = verifyNotTree checkUnassignedReferences ": ${
oo=bar}" foo=bar}"
prop_checkUnassignedReferences41= verifyNotTree checkUnassignedReferences "mapfi prop_checkUnassignedReferences41 = verifyNotTree checkUnassignedReferences "mapf
le -t files 123; echo \"${files[@]}\"" ile -t files 123; echo \"${files[@]}\""
prop_checkUnassignedReferences42= verifyNotTree checkUnassignedReferences "mapfi prop_checkUnassignedReferences42 = verifyNotTree checkUnassignedReferences "mapf
le files -t; echo \"${files[@]}\"" ile files -t; echo \"${files[@]}\""
prop_checkUnassignedReferences43= verifyNotTree checkUnassignedReferences "mapfi prop_checkUnassignedReferences43 = verifyNotTree checkUnassignedReferences "mapf
le --future files; echo \"${files[@]}\"" ile --future files; echo \"${files[@]}\""
prop_checkUnassignedReferences_minusNPlain = verifyNotTree checkUnassignedRefe rences "if [ -n \"$x\" ]; then echo $x; fi" prop_checkUnassignedReferences_minusNPlain = verifyNotTree checkUnassignedRefe rences "if [ -n \"$x\" ]; then echo $x; fi"
prop_checkUnassignedReferences_minusZPlain = verifyNotTree checkUnassignedRefe rences "if [ -z \"$x\" ]; then echo \"\"; fi" prop_checkUnassignedReferences_minusZPlain = verifyNotTree checkUnassignedRefe rences "if [ -z \"$x\" ]; then echo \"\"; fi"
prop_checkUnassignedReferences_minusNBraced = verifyNotTree checkUnassignedRefe rences "if [ -n \"${x}\" ]; then echo $x; fi" prop_checkUnassignedReferences_minusNBraced = verifyNotTree checkUnassignedRefe rences "if [ -n \"${x}\" ]; then echo $x; fi"
prop_checkUnassignedReferences_minusZBraced = verifyNotTree checkUnassignedRefe rences "if [ -z \"${x}\" ]; then echo \"\"; fi" prop_checkUnassignedReferences_minusZBraced = verifyNotTree checkUnassignedRefe rences "if [ -z \"${x}\" ]; then echo \"\"; fi"
prop_checkUnassignedReferences_minusNDefault = verifyNotTree checkUnassignedRefe rences "if [ -n \"${x:-}\" ]; then echo $x; fi" prop_checkUnassignedReferences_minusNDefault = verifyNotTree checkUnassignedRefe rences "if [ -n \"${x:-}\" ]; then echo $x; fi"
prop_checkUnassignedReferences_minusZDefault = verifyNotTree checkUnassignedRefe rences "if [ -z \"${x:-}\" ]; then echo \"\"; fi" prop_checkUnassignedReferences_minusZDefault = verifyNotTree checkUnassignedRefe rences "if [ -z \"${x:-}\" ]; then echo \"\"; fi"
prop_checkUnassignedReferences50 = verifyNotTree checkUnassignedReferences "echo ${foo:+bar}" prop_checkUnassignedReferences50 = verifyNotTree checkUnassignedReferences "echo ${foo:+bar}"
prop_checkUnassignedReferences51 = verifyNotTree checkUnassignedReferences "echo ${foo:+$foo}" prop_checkUnassignedReferences51 = verifyNotTree checkUnassignedReferences "echo ${foo:+$foo}"
prop_checkUnassignedReferences52 = verifyNotTree checkUnassignedReferences "wait -p pid; echo $pid" prop_checkUnassignedReferences52 = verifyNotTree checkUnassignedReferences "wait -p pid; echo $pid"
skipping to change at line 2796 skipping to change at line 2783
prop_checkUnpassedInFunctions1 = verifyTree checkUnpassedInFunctions "foo() { ec ho $1; }; foo" prop_checkUnpassedInFunctions1 = verifyTree checkUnpassedInFunctions "foo() { ec ho $1; }; foo"
prop_checkUnpassedInFunctions2 = verifyNotTree checkUnpassedInFunctions "foo() { echo $1; };" prop_checkUnpassedInFunctions2 = verifyNotTree checkUnpassedInFunctions "foo() { echo $1; };"
prop_checkUnpassedInFunctions3 = verifyNotTree checkUnpassedInFunctions "foo() { echo $lol; }; foo" prop_checkUnpassedInFunctions3 = verifyNotTree checkUnpassedInFunctions "foo() { echo $lol; }; foo"
prop_checkUnpassedInFunctions4 = verifyNotTree checkUnpassedInFunctions "foo() { echo $0; }; foo" prop_checkUnpassedInFunctions4 = verifyNotTree checkUnpassedInFunctions "foo() { echo $0; }; foo"
prop_checkUnpassedInFunctions5 = verifyNotTree checkUnpassedInFunctions "foo() { echo $1; }; foo 'lol'; foo" prop_checkUnpassedInFunctions5 = verifyNotTree checkUnpassedInFunctions "foo() { echo $1; }; foo 'lol'; foo"
prop_checkUnpassedInFunctions6 = verifyNotTree checkUnpassedInFunctions "foo() { set -- *; echo $1; }; foo" prop_checkUnpassedInFunctions6 = verifyNotTree checkUnpassedInFunctions "foo() { set -- *; echo $1; }; foo"
prop_checkUnpassedInFunctions7 = verifyTree checkUnpassedInFunctions "foo() { ec ho $1; }; foo; foo;" prop_checkUnpassedInFunctions7 = verifyTree checkUnpassedInFunctions "foo() { ec ho $1; }; foo; foo;"
prop_checkUnpassedInFunctions8 = verifyNotTree checkUnpassedInFunctions "foo() { echo $((1)); }; foo;" prop_checkUnpassedInFunctions8 = verifyNotTree checkUnpassedInFunctions "foo() { echo $((1)); }; foo;"
prop_checkUnpassedInFunctions9 = verifyNotTree checkUnpassedInFunctions "foo() { echo $(($b)); }; foo;" prop_checkUnpassedInFunctions9 = verifyNotTree checkUnpassedInFunctions "foo() { echo $(($b)); }; foo;"
prop_checkUnpassedInFunctions10= verifyNotTree checkUnpassedInFunctions "foo() { prop_checkUnpassedInFunctions10 = verifyNotTree checkUnpassedInFunctions "foo()
echo $!; }; foo;" { echo $!; }; foo;"
prop_checkUnpassedInFunctions11= verifyNotTree checkUnpassedInFunctions "foo() { prop_checkUnpassedInFunctions11 = verifyNotTree checkUnpassedInFunctions "foo()
bar() { echo $1; }; bar baz; }; foo;" { bar() { echo $1; }; bar baz; }; foo;"
prop_checkUnpassedInFunctions12= verifyNotTree checkUnpassedInFunctions "foo() { prop_checkUnpassedInFunctions12 = verifyNotTree checkUnpassedInFunctions "foo()
echo ${!var*}; }; foo;" { echo ${!var*}; }; foo;"
prop_checkUnpassedInFunctions13= verifyNotTree checkUnpassedInFunctions "# shell prop_checkUnpassedInFunctions13 = verifyNotTree checkUnpassedInFunctions "# shel
check disable=SC2120\nfoo() { echo $1; }\nfoo\n" lcheck disable=SC2120\nfoo() { echo $1; }\nfoo\n"
prop_checkUnpassedInFunctions14= verifyTree checkUnpassedInFunctions "foo() { ec prop_checkUnpassedInFunctions14 = verifyTree checkUnpassedInFunctions "foo() { e
ho $#; }; foo" cho $#; }; foo"
checkUnpassedInFunctions params root = checkUnpassedInFunctions params root =
execWriter $ mapM_ warnForGroup referenceGroups execWriter $ mapM_ warnForGroup referenceGroups
where where
functionMap :: Map.Map String Token functionMap :: Map.Map String Token
functionMap = Map.fromList $ functionMap = Map.fromList $
map (\t@(T_Function _ _ _ name _) -> (name,t)) functions map (\t@(T_Function _ _ _ name _) -> (name,t)) functions
functions = execWriter $ doAnalysis (tell . maybeToList . findFunction) root functions = execWriter $ doAnalysis (tell . maybeToList . findFunction) root
findFunction t@(T_Function id _ _ name body) findFunction t@(T_Function id _ _ name body)
| any (isPositionalReference t) flow && not (any isPositionalAssignment flow) | any (isPositionalReference t) flow && not (any isPositionalAssignment flow)
skipping to change at line 2971 skipping to change at line 2958
"\\n" -> suggest n "\\n" -> suggest n
"\\t" -> suggest t "\\t" -> suggest t
x | '\\' `elem` x -> suggest2 "a literal backslash" x | '\\' `elem` x -> suggest2 "a literal backslash"
x | 'n' `elem` x -> suggest2 "the literal letter 'n'" x | 'n' `elem` x -> suggest2 "the literal letter 'n'"
x | 't' `elem` x -> suggest2 "the literal letter 't'" x | 't' `elem` x -> suggest2 "the literal letter 't'"
_ -> return () _ -> return ()
suggest r = warn (getId value) 2141 $ "This backslash is literal. Did you me an IFS=" ++ r ++ " ?" suggest r = warn (getId value) 2141 $ "This backslash is literal. Did you me an IFS=" ++ r ++ " ?"
suggest2 desc = warn (getId value) 2141 $ "This IFS value contains " ++ desc ++ ". For tabs/linefeeds/escapes, use $'..', literal, or printf." suggest2 desc = warn (getId value) 2141 $ "This IFS value contains " ++ desc ++ ". For tabs/linefeeds/escapes, use $'..', literal, or printf."
checkSuspiciousIFS _ _ = return () checkSuspiciousIFS _ _ = return ()
prop_checkGrepQ1= verify checkShouldUseGrepQ "[[ $(foo | grep bar) ]]" prop_checkGrepQ1 = verify checkShouldUseGrepQ "[[ $(foo | grep bar) ]]"
prop_checkGrepQ2= verify checkShouldUseGrepQ "[ -z $(fgrep lol) ]" prop_checkGrepQ2 = verify checkShouldUseGrepQ "[ -z $(fgrep lol) ]"
prop_checkGrepQ3= verify checkShouldUseGrepQ "[ -n \"$(foo | zgrep lol)\" ]" prop_checkGrepQ3 = verify checkShouldUseGrepQ "[ -n \"$(foo | zgrep lol)\" ]"
prop_checkGrepQ4= verifyNot checkShouldUseGrepQ "[ -z $(grep bar | cmd) ]" prop_checkGrepQ4 = verifyNot checkShouldUseGrepQ "[ -z $(grep bar | cmd) ]"
prop_checkGrepQ5= verifyNot checkShouldUseGrepQ "rm $(ls | grep file)" prop_checkGrepQ5 = verifyNot checkShouldUseGrepQ "rm $(ls | grep file)"
prop_checkGrepQ6= verifyNot checkShouldUseGrepQ "[[ -n $(pgrep foo) ]]" prop_checkGrepQ6 = verifyNot checkShouldUseGrepQ "[[ -n $(pgrep foo) ]]"
checkShouldUseGrepQ params t = checkShouldUseGrepQ params t =
sequence_ $ case t of sequence_ $ case t of
TC_Nullary id _ token -> check id True token TC_Nullary id _ token -> check id True token
TC_Unary id _ "-n" token -> check id True token TC_Unary id _ "-n" token -> check id True token
TC_Unary id _ "-z" token -> check id False token TC_Unary id _ "-z" token -> check id False token
_ -> fail "not check" _ -> fail "not check"
where where
check id bool token = do check id bool token = do
name <- getFinalGrep token name <- getFinalGrep token
let op = if bool then "-n" else "-z" let op = if bool then "-n" else "-z"
skipping to change at line 3277 skipping to change at line 3264
case getPath (parentMap params) t of case getPath (parentMap params) t of
_:(T_Condition {}):_ -> True _:(T_Condition {}):_ -> True
_:(T_Arithmetic {}):_ -> True _:(T_Arithmetic {}):_ -> True
_:(TA_Sequence _ [_]):(T_Arithmetic {}):_ -> True _:(TA_Sequence _ [_]):(T_Arithmetic {}):_ -> True
-- Some negations and groupings are also fine -- Some negations and groupings are also fine
_:next@(TC_Unary _ _ "!" _):_ -> isOnlyTestInCommand next _:next@(TC_Unary _ _ "!" _):_ -> isOnlyTestInCommand next
_:next@(TA_Unary _ "!" _):_ -> isOnlyTestInCommand next _:next@(TA_Unary _ "!" _):_ -> isOnlyTestInCommand next
_:next@(TC_Group {}):_ -> isOnlyTestInCommand next _:next@(TC_Group {}):_ -> isOnlyTestInCommand next
_:next@(TA_Sequence _ [_]):_ -> isOnlyTestInCommand next _:next@(TA_Sequence _ [_]):_ -> isOnlyTestInCommand next
_:next@(TA_Parentesis _ _):_ -> isOnlyTestInCommand next
_ -> False _ -> False
-- TODO: Do better $? tracking and filter on whether -- TODO: Do better $? tracking and filter on whether
-- the target command is in the same function -- the target command is in the same function
getFirstCommandInFunction = f getFirstCommandInFunction = f
where where
f t = case t of f t = case t of
T_Function _ _ _ _ x -> f x T_Function _ _ _ _ x -> f x
T_BraceGroup _ (x:_) -> f x T_BraceGroup _ (x:_) -> f x
T_Subshell _ (x:_) -> f x T_Subshell _ (x:_) -> f x
skipping to change at line 3550 skipping to change at line 3538
TC_Empty id _ -> style id 2212 "Use 'false' instead of empty [/[[ conditiona ls." TC_Empty id _ -> style id 2212 "Use 'false' instead of empty [/[[ conditiona ls."
_ -> return () _ -> return ()
prop_checkPipeToNowhere1 = verify checkPipeToNowhere "foo | echo bar" prop_checkPipeToNowhere1 = verify checkPipeToNowhere "foo | echo bar"
prop_checkPipeToNowhere2 = verify checkPipeToNowhere "basename < file.txt" prop_checkPipeToNowhere2 = verify checkPipeToNowhere "basename < file.txt"
prop_checkPipeToNowhere3 = verify checkPipeToNowhere "printf 'Lol' <<< str" prop_checkPipeToNowhere3 = verify checkPipeToNowhere "printf 'Lol' <<< str"
prop_checkPipeToNowhere4 = verify checkPipeToNowhere "printf 'Lol' << eof\nlol\n eof\n" prop_checkPipeToNowhere4 = verify checkPipeToNowhere "printf 'Lol' << eof\nlol\n eof\n"
prop_checkPipeToNowhere5 = verifyNot checkPipeToNowhere "echo foo | xargs du" prop_checkPipeToNowhere5 = verifyNot checkPipeToNowhere "echo foo | xargs du"
prop_checkPipeToNowhere6 = verifyNot checkPipeToNowhere "ls | echo $(cat)" prop_checkPipeToNowhere6 = verifyNot checkPipeToNowhere "ls | echo $(cat)"
prop_checkPipeToNowhere7 = verifyNot checkPipeToNowhere "echo foo | var=$(cat) l s" prop_checkPipeToNowhere7 = verifyNot checkPipeToNowhere "echo foo | var=$(cat) l s"
prop_checkPipeToNowhere8 = verify checkPipeToNowhere "foo | true"
prop_checkPipeToNowhere9 = verifyNot checkPipeToNowhere "mv -i f . < /dev/stdin" prop_checkPipeToNowhere9 = verifyNot checkPipeToNowhere "mv -i f . < /dev/stdin"
prop_checkPipeToNowhere10 = verify checkPipeToNowhere "ls > file | grep foo" prop_checkPipeToNowhere10 = verify checkPipeToNowhere "ls > file | grep foo"
prop_checkPipeToNowhere11 = verify checkPipeToNowhere "ls | grep foo < file" prop_checkPipeToNowhere11 = verify checkPipeToNowhere "ls | grep foo < file"
prop_checkPipeToNowhere12 = verify checkPipeToNowhere "ls > foo > bar" prop_checkPipeToNowhere12 = verify checkPipeToNowhere "ls > foo > bar"
prop_checkPipeToNowhere13 = verify checkPipeToNowhere "ls > foo 2> bar > baz" prop_checkPipeToNowhere13 = verify checkPipeToNowhere "ls > foo 2> bar > baz"
prop_checkPipeToNowhere14 = verify checkPipeToNowhere "ls > foo &> bar" prop_checkPipeToNowhere14 = verify checkPipeToNowhere "ls > foo &> bar"
prop_checkPipeToNowhere15 = verifyNot checkPipeToNowhere "ls > foo 2> bar |& gre p 'No space left'" prop_checkPipeToNowhere15 = verifyNot checkPipeToNowhere "ls > foo 2> bar |& gre p 'No space left'"
prop_checkPipeToNowhere16 = verifyNot checkPipeToNowhere "echo World | cat << EO F\nhello $(cat)\nEOF\n" prop_checkPipeToNowhere16 = verifyNot checkPipeToNowhere "echo World | cat << EO F\nhello $(cat)\nEOF\n"
prop_checkPipeToNowhere17 = verify checkPipeToNowhere "echo World | cat << 'EOF' \nhello $(cat)\nEOF\n" prop_checkPipeToNowhere17 = verify checkPipeToNowhere "echo World | cat << 'EOF' \nhello $(cat)\nEOF\n"
prop_checkPipeToNowhere18 = verifyNot checkPipeToNowhere "ls 1>&3 3>&1 3>&- | wc -l" prop_checkPipeToNowhere18 = verifyNot checkPipeToNowhere "ls 1>&3 3>&1 3>&- | wc -l"
skipping to change at line 4068 skipping to change at line 4055
return $ (posLine start, posLine end) return $ (posLine start, posLine end)
followsOnLine a b = fromMaybe False $ do followsOnLine a b = fromMaybe False $ do
(_, end) <- lineSpan (getId a) (_, end) <- lineSpan (getId a)
(start, _) <- lineSpan (getId b) (start, _) <- lineSpan (getId b)
return $ end == start return $ end == start
checkUnit :: [Token] -> Writer [TokenComment] () checkUnit :: [Token] -> Writer [TokenComment] ()
checkUnit unit = evalStateT (mapM_ (doAnalysis findCommands) unit) (Map.empt y) checkUnit unit = evalStateT (mapM_ (doAnalysis findCommands) unit) (Map.empt y)
isSourced t =
let
f (T_SourceCommand {}) = True
f _ = False
in
any f $ getPath (parentMap params) t
findCommands :: Token -> StateT (Map.Map String Token) (Writer [TokenComment ]) () findCommands :: Token -> StateT (Map.Map String Token) (Writer [TokenComment ]) ()
findCommands t = case t of findCommands t = case t of
T_SimpleCommand _ _ (cmd:args) -> T_SimpleCommand _ _ (cmd:args) ->
case getUnquotedLiteral cmd of case getUnquotedLiteral cmd of
Just "alias" -> Just "alias" ->
mapM_ addAlias args mapM_ addAlias args
Just name | '/' `notElem` name -> do Just name | '/' `notElem` name -> do
cmd <- gets (Map.lookup name) cmd <- gets (Map.lookup name)
case cmd of case cmd of
Just alias -> Just alias ->
unless (isSourced t || shouldIgnoreCode params 2 262 alias) $ do unless (isSourced params t || shouldIgnoreCode p arams 2262 alias) $ do
warn (getId alias) 2262 "This alias can't be defined and used in the same parsing unit. Use a function instead." warn (getId alias) 2262 "This alias can't be defined and used in the same parsing unit. Use a function instead."
info (getId t) 2263 "Since they're in the sa me parsing unit, this command will not refer to the previously mentioned alias." info (getId t) 2263 "Since they're in the sa me parsing unit, this command will not refer to the previously mentioned alias."
_ -> return () _ -> return ()
_ -> return () _ -> return ()
_ -> return () _ -> return ()
addAlias arg = do addAlias arg = do
let (name, value) = break (== '=') $ getLiteralStringDef "-" arg let (name, value) = break (== '=') $ getLiteralStringDef "-" arg
when (isVariableName name && not (null value)) $ when (isVariableName name && not (null value)) $
modify (Map.insertWith (\new old -> old) name arg) modify (Map.insertWith (\new old -> old) name arg)
isSourced params t =
let
f (T_SourceCommand {}) = True
f _ = False
in
any f $ getPath (parentMap params) t
-- Like groupBy, but compares pairs of adjacent elements, rather than against th e first of the span -- Like groupBy, but compares pairs of adjacent elements, rather than against th e first of the span
prop_groupByLink1 = groupByLink (\a b -> a+1 == b) [1,2,3,2,3,7,8,9] == [[1,2,3] , [2,3], [7,8,9]] prop_groupByLink1 = groupByLink (\a b -> a+1 == b) [1,2,3,2,3,7,8,9] == [[1,2,3] , [2,3], [7,8,9]]
prop_groupByLink2 = groupByLink (==) ([] :: [()]) == [] prop_groupByLink2 = groupByLink (==) ([] :: [()]) == []
groupByLink :: (a -> a -> Bool) -> [a] -> [[a]] groupByLink :: (a -> a -> Bool) -> [a] -> [[a]]
groupByLink f list = groupByLink f list =
case list of case list of
[] -> [] [] -> []
(x:xs) -> foldr c n xs x [] (x:xs) -> foldr c n xs x []
where where
c next rest current span = c next rest current span =
skipping to change at line 4651 skipping to change at line 4638
prop_checkSetESuppressed9 = verifyNotTree checkSetESuppressed "set -e; f(){ :; }; echo || f" prop_checkSetESuppressed9 = verifyNotTree checkSetESuppressed "set -e; f(){ :; }; echo || f"
prop_checkSetESuppressed10 = verifyTree checkSetESuppressed "set -e; f(){ :; }; ! f" prop_checkSetESuppressed10 = verifyTree checkSetESuppressed "set -e; f(){ :; }; ! f"
prop_checkSetESuppressed11 = verifyTree checkSetESuppressed "set -e; f(){ :; }; if f; then :; fi" prop_checkSetESuppressed11 = verifyTree checkSetESuppressed "set -e; f(){ :; }; if f; then :; fi"
prop_checkSetESuppressed12 = verifyTree checkSetESuppressed "set -e; f(){ :; }; if set -e; f; then :; fi" prop_checkSetESuppressed12 = verifyTree checkSetESuppressed "set -e; f(){ :; }; if set -e; f; then :; fi"
prop_checkSetESuppressed13 = verifyTree checkSetESuppressed "set -e; f(){ :; }; while f; do :; done" prop_checkSetESuppressed13 = verifyTree checkSetESuppressed "set -e; f(){ :; }; while f; do :; done"
prop_checkSetESuppressed14 = verifyTree checkSetESuppressed "set -e; f(){ :; }; while set -e; f; do :; done" prop_checkSetESuppressed14 = verifyTree checkSetESuppressed "set -e; f(){ :; }; while set -e; f; do :; done"
prop_checkSetESuppressed15 = verifyTree checkSetESuppressed "set -e; f(){ :; }; until f; do :; done" prop_checkSetESuppressed15 = verifyTree checkSetESuppressed "set -e; f(){ :; }; until f; do :; done"
prop_checkSetESuppressed16 = verifyTree checkSetESuppressed "set -e; f(){ :; }; until set -e; f; do :; done" prop_checkSetESuppressed16 = verifyTree checkSetESuppressed "set -e; f(){ :; }; until set -e; f; do :; done"
prop_checkSetESuppressed17 = verifyNotTree checkSetESuppressed "set -e; f(){ :; }; g(){ :; }; g f" prop_checkSetESuppressed17 = verifyNotTree checkSetESuppressed "set -e; f(){ :; }; g(){ :; }; g f"
prop_checkSetESuppressed18 = verifyNotTree checkSetESuppressed "set -e; shopt -s inherit_errexit; f(){ :; }; x=$(f)" prop_checkSetESuppressed18 = verifyNotTree checkSetESuppressed "set -e; shopt -s inherit_errexit; f(){ :; }; x=$(f)"
prop_checkSetESuppressed19 = verifyNotTree checkSetESuppressed "set -e; set -o p osix; f(){ :; }; x=$(f)"
checkSetESuppressed params t = checkSetESuppressed params t =
if hasSetE params then runNodeAnalysis checkNode params t else [] if hasSetE params then runNodeAnalysis checkNode params t else []
where where
checkNode _ (T_SimpleCommand _ _ (cmd:_)) = when (isFunction cmd) (checkCmd cmd) checkNode _ (T_SimpleCommand _ _ (cmd:_)) = when (isFunction cmd) (checkCmd cmd)
checkNode _ _ = return () checkNode _ _ = return ()
functions_ = functions t functions_ = functions t
isFunction cmd = isJust $ do isFunction cmd = isJust $ do
literalArg <- getUnquotedLiteral cmd literalArg <- getUnquotedLiteral cmd
skipping to change at line 4727 skipping to change at line 4715
prop_checkExtraMaskedReturns26 = verifyNotTree checkExtraMaskedReturns "x=( $(fa lse) )" prop_checkExtraMaskedReturns26 = verifyNotTree checkExtraMaskedReturns "x=( $(fa lse) )"
prop_checkExtraMaskedReturns27 = verifyTree checkExtraMaskedReturns "x=$(fals e) false" prop_checkExtraMaskedReturns27 = verifyTree checkExtraMaskedReturns "x=$(fals e) false"
prop_checkExtraMaskedReturns28 = verifyTree checkExtraMaskedReturns "x=$(fals e) y=$(false)" prop_checkExtraMaskedReturns28 = verifyTree checkExtraMaskedReturns "x=$(fals e) y=$(false)"
prop_checkExtraMaskedReturns29 = verifyNotTree checkExtraMaskedReturns "false < <(set -e)" prop_checkExtraMaskedReturns29 = verifyNotTree checkExtraMaskedReturns "false < <(set -e)"
prop_checkExtraMaskedReturns30 = verifyNotTree checkExtraMaskedReturns "false < <(shopt -s cdspell)" prop_checkExtraMaskedReturns30 = verifyNotTree checkExtraMaskedReturns "false < <(shopt -s cdspell)"
prop_checkExtraMaskedReturns31 = verifyNotTree checkExtraMaskedReturns "false < <(dirname \"${BASH_SOURCE[0]}\")" prop_checkExtraMaskedReturns31 = verifyNotTree checkExtraMaskedReturns "false < <(dirname \"${BASH_SOURCE[0]}\")"
prop_checkExtraMaskedReturns32 = verifyNotTree checkExtraMaskedReturns "false < <(basename \"${BASH_SOURCE[0]}\")" prop_checkExtraMaskedReturns32 = verifyNotTree checkExtraMaskedReturns "false < <(basename \"${BASH_SOURCE[0]}\")"
prop_checkExtraMaskedReturns33 = verifyNotTree checkExtraMaskedReturns "{ false || true; } | true" prop_checkExtraMaskedReturns33 = verifyNotTree checkExtraMaskedReturns "{ false || true; } | true"
prop_checkExtraMaskedReturns34 = verifyNotTree checkExtraMaskedReturns "{ false || :; } | true" prop_checkExtraMaskedReturns34 = verifyNotTree checkExtraMaskedReturns "{ false || :; } | true"
prop_checkExtraMaskedReturns35 = verifyTree checkExtraMaskedReturns "f() { local -r x=$(false); }" prop_checkExtraMaskedReturns35 = verifyTree checkExtraMaskedReturns "f() { local -r x=$(false); }"
prop_checkExtraMaskedReturns36 = verifyNotTree checkExtraMaskedReturns "time fal
se"
prop_checkExtraMaskedReturns37 = verifyNotTree checkExtraMaskedReturns "time $(t
ime false)"
prop_checkExtraMaskedReturns38 = verifyTree checkExtraMaskedReturns "x=$(time ti
me time false) time $(time false)"
checkExtraMaskedReturns params t = runNodeAnalysis findMaskingNodes params t checkExtraMaskedReturns params t =
runNodeAnalysis findMaskingNodes params (removeTransparentCommands t)
where where
findMaskingNodes _ (T_Arithmetic _ list) = findMaskedNodesInList [list] findMaskingNodes _ (T_Arithmetic _ list) = findMaskedNodesInList [list]
findMaskingNodes _ (T_Array _ list) = findMaskedNodesInList $ allButLastSimp leCommands list findMaskingNodes _ (T_Array _ list) = findMaskedNodesInList $ allButLastSimp leCommands list
findMaskingNodes _ (T_Condition _ _ condition) = findMaskedNodesInList [cond ition] findMaskingNodes _ (T_Condition _ _ condition) = findMaskedNodesInList [cond ition]
findMaskingNodes _ (T_DoubleQuoted _ list) = findMaskedNodesInList $ allButL astSimpleCommands list findMaskingNodes _ (T_DoubleQuoted _ list) = findMaskedNodesInList $ allButL astSimpleCommands list
findMaskingNodes _ (T_HereDoc _ _ _ _ list) = findMaskedNodesInList list findMaskingNodes _ (T_HereDoc _ _ _ _ list) = findMaskedNodesInList list
findMaskingNodes _ (T_HereString _ word) = findMaskedNodesInList [word] findMaskingNodes _ (T_HereString _ word) = findMaskedNodesInList [word]
findMaskingNodes _ (T_NormalWord _ parts) = findMaskedNodesInList $ allButLa stSimpleCommands parts findMaskingNodes _ (T_NormalWord _ parts) = findMaskedNodesInList $ allButLa stSimpleCommands parts
findMaskingNodes _ (T_Pipeline _ _ cmds) | not (hasPipefail params) = findMa skedNodesInList $ allButLastSimpleCommands cmds findMaskingNodes _ (T_Pipeline _ _ cmds) | not (hasPipefail params) = findMa skedNodesInList $ allButLastSimpleCommands cmds
findMaskingNodes _ (T_ProcSub _ _ list) = findMaskedNodesInList list findMaskingNodes _ (T_ProcSub _ _ list) = findMaskedNodesInList list
skipping to change at line 4761 skipping to change at line 4753
where where
go t = case t of go t = case t of
T_SimpleCommand {} -> fail "" T_SimpleCommand {} -> fail ""
_ -> return () _ -> return ()
allButLastSimpleCommands cmds = allButLastSimpleCommands cmds =
if null simpleCommands then [] else init simpleCommands if null simpleCommands then [] else init simpleCommands
where where
simpleCommands = filter containsSimpleCommand cmds simpleCommands = filter containsSimpleCommand cmds
removeTransparentCommands t =
doTransform go t
where
go cmd@(T_SimpleCommand id assigns (_:args)) | isTransparentCommand cmd
= T_SimpleCommand id assigns args
go t = t
inform t = info (getId t) 2312 ("Consider invoking this command " inform t = info (getId t) 2312 ("Consider invoking this command "
++ "separately to avoid masking its return value (or use '|| true' " ++ "separately to avoid masking its return value (or use '|| true' "
++ "to ignore).") ++ "to ignore).")
isMaskDeliberate t = hasParent isOrIf t isMaskDeliberate t = hasParent isOrIf t
where where
isOrIf _ (T_OrIf _ _ (T_Pipeline _ _ [T_Redirecting _ _ cmd])) isOrIf _ (T_OrIf _ _ (T_Pipeline _ _ [T_Redirecting _ _ cmd]))
= getCommandBasename cmd `elem` [Just "true", Just ":"] = getCommandBasename cmd `elem` [Just "true", Just ":"]
isOrIf _ _ = False isOrIf _ _ = False
skipping to change at line 4793 skipping to change at line 4792
basename <- getCommandBasename t basename <- getCommandBasename t
return $ basename `elem` [ return $ basename `elem` [
"echo" "echo"
,"basename" ,"basename"
,"dirname" ,"dirname"
,"printf" ,"printf"
,"set" ,"set"
,"shopt" ,"shopt"
] ]
isTransparentCommand t = fromMaybe False $ do
basename <- getCommandBasename t
return $ basename == "time"
parentChildPairs t = go $ parents params t parentChildPairs t = go $ parents params t
where where
go (child:parent:rest) = (parent, child):go (parent:rest) go (child:parent:rest) = (parent, child):go (parent:rest)
go _ = [] go _ = []
hasParent pred t = any (uncurry pred) (parentChildPairs t) hasParent pred t = any (uncurry pred) (parentChildPairs t)
-- hard error on negated command that is not last
prop_checkBatsTestDoesNotUseNegation1 = verify checkBatsTestDoesNotUseNegation "
#!/usr/bin/env/bats\n@test \"name\" { ! true; false; }"
prop_checkBatsTestDoesNotUseNegation2 = verify checkBatsTestDoesNotUseNegation "
#!/usr/bin/env/bats\n@test \"name\" { ! [[ -e test ]]; false; }"
prop_checkBatsTestDoesNotUseNegation3 = verify checkBatsTestDoesNotUseNegation "
#!/usr/bin/env/bats\n@test \"name\" { ! [ -e test ]; false; }"
-- acceptable formats:
-- using run
prop_checkBatsTestDoesNotUseNegation4 = verifyNot checkBatsTestDoesNotUseNegatio
n "#!/usr/bin/env/bats\n@test \"name\" { run ! true; }"
-- using || false
prop_checkBatsTestDoesNotUseNegation5 = verifyNot checkBatsTestDoesNotUseNegatio
n "#!/usr/bin/env/bats\n@test \"name\" { ! [[ -e test ]] || false; }"
prop_checkBatsTestDoesNotUseNegation6 = verifyNot checkBatsTestDoesNotUseNegatio
n "#!/usr/bin/env/bats\n@test \"name\" { ! [ -e test ] || false; }"
-- only style warning when last command
prop_checkBatsTestDoesNotUseNegation7 = verifyCodes checkBatsTestDoesNotUseNegat
ion [2314] "#!/usr/bin/env/bats\n@test \"name\" { ! true; }"
prop_checkBatsTestDoesNotUseNegation8 = verifyCodes checkBatsTestDoesNotUseNegat
ion [2315] "#!/usr/bin/env/bats\n@test \"name\" { ! [[ -e test ]]; }"
prop_checkBatsTestDoesNotUseNegation9 = verifyCodes checkBatsTestDoesNotUseNegat
ion [2315] "#!/usr/bin/env/bats\n@test \"name\" { ! [ -e test ]; }"
checkBatsTestDoesNotUseNegation params t =
case t of
T_BatsTest _ _ (T_BraceGroup _ commands) -> mapM_ (check commands) comma
nds
_ -> return ()
where
check commands t =
case t of
T_Banged id (T_Pipeline _ _ [T_Redirecting _ _ (T_Condition idCondit
ion _ _)]) ->
if t `isLastOf` commands
then style id 2315 "In Bats, ! will not fail the
test if it is not the last command anymore. Fold the `!` into the conditional!"
else err id 2315 "In Bats, ! does not cause a
test failure. Fold the `!` into the conditional!"
T_Banged id cmd -> if t `isLastOf` commands
then styleWithFix id 2314 "In Bats, ! will not f
ail the test if it is not the last command anymore. Use `run ! ` (on Bats >= 1.5
.0) instead."
(fixWith [replaceStart id params
0 "run "])
else errWithFix id 2314 "In Bats, ! does not c
ause a test failure. Use 'run ! ' (on Bats >= 1.5.0) instead."
(fixWith [replaceStart id params
0 "run "])
_ -> return ()
isLastOf t commands =
case commands of
[x] -> x == t
x:rest -> isLastOf t rest
[] -> False
prop_checkCommandIsUnreachable1 = verify checkCommandIsUnreachable "foo; bar; ex
it; baz"
prop_checkCommandIsUnreachable2 = verify checkCommandIsUnreachable "die() { exit
; }; foo; bar; die; baz"
prop_checkCommandIsUnreachable3 = verifyNot checkCommandIsUnreachable "foo; bar
|| exit; baz"
checkCommandIsUnreachable params t =
case t of
T_Pipeline {} -> sequence_ $ do
state <- CF.getIncomingState (cfgAnalysis params) id
guard . not $ CF.stateIsReachable state
guard . not $ isSourced params t
return $ info id 2317 "Command appears to be unreachable. Check usag
e (or ignore if invoked indirectly)."
_ -> return ()
where id = getId t
prop_checkOverwrittenExitCode1 = verify checkOverwrittenExitCode "x; [ $? -eq 1
] || [ $? -eq 2 ]"
prop_checkOverwrittenExitCode2 = verifyNot checkOverwrittenExitCode "x; [ $? -eq
1 ]"
prop_checkOverwrittenExitCode3 = verify checkOverwrittenExitCode "x; echo \"Exit
is $?\"; [ $? -eq 0 ]"
prop_checkOverwrittenExitCode4 = verifyNot checkOverwrittenExitCode "x; [ $? -eq
0 ] && echo Success"
prop_checkOverwrittenExitCode5 = verify checkOverwrittenExitCode "x; if [ $? -eq
0 ]; then var=$?; fi"
prop_checkOverwrittenExitCode6 = verify checkOverwrittenExitCode "x; [ $? -gt 0
] && fail=$?"
prop_checkOverwrittenExitCode7 = verifyNot checkOverwrittenExitCode "[ 1 -eq 2 ]
; status=$?"
prop_checkOverwrittenExitCode8 = verifyNot checkOverwrittenExitCode "[ 1 -eq 2 ]
; exit $?"
checkOverwrittenExitCode params t =
case t of
T_DollarBraced id _ val | getLiteralString val == Just "?" -> check id
_ -> return ()
where
check id = sequence_ $ do
state <- CF.getIncomingState (cfgAnalysis params) id
let exitCodeIds = CF.exitCodes state
guard . not $ S.null exitCodeIds
let idToToken = idMap params
exitCodeTokens <- sequence $ map (\k -> Map.lookup k idToToken) $ S.toLi
st exitCodeIds
return $ do
when (all isCondition exitCodeTokens && not (usedUnconditionally t e
xitCodeIds)) $
warn id 2319 "This $? refers to a condition, not a command. Assi
gn to a variable to avoid it being overwritten."
when (all isPrinting exitCodeTokens) $
warn id 2320 "This $? refers to echo/printf, not a previous comm
and. Assign to variable to avoid it being overwritten."
isCondition t =
case t of
T_Condition {} -> True
T_SimpleCommand {} -> getCommandName t == Just "test"
_ -> False
-- If we don't do anything based on the condition, assume we wanted the cond
ition itself
-- This helps differentiate `x; [ $? -gt 0 ] && exit $?` vs `[ cond ]; exit
$?`
usedUnconditionally t testIds =
all (\c -> CF.doesPostDominate (cfgAnalysis params) (getId t) c) testIds
isPrinting t =
case getCommandBasename t of
Just "echo" -> True
Just "printf" -> True
_ -> False
prop_checkUnnecessaryArithmeticExpansionIndex1 = verify checkUnnecessaryArithmet
icExpansionIndex "a[$((1+1))]=n"
prop_checkUnnecessaryArithmeticExpansionIndex2 = verifyNot checkUnnecessaryArith
meticExpansionIndex "a[1+1]=n"
prop_checkUnnecessaryArithmeticExpansionIndex3 = verifyNot checkUnnecessaryArith
meticExpansionIndex "a[$(echo $((1+1)))]=n"
prop_checkUnnecessaryArithmeticExpansionIndex4 = verifyNot checkUnnecessaryArith
meticExpansionIndex "declare -A a; a[$((1+1))]=val"
checkUnnecessaryArithmeticExpansionIndex params t =
case t of
T_Assignment _ mode var [TA_Sequence _ [ TA_Expansion _ [expansion@(T_Do
llarArithmetic id _)]]] val ->
styleWithFix id 2321 "Array indices are already arithmetic contexts.
Prefer removing the $(( and ))." $ fix id
_ -> return ()
where
fix id =
fixWith [
replaceStart id params 3 "", -- Remove "$(("
replaceEnd id params 2 "" -- Remove "))"
]
prop_checkUnnecessaryParens1 = verify checkUnnecessaryParens "echo $(( ((1+1)) )
)"
prop_checkUnnecessaryParens2 = verify checkUnnecessaryParens "x[((1+1))+1]=1"
prop_checkUnnecessaryParens3 = verify checkUnnecessaryParens "x[(1+1)]=1"
prop_checkUnnecessaryParens4 = verify checkUnnecessaryParens "$(( (x) ))"
prop_checkUnnecessaryParens5 = verify checkUnnecessaryParens "(( (x) ))"
prop_checkUnnecessaryParens6 = verifyNot checkUnnecessaryParens "x[(1+1)+1]=1"
prop_checkUnnecessaryParens7 = verifyNot checkUnnecessaryParens "(( (1*1)+1 ))"
prop_checkUnnecessaryParens8 = verifyNot checkUnnecessaryParens "(( (1)+1 ))"
checkUnnecessaryParens params t =
case t of
T_DollarArithmetic _ t -> checkLeading "$(( (x) )) is the same as $(( x
))" t
T_ForArithmetic _ x y z _ -> mapM_ (checkLeading "for (((x); (y); (z)))
is the same as for ((x; y; z))") [x,y,z]
T_Assignment _ _ _ [t] _ -> checkLeading "a[(x)] is the same as a[x]" t
T_Arithmetic _ t -> checkLeading "(( (x) )) is the same as (( x ))" t
TA_Parentesis _ (TA_Sequence _ [ TA_Parentesis id _ ]) ->
styleWithFix id 2322 "In arithmetic contexts, ((x)) is the same as (
x). Prefer only one layer of parentheses." $ fix id
_ -> return ()
where
checkLeading str t =
case t of
TA_Sequence _ [TA_Parentesis id _ ] -> styleWithFix id 2323 (str ++
". Prefer not wrapping in additional parentheses.") $ fix id
_ -> return ()
fix id =
fixWith [
replaceStart id params 1 "", -- Remove "("
replaceEnd id params 1 "" -- Remove ")"
]
return [] return []
runTests = $( [| $(forAllProperties) (quickCheckWithResult (stdArgs { maxSucces s = 1 }) ) |]) runTests = $( [| $(forAllProperties) (quickCheckWithResult (stdArgs { maxSucces s = 1 }) ) |])
 End of changes. 71 change blocks. 
475 lines changed or deleted 706 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)