From 7fb399528cb9189b29dc92110fab76fcf5d81886 Mon Sep 17 00:00:00 2001 From: Supanat Pothivarakorn Date: Wed, 2 Oct 2019 22:34:43 +0700 Subject: [PATCH 1/2] Allow `read -t 0` to not require -r flag since it has specific purpose for checking only --- src/ShellCheck/Analytics.hs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ShellCheck/Analytics.hs b/src/ShellCheck/Analytics.hs index 5e0adf7..9876679 100644 --- a/src/ShellCheck/Analytics.hs +++ b/src/ShellCheck/Analytics.hs @@ -2786,8 +2786,11 @@ checkMaskedReturns _ _ = return () prop_checkReadWithoutR1 = verify checkReadWithoutR "read -a foo" prop_checkReadWithoutR2 = verifyNot checkReadWithoutR "read -ar foo" +prop_checkReadWithoutR3 = verifyNot checkReadWithoutR "read -t 0" +prop_checkReadWithoutR4 = verifyNot checkReadWithoutR "read -t 0 && read --d '' -r bar" +prop_checkReadWithoutR5 = verify checkReadWithoutR "read -t 0 foo < file.txt" checkReadWithoutR _ t@T_SimpleCommand {} | t `isUnqualifiedCommand` "read" = - unless ("r" `elem` map snd (getAllFlags t)) $ + unless (oversimplify t == ["read", "-t", "0"] || "r" `elem` map snd (getAllFlags t)) $ info (getId $ getCommandTokenOrThis t) 2162 "read without -r will mangle backslashes." checkReadWithoutR _ _ = return () From 7473d4a7434fb7c0ff3b0c9cbc2455f2513d0ce7 Mon Sep 17 00:00:00 2001 From: Vidar Holen Date: Sat, 12 Oct 2019 20:45:36 -0700 Subject: [PATCH 2/2] Make `read -t 0` test more forgiving towards other flags --- src/ShellCheck/Analytics.hs | 13 +++++++++++-- src/ShellCheck/AnalyzerLib.hs | 11 ++++++----- src/ShellCheck/Checks/Commands.hs | 2 +- src/ShellCheck/Data.hs | 2 ++ 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/ShellCheck/Analytics.hs b/src/ShellCheck/Analytics.hs index f778d34..a182afc 100644 --- a/src/ShellCheck/Analytics.hs +++ b/src/ShellCheck/Analytics.hs @@ -2809,10 +2809,19 @@ prop_checkReadWithoutR1 = verify checkReadWithoutR "read -a foo" prop_checkReadWithoutR2 = verifyNot checkReadWithoutR "read -ar foo" prop_checkReadWithoutR3 = verifyNot checkReadWithoutR "read -t 0" prop_checkReadWithoutR4 = verifyNot checkReadWithoutR "read -t 0 && read --d '' -r bar" -prop_checkReadWithoutR5 = verify checkReadWithoutR "read -t 0 foo < file.txt" +prop_checkReadWithoutR5 = verifyNot checkReadWithoutR "read -t 0 foo < file.txt" +prop_checkReadWithoutR6 = verifyNot checkReadWithoutR "read -u 3 -t 0" checkReadWithoutR _ t@T_SimpleCommand {} | t `isUnqualifiedCommand` "read" = - unless (oversimplify t == ["read", "-t", "0"] || "r" `elem` map snd (getAllFlags t)) $ + unless ("r" `elem` map snd flags || has_t0) $ info (getId $ getCommandTokenOrThis t) 2162 "read without -r will mangle backslashes." + where + flags = getAllFlags t + has_t0 = fromMaybe False $ do + parsed <- getOpts flagsForRead flags + t <- getOpt "t" parsed + str <- getLiteralString t + return $ str == "0" + checkReadWithoutR _ _ = return () prop_checkUncheckedCd1 = verifyTree checkUncheckedCdPushdPopd "cd ~/src; rm -r foo" diff --git a/src/ShellCheck/AnalyzerLib.hs b/src/ShellCheck/AnalyzerLib.hs index 70b781e..7803fdf 100644 --- a/src/ShellCheck/AnalyzerLib.hs +++ b/src/ShellCheck/AnalyzerLib.hs @@ -932,12 +932,11 @@ isQuotedAlternativeReference t = -- Just [("r", -re), ("e", -re), ("d", :), ("u", 3), ("", bar)] -- where flags with arguments map to arguments, while others map to themselves. -- Any unrecognized flag will result in Nothing. -getGnuOpts = getOpts getAllFlags -getBsdOpts = getOpts getLeadingFlags -getOpts :: (Token -> [(Token, String)]) -> String -> Token -> Maybe [(String, Token)] -getOpts flagTokenizer string cmd = process flags +getGnuOpts str t = getOpts str $ getAllFlags t +getBsdOpts str t = getOpts str $ getLeadingFlags t +getOpts :: String -> [(Token, String)] -> Maybe [(String, Token)] +getOpts string flags = process flags where - flags = flagTokenizer cmd flagList (c:':':rest) = ([c], True) : flagList rest flagList (c:rest) = ([c], False) : flagList rest flagList [] = [] @@ -959,6 +958,8 @@ getOpts flagTokenizer string cmd = process flags more <- process rest2 return $ (flag1, token1) : more +getOpt str flags = snd <$> (listToMaybe $ filter (\(f, _) -> f == str) $ flags) + supportsArrays shell = shell == Bash || shell == Ksh -- Returns true if the shell is Bash or Ksh (sorry for the name, Ksh) diff --git a/src/ShellCheck/Checks/Commands.hs b/src/ShellCheck/Checks/Commands.hs index c6346a9..441e43f 100644 --- a/src/ShellCheck/Checks/Commands.hs +++ b/src/ShellCheck/Checks/Commands.hs @@ -676,7 +676,7 @@ prop_checkReadExpansions7 = verifyNot checkReadExpansions "read $1" prop_checkReadExpansions8 = verifyNot checkReadExpansions "read ${var?}" checkReadExpansions = CommandCheck (Exactly "read") check where - options = getGnuOpts "sreu:n:N:i:p:a:" + options = getGnuOpts flagsForRead getVars cmd = fromMaybe [] $ do opts <- options cmd return . map snd $ filter (\(x,_) -> x == "" || x == "a") opts diff --git a/src/ShellCheck/Data.hs b/src/ShellCheck/Data.hs index e2eeb74..732619d 100644 --- a/src/ShellCheck/Data.hs +++ b/src/ShellCheck/Data.hs @@ -136,3 +136,5 @@ shellForExecutable name = "ksh88" -> return Ksh "ksh93" -> return Ksh otherwise -> Nothing + +flagsForRead = "sreu:n:N:i:p:a:t:"