Skip to content

Commit 20d11c1

Browse files
committed
Merge branch 'e-kwsm-tautologically-false'
2 parents 62a8ecf + 47d358c commit 20d11c1

2 files changed

Lines changed: 65 additions & 0 deletions

File tree

src/ShellCheck/ASTLib.hs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,12 @@ getLiteralStringExt more = g
446446
-- Is this token a string literal?
447447
isLiteral t = isJust $ getLiteralString t
448448

449+
-- Is this token a string literal number?
450+
isLiteralNumber t = fromMaybe False $ do
451+
s <- getLiteralString t
452+
guard $ all isDigit s
453+
return True
454+
449455
-- Escape user data for messages.
450456
-- Messages generally avoid repeating user data, but sometimes it's helpful.
451457
e4m = escapeForMessage

src/ShellCheck/Analytics.hs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ nodeChecks = [
123123
,checkCaseAgainstGlob
124124
,checkCommarrays
125125
,checkOrNeq
126+
,checkAndEq
126127
,checkEchoWc
127128
,checkConstantIfs
128129
,checkPipedAssignment
@@ -1634,6 +1635,64 @@ checkOrNeq _ (T_OrIf id lhs rhs) = sequence_ $ do
16341635
checkOrNeq _ _ = return ()
16351636

16361637

1638+
prop_checkAndEq1 = verifyNot checkAndEq "cow=0; foo=0; if [[ $lol -eq cow && $lol -eq foo ]]; then echo foo; fi"
1639+
prop_checkAndEq2 = verifyNot checkAndEq "lol=0 foo=0; (( a==lol && a==foo ))"
1640+
prop_checkAndEq3 = verify checkAndEq "[ \"$a\" = lol && \"$a\" = foo ]"
1641+
prop_checkAndEq4 = verifyNot checkAndEq "[ a = $cow && b = $foo ]"
1642+
prop_checkAndEq5 = verifyNot checkAndEq "[[ $a = /home && $a = */public_html/* ]]"
1643+
prop_checkAndEq6 = verify checkAndEq "[ $a = a ] && [ $a = b ]"
1644+
prop_checkAndEq7 = verify checkAndEq "[ $a = a ] && [ $a = b ] || true"
1645+
prop_checkAndEq8 = verifyNot checkAndEq "[[ $a == x && $a == x ]]"
1646+
prop_checkAndEq9 = verifyNot checkAndEq "[ 0 -eq $FOO ] && [ 0 -eq $BAR ]"
1647+
prop_checkAndEq10 = verify checkAndEq "(( a == 1 && a == 2 ))"
1648+
prop_checkAndEq11 = verify checkAndEq "[ $x -eq 1 ] && [ $x -eq 2 ]"
1649+
prop_checkAndEq12 = verify checkAndEq "[ 1 -eq $x ] && [ $x -eq 2 ]"
1650+
prop_checkAndEq13 = verifyNot checkAndEq "[ 1 -eq $x ] && [ $x -eq 1 ]"
1651+
prop_checkAndEq14 = verifyNot checkAndEq "[ $a = $b ] && [ $a = $c ]"
1652+
1653+
checkAndEqOperands "-eq" rhs1 rhs2 = isLiteralNumber rhs1 && isLiteralNumber rhs2
1654+
checkAndEqOperands op rhs1 rhs2 | op == "=" || op == "==" = isLiteral rhs1 && isLiteral rhs2
1655+
checkAndEqOperands _ _ _ = False
1656+
1657+
-- For test-level "and": [ x = y -a x = z ]
1658+
checkAndEq _ (TC_And id typ op (TC_Binary _ _ op1 lhs1 rhs1 ) (TC_Binary _ _ op2 lhs2 rhs2))
1659+
| op1 == op2 && lhs1 == lhs2 && rhs1 /= rhs2 && checkAndEqOperands op1 rhs1 rhs2 =
1660+
warn id 2333 $ "You probably wanted " ++ (if typ == SingleBracket then "-o" else "||") ++ " here, otherwise it's always false."
1661+
1662+
-- For arithmetic context "and"
1663+
checkAndEq _ (TA_Binary id "&&" (TA_Binary _ "==" lhs1 rhs1) (TA_Binary _ "==" lhs2 rhs2))
1664+
| lhs1 == lhs2 && isLiteralNumber rhs1 && isLiteralNumber rhs2 =
1665+
warn id 2334 "You probably wanted || here, otherwise it's always false."
1666+
1667+
-- For command level "and": [ x = y ] && [ x = z ]
1668+
checkAndEq _ (T_AndIf id lhs rhs) = sequence_ $ do
1669+
(lhs1, op1, rhs1) <- getExpr lhs
1670+
(lhs2, op2, rhs2) <- getExpr rhs
1671+
guard $ op1 == op2
1672+
guard $ lhs1 == lhs2 && rhs1 /= rhs2
1673+
guard $ checkAndEqOperands op1 rhs1 rhs2
1674+
return $ warn id 2333 "You probably wanted || here, otherwise it's always false."
1675+
where
1676+
getExpr x =
1677+
case x of
1678+
T_AndIf _ lhs _ -> getExpr lhs -- Fetches x and y in `T_AndIf x (T_AndIf y z)`
1679+
T_Pipeline _ _ [x] -> getExpr x
1680+
T_Redirecting _ _ c -> getExpr c
1681+
T_Condition _ _ c -> getExpr c
1682+
TC_Binary _ _ op lhs rhs -> orient (lhs, op, rhs)
1683+
_ -> Nothing
1684+
1685+
-- Swap items so that the constant side is rhs (or Nothing if both/neither is constant)
1686+
orient (lhs, op, rhs) =
1687+
case (isConstant lhs, isConstant rhs) of
1688+
(True, False) -> return (rhs, op, lhs)
1689+
(False, True) -> return (lhs, op, rhs)
1690+
_ -> Nothing
1691+
1692+
1693+
checkAndEq _ _ = return ()
1694+
1695+
16371696
prop_checkValidCondOps1 = verify checkValidCondOps "[[ a -xz b ]]"
16381697
prop_checkValidCondOps2 = verify checkValidCondOps "[ -M a ]"
16391698
prop_checkValidCondOps2a = verifyNot checkValidCondOps "[ 3 \\> 2 ]"

0 commit comments

Comments
 (0)