@@ -5049,6 +5049,8 @@ prop_checkCommandIsUnreachable2 = verify checkCommandIsUnreachable "die() { exit
50495049prop_checkCommandIsUnreachable3 = verifyNot checkCommandIsUnreachable " foo; bar || exit; baz"
50505050prop_checkCommandIsUnreachable4 = verifyNot checkCommandIsUnreachable " f() { foo; }; # Maybe sourced"
50515051prop_checkCommandIsUnreachable5 = verify checkCommandIsUnreachable " f() { foo; }; exit # Not sourced"
5052+ prop_checkCommandIsUnreachable10 = verifyNot checkCommandIsUnreachable " f() { :; }; PS4='$(f)'; exit"
5053+ prop_checkCommandIsUnreachable11 = verifyNot checkCommandIsUnreachable " f() { :; }; PS4='`f`'; exit"
50525054checkCommandIsUnreachable params t =
50535055 case t of
50545056 T_Pipeline {} -> sequence_ $ do
@@ -5058,10 +5060,12 @@ checkCommandIsUnreachable params t =
50585060 guard . not $ isSourced params t
50595061 guard . not $ any (\ t -> isUnreachable t || isUnreachableFunction t) $ NE. drop 1 $ getPath (parentMap params) t
50605062 return $ info (getId t) 2317 " Command appears to be unreachable. Check usage (or ignore if invoked indirectly)."
5061- T_Function id _ _ _ _ ->
5063+ T_Function id _ _ name _ ->
50625064 when (isUnreachableFunction t
50635065 && (not . any isUnreachableFunction . NE. drop 1 $ getPath (parentMap params) t)
5064- && (not $ isSourced params t)) $
5066+ && not (isSourced params t)
5067+ && not (ps4CallsFunction name)
5068+ ) $
50655069 info id 2329 " This function is never invoked. Check usage (or ignored if invoked indirectly)."
50665070 _ -> return ()
50675071 where
@@ -5075,6 +5079,28 @@ checkCommandIsUnreachable params t =
50755079 state <- CF. getIncomingState cfga (getId t)
50765080 return . not $ CF. stateIsReachable state
50775081
5082+ ps4CallsFunction :: String -> Bool
5083+ ps4CallsFunction name =
5084+ any (any mentionsTok) ps4Values
5085+ where
5086+ ps4Values :: [[Token ]]
5087+ ps4Values =
5088+ [ values
5089+ | Assignment (_, _, " PS4" , DataString (SourceFrom values)) <- variableFlow params
5090+ ]
5091+
5092+ mentionsTok :: Token -> Bool
5093+ mentionsTok tok
5094+ | isCommandSubstitution tok =
5095+ getCommandNameFromExpansion tok == Just name
5096+ | otherwise = case tok of
5097+ T_NormalWord _ parts -> any mentionsTok parts
5098+ T_DoubleQuoted _ parts -> any mentionsTok parts
5099+ T_DollarDoubleQuoted _ parts -> any mentionsTok parts
5100+ T_SingleQuoted _ s -> s `matches` mkRegex (" \\ $\\ ([[:space:]]*" ++ name ++ " ([[:space:]]|\\ )|$)" )
5101+ || s `matches` mkRegex (" `[[:space:]]*" ++ name ++ " ([[:space:]]|`|$)" )
5102+ _ -> False
5103+
50785104
50795105prop_checkOverwrittenExitCode1 = verify checkOverwrittenExitCode " x; [ $? -eq 1 ] || [ $? -eq 2 ]"
50805106prop_checkOverwrittenExitCode2 = verifyNot checkOverwrittenExitCode " x; [ $? -eq 1 ]"
0 commit comments