Skip to content

Commit 3850a7a

Browse files
committed
Allow a function to be referenced in PS4
1 parent 401987f commit 3850a7a

1 file changed

Lines changed: 28 additions & 2 deletions

File tree

src/ShellCheck/Analytics.hs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5049,6 +5049,8 @@ prop_checkCommandIsUnreachable2 = verify checkCommandIsUnreachable "die() { exit
50495049
prop_checkCommandIsUnreachable3 = verifyNot checkCommandIsUnreachable "foo; bar || exit; baz"
50505050
prop_checkCommandIsUnreachable4 = verifyNot checkCommandIsUnreachable "f() { foo; }; # Maybe sourced"
50515051
prop_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"
50525054
checkCommandIsUnreachable 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

50795105
prop_checkOverwrittenExitCode1 = verify checkOverwrittenExitCode "x; [ $? -eq 1 ] || [ $? -eq 2 ]"
50805106
prop_checkOverwrittenExitCode2 = verifyNot checkOverwrittenExitCode "x; [ $? -eq 1 ]"

0 commit comments

Comments
 (0)