Use CFG to determine use-before-define for SC2218 (fixes #3070)
This commit is contained in:
parent
68bc17b8ea
commit
5e3e98bcb0
|
@ -7,6 +7,7 @@
|
||||||
### Changed
|
### Changed
|
||||||
- SC2015 about `A && B || C` no longer triggers when B is a test command.
|
- SC2015 about `A && B || C` no longer triggers when B is a test command.
|
||||||
### Fixed
|
### Fixed
|
||||||
|
- SC2218 about function use-before-define is now more accurate.
|
||||||
- SC2317 about unreachable commands is now less spammy for nested ones.
|
- SC2317 about unreachable commands is now less spammy for nested ones.
|
||||||
- SC2292, optional suggestion for [[ ]], now triggers for Busybox.
|
- SC2292, optional suggestion for [[ ]], now triggers for Busybox.
|
||||||
|
|
||||||
|
|
|
@ -3765,32 +3765,32 @@ prop_checkUseBeforeDefinition1 = verifyTree checkUseBeforeDefinition "f; f() { t
|
||||||
prop_checkUseBeforeDefinition2 = verifyNotTree checkUseBeforeDefinition "f() { true; }; f"
|
prop_checkUseBeforeDefinition2 = verifyNotTree checkUseBeforeDefinition "f() { true; }; f"
|
||||||
prop_checkUseBeforeDefinition3 = verifyNotTree checkUseBeforeDefinition "if ! mycmd --version; then mycmd() { true; }; fi"
|
prop_checkUseBeforeDefinition3 = verifyNotTree checkUseBeforeDefinition "if ! mycmd --version; then mycmd() { true; }; fi"
|
||||||
prop_checkUseBeforeDefinition4 = verifyNotTree checkUseBeforeDefinition "mycmd || mycmd() { f; }"
|
prop_checkUseBeforeDefinition4 = verifyNotTree checkUseBeforeDefinition "mycmd || mycmd() { f; }"
|
||||||
checkUseBeforeDefinition _ t =
|
prop_checkUseBeforeDefinition5 = verifyTree checkUseBeforeDefinition "false || mycmd; mycmd() { f; }"
|
||||||
execWriter $ evalStateT (mapM_ examine $ revCommands) Map.empty
|
prop_checkUseBeforeDefinition6 = verifyNotTree checkUseBeforeDefinition "f() { one; }; f; f() { two; }; f"
|
||||||
|
checkUseBeforeDefinition :: Parameters -> Token -> [TokenComment]
|
||||||
|
checkUseBeforeDefinition params t = fromMaybe [] $ do
|
||||||
|
cfga <- cfgAnalysis params
|
||||||
|
let funcs = execState (doAnalysis findFunction t) Map.empty
|
||||||
|
-- Green cut: no point enumerating commands if there are no functions.
|
||||||
|
guard . not $ Map.null funcs
|
||||||
|
return $ execWriter $ doAnalysis (findInvocation cfga funcs) t
|
||||||
where
|
where
|
||||||
examine t = case t of
|
findFunction t =
|
||||||
T_Pipeline _ _ [T_Redirecting _ _ (T_Function _ _ _ name _)] ->
|
case t of
|
||||||
modify $ Map.insert name t
|
T_Function id _ _ name _ -> modify (Map.insertWith (++) name [id])
|
||||||
T_Annotation _ _ w -> examine w
|
_ -> return ()
|
||||||
T_Pipeline _ _ cmds -> do
|
|
||||||
m <- get
|
|
||||||
unless (Map.null m) $
|
|
||||||
mapM_ (checkUsage m) $ concatMap recursiveSequences cmds
|
|
||||||
_ -> return ()
|
|
||||||
|
|
||||||
checkUsage map cmd = sequence_ $ do
|
findInvocation cfga funcs t =
|
||||||
name <- getCommandName cmd
|
case t of
|
||||||
def <- Map.lookup name map
|
T_SimpleCommand id _ (cmd:_) -> sequence_ $ do
|
||||||
return $
|
name <- getLiteralString cmd
|
||||||
err (getId cmd) 2218
|
invocations <- Map.lookup name funcs
|
||||||
"This function is only defined later. Move the definition up."
|
-- Is the function definitely being defined later?
|
||||||
|
guard $ any (\c -> CF.doesPostDominate cfga c id) invocations
|
||||||
revCommands = reverse $ concat $ getCommandSequences t
|
-- Was one already defined, so it's actually a re-definition?
|
||||||
recursiveSequences x =
|
guard . not $ any (\c -> CF.doesPostDominate cfga id c) invocations
|
||||||
let list = concat $ getCommandSequences x in
|
return $ err id 2218 "This function is only defined later. Move the definition up."
|
||||||
if null list
|
_ -> return ()
|
||||||
then [x]
|
|
||||||
else concatMap recursiveSequences list
|
|
||||||
|
|
||||||
prop_checkForLoopGlobVariables1 = verify checkForLoopGlobVariables "for i in $var/*.txt; do true; done"
|
prop_checkForLoopGlobVariables1 = verify checkForLoopGlobVariables "for i in $var/*.txt; do true; done"
|
||||||
prop_checkForLoopGlobVariables2 = verifyNot checkForLoopGlobVariables "for i in \"$var\"/*.txt; do true; done"
|
prop_checkForLoopGlobVariables2 = verifyNot checkForLoopGlobVariables "for i in \"$var\"/*.txt; do true; done"
|
||||||
|
|
Loading…
Reference in New Issue