Skip to content

Commit 9d02364

Browse files
Copilotdotysan
andcommitted
Fix SC2329 false positive when function is invoked before definition with exit $?
Co-authored-by: dotysan <5060170+dotysan@users.noreply.github.com>
1 parent b6f551b commit 9d02364

1 file changed

Lines changed: 17 additions & 2 deletions

File tree

src/ShellCheck/Analytics.hs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5053,6 +5053,8 @@ prop_checkCommandIsUnreachable6 = verifyNot checkCommandIsUnreachable "return ||
50535053
prop_checkCommandIsUnreachable7 = verifyNot checkCommandIsUnreachable "return 2>/dev/null ||:"
50545054
prop_checkCommandIsUnreachable8 = verifyNot checkCommandIsUnreachable "return; echo 'reachable when not in function'"
50555055
prop_checkCommandIsUnreachable9 = verify checkCommandIsUnreachable "f() { return; echo unreachable; }"
5056+
prop_checkCommandIsUnreachable10 = verifyNot checkCommandIsUnreachable "f; f() { :; }; exit $?"
5057+
prop_checkCommandIsUnreachable11 = verifyNot checkCommandIsUnreachable "PS4func; PS4func() { echo test; }; exit $?"
50565058
checkCommandIsUnreachable params t =
50575059
case t of
50585060
T_Pipeline {} -> sequence_ $ do
@@ -5062,10 +5064,11 @@ checkCommandIsUnreachable params t =
50625064
guard . not $ isSourced params t
50635065
guard . not $ any (\t -> isUnreachable t || isUnreachableFunction t) $ NE.drop 1 $ getPath (parentMap params) t
50645066
return $ info (getId t) 2317 "Command appears to be unreachable. Check usage (or ignore if invoked indirectly)."
5065-
T_Function id _ _ _ _ ->
5067+
T_Function id _ _ name _ ->
50665068
when (isUnreachableFunction t
50675069
&& (not . any isUnreachableFunction . NE.drop 1 $ getPath (parentMap params) t)
5068-
&& (not $ isSourced params t)) $
5070+
&& (not $ isSourced params t)
5071+
&& (not $ isFunctionInvokedInReachableCode name)) $
50695072
info id 2329 "This function is never invoked. Check usage (or ignored if invoked indirectly)."
50705073
_ -> return ()
50715074
where
@@ -5078,6 +5081,18 @@ checkCommandIsUnreachable params t =
50785081
cfga <- cfgAnalysis params
50795082
state <- CF.getIncomingState cfga (getId t)
50805083
return . not $ CF.stateIsReachable state
5084+
5085+
-- Check if a function is invoked anywhere in reachable code
5086+
isFunctionInvokedInReachableCode :: String -> Bool
5087+
isFunctionInvokedInReachableCode name =
5088+
any isFunctionCall $ analyse findCalls (rootNode params)
5089+
where
5090+
findCalls token = when (isFunctionCall token) $ modify (token:)
5091+
isFunctionCall token =
5092+
case token of
5093+
T_SimpleCommand _ _ (cmd:_) ->
5094+
getUnquotedLiteral cmd == Just name && not (isUnreachable token)
5095+
_ -> False
50815096

50825097

50835098
prop_checkOverwrittenExitCode1 = verify checkOverwrittenExitCode "x; [ $? -eq 1 ] || [ $? -eq 2 ]"

0 commit comments

Comments
 (0)