Warn about break/continue in subshells and outside loops

This commit is contained in:
Vidar Holen 2014-02-01 23:45:26 -08:00
parent e8634a3c27
commit 8ec9fa43fd
2 changed files with 35 additions and 0 deletions

View File

@ -342,3 +342,10 @@ doAnalysis f t = analyze f blank id t
doStackAnalysis startToken endToken t = analyze startToken endToken id t
doTransform i t = runIdentity $ analyze blank blank i t
isLoop t = case t of
T_WhileExpression _ _ _ -> True
T_UntilExpression _ _ _ -> True
T_ForIn _ _ _ _ -> True
T_ForArithmetic _ _ _ _ _ -> True
T_SelectIn _ _ _ _ -> True
_ -> False

View File

@ -149,6 +149,7 @@ treeChecks = [
,checkSingleQuotedVariables
,checkRedirectToSame
,checkPrefixAssignmentReference
,checkLoopKeywordScope
]
@ -2010,3 +2011,30 @@ checkCdAndBack shell = doLists
if shell == Bash || shell == Zsh
then "Consider using ( subshell ), 'cd foo||exit', or pushd/popd instead."
else "Consider using ( subshell ) or 'cd foo||exit' instead."
prop_checkLoopKeywordScope1 = verifyTree checkLoopKeywordScope "continue 2"
prop_checkLoopKeywordScope2 = verifyTree checkLoopKeywordScope "for f; do ( break; ); done"
prop_checkLoopKeywordScope3 = verifyTree checkLoopKeywordScope "if true; then continue; fi"
prop_checkLoopKeywordScope4 = verifyNotTree checkLoopKeywordScope "while true; do break; done"
prop_checkLoopKeywordScope5 = verifyTree checkLoopKeywordScope "if true; then break; fi"
checkLoopKeywordScope t tree |
name `elem` map Just ["continue", "break"] =
if not $ any isLoop path
then if any isFunction $ take 1 path
-- breaking at a source/function invocation is an abomination. Let's ignore it.
then err (getId t) 2104 $ "In functions, use return instead of " ++ (fromJust name) ++ "."
else err (getId t) 2105 $ (fromJust name) ++ " is only valid in loops."
else case map subshellType $ filter (not . isFunction) path of
-- TODO: Fix warning for Ksh/Zsh when this is the last step in the pipeline
(Just str):_ -> warn (getId t) 2106 $
"This only exits the subshell caused by the " ++ str ++ "."
_ -> return ()
where
name = getCommandName t
path = let p = getPath tree t in filter relevant p
subshellType t = case leadType t of
NoneScope -> Nothing
SubshellScope str -> return str
isFunction t = case t of T_Function _ _ _ -> True; _ -> False
relevant t = isLoop t || isFunction t || isJust (subshellType t)
checkLoopKeywordScope _ _ = return ()