False positive METHOD_RETURN_TYPE_CHANGED for covariant return type overrides (original) (raw)

Summary

When a class overrides an interface method with a covariant return type (i.e. a more
specific subtype), japicmp reports METHOD_RETURN_TYPE_CHANGED and treats this as a
binary incompatibility. However, this is a false positive: the Java compiler automatically
generates a bridge method with the original (erased) return type, ensuring full binary
compatibility for existing callers.

Background

This was discovered in the context of apache/maven-resolver#1888
after upgrading to japicmp 0.25.7 (which correctly fixed bridge-method matching via #507).

The class org.eclipse.aether.util.version.GenericVersionScheme implements VersionScheme
and intentionally uses covariant return types:

// Interface VersionScheme declares: Version parseVersion(String version) throws InvalidVersionSpecificationException; VersionRange parseVersionRange(String range) throws InvalidVersionSpecificationException; VersionConstraint parseVersionConstraint(String constraint) throws InvalidVersionSpecificationException;

// GenericVersionScheme overrides with covariant return types: @Override public GenericVersion parseVersion(String version) { ... } // GenericVersion extends Version

@Override public GenericVersionRange parseVersionRange(String range) { ... } // GenericVersionRange extends VersionRange

@Override public GenericVersionConstraint parseVersionConstraint(String c) { ... } // GenericVersionConstraint extends VersionConstraint

The Java compiler generates bridge methods for each override, so the bytecode of
GenericVersionScheme actually contains both:

japicmp 0.25.7 correctly stopped doing that —but now the change is reported as METHOD_RETURN_TYPE_CHANGED and treated as a binary incompatibility.

Why This Is Not a Binary Incompatibility
Java's covariant return type mechanism is designed precisely to be backward-compatible:

The one legitimate edge case where this CAN break is subclassing: if a third-party class
extends the changed class and overrides the method with the old (non-covariant) return type,
that override silently stops being an override of the new method. This is worth detecting,
but it is a different and less severe category than a full binary break.

Proposed Solution

Introduce a new, distinct compatibility change type for this scenario:
METHOD_RETURN_TYPE_COVARIANT_CHANGED

Conditions for this classification (all must hold):

This new type should be:

PR #508 / Issue #507 — fixed bridge-method matching (prerequisite fix, directly related)
apache/maven-resolver#1888 — real-world trigger for this issue