diff --git a/CHANGELOG.md b/CHANGELOG.md index 4763ddd..6e1dba3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## Git ### Added - SC2316: Warn about 'local readonly foo' and similar (thanks, patrickxia!) +- SC2317: Warn about unreachable commands ### Fixed diff --git a/src/ShellCheck/Analytics.hs b/src/ShellCheck/Analytics.hs index bf5d179..e6c0160 100644 --- a/src/ShellCheck/Analytics.hs +++ b/src/ShellCheck/Analytics.hs @@ -202,6 +202,7 @@ nodeChecks = [ ,checkCommandWithTrailingSymbol ,checkUnquotedParameterExpansionPattern ,checkBatsTestDoesNotUseNegation + ,checkCommandIsUnreachable ] optionalChecks = map fst optionalTreeChecks @@ -4129,13 +4130,6 @@ checkAliasUsedInSameParsingUnit params root = checkUnit :: [Token] -> Writer [TokenComment] () checkUnit unit = evalStateT (mapM_ (doAnalysis findCommands) unit) (Map.empty) - isSourced t = - let - f (T_SourceCommand {}) = True - f _ = False - in - any f $ getPath (parentMap params) t - findCommands :: Token -> StateT (Map.Map String Token) (Writer [TokenComment]) () findCommands t = case t of T_SimpleCommand _ _ (cmd:args) -> @@ -4146,7 +4140,7 @@ checkAliasUsedInSameParsingUnit params root = cmd <- gets (Map.lookup name) case cmd of Just alias -> - unless (isSourced t || shouldIgnoreCode params 2262 alias) $ do + unless (isSourced params t || shouldIgnoreCode params 2262 alias) $ do warn (getId alias) 2262 "This alias can't be defined and used in the same parsing unit. Use a function instead." info (getId t) 2263 "Since they're in the same parsing unit, this command will not refer to the previously mentioned alias." _ -> return () @@ -4157,6 +4151,14 @@ checkAliasUsedInSameParsingUnit params root = when (isVariableName name && not (null value)) $ modify (Map.insertWith (\new old -> old) name arg) +isSourced params t = + let + f (T_SourceCommand {}) = True + f _ = False + in + any f $ getPath (parentMap params) t + + -- Like groupBy, but compares pairs of adjacent elements, rather than against the first of the span prop_groupByLink1 = groupByLink (\a b -> a+1 == b) [1,2,3,2,3,7,8,9] == [[1,2,3], [2,3], [7,8,9]] prop_groupByLink2 = groupByLink (==) ([] :: [()]) == [] @@ -4910,5 +4912,19 @@ checkBatsTestDoesNotUseNegation params t = x:rest -> isLastOf t rest [] -> False + +prop_checkCommandIsUnreachable1 = verify checkCommandIsUnreachable "foo; bar; exit; baz" +prop_checkCommandIsUnreachable2 = verify checkCommandIsUnreachable "die() { exit; }; foo; bar; die; baz" +prop_checkCommandIsUnreachable3 = verifyNot checkCommandIsUnreachable "foo; bar || exit; baz" +checkCommandIsUnreachable params t = + case t of + T_Pipeline {} -> sequence_ $ do + state <- CF.getIncomingState (cfgAnalysis params) id + guard . not $ CF.stateIsReachable state + guard . not $ isSourced params t + return $ info id 2317 "Command appears to be unreachable. Check usage (or ignore if invoked indirectly)." + _ -> return () + where id = getId t + return [] runTests = $( [| $(forAllProperties) (quickCheckWithResult (stdArgs { maxSuccess = 1 }) ) |])