From 34cdbaa5e0c0ad5f4d961650f8e5aaba844cc560 Mon Sep 17 00:00:00 2001 From: Eisuke Kawashima Date: Tue, 15 Apr 2025 02:19:31 +0900 Subject: [PATCH] feat: avoid double negative of a binary operator in test suggest `[ a != b ]` over `[ ! a = b ]` and `! [ a = b ]`, and so forth. c.f. SC2236 and SC2237 (unary operations) close #3164 --- CHANGELOG.md | 1 + src/ShellCheck/Analytics.hs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f834aa..5b9a692 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - SC2330: Warn about unsupported glob matches with [[ .. ]] in BusyBox. - SC2331: Suggest using standard -e instead of unary -a in tests. - SC2332: Warn about `[ ! -o opt ]` being unconditionally true in Bash. +- SC2335: Avoid double negative of a binary operator in test—suggest `[ a != b ]` over `[ ! a = b ]` and `! [ a = b ]`, and so forth. - SC3062: Warn about bashism `[ -o opt ]`. - Precompiled binaries for Linux riscv64 (linux.riscv64) ### Changed diff --git a/src/ShellCheck/Analytics.hs b/src/ShellCheck/Analytics.hs index 5a10df5..343906e 100644 --- a/src/ShellCheck/Analytics.hs +++ b/src/ShellCheck/Analytics.hs @@ -3992,6 +3992,11 @@ prop_checkInvertedStringTest2 = verify checkInvertedStringTest "! [[ -n $var ]]" prop_checkInvertedStringTest3 = verifyNot checkInvertedStringTest "! [ -x $var ]" prop_checkInvertedStringTest4 = verifyNot checkInvertedStringTest "[[ ! -w $var ]]" prop_checkInvertedStringTest5 = verifyNot checkInvertedStringTest "[ -z $var ]" +prop_checkInvertedStringTest6 = verify checkInvertedStringTest "! [ $var != foo ]" +prop_checkInvertedStringTest7 = verify checkInvertedStringTest "[[ ! $var == foo ]]" +prop_checkInvertedStringTest8 = verifyNot checkInvertedStringTest "! [[ $var =~ .* ]]" +prop_checkInvertedStringTest9 = verify checkInvertedStringTest "[ ! $var -eq 0 ]" +prop_checkInvertedStringTest10 = verify checkInvertedStringTest "! [[ $var -gt 3 ]]" checkInvertedStringTest _ t = case t of TC_Unary _ _ "!" (TC_Unary _ _ op _) -> @@ -3999,12 +4004,37 @@ checkInvertedStringTest _ t = "-n" -> style (getId t) 2236 "Use -z instead of ! -n." "-z" -> style (getId t) 2236 "Use -n instead of ! -z." _ -> return () + TC_Unary _ _ "!" (TC_Binary _ _ op _ _) -> + case op of + "=" -> style (getId t) 2335 "Use a != b instead of ! a = b." + "==" -> style (getId t) 2335 "Use a != b instead of ! a == b." + "!=" -> style (getId t) 2335 "Use a = b instead of ! a != b." + "-eq" -> style (getId t) 2335 "Use a -ne b instead of ! a -eq b." + "-ne" -> style (getId t) 2335 "Use a -eq b instead of ! a -ne b." + "-gt" -> style (getId t) 2335 "Use a -le b instead of ! a -gt b." + "-ge" -> style (getId t) 2335 "Use a -lt b instead of ! a -ge b." + "-lt" -> style (getId t) 2335 "Use a -ge b instead of ! a -lt b." + "-le" -> style (getId t) 2335 "Use a -gt b instead of ! a -le b." + _ -> return () T_Banged _ (T_Pipeline _ _ [T_Redirecting _ _ (T_Condition _ _ (TC_Unary _ _ op _))]) -> case op of "-n" -> style (getId t) 2237 "Use [ -z .. ] instead of ! [ -n .. ]." "-z" -> style (getId t) 2237 "Use [ -n .. ] instead of ! [ -z .. ]." _ -> return () + T_Banged _ (T_Pipeline _ _ + [T_Redirecting _ _ (T_Condition _ _ (TC_Binary _ _ op _ _))]) -> + case op of + "=" -> style (getId t) 2335 "Use [ a != b ] instead of ! [ a = b ]." + "==" -> style (getId t) 2335 "Use [[ a != b ]] instead of ! [[ a == b ]]." + "!=" -> style (getId t) 2335 "Use [ a = b ] instead of ! [ a != b ]." + "-eq" -> style (getId t) 2335 "Use [ a -ne b ] instead of ! [ a -eq b ]." + "-ne" -> style (getId t) 2335 "Use [ a -eq b ] instead of ! [ a -ne b ]." + "-gt" -> style (getId t) 2335 "Use [ a -le b ] instead of ! [ a -gt b ]." + "-ge" -> style (getId t) 2335 "Use [ a -lt b ] instead of ! [ a -ge b ]." + "-lt" -> style (getId t) 2335 "Use [ a -ge b ] instead of ! [ a -lt b ]." + "-le" -> style (getId t) 2335 "Use [ a -gt b ] instead of ! [ a -le b ]." + _ -> return () _ -> return () prop_checkRedirectionToCommand1 = verify checkRedirectionToCommand "ls > rm"