select loops and bases in arithmetic contexts
This commit is contained in:
parent
059ef63b44
commit
b517ad9e19
|
@ -23,7 +23,7 @@ import qualified Text.Regex as Re
|
||||||
|
|
||||||
data Id = Id Int deriving (Show, Eq, Ord)
|
data Id = Id Int deriving (Show, Eq, Ord)
|
||||||
|
|
||||||
data Token = T_AND_IF Id | T_OR_IF Id | T_DSEMI Id | T_Semi Id | T_DLESS Id | T_DGREAT Id | T_LESSAND Id | T_GREATAND Id | T_LESSGREAT Id | T_DLESSDASH Id | T_CLOBBER Id | T_If Id | T_Then Id | T_Else Id | T_Elif Id | T_Fi Id | T_Do Id | T_Done Id | T_Case Id | T_Esac Id | T_While Id | T_Until Id | T_For Id | T_Lbrace Id | T_Rbrace Id | T_Lparen Id | T_Rparen Id | T_Bang Id | T_In Id | T_NEWLINE Id | T_EOF Id | T_Less Id | T_Greater Id | T_SingleQuoted Id String | T_Literal Id String | T_NormalWord Id [Token] | T_DoubleQuoted Id [Token] | T_DollarExpansion Id [Token] | T_DollarBraced Id Token | T_DollarArithmetic Id Token | T_BraceExpansion Id String | T_IoFile Id Token Token | T_HereDoc Id Bool Bool String | T_HereString Id Token | T_FdRedirect Id String Token | T_Assignment Id String Token | T_Array Id [Token] | T_Redirecting Id [Token] Token | T_SimpleCommand Id [Token] [Token] | T_Pipeline Id [Token] | T_Banged Id Token | T_AndIf Id (Token) (Token) | T_OrIf Id (Token) (Token) | T_Backgrounded Id Token | T_IfExpression Id [([Token],[Token])] [Token] | T_Subshell Id [Token] | T_BraceGroup Id [Token] | T_WhileExpression Id [Token] [Token] | T_UntilExpression Id [Token] [Token] | T_ForIn Id String [Token] [Token] | T_CaseExpression Id Token [([Token],[Token])] | T_Function Id String Token | T_Arithmetic Id Token | T_Script Id String [Token] | T_Condition Id ConditionType Token | T_Extglob Id String [Token] | TC_And Id ConditionType String Token Token | TC_Or Id ConditionType String Token Token | TC_Group Id ConditionType Token | TC_Binary Id ConditionType String Token Token | TC_Unary Id ConditionType String Token | TC_Noary Id ConditionType Token | TA_Binary Id String Token Token | TA_Unary Id String Token | TA_Sequence Id [Token] | TA_Variable Id String | TA_Trinary Id Token Token Token | TA_Expansion Id Token | TA_Literal Id String | T_Backticked Id String | T_ProcSub Id String [Token] | T_Glob Id String | T_ForArithmetic Id Token Token Token [Token] | T_DollarSingleQuoted Id String | T_DollarDoubleQuoted Id [Token]
|
data Token = T_AND_IF Id | T_OR_IF Id | T_DSEMI Id | T_Semi Id | T_DLESS Id | T_DGREAT Id | T_LESSAND Id | T_GREATAND Id | T_LESSGREAT Id | T_DLESSDASH Id | T_CLOBBER Id | T_If Id | T_Then Id | T_Else Id | T_Elif Id | T_Fi Id | T_Do Id | T_Done Id | T_Case Id | T_Esac Id | T_While Id | T_Until Id | T_For Id | T_Select Id | T_Lbrace Id | T_Rbrace Id | T_Lparen Id | T_Rparen Id | T_Bang Id | T_In Id | T_NEWLINE Id | T_EOF Id | T_Less Id | T_Greater Id | T_SingleQuoted Id String | T_Literal Id String | T_NormalWord Id [Token] | T_DoubleQuoted Id [Token] | T_DollarExpansion Id [Token] | T_DollarBraced Id Token | T_DollarArithmetic Id Token | T_BraceExpansion Id String | T_IoFile Id Token Token | T_HereDoc Id Bool Bool String | T_HereString Id Token | T_FdRedirect Id String Token | T_Assignment Id String Token | T_Array Id [Token] | T_Redirecting Id [Token] Token | T_SimpleCommand Id [Token] [Token] | T_Pipeline Id [Token] | T_Banged Id Token | T_AndIf Id (Token) (Token) | T_OrIf Id (Token) (Token) | T_Backgrounded Id Token | T_IfExpression Id [([Token],[Token])] [Token] | T_Subshell Id [Token] | T_BraceGroup Id [Token] | T_WhileExpression Id [Token] [Token] | T_UntilExpression Id [Token] [Token] | T_ForIn Id String [Token] [Token] | T_SelectIn Id String [Token] [Token] | T_CaseExpression Id Token [([Token],[Token])] | T_Function Id String Token | T_Arithmetic Id Token | T_Script Id String [Token] | T_Condition Id ConditionType Token | T_Extglob Id String [Token] | TC_And Id ConditionType String Token Token | TC_Or Id ConditionType String Token Token | TC_Group Id ConditionType Token | TC_Binary Id ConditionType String Token Token | TC_Unary Id ConditionType String Token | TC_Noary Id ConditionType Token | TA_Binary Id String Token Token | TA_Unary Id String Token | TA_Sequence Id [Token] | TA_Variable Id String | TA_Trinary Id Token Token Token | TA_Expansion Id Token | TA_Literal Id String | T_Backticked Id String | T_ProcSub Id String [Token] | T_Glob Id String | T_ForArithmetic Id Token Token Token [Token] | T_DollarSingleQuoted Id String | T_DollarDoubleQuoted Id [Token] | TA_Base Id String Token
|
||||||
|
|
||||||
deriving (Show)
|
deriving (Show)
|
||||||
|
|
||||||
|
@ -96,6 +96,7 @@ analyze f g i t =
|
||||||
delve (T_WhileExpression id c l) = dll c l $ T_WhileExpression id
|
delve (T_WhileExpression id c l) = dll c l $ T_WhileExpression id
|
||||||
delve (T_UntilExpression id c l) = dll c l $ T_UntilExpression id
|
delve (T_UntilExpression id c l) = dll c l $ T_UntilExpression id
|
||||||
delve (T_ForIn id v w l) = dll w l $ T_ForIn id v
|
delve (T_ForIn id v w l) = dll w l $ T_ForIn id v
|
||||||
|
delve (T_SelectIn id v w l) = dll w l $ T_SelectIn id v
|
||||||
delve (T_CaseExpression id word cases) = do
|
delve (T_CaseExpression id word cases) = do
|
||||||
newWord <- round word
|
newWord <- round word
|
||||||
newCases <- mapM (\(c, t) -> do
|
newCases <- mapM (\(c, t) -> do
|
||||||
|
@ -134,6 +135,7 @@ analyze f g i t =
|
||||||
c <- round t3
|
c <- round t3
|
||||||
return $ TA_Trinary id a b c
|
return $ TA_Trinary id a b c
|
||||||
delve (TA_Expansion id t) = d1 t $ TA_Expansion id
|
delve (TA_Expansion id t) = d1 t $ TA_Expansion id
|
||||||
|
delve (TA_Base id b t) = d1 t $ TA_Base id b
|
||||||
delve t = return t
|
delve t = return t
|
||||||
|
|
||||||
getId t = case t of
|
getId t = case t of
|
||||||
|
@ -160,6 +162,7 @@ getId t = case t of
|
||||||
T_While id -> id
|
T_While id -> id
|
||||||
T_Until id -> id
|
T_Until id -> id
|
||||||
T_For id -> id
|
T_For id -> id
|
||||||
|
T_Select id -> id
|
||||||
T_Lbrace id -> id
|
T_Lbrace id -> id
|
||||||
T_Rbrace id -> id
|
T_Rbrace id -> id
|
||||||
T_Lparen id -> id
|
T_Lparen id -> id
|
||||||
|
@ -197,6 +200,7 @@ getId t = case t of
|
||||||
T_WhileExpression id _ _ -> id
|
T_WhileExpression id _ _ -> id
|
||||||
T_UntilExpression id _ _ -> id
|
T_UntilExpression id _ _ -> id
|
||||||
T_ForIn id _ _ _ -> id
|
T_ForIn id _ _ _ -> id
|
||||||
|
T_SelectIn id _ _ _ -> id
|
||||||
T_CaseExpression id _ _ -> id
|
T_CaseExpression id _ _ -> id
|
||||||
T_Function id _ _ -> id
|
T_Function id _ _ -> id
|
||||||
T_Arithmetic id _ -> id
|
T_Arithmetic id _ -> id
|
||||||
|
@ -217,6 +221,7 @@ getId t = case t of
|
||||||
TA_Trinary id _ _ _ -> id
|
TA_Trinary id _ _ _ -> id
|
||||||
TA_Expansion id _ -> id
|
TA_Expansion id _ -> id
|
||||||
TA_Literal id _ -> id
|
TA_Literal id _ -> id
|
||||||
|
TA_Base id _ _ -> id
|
||||||
T_ProcSub id _ _ -> id
|
T_ProcSub id _ _ -> id
|
||||||
T_Glob id _ -> id
|
T_Glob id _ -> id
|
||||||
T_ForArithmetic id _ _ _ _ -> id
|
T_ForArithmetic id _ _ _ _ -> id
|
||||||
|
|
|
@ -38,7 +38,6 @@ checks = concat [
|
||||||
|
|
||||||
runAllAnalytics = checkList checks
|
runAllAnalytics = checkList checks
|
||||||
checkList l t m = foldl (\x f -> f t x) m l
|
checkList l t m = foldl (\x f -> f t x) m l
|
||||||
checkList l t m = foldl (\x f -> f t x) m l
|
|
||||||
|
|
||||||
runBasicAnalysis f t m = snd $ runState (doAnalysis f t) m
|
runBasicAnalysis f t m = snd $ runState (doAnalysis f t) m
|
||||||
basicChecks = [
|
basicChecks = [
|
||||||
|
@ -58,6 +57,7 @@ basicChecks = [
|
||||||
,checkForDecimals
|
,checkForDecimals
|
||||||
,checkDivBeforeMult
|
,checkDivBeforeMult
|
||||||
,checkArithmeticDeref
|
,checkArithmeticDeref
|
||||||
|
,checkArithmeticBadOctal
|
||||||
,checkComparisonAgainstGlob
|
,checkComparisonAgainstGlob
|
||||||
,checkPrintfVar
|
,checkPrintfVar
|
||||||
,checkCommarrays
|
,checkCommarrays
|
||||||
|
@ -211,7 +211,7 @@ checkPipePitfalls (T_Pipeline id commands) = do
|
||||||
for [["grep"], ["sed"]] $ \id -> style id "You don't need grep | sed, sed can filter lines by itself."
|
for [["grep"], ["sed"]] $ \id -> style id "You don't need grep | sed, sed can filter lines by itself."
|
||||||
for [["grep"], ["awk"]] $ \id -> style id "You don't need grep | awk, awk can filter lines by itself."
|
for [["grep"], ["awk"]] $ \id -> style id "You don't need grep | awk, awk can filter lines by itself."
|
||||||
for [["ls"], ["?"]] $ \id -> warn id "Don't parse ls output; it mangles filenames."
|
for [["ls"], ["?"]] $ \id -> warn id "Don't parse ls output; it mangles filenames."
|
||||||
for [["ls"], ["grep"]] $ \id -> warn id "Don't use ls | grep. Use a for loop with a condition in it."
|
for [["ls"], ["grep"]] $ \id -> warn id "Don't use ls | grep. Use a glob or a for loop with a condition."
|
||||||
for [["ls"], ["xargs"]] $ \id -> warn id "Don't use ls | xargs. Use find -exec .. +"
|
for [["ls"], ["xargs"]] $ \id -> warn id "Don't use ls | xargs. Use find -exec .. +"
|
||||||
for [["find"], ["xargs"]]$ \id -> warn id "Don't use find | xargs cmd. find -exec cmd {} + handles whitespace."
|
for [["find"], ["xargs"]]$ \id -> warn id "Don't use find | xargs cmd. find -exec cmd {} + handles whitespace."
|
||||||
for [["?"], ["echo"]] $ \id -> info id "echo doesn't read from stdin, are you sure you should be piping to it?"
|
for [["?"], ["echo"]] $ \id -> info id "echo doesn't read from stdin, are you sure you should be piping to it?"
|
||||||
|
@ -263,6 +263,7 @@ checkUndeclaredBash t@(T_Script id sb _) m =
|
||||||
bashism (T_DollarDoubleQuoted id _) = warnMsg id "$\"..\""
|
bashism (T_DollarDoubleQuoted id _) = warnMsg id "$\"..\""
|
||||||
bashism (T_ForArithmetic id _ _ _ _) = warnMsg id "arithmetic for loops"
|
bashism (T_ForArithmetic id _ _ _ _) = warnMsg id "arithmetic for loops"
|
||||||
bashism (T_Arithmetic id _) = warnMsg id "((..))"
|
bashism (T_Arithmetic id _) = warnMsg id "((..))"
|
||||||
|
bashism (T_SelectIn id _ _ _) = warnMsg id "select loops"
|
||||||
bashism _ = return()
|
bashism _ = return()
|
||||||
|
|
||||||
prop_checkForInQuoted = verify checkForInQuoted "for f in \"$(ls)\"; do echo foo; done"
|
prop_checkForInQuoted = verify checkForInQuoted "for f in \"$(ls)\"; do echo foo; done"
|
||||||
|
@ -509,6 +510,12 @@ checkArithmeticDeref (TA_Expansion _ (T_DollarBraced id l)) | not . excepting $
|
||||||
excepting s = (any (`elem` "/.:#%?*@") s) || (isDigit $ head s)
|
excepting s = (any (`elem` "/.:#%?*@") s) || (isDigit $ head s)
|
||||||
checkArithmeticDeref _ = return ()
|
checkArithmeticDeref _ = return ()
|
||||||
|
|
||||||
|
prop_checkArithmeticBadOctal1 = verify checkArithmeticBadOctal "(( 0192 ))"
|
||||||
|
prop_checkArithmeticBadOctal2 = verifyNot checkArithmeticBadOctal "(( 0x192 ))"
|
||||||
|
prop_checkArithmeticBadOctal3 = verifyNot checkArithmeticBadOctal "(( 1 ^ 0777 ))"
|
||||||
|
checkArithmeticBadOctal (TA_Base id "0" (TA_Literal _ str)) | '9' `elem` str || '8' `elem` str =
|
||||||
|
err id $ "Numbers with leading 0 are considered octal."
|
||||||
|
checkArithmeticBadOctal _ = return ()
|
||||||
|
|
||||||
prop_checkComparisonAgainstGlob = verify checkComparisonAgainstGlob "[[ $cow == $bar ]]"
|
prop_checkComparisonAgainstGlob = verify checkComparisonAgainstGlob "[[ $cow == $bar ]]"
|
||||||
prop_checkComparisonAgainstGlob2 = verifyNot checkComparisonAgainstGlob "[[ $cow == \"$bar\" ]]"
|
prop_checkComparisonAgainstGlob2 = verifyNot checkComparisonAgainstGlob "[[ $cow == \"$bar\" ]]"
|
||||||
|
@ -782,7 +789,8 @@ getModifiedVariablesWithType spacefulF t =
|
||||||
else []
|
else []
|
||||||
|
|
||||||
--Points to 'for' rather than variable
|
--Points to 'for' rather than variable
|
||||||
T_ForIn id str words _ -> [(id, str, if any (isSpaceful spacefulF) words then Spaceful else Spaceless)]
|
T_ForIn id str words _ -> [(id, str, if any (isSpaceful spacefulF) words || null words then Spaceful else Spaceless)]
|
||||||
|
T_SelectIn id str words _ -> [(id, str, if any (isSpaceful spacefulF) words || null words then Spaceful else Spaceless)]
|
||||||
_ -> []
|
_ -> []
|
||||||
|
|
||||||
isSpaceful :: (String -> Bool) -> Token -> Bool
|
isSpaceful :: (String -> Bool) -> Token -> Bool
|
||||||
|
|
|
@ -343,6 +343,7 @@ prop_a7 = isOk readArithmeticContents "3*2**10"
|
||||||
prop_a8 = isOk readArithmeticContents "3"
|
prop_a8 = isOk readArithmeticContents "3"
|
||||||
prop_a9 = isOk readArithmeticContents "a^!-b"
|
prop_a9 = isOk readArithmeticContents "a^!-b"
|
||||||
prop_aA = isOk readArithmeticContents "! $?"
|
prop_aA = isOk readArithmeticContents "! $?"
|
||||||
|
prop_aB = isOk readArithmeticContents "10#08 * 16#f"
|
||||||
readArithmeticContents =
|
readArithmeticContents =
|
||||||
readSequence
|
readSequence
|
||||||
where
|
where
|
||||||
|
@ -381,9 +382,35 @@ readArithmeticContents =
|
||||||
readNumber = do
|
readNumber = do
|
||||||
id <- getNextId
|
id <- getNextId
|
||||||
num <- many1 $ oneOf "0123456789."
|
num <- many1 $ oneOf "0123456789."
|
||||||
return $ TA_Literal id num
|
return $ TA_Literal id (num)
|
||||||
|
|
||||||
readArithTerm = readGroup <|> readExpansion <|> readNumber <|> readVar
|
readBased = getArbitrary <|> getHex <|> getOct
|
||||||
|
where
|
||||||
|
getThing prefix litchars = try $ do
|
||||||
|
id <- getNextId
|
||||||
|
x <- prefix
|
||||||
|
t <- readExpansion <|> (do
|
||||||
|
i <- getNextId
|
||||||
|
stuff <- many1 litchars
|
||||||
|
return $ TA_Literal i stuff)
|
||||||
|
return $ TA_Base id x t
|
||||||
|
|
||||||
|
getArbitrary = getThing arbitrary variableChars
|
||||||
|
getHex = getThing hex hexDigit
|
||||||
|
getOct = getThing oct digit
|
||||||
|
|
||||||
|
arbitrary = try $ do
|
||||||
|
b <- many1 digit
|
||||||
|
s <- char '#'
|
||||||
|
return (b ++ [s])
|
||||||
|
hex = try $ do
|
||||||
|
z <- char '0'
|
||||||
|
x <- oneOf "xX"
|
||||||
|
return (z:x:[])
|
||||||
|
oct = string "0"
|
||||||
|
|
||||||
|
readArithTerm = readBased <|> readArithTermUnit
|
||||||
|
readArithTermUnit = readGroup <|> readExpansion <|> readNumber <|> readVar
|
||||||
|
|
||||||
readSequence = do
|
readSequence = do
|
||||||
spacing
|
spacing
|
||||||
|
@ -1138,6 +1165,22 @@ readForClause = called "for loop" $ do
|
||||||
values <- readInClause <|> (readSequentialSep >> return [])
|
values <- readInClause <|> (readSequentialSep >> return [])
|
||||||
return $ \id group -> (return $ T_ForIn id name values group)
|
return $ \id group -> (return $ T_ForIn id name values group)
|
||||||
|
|
||||||
|
prop_readSelectClause1 = isOk readSelectClause "select foo in *; do echo $foo; done"
|
||||||
|
prop_readSelectClause2 = isOk readSelectClause "select foo; do echo $foo; done"
|
||||||
|
readSelectClause = called "select loop" $ do
|
||||||
|
pos <- getPosition
|
||||||
|
(T_Select id) <- g_Select
|
||||||
|
spacing
|
||||||
|
typ <- readRegular
|
||||||
|
group <- readDoGroup pos
|
||||||
|
typ id group
|
||||||
|
where
|
||||||
|
readRegular = do
|
||||||
|
name <- readVariableName
|
||||||
|
spacing
|
||||||
|
values <- readInClause <|> (readSequentialSep >> return [])
|
||||||
|
return $ \id group -> (return $ T_SelectIn id name values group)
|
||||||
|
|
||||||
readInClause = do
|
readInClause = do
|
||||||
g_In
|
g_In
|
||||||
things <- (readCmdWord) `reluctantlyTill`
|
things <- (readCmdWord) `reluctantlyTill`
|
||||||
|
@ -1232,7 +1275,7 @@ readPattern = (readNormalWord `thenSkip` spacing) `sepBy1` (char '|' `thenSkip`
|
||||||
|
|
||||||
readCompoundCommand = do
|
readCompoundCommand = do
|
||||||
id <- getNextId
|
id <- getNextId
|
||||||
cmd <- choice [ readBraceGroup, readArithmeticExpression, readSubshell, readCondition, readWhileClause, readUntilClause, readIfClause, readForClause, readCaseClause, readFunctionDefinition]
|
cmd <- choice [ readBraceGroup, readArithmeticExpression, readSubshell, readCondition, readWhileClause, readUntilClause, readIfClause, readForClause, readSelectClause, readCaseClause, readFunctionDefinition]
|
||||||
spacing
|
spacing
|
||||||
redirs <- many readIoRedirect
|
redirs <- many readIoRedirect
|
||||||
when (not . null $ redirs) $ optional $ do
|
when (not . null $ redirs) $ optional $ do
|
||||||
|
@ -1334,6 +1377,7 @@ g_Esac = tryWordToken "esac" T_Esac
|
||||||
g_While = tryWordToken "while" T_While
|
g_While = tryWordToken "while" T_While
|
||||||
g_Until = tryWordToken "until" T_Until
|
g_Until = tryWordToken "until" T_Until
|
||||||
g_For = tryWordToken "for" T_For
|
g_For = tryWordToken "for" T_For
|
||||||
|
g_Select = tryWordToken "select" T_Select
|
||||||
g_In = tryWordToken "in" T_In
|
g_In = tryWordToken "in" T_In
|
||||||
g_Lbrace = tryWordToken "{" T_Lbrace
|
g_Lbrace = tryWordToken "{" T_Lbrace
|
||||||
g_Rbrace = tryWordToken "}" T_Rbrace
|
g_Rbrace = tryWordToken "}" T_Rbrace
|
||||||
|
|
Loading…
Reference in New Issue