Warn about 'i=i+1' and 'i=i + 1'
This commit is contained in:
parent
95ebe1cd07
commit
d1990e3396
|
@ -24,6 +24,7 @@ import Control.Monad
|
||||||
import Control.Monad.State
|
import Control.Monad.State
|
||||||
import qualified Data.Map as Map
|
import qualified Data.Map as Map
|
||||||
import Data.Char
|
import Data.Char
|
||||||
|
import Data.Functor
|
||||||
import Data.List
|
import Data.List
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Debug.Trace
|
import Debug.Trace
|
||||||
|
@ -42,6 +43,7 @@ genericChecks = [
|
||||||
,checkShebang
|
,checkShebang
|
||||||
,checkFunctionsUsedExternally
|
,checkFunctionsUsedExternally
|
||||||
,checkUnusedAssignments
|
,checkUnusedAssignments
|
||||||
|
,checkWrongArithmeticAssignment
|
||||||
]
|
]
|
||||||
|
|
||||||
checksFor Sh = map runBasicAnalysis [
|
checksFor Sh = map runBasicAnalysis [
|
||||||
|
@ -135,6 +137,7 @@ basicChecks = [
|
||||||
,checkSshCommandString
|
,checkSshCommandString
|
||||||
,checkGlobsAsOptions
|
,checkGlobsAsOptions
|
||||||
,checkWhileReadPitfalls
|
,checkWhileReadPitfalls
|
||||||
|
,checkArithmeticOpCommand
|
||||||
]
|
]
|
||||||
treeChecks = [
|
treeChecks = [
|
||||||
checkUnquotedExpansions
|
checkUnquotedExpansions
|
||||||
|
@ -232,6 +235,11 @@ deadSimple (T_Redirecting _ _ foo) = deadSimple foo
|
||||||
deadSimple (T_DollarSingleQuoted _ s) = [s]
|
deadSimple (T_DollarSingleQuoted _ s) = [s]
|
||||||
deadSimple _ = []
|
deadSimple _ = []
|
||||||
|
|
||||||
|
(!!!) list i =
|
||||||
|
case drop i list of
|
||||||
|
[] -> Nothing
|
||||||
|
(r:_) -> Just r
|
||||||
|
|
||||||
verify f s = checkBasic f s == Just True
|
verify f s = checkBasic f s == Just True
|
||||||
verifyNot f s = checkBasic f s == Just False
|
verifyNot f s = checkBasic f s == Just False
|
||||||
verifyFull f s = checkFull f s == Just True
|
verifyFull f s = checkFull f s == Just True
|
||||||
|
@ -298,6 +306,49 @@ checkAssignAteCommand (T_SimpleCommand id ((T_Assignment _ _ _ _ assignmentTerm)
|
||||||
isCommonCommand _ = False
|
isCommonCommand _ = False
|
||||||
checkAssignAteCommand _ = return ()
|
checkAssignAteCommand _ = return ()
|
||||||
|
|
||||||
|
prop_checkArithmeticOpCommand1 = verify checkArithmeticOpCommand "i=i + 1"
|
||||||
|
prop_checkArithmeticOpCommand2 = verify checkArithmeticOpCommand "foo=bar * 2"
|
||||||
|
prop_checkArithmeticOpCommand3 = verifyNot checkArithmeticOpCommand "foo + opts"
|
||||||
|
checkArithmeticOpCommand (T_SimpleCommand id ((T_Assignment _ _ _ _ _):[]) (firstWord:_)) =
|
||||||
|
fromMaybe (return ()) $ check <$> getGlobOrLiteralString firstWord
|
||||||
|
where
|
||||||
|
check op =
|
||||||
|
when (op `elem` ["+", "-", "*", "/"]) $
|
||||||
|
warn (getId firstWord) 2099 $
|
||||||
|
"Use $((..)) for arithmetics, e.g. i=$((i " ++ op ++ " 2))"
|
||||||
|
checkArithmeticOpCommand _ = return ()
|
||||||
|
|
||||||
|
prop_checkWrongArit = verifyFull checkWrongArithmeticAssignment "i=i+1"
|
||||||
|
prop_checkWrongArit2 = verifyFull checkWrongArithmeticAssignment "n=2; i=n*2"
|
||||||
|
checkWrongArithmeticAssignment t = runBasicAnalysis f t
|
||||||
|
where
|
||||||
|
regex = mkRegex "^([_a-zA-Z][_a-zA-Z0-9]*)([+*-]).+$"
|
||||||
|
flow = getVariableFlow t
|
||||||
|
references = foldl (flip ($)) Map.empty (map insertRef flow)
|
||||||
|
insertRef (Assignment (_, _, name, _)) =
|
||||||
|
Map.insert name ()
|
||||||
|
insertRef _ = id
|
||||||
|
|
||||||
|
getNormalString (T_NormalWord _ words) = do
|
||||||
|
parts <- foldl (liftM2 (\x y -> x ++ [y])) (Just []) $ map getLiterals words
|
||||||
|
return $ concat parts
|
||||||
|
getNormalString _ = Nothing
|
||||||
|
|
||||||
|
getLiterals (T_Literal _ s) = return s
|
||||||
|
getLiterals (T_Glob _ s) = return s
|
||||||
|
getLiterals _ = Nothing
|
||||||
|
|
||||||
|
f (T_SimpleCommand id ((T_Assignment _ _ _ _ val):[]) []) =
|
||||||
|
fromMaybe (return ()) $ do
|
||||||
|
str <- getNormalString val
|
||||||
|
match <- matchRegex regex str
|
||||||
|
var <- match !!! 0
|
||||||
|
op <- match !!! 1
|
||||||
|
Map.lookup var references
|
||||||
|
return $ do
|
||||||
|
warn (getId val) 2100 $
|
||||||
|
"Use $((..)) for arithmetics, e.g. i=$((i " ++ op ++ " 2))"
|
||||||
|
f _ = return ()
|
||||||
|
|
||||||
prop_checkUuoc1 = verify checkUuoc "cat foo | grep bar"
|
prop_checkUuoc1 = verify checkUuoc "cat foo | grep bar"
|
||||||
prop_checkUuoc2 = verifyNot checkUuoc "cat * | grep bar"
|
prop_checkUuoc2 = verifyNot checkUuoc "cat * | grep bar"
|
||||||
|
@ -972,7 +1023,14 @@ checkUnqualifiedCommand str f t@(T_SimpleCommand id _ (cmd:rest)) =
|
||||||
if t `isUnqualifiedCommand` str then f rest else return ()
|
if t `isUnqualifiedCommand` str then f rest else return ()
|
||||||
checkUnqualifiedCommand _ _ _ = return ()
|
checkUnqualifiedCommand _ _ _ = return ()
|
||||||
|
|
||||||
getLiteralString t = g t
|
getLiteralString = getLiteralStringExt (const Nothing)
|
||||||
|
|
||||||
|
getGlobOrLiteralString = getLiteralStringExt f
|
||||||
|
where
|
||||||
|
f (T_Glob _ str) = return str
|
||||||
|
f _ = Nothing
|
||||||
|
|
||||||
|
getLiteralStringExt more t = g t
|
||||||
where
|
where
|
||||||
allInList l = let foo = map g l in if all isJust foo then return $ concat (catMaybes foo) else Nothing
|
allInList l = let foo = map g l in if all isJust foo then return $ concat (catMaybes foo) else Nothing
|
||||||
g s@(T_DoubleQuoted _ l) = allInList l
|
g s@(T_DoubleQuoted _ l) = allInList l
|
||||||
|
@ -980,7 +1038,7 @@ getLiteralString t = g t
|
||||||
g s@(T_NormalWord _ l) = allInList l
|
g s@(T_NormalWord _ l) = allInList l
|
||||||
g (T_SingleQuoted _ s) = return s
|
g (T_SingleQuoted _ s) = return s
|
||||||
g (T_Literal _ s) = return s
|
g (T_Literal _ s) = return s
|
||||||
g _ = Nothing
|
g x = more x
|
||||||
|
|
||||||
isLiteral t = isJust $ getLiteralString t
|
isLiteral t = isJust $ getLiteralString t
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue