Correctly handle empty variables for SC2086 (fixes #1722)

This commit is contained in:
Vidar Holen 2019-11-03 12:45:13 -08:00
parent 4dfd7eb1cf
commit 5962b01816
2 changed files with 40 additions and 21 deletions

View File

@ -1,6 +1,7 @@
## v0.7.1 - soon ## v0.7.1 - soon
### Fixed ### Fixed
- `-f diff` no longer claims that it found more issues when it didn't - `-f diff` no longer claims that it found more issues when it didn't
- Known empty variables now correctly trigger SC2086
### Added ### Added
- SC2254: Suggest quoting expansions in case statements - SC2254: Suggest quoting expansions in case statements

View File

@ -1807,6 +1807,21 @@ prop_checkSpacefulness35= verifyNotTree checkSpacefulness "echo ${1+\"$1\"}"
prop_checkSpacefulness36= verifyNotTree checkSpacefulness "arg=$#; echo $arg" prop_checkSpacefulness36= verifyNotTree checkSpacefulness "arg=$#; echo $arg"
prop_checkSpacefulness37= verifyNotTree checkSpacefulness "@test 'status' {\n [ $status -eq 0 ]\n}" prop_checkSpacefulness37= verifyNotTree checkSpacefulness "@test 'status' {\n [ $status -eq 0 ]\n}"
prop_checkSpacefulness37v = verifyTree checkVerboseSpacefulness "@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; echo $b"
prop_checkSpacefulness40= verifyNotTree checkSpacefulness "a=$((x+1)); echo $a"
data SpaceStatus = SpaceSome | SpaceNone | SpaceEmpty deriving (Eq)
instance Semigroup SpaceStatus where
(<>) x y =
case (x,y) of
(SpaceNone, SpaceNone) -> SpaceNone
(SpaceSome, _) -> SpaceSome
(_, SpaceSome) -> SpaceSome
(SpaceEmpty, x) -> x
(x, SpaceEmpty) -> x
instance Monoid SpaceStatus where
mempty = SpaceEmpty
-- This is slightly awkward because we want to support structured -- This is slightly awkward because we want to support structured
-- optional checks based on nearly the same logic -- optional checks based on nearly the same logic
@ -1814,7 +1829,7 @@ checkSpacefulness params = checkSpacefulness' onFind params
where where
emit x = tell [x] emit x = tell [x]
onFind spaces token _ = onFind spaces token _ =
when spaces $ when (spaces /= SpaceNone) $
if isDefaultAssignment (parentMap params) token if isDefaultAssignment (parentMap params) token
then then
emit $ makeComment InfoC (getId token) 2223 emit $ makeComment InfoC (getId token) 2223
@ -1829,7 +1844,6 @@ checkSpacefulness params = checkSpacefulness' onFind params
any (`isPrefixOf` modifier) ["=", ":="] any (`isPrefixOf` modifier) ["=", ":="]
&& isParamTo parents ":" token && isParamTo parents ":" token
prop_checkSpacefulness4v= verifyTree checkVerboseSpacefulness "foo=3; foo=$(echo $foo)" prop_checkSpacefulness4v= verifyTree checkVerboseSpacefulness "foo=3; foo=$(echo $foo)"
prop_checkSpacefulness8v= verifyTree checkVerboseSpacefulness "a=foo\\ bar; a=foo; rm $a" prop_checkSpacefulness8v= verifyTree checkVerboseSpacefulness "a=foo\\ bar; a=foo; rm $a"
prop_checkSpacefulness28v = verifyTree checkVerboseSpacefulness "exec {n}>&1; echo $n" prop_checkSpacefulness28v = verifyTree checkVerboseSpacefulness "exec {n}>&1; echo $n"
@ -1837,24 +1851,24 @@ prop_checkSpacefulness36v = verifyTree checkVerboseSpacefulness "arg=$#; echo $a
checkVerboseSpacefulness params = checkSpacefulness' onFind params checkVerboseSpacefulness params = checkSpacefulness' onFind params
where where
onFind spaces token name = onFind spaces token name =
when (not spaces && name `notElem` specialVariablesWithoutSpaces) $ when (spaces == SpaceNone && name `notElem` specialVariablesWithoutSpaces) $
tell [makeCommentWithFix StyleC (getId token) 2248 tell [makeCommentWithFix StyleC (getId token) 2248
"Prefer double quoting even when variables don't contain special characters." "Prefer double quoting even when variables don't contain special characters."
(addDoubleQuotesAround params token)] (addDoubleQuotesAround params token)]
addDoubleQuotesAround params token = (surroundWidth (getId token) params "\"") addDoubleQuotesAround params token = (surroundWidth (getId token) params "\"")
checkSpacefulness' checkSpacefulness'
:: (Bool -> Token -> String -> Writer [TokenComment] ()) -> :: (SpaceStatus -> Token -> String -> Writer [TokenComment] ()) ->
Parameters -> Token -> [TokenComment] Parameters -> Token -> [TokenComment]
checkSpacefulness' onFind params t = checkSpacefulness' onFind params t =
doVariableFlowAnalysis readF writeF (Map.fromList defaults) (variableFlow params) doVariableFlowAnalysis readF writeF (Map.fromList defaults) (variableFlow params)
where where
defaults = zip variablesWithoutSpaces (repeat False) defaults = zip variablesWithoutSpaces (repeat SpaceNone)
hasSpaces name = gets (Map.findWithDefault True name) hasSpaces name = gets (Map.findWithDefault SpaceSome name)
setSpaces name bool = setSpaces name status =
modify $ Map.insert name bool modify $ Map.insert name status
readF _ token name = do readF _ token name = do
spaces <- hasSpaces name spaces <- hasSpaces name
@ -1871,13 +1885,13 @@ checkSpacefulness' onFind params t =
where where
emit x = tell [x] emit x = tell [x]
writeF _ _ name (DataString SourceExternal) = setSpaces name True >> return [] writeF _ _ name (DataString SourceExternal) = setSpaces name SpaceSome >> return []
writeF _ _ name (DataString SourceInteger) = setSpaces name False >> return [] writeF _ _ name (DataString SourceInteger) = setSpaces name SpaceNone >> return []
writeF _ _ name (DataString (SourceFrom vals)) = do writeF _ _ name (DataString (SourceFrom vals)) = do
map <- get map <- get
setSpaces name setSpaces name
(isSpacefulWord (\x -> Map.findWithDefault True x map) vals) (isSpacefulWord (\x -> Map.findWithDefault SpaceSome x map) vals)
return [] return []
writeF _ _ _ _ = return [] writeF _ _ _ _ = return []
@ -1889,24 +1903,28 @@ checkSpacefulness' onFind params t =
(T_DollarBraced _ _ _ ) -> True (T_DollarBraced _ _ _ ) -> True
_ -> False _ -> False
isSpacefulWord :: (String -> Bool) -> [Token] -> Bool isSpacefulWord :: (String -> SpaceStatus) -> [Token] -> SpaceStatus
isSpacefulWord f = any (isSpaceful f) isSpacefulWord f = mconcat . map (isSpaceful f)
isSpaceful :: (String -> Bool) -> Token -> Bool isSpaceful :: (String -> SpaceStatus) -> Token -> SpaceStatus
isSpaceful spacefulF x = isSpaceful spacefulF x =
case x of case x of
T_DollarExpansion _ _ -> True T_DollarExpansion _ _ -> SpaceSome
T_Backticked _ _ -> True T_Backticked _ _ -> SpaceSome
T_Glob _ _ -> True T_Glob _ _ -> SpaceSome
T_Extglob {} -> True T_Extglob {} -> SpaceSome
T_Literal _ s -> s `containsAny` globspace T_DollarArithmetic _ _ -> SpaceNone
T_SingleQuoted _ s -> s `containsAny` globspace T_Literal _ s -> fromLiteral s
T_SingleQuoted _ s -> fromLiteral s
T_DollarBraced _ _ _ -> spacefulF $ getBracedReference $ bracedString x T_DollarBraced _ _ _ -> spacefulF $ getBracedReference $ bracedString x
T_NormalWord _ w -> isSpacefulWord spacefulF w T_NormalWord _ w -> isSpacefulWord spacefulF w
T_DoubleQuoted _ w -> isSpacefulWord spacefulF w T_DoubleQuoted _ w -> isSpacefulWord spacefulF w
_ -> False _ -> SpaceEmpty
where where
globspace = "*?[] \t\n" globspace = "*?[] \t\n"
containsAny s = any (`elem` s) 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}"