COMPARETO_OVERFLOW
Summary
- Rule ID:
COMPARETO_OVERFLOW - Name: compareTo integer subtraction overflow
- Description: Detects
compareToimplementations that use integer subtraction to compute the return value, which can produce incorrect ordering for extreme integer values due to arithmetic overflow. - Annotation policy:
@Suppress-style suppression is unsupported. Annotation-driven semantics support JSpecify only; non-JSpecify annotations are unsupported for this rule.
Motivation
A common Java pitfall is implementing compareTo with integer subtraction:
This appears correct but is broken for extreme values. When this.value = Integer.MAX_VALUE and other.value = -1, the subtraction Integer.MAX_VALUE - (-1) overflows to Integer.MIN_VALUE, which is negative. The comparator incorrectly reports that MAX_VALUE is less than -1, violating the compareTo contract and corrupting sort order in collections such as TreeSet, TreeMap, and PriorityQueue.
This bug is subtle and easy to miss during code review because it only manifests for values near Integer.MAX_VALUE or Integer.MIN_VALUE. The safe replacement is Integer.compare(this.value, other.value).
What it detects
- A method named
compareTowith anintreturn type (descriptor ending in)I) that contains theisub(integer subtract, JVM opcode 0x64) bytecode instruction. - The rule additionally requires that the method does not call
java/lang/Integer.compareorjava/lang/Long.compare, which are the overflow-safe comparison utilities.
What it does NOT detect
compareTomethods that useInteger.compare,Long.compare, or other overflow-safe comparison utilities. These are correct and are excluded.- Long arithmetic subtraction (
lsub) narrowed toint. - Overflow in comparator lambda bodies defined in separate synthetic methods.
- Methods outside analysis target classes.
- Findings based on non-JSpecify annotation semantics.
- Any suppression behavior via
@Suppressor@SuppressWarnings.
Examples (TP/TN/Edge)
TP: direct integer subtraction return (reported)
package com.example;
public class ClassA implements Comparable<ClassA> {
int varOne;
@Override
public int compareTo(ClassA other) {
return this.varOne - other.varOne;
}
}
TP: multi-field comparison using subtraction (reported)
package com.example;
public class ClassB implements Comparable<ClassB> {
int varOne;
int varTwo;
@Override
public int compareTo(ClassB other) {
int diff = this.varOne - other.varOne;
if (diff != 0) return diff;
return this.varTwo - other.varTwo;
}
}
TN: Integer.compare used (not reported)
package com.example;
public class ClassC implements Comparable<ClassC> {
int varOne;
@Override
public int compareTo(ClassC other) {
return Integer.compare(this.varOne, other.varOne);
}
}
TN: String compareTo delegation, no integer subtraction (not reported)
package com.example;
public class ClassD implements Comparable<ClassD> {
String varOne;
@Override
public int compareTo(ClassD other) {
return this.varOne.compareTo(other.varOne);
}
}
Edge: subtraction for intermediate value but Integer.compare for return (not reported)
package com.example;
public class ClassE implements Comparable<ClassE> {
int varOne;
int varTwo;
@Override
public int compareTo(ClassE other) {
int adjustment = this.varTwo - 1; // isub, but excluded because Integer.compare is called
return Integer.compare(this.varOne + adjustment, other.varOne + adjustment);
}
}
Edge: classpath-only class (not reported)
A class compiled as a classpath dependency and not included in the analysis target is not reported, even if it contains a subtraction-based compareTo.
Output
- Report one finding per
compareTomethod that contains integer subtraction without an overflow-safe comparison call. - Message must be actionable and include the method context:
Avoid integer subtraction in compareTo in <class>.<method><descriptor>; use Integer.compare() to prevent overflow. - Location is reported at the method level using method logical location and, where available, the source line of the first
isubinstruction.
Performance considerations
- Analysis is bounded by the number of basic block instructions per
compareTomethod. - Only methods named
compareTowith int return descriptor are scanned; all other methods are skipped. - No inter-method or inter-class analysis is performed.
- Traversal order over blocks and instructions must be deterministic (use the natural iteration order of
method.cfg.blocks).
Acceptance criteria
- Reports when a
compareTomethod (returningint) containsisuband does not callInteger.compareorLong.compare. - Does not report when
Integer.compareorLong.compareis present in the same method. - Does not report methods not named
compareTo. - Does not report
compareTomethods that return non-int types. - Does not report classpath-only classes.
- Covers TP, TN, and edge cases in tests.
- Produces deterministic finding order and count across repeated runs.
- Keeps
@Suppress-style suppression unsupported and does not add non-JSpecify annotation semantics.