[ 
https://issues.apache.org/jira/browse/GROOVY-12699?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18138766#comment-18138766
 ] 

ASF GitHub Bot commented on GROOVY-12699:
-----------------------------------------

codecov-commenter commented on PR #3223:
URL: https://github.com/apache/groovy/pull/3223#issuecomment-6658777923

   ## 
[Codecov](https://app.codecov.io/gh/apache/groovy/pull/3223?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)
 Report
   :white_check_mark: All modified and coverable lines are covered by tests.
   :white_check_mark: Project coverage is 74.1763%. Comparing base 
([`38b012e`](https://app.codecov.io/gh/apache/groovy/commit/38b012ed693b6d791a7efde760056e4086b8b0fc?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache))
 to head 
([`87c9a5f`](https://app.codecov.io/gh/apache/groovy/commit/87c9a5f84fce8a4b19e833eb9880bfbd4df46fb2?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)).
   :warning: Report is 4 commits behind head on master.
   
   <details><summary>Additional details and impacted files</summary>
   
   
   
   [![Impacted file tree 
graph](https://app.codecov.io/gh/apache/groovy/pull/3223/graphs/tree.svg?width=650&height=150&src=pr&token=1r45138NfQ&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)](https://app.codecov.io/gh/apache/groovy/pull/3223?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)
   
   ```diff
   @@                Coverage Diff                 @@
   ##               master      #3223        +/-   ##
   ==================================================
   + Coverage     74.1656%   74.1763%   +0.0107%     
   - Complexity      43752      43764        +12     
   ==================================================
     Files            1692       1692                
     Lines          149591     149599         +8     
     Branches        28382      28386         +4     
   ==================================================
   + Hits           110945     110967        +22     
   + Misses          28435      28428         -7     
   + Partials        10211      10204         -7     
   ```
   
   | [Files with missing 
lines](https://app.codecov.io/gh/apache/groovy/pull/3223?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)
 | Coverage Δ | |
   |---|---|---|
   | 
[...untime/typehandling/DefaultTypeTransformation.java](https://app.codecov.io/gh/apache/groovy/pull/3223?src=pr&el=tree&filepath=src%2Fmain%2Fjava%2Forg%2Fcodehaus%2Fgroovy%2Fruntime%2Ftypehandling%2FDefaultTypeTransformation.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-c3JjL21haW4vamF2YS9vcmcvY29kZWhhdXMvZ3Jvb3Z5L3J1bnRpbWUvdHlwZWhhbmRsaW5nL0RlZmF1bHRUeXBlVHJhbnNmb3JtYXRpb24uamF2YQ==)
 | `64.8855% <100.0000%> (ø)` | |
   
   ... and [17 files with indirect coverage 
changes](https://app.codecov.io/gh/apache/groovy/pull/3223/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)
   </details>
   <details><summary> :rocket: New features to boost your workflow: </summary>
   
   - :snowflake: [Test 
Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, 
report on failures, and find test suite problems.
   - :package: [JS Bundle 
Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save 
yourself from yourself by tracking and limiting bundle sizes in JS merges.
   </details>




> == on a non-Comparable Number ignores numeric equality that <=> and < honour
> ----------------------------------------------------------------------------
>
>                 Key: GROOVY-12699
>                 URL: https://issues.apache.org/jira/browse/GROOVY-12699
>             Project: Groovy
>          Issue Type: Bug
>            Reporter: Paul King
>            Priority: Major
>
> h3. Description
> {{compareToWithEqualityCheck}} accepts a {{Number}} on the left even when it 
> isn't {{Comparable}}, but {{compareEqual}} doesn't. So for a {{Number}} 
> subclass that doesn't implement {{Comparable}}, {{<=>}}, {{<}} and friends 
> compare numerically (via {{NumberMath}}), while {{==}} falls through to 
> {{equals()}}:
> {code:groovy}
> class Frac extends Number {               // a Number, but not Comparable
>     final BigInteger n, d
>     Frac(long n, long d) { this.n = n; this.d = d }
>     int intValue()       { (n / d) as int }
>     long longValue()     { (n / d) as long }
>     float floatValue()   { doubleValue() as float }
>     double doubleValue() { n.doubleValue() / d.doubleValue() }
>     String toString()    { "$n/$d" }
> }
> def f = new Frac(1, 4)
> assert (f <=> 0.25) == 0     // numerically equal
> assert !(f > 0.25) && !(f < 0.25)
> assert !(f == 0.25)          // but not ==
> assert !(f == 0.25d)
> assert ![f].contains(0.25)
> {code}
> A real-world case is JSR 385's reference implementation, indriya, whose 
> quantity values are often a {{tech.units.indriya.function.RationalNumber}} 
> ({{extends Number}}, not {{Comparable}}):
> {code:groovy}
> @Grab('tech.units:indriya:2.2.4')
> import tech.units.indriya.function.RationalNumber
> import tech.units.indriya.quantity.Quantities
> import static tech.units.indriya.unit.Units.METRE
> import static javax.measure.MetricPrefix.CENTI
> def v = Quantities.getQuantity(1, CENTI(METRE)).to(METRE).value
> assert v instanceof RationalNumber
> assert (v <=> 0.01) == 0
> assert !(v == 0.01)                         // surprising
> assert !(RationalNumber.of(4, 1) == 4)      // even a whole-number value
> {code}
> The same happens under {{@CompileStatic}}: both {{==}} and {{<=>}} compile to 
> the same runtime helpers ({{ScriptBytecodeAdapter.compareEqual}} / 
> {{ScriptBytecodeAdapter.compareTo}}, a primitive operand boxed first), whose 
> same-class JDK fast path doesn't apply here, so they reach 
> {{DefaultTypeTransformation}} exactly as dynamic code does:
> {code:groovy}
> @groovy.transform.CompileStatic
> class CS {
>     static boolean eq(Number f)  { f == 0.25G }     // false for new Frac(1, 
> 4)
>     static boolean eqD(Number f) { f == 0.25d }     // false
>     static int cmp(Frac f)       { f <=> 0.25G }    // 0
>     static boolean rev(Frac f)   { 0.25G == f }     // true
> }
> {code}
> Observed on 6.0.0, dynamic and {{@CompileStatic}} alike (and still true in 
> master).
> h3. Cause
> {{DefaultTypeTransformation.compareToWithEqualityCheck}} opens with
> {code:java}
> if (left instanceof Comparable || left instanceof Number) {
>     if (left instanceof Number) {
>         if (right instanceof Character || right instanceof Number) {
>             return DefaultGroovyMethods.compareTo((Number) left, 
> castToNumber(right));
> {code}
> so {{compareTo}} (used by {{<=>}}, {{<}}, ...) reaches 
> {{NumberMath.compareTo}} for any {{Number}}, and {{NumberMath.getMath}} 
> deliberately maps an unknown {{Number}} to {{BigDecimalMath}} ("also for 
> custom Number implementations"). But {{compareEqual}} only routes through 
> that method under
> {code:java}
> if (left instanceof Comparable) {
>     return compareToWithEqualityCheck(left, right, true) == 0;
> }
> {code}
> with no matching {{Number}} clause, so a non-Comparable {{Number}} falls 
> through to {{left.equals(right)}}.
> h3. Suggested fix
> Route a {{Number}}-vs-{{Number}} comparison numerically in {{compareEqual}} 
> too:
> {code:java}
> if (left instanceof Comparable || (left instanceof Number && right instanceof 
> Number)) {
>     return compareToWithEqualityCheck(left, right, true) == 0;
> }
> {code}
> Requiring both sides to be numbers matters: widening to {{left instanceof 
> Number}} alone would send e.g. {{frac == 'hello'}} into the GROOVY-7954 
> {{right instanceof Comparable}} branch, whose {{invokeMethod(left, 
> "compareTo", right)}} throws {{MissingMethodException}} for a non-Comparable 
> left (only {{ClassCastException}} is caught there). With both numbers, the 
> {{Number}} branch always returns before that.
> h3. Compatibility
> A non-Comparable {{Number}} whose {{equals()}} intentionally differs from 
> numeric equality would see {{==}} change. That seems unlikely to be relied on 
> given {{<=>}} already reports the numeric answer, but it may merit a 
> breaking-change note. Note also that the numeric route converts an unknown 
> {{Number}} via {{new BigDecimal(n.toString())}}, falling back to 
> {{doubleValue()}}, which is what {{<=>}} already does today.



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to