diff --git a/ShellCheck/Analytics.hs b/ShellCheck/Analytics.hs index 4923e70..2dc9bd8 100644 --- a/ShellCheck/Analytics.hs +++ b/ShellCheck/Analytics.hs @@ -207,6 +207,7 @@ nodeChecks = [ ,checkTildeInPath ,checkFindExecWithSingleArgument ,checkReturn + ,checkMaskedReturns ] @@ -3261,5 +3262,27 @@ checkFindExecWithSingleArgument _ = checkCommand "find" (const f) commandRegex = mkRegex "[ |;]" +prop_checkMaskedReturns1 = verify checkMaskedReturns "f() { local a=$(false); }" +prop_checkMaskedReturns2 = verify checkMaskedReturns "declare a=$(false)" +prop_checkMaskedReturns3 = verify checkMaskedReturns "declare a=\"`false`\"" +prop_checkMaskedReturns4 = verifyNot checkMaskedReturns "declare a; a=$(false)" +prop_checkMaskedReturns5 = verifyNot checkMaskedReturns "f() { local -r a=$(false); }" +checkMaskedReturns _ t@(T_SimpleCommand id _ (cmd:rest)) = potentially $ do + name <- getCommandName t + guard $ name `elem` ["declare", "export"] + || name == "local" && "r" `notElem` getFlags t + return $ mapM_ checkArgs rest + where + checkArgs (T_Assignment id _ _ _ word) | any hasReturn $ getWordParts word = + warn id 2155 "Declare and assign separately to avoid masking return values." + checkArgs _ = return () + + hasReturn t = case t of + T_Backticked {} -> True + T_DollarExpansion {} -> True + _ -> False +checkMaskedReturns _ _ = return () + + return [] runTests = $( [| $(forAllProperties) (quickCheckWithResult (stdArgs { maxSuccess = 1 }) ) |]) diff --git a/ShellCheck/Parser.hs b/ShellCheck/Parser.hs index d3cd670..315f65d 100644 --- a/ShellCheck/Parser.hs +++ b/ShellCheck/Parser.hs @@ -950,7 +950,7 @@ readNormalEscaped = called "escaped char" $ do pos <- getPosition backslash do - next <- quotable <|> oneOf "?*@!+[]{}.,~" + next <- quotable <|> oneOf "?*@!+[]{}.,~#" return $ if next == '\n' then "" else [next] <|> do