Skip to content

Commit b6c0673

Browse files
committed
Merge branch 'e-kwsm-fix-3164'
2 parents bbd5d21 + 5e63835 commit b6c0673

File tree

2 files changed

+49
-7
lines changed

2 files changed

+49
-7
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,14 @@
66
- SC2331: Suggest using standard -e instead of unary -a in tests.
77
- SC2332: Warn about `[ ! -o opt ]` being unconditionally true in Bash.
88
- SC3062: Warn about bashism `[ -o opt ]`.
9+
- Optional `avoid-negated-conditions`: suggest replacing `[ ! a -eq b ]`
10+
with `[ a -ne b ]`, and similar for -ge/-lt/=/!=/etc (SC2335).
911
- Precompiled binaries for Linux riscv64 (linux.riscv64)
1012
### Changed
1113
- SC2002 about Useless Use Of Cat is now disabled by default. It can be
1214
re-enabled with `--enable=useless-use-of-cat` or equivalent directive.
15+
- SC2236/SC2237 about replacing `[ ! -n .. ]` with `[ -z ]` and vice versa
16+
is now optional under `avoid-negated-conditions`.
1317
- SC2015 about `A && B || C` no longer triggers when B is a test command.
1418
- SC3012: Do not warn about `\<` and `\>` in test/[] as specified in POSIX.1-2024
1519
- Diff output now uses / as path separator on Windows

src/ShellCheck/Analytics.hs

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,6 @@ nodeChecks = [
183183
,checkPipeToNowhere
184184
,checkForLoopGlobVariables
185185
,checkSubshelledTests
186-
,checkInvertedStringTest
187186
,checkRedirectionToCommand
188187
,checkDollarQuoteParen
189188
,checkUselessBang
@@ -233,6 +232,13 @@ optionalTreeChecks = [
233232
cdNegative = "[ -n \"$var\" ]"
234233
}, nodeChecksToTreeCheck [checkNullaryExpansionTest])
235234

235+
,(newCheckDescription {
236+
cdName = "avoid-negated-conditions",
237+
cdDescription = "Suggest removing unnecessary comparison negations",
238+
cdPositive = "[ ! \"$var\" -eq 1 ]",
239+
cdNegative = "[ \"$var\" -ne 1 ]"
240+
}, nodeChecksToTreeCheck [checkUnnecessarilyInvertedTest])
241+
236242
,(newCheckDescription {
237243
cdName = "add-default-case",
238244
cdDescription = "Suggest adding a default case in `case` statements",
@@ -3991,12 +3997,17 @@ checkSubshelledTests params t =
39913997
T_Annotation {} -> True
39923998
_ -> False
39933999

3994-
prop_checkInvertedStringTest1 = verify checkInvertedStringTest "[ ! -z $var ]"
3995-
prop_checkInvertedStringTest2 = verify checkInvertedStringTest "! [[ -n $var ]]"
3996-
prop_checkInvertedStringTest3 = verifyNot checkInvertedStringTest "! [ -x $var ]"
3997-
prop_checkInvertedStringTest4 = verifyNot checkInvertedStringTest "[[ ! -w $var ]]"
3998-
prop_checkInvertedStringTest5 = verifyNot checkInvertedStringTest "[ -z $var ]"
3999-
checkInvertedStringTest _ t =
4000+
prop_checkUnnecessarilyInvertedTest1 = verify checkUnnecessarilyInvertedTest "[ ! -z $var ]"
4001+
prop_checkUnnecessarilyInvertedTest2 = verify checkUnnecessarilyInvertedTest "! [[ -n $var ]]"
4002+
prop_checkUnnecessarilyInvertedTest3 = verifyNot checkUnnecessarilyInvertedTest "! [ -x $var ]"
4003+
prop_checkUnnecessarilyInvertedTest4 = verifyNot checkUnnecessarilyInvertedTest "[[ ! -w $var ]]"
4004+
prop_checkUnnecessarilyInvertedTest5 = verifyNot checkUnnecessarilyInvertedTest "[ -z $var ]"
4005+
prop_checkUnnecessarilyInvertedTest6 = verify checkUnnecessarilyInvertedTest "! [ $var != foo ]"
4006+
prop_checkUnnecessarilyInvertedTest7 = verify checkUnnecessarilyInvertedTest "[[ ! $var == foo ]]"
4007+
prop_checkUnnecessarilyInvertedTest8 = verifyNot checkUnnecessarilyInvertedTest "! [[ $var =~ .* ]]"
4008+
prop_checkUnnecessarilyInvertedTest9 = verify checkUnnecessarilyInvertedTest "[ ! $var -eq 0 ]"
4009+
prop_checkUnnecessarilyInvertedTest10 = verify checkUnnecessarilyInvertedTest "! [[ $var -gt 3 ]]"
4010+
checkUnnecessarilyInvertedTest _ t =
40004011
case t of
40014012
TC_Unary _ _ "!" (TC_Unary _ _ op _) ->
40024013
case op of
@@ -4009,7 +4020,34 @@ checkInvertedStringTest _ t =
40094020
"-n" -> style (getId t) 2237 "Use [ -z .. ] instead of ! [ -n .. ]."
40104021
"-z" -> style (getId t) 2237 "Use [ -n .. ] instead of ! [ -z .. ]."
40114022
_ -> return ()
4023+
TC_Unary _ _ "!" (TC_Binary _ bracketStyle op _ _) ->
4024+
maybeSuggestRewrite True bracketStyle (getId t) op
4025+
T_Banged _ (T_Pipeline _ _
4026+
[T_Redirecting _ _ (T_Condition _ _ (TC_Binary _ bracketStyle op _ _))]) ->
4027+
maybeSuggestRewrite False bracketStyle (getId t) op
40124028
_ -> return ()
4029+
where
4030+
inversionMap = Map.fromList [
4031+
("=", "!="),
4032+
("==", "!="),
4033+
("!=", "="),
4034+
("-eq", "-ne"),
4035+
("-ne", "-eq"),
4036+
("-le", "-gt"),
4037+
("-gt", "-le"),
4038+
("-ge", "-lt"),
4039+
("-lt", "-ge")
4040+
]
4041+
maybeSuggestRewrite bangInside bracketStyle id op = sequence_ $ do
4042+
newOp <- Map.lookup op inversionMap
4043+
let oldExpr = "a " ++ op ++ " b"
4044+
let newExpr = "a " ++ newOp ++ " b"
4045+
let bracket s = if bracketStyle == SingleBracket then "[ " ++ s ++ " ]" else "[[ " ++ s ++ " ]]"
4046+
return $
4047+
if bangInside
4048+
then style id 2335 $ "Use " ++ newExpr ++ " instead of ! " ++ oldExpr ++ "."
4049+
else style id 2335 $ "Use " ++ (bracket newExpr) ++ " instead of ! " ++ (bracket oldExpr) ++ "."
4050+
40134051

40144052
prop_checkRedirectionToCommand1 = verify checkRedirectionToCommand "ls > rm"
40154053
prop_checkRedirectionToCommand2 = verifyNot checkRedirectionToCommand "ls > 'rm'"

0 commit comments

Comments
 (0)