Merge branch 'e-kwsm-fix-3164'
Some checks are pending
Build ShellCheck / Build (push) Blocked by required conditions
Build ShellCheck / Package Source Code (push) Waiting to run
Build ShellCheck / Run tests (push) Blocked by required conditions
Build ShellCheck / Package Binaries (push) Blocked by required conditions
Build ShellCheck / Deploy binaries (push) Blocked by required conditions

This commit is contained in:
Vidar Holen 2025-07-28 15:19:03 -07:00
commit b6c0673edf
2 changed files with 49 additions and 7 deletions

View file

@ -6,10 +6,14 @@
- SC2331: Suggest using standard -e instead of unary -a in tests. - SC2331: Suggest using standard -e instead of unary -a in tests.
- SC2332: Warn about `[ ! -o opt ]` being unconditionally true in Bash. - SC2332: Warn about `[ ! -o opt ]` being unconditionally true in Bash.
- SC3062: Warn about bashism `[ -o opt ]`. - SC3062: Warn about bashism `[ -o opt ]`.
- Optional `avoid-negated-conditions`: suggest replacing `[ ! a -eq b ]`
with `[ a -ne b ]`, and similar for -ge/-lt/=/!=/etc (SC2335).
- Precompiled binaries for Linux riscv64 (linux.riscv64) - Precompiled binaries for Linux riscv64 (linux.riscv64)
### Changed ### Changed
- SC2002 about Useless Use Of Cat is now disabled by default. It can be - SC2002 about Useless Use Of Cat is now disabled by default. It can be
re-enabled with `--enable=useless-use-of-cat` or equivalent directive. re-enabled with `--enable=useless-use-of-cat` or equivalent directive.
- SC2236/SC2237 about replacing `[ ! -n .. ]` with `[ -z ]` and vice versa
is now optional under `avoid-negated-conditions`.
- 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.
- SC3012: Do not warn about `\<` and `\>` in test/[] as specified in POSIX.1-2024 - SC3012: Do not warn about `\<` and `\>` in test/[] as specified in POSIX.1-2024
- Diff output now uses / as path separator on Windows - Diff output now uses / as path separator on Windows

View file

@ -183,7 +183,6 @@ nodeChecks = [
,checkPipeToNowhere ,checkPipeToNowhere
,checkForLoopGlobVariables ,checkForLoopGlobVariables
,checkSubshelledTests ,checkSubshelledTests
,checkInvertedStringTest
,checkRedirectionToCommand ,checkRedirectionToCommand
,checkDollarQuoteParen ,checkDollarQuoteParen
,checkUselessBang ,checkUselessBang
@ -233,6 +232,13 @@ optionalTreeChecks = [
cdNegative = "[ -n \"$var\" ]" cdNegative = "[ -n \"$var\" ]"
}, nodeChecksToTreeCheck [checkNullaryExpansionTest]) }, nodeChecksToTreeCheck [checkNullaryExpansionTest])
,(newCheckDescription {
cdName = "avoid-negated-conditions",
cdDescription = "Suggest removing unnecessary comparison negations",
cdPositive = "[ ! \"$var\" -eq 1 ]",
cdNegative = "[ \"$var\" -ne 1 ]"
}, nodeChecksToTreeCheck [checkUnnecessarilyInvertedTest])
,(newCheckDescription { ,(newCheckDescription {
cdName = "add-default-case", cdName = "add-default-case",
cdDescription = "Suggest adding a default case in `case` statements", cdDescription = "Suggest adding a default case in `case` statements",
@ -3991,12 +3997,17 @@ checkSubshelledTests params t =
T_Annotation {} -> True T_Annotation {} -> True
_ -> False _ -> False
prop_checkInvertedStringTest1 = verify checkInvertedStringTest "[ ! -z $var ]" prop_checkUnnecessarilyInvertedTest1 = verify checkUnnecessarilyInvertedTest "[ ! -z $var ]"
prop_checkInvertedStringTest2 = verify checkInvertedStringTest "! [[ -n $var ]]" prop_checkUnnecessarilyInvertedTest2 = verify checkUnnecessarilyInvertedTest "! [[ -n $var ]]"
prop_checkInvertedStringTest3 = verifyNot checkInvertedStringTest "! [ -x $var ]" prop_checkUnnecessarilyInvertedTest3 = verifyNot checkUnnecessarilyInvertedTest "! [ -x $var ]"
prop_checkInvertedStringTest4 = verifyNot checkInvertedStringTest "[[ ! -w $var ]]" prop_checkUnnecessarilyInvertedTest4 = verifyNot checkUnnecessarilyInvertedTest "[[ ! -w $var ]]"
prop_checkInvertedStringTest5 = verifyNot checkInvertedStringTest "[ -z $var ]" prop_checkUnnecessarilyInvertedTest5 = verifyNot checkUnnecessarilyInvertedTest "[ -z $var ]"
checkInvertedStringTest _ t = prop_checkUnnecessarilyInvertedTest6 = verify checkUnnecessarilyInvertedTest "! [ $var != foo ]"
prop_checkUnnecessarilyInvertedTest7 = verify checkUnnecessarilyInvertedTest "[[ ! $var == foo ]]"
prop_checkUnnecessarilyInvertedTest8 = verifyNot checkUnnecessarilyInvertedTest "! [[ $var =~ .* ]]"
prop_checkUnnecessarilyInvertedTest9 = verify checkUnnecessarilyInvertedTest "[ ! $var -eq 0 ]"
prop_checkUnnecessarilyInvertedTest10 = verify checkUnnecessarilyInvertedTest "! [[ $var -gt 3 ]]"
checkUnnecessarilyInvertedTest _ t =
case t of case t of
TC_Unary _ _ "!" (TC_Unary _ _ op _) -> TC_Unary _ _ "!" (TC_Unary _ _ op _) ->
case op of case op of
@ -4009,7 +4020,34 @@ checkInvertedStringTest _ t =
"-n" -> style (getId t) 2237 "Use [ -z .. ] instead of ! [ -n .. ]." "-n" -> style (getId t) 2237 "Use [ -z .. ] instead of ! [ -n .. ]."
"-z" -> style (getId t) 2237 "Use [ -n .. ] instead of ! [ -z .. ]." "-z" -> style (getId t) 2237 "Use [ -n .. ] instead of ! [ -z .. ]."
_ -> return () _ -> return ()
TC_Unary _ _ "!" (TC_Binary _ bracketStyle op _ _) ->
maybeSuggestRewrite True bracketStyle (getId t) op
T_Banged _ (T_Pipeline _ _
[T_Redirecting _ _ (T_Condition _ _ (TC_Binary _ bracketStyle op _ _))]) ->
maybeSuggestRewrite False bracketStyle (getId t) op
_ -> return () _ -> return ()
where
inversionMap = Map.fromList [
("=", "!="),
("==", "!="),
("!=", "="),
("-eq", "-ne"),
("-ne", "-eq"),
("-le", "-gt"),
("-gt", "-le"),
("-ge", "-lt"),
("-lt", "-ge")
]
maybeSuggestRewrite bangInside bracketStyle id op = sequence_ $ do
newOp <- Map.lookup op inversionMap
let oldExpr = "a " ++ op ++ " b"
let newExpr = "a " ++ newOp ++ " b"
let bracket s = if bracketStyle == SingleBracket then "[ " ++ s ++ " ]" else "[[ " ++ s ++ " ]]"
return $
if bangInside
then style id 2335 $ "Use " ++ newExpr ++ " instead of ! " ++ oldExpr ++ "."
else style id 2335 $ "Use " ++ (bracket newExpr) ++ " instead of ! " ++ (bracket oldExpr) ++ "."
prop_checkRedirectionToCommand1 = verify checkRedirectionToCommand "ls > rm" prop_checkRedirectionToCommand1 = verify checkRedirectionToCommand "ls > rm"
prop_checkRedirectionToCommand2 = verifyNot checkRedirectionToCommand "ls > 'rm'" prop_checkRedirectionToCommand2 = verifyNot checkRedirectionToCommand "ls > 'rm'"