mirror of
				https://github.com/koalaman/shellcheck.git
				synced 2025-10-31 14:39:20 +08:00 
			
		
		
		
	select loops and bases in arithmetic contexts
This commit is contained in:
		ShellCheck
| @@ -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 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user