SC2229: Warn about 'read $var'
This commit is contained in:
parent
72044a79c6
commit
b7a8b090d2
|
@ -1,5 +1,6 @@
|
||||||
## Latest - ???
|
## Latest - ???
|
||||||
### Added
|
### Added
|
||||||
|
- SC2229: Warn about 'read $var'
|
||||||
- SC2227: Warn about redirections in the middle of 'find' commands
|
- SC2227: Warn about redirections in the middle of 'find' commands
|
||||||
- SC2224,SC2225,SC2226: Warn when using mv/cp/ln without a destination
|
- SC2224,SC2225,SC2226: Warn when using mv/cp/ln without a destination
|
||||||
- SC2223: Quote warning specific to `: ${var=value}`
|
- SC2223: Quote warning specific to `: ${var=value}`
|
||||||
|
|
|
@ -837,7 +837,36 @@ isQuotedAlternativeReference t =
|
||||||
where
|
where
|
||||||
re = mkRegex "(^|\\]):?\\+"
|
re = mkRegex "(^|\\]):?\\+"
|
||||||
|
|
||||||
|
-- getOpts "erd:u:" will parse a SimpleCommand like
|
||||||
|
-- read -re -d : -u 3 bar
|
||||||
|
-- into
|
||||||
|
-- Just [("r", -re), ("e", -re), ("d", :), ("u", 3), ("", bar)]
|
||||||
|
-- where flags with arguments map to arguments, while others map to themselves.
|
||||||
|
-- Any unrecognized flag will result in Nothing.
|
||||||
|
getOpts :: String -> Token -> Maybe [(String, Token)]
|
||||||
|
getOpts string cmd = process flags
|
||||||
|
where
|
||||||
|
flags = getAllFlags cmd
|
||||||
|
flagList (c:':':rest) = ([c], True) : flagList rest
|
||||||
|
flagList (c:rest) = ([c], False) : flagList rest
|
||||||
|
flagList [] = []
|
||||||
|
flagMap = Map.fromList $ ("", False) : flagList string
|
||||||
|
|
||||||
|
process [] = return []
|
||||||
|
process [(token, flag)] = do
|
||||||
|
takesArg <- Map.lookup flag flagMap
|
||||||
|
guard $ not takesArg
|
||||||
|
return [(flag, token)]
|
||||||
|
process ((token1, flag1):rest2@((token2, flag2):rest)) = do
|
||||||
|
takesArg <- Map.lookup flag1 flagMap
|
||||||
|
if takesArg
|
||||||
|
then do
|
||||||
|
guard $ flag2 == ""
|
||||||
|
more <- process rest
|
||||||
|
return $ (flag1, token2) : more
|
||||||
|
else do
|
||||||
|
more <- process rest2
|
||||||
|
return $ (flag1, token1) : more
|
||||||
|
|
||||||
return []
|
return []
|
||||||
runTests = $( [| $(forAllProperties) (quickCheckWithResult (stdArgs { maxSuccess = 1 }) ) |])
|
runTests = $( [| $(forAllProperties) (quickCheckWithResult (stdArgs { maxSuccess = 1 }) ) |])
|
||||||
|
|
|
@ -90,6 +90,7 @@ commandChecks = [
|
||||||
,checkLetUsage
|
,checkLetUsage
|
||||||
,checkMvArguments, checkCpArguments, checkLnArguments
|
,checkMvArguments, checkCpArguments, checkLnArguments
|
||||||
,checkFindRedirections
|
,checkFindRedirections
|
||||||
|
,checkReadExpansions
|
||||||
]
|
]
|
||||||
|
|
||||||
buildCommandMap :: [CommandCheck] -> Map.Map CommandName (Token -> Analysis)
|
buildCommandMap :: [CommandCheck] -> Map.Map CommandName (Token -> Analysis)
|
||||||
|
@ -588,19 +589,47 @@ prop_checkExportedExpansions1 = verify checkExportedExpansions "export $foo"
|
||||||
prop_checkExportedExpansions2 = verify checkExportedExpansions "export \"$foo\""
|
prop_checkExportedExpansions2 = verify checkExportedExpansions "export \"$foo\""
|
||||||
prop_checkExportedExpansions3 = verifyNot checkExportedExpansions "export foo"
|
prop_checkExportedExpansions3 = verifyNot checkExportedExpansions "export foo"
|
||||||
prop_checkExportedExpansions4 = verifyNot checkExportedExpansions "export ${foo?}"
|
prop_checkExportedExpansions4 = verifyNot checkExportedExpansions "export ${foo?}"
|
||||||
checkExportedExpansions = CommandCheck (Exactly "export") (check . arguments)
|
checkExportedExpansions = CommandCheck (Exactly "export") (mapM_ check . arguments)
|
||||||
where
|
where
|
||||||
check = mapM_ checkForVariables
|
check t = potentially $ do
|
||||||
checkForVariables f =
|
var <- getSingleUnmodifiedVariable t
|
||||||
case getWordParts f of
|
let name = bracedString var
|
||||||
[t@(T_DollarBraced {})] -> potentially $ do
|
|
||||||
let contents = bracedString t
|
|
||||||
let name = getBracedReference contents
|
|
||||||
guard $ name == contents
|
|
||||||
return . warn (getId t) 2163 $
|
return . warn (getId t) 2163 $
|
||||||
"This does not export '" ++ name ++ "'. Remove $/${} for that, or use ${var?} to quiet."
|
"This does not export '" ++ name ++ "'. Remove $/${} for that, or use ${var?} to quiet."
|
||||||
_ -> return ()
|
|
||||||
|
|
||||||
|
prop_checkReadExpansions1 = verify checkReadExpansions "read $var"
|
||||||
|
prop_checkReadExpansions2 = verify checkReadExpansions "read -r $var"
|
||||||
|
prop_checkReadExpansions3 = verifyNot checkReadExpansions "read -p $var"
|
||||||
|
prop_checkReadExpansions4 = verifyNot checkReadExpansions "read -rd $delim name"
|
||||||
|
prop_checkReadExpansions5 = verify checkReadExpansions "read \"$var\""
|
||||||
|
prop_checkReadExpansions6 = verify checkReadExpansions "read -a $var"
|
||||||
|
prop_checkReadExpansions7 = verifyNot checkReadExpansions "read $1"
|
||||||
|
prop_checkReadExpansions8 = verifyNot checkReadExpansions "read ${var?}"
|
||||||
|
checkReadExpansions = CommandCheck (Exactly "read") check
|
||||||
|
where
|
||||||
|
options = getOpts "sreu:n:N:i:p:a:"
|
||||||
|
getVars cmd = fromMaybe [] $ do
|
||||||
|
opts <- options cmd
|
||||||
|
return . map snd $ filter (\(x,_) -> x == "" || x == "a") opts
|
||||||
|
|
||||||
|
check cmd = mapM_ warning $ getVars cmd
|
||||||
|
warning t = potentially $ do
|
||||||
|
var <- getSingleUnmodifiedVariable t
|
||||||
|
let name = bracedString var
|
||||||
|
guard $ isVariableName name -- e.g. not $1
|
||||||
|
return . warn (getId t) 2229 $
|
||||||
|
"This does not read '" ++ name ++ "'. Remove $/${} for that, or use ${var?} to quiet."
|
||||||
|
|
||||||
|
-- Return the single variable expansion that makes up this word, if any.
|
||||||
|
-- e.g. $foo -> $foo, "$foo"'' -> $foo , "hello $name" -> Nothing
|
||||||
|
getSingleUnmodifiedVariable :: Token -> Maybe Token
|
||||||
|
getSingleUnmodifiedVariable word =
|
||||||
|
case getWordParts word of
|
||||||
|
[t@(T_DollarBraced {})] ->
|
||||||
|
let contents = bracedString t
|
||||||
|
name = getBracedReference contents
|
||||||
|
in guard (contents == name) >> return t
|
||||||
|
_ -> Nothing
|
||||||
|
|
||||||
prop_checkAliasesUsesArgs1 = verify checkAliasesUsesArgs "alias a='cp $1 /a'"
|
prop_checkAliasesUsesArgs1 = verify checkAliasesUsesArgs "alias a='cp $1 /a'"
|
||||||
prop_checkAliasesUsesArgs2 = verifyNot checkAliasesUsesArgs "alias $1='foo'"
|
prop_checkAliasesUsesArgs2 = verifyNot checkAliasesUsesArgs "alias $1='foo'"
|
||||||
|
|
Loading…
Reference in New Issue