diff --git a/CHANGELOG.md b/CHANGELOG.md index f7e5ce20d..4247f75d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- A check for Scala with its `-Xcheckinit` flag switched on. This flag generates a field that should have been marked as synthetic, but isn't, so EqualsVerifier has to check for this field explicitly. + ## [3.15.3] - 2023-11-01 ### Changed diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/FieldIterable.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/FieldIterable.java index d8be8deb0..b50d3f57c 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/FieldIterable.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/FieldIterable.java @@ -85,7 +85,11 @@ private List addFieldsFor(Class c) { List statics = new ArrayList<>(); for (Field field : c.getDeclaredFields()) { - if (!field.isSynthetic() && !"__cobertura_counters".equals(field.getName())) { + if ( + !field.isSynthetic() && + !"__cobertura_counters".equals(field.getName()) && + !field.getName().startsWith("bitmap$init$") // Generated by Scala 2.x's -Xcheckinit flag + ) { boolean isStatic = Modifier.isStatic(field.getModifiers()); if (isStatic && includeStatic) { statics.add(field); diff --git a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/integration/extended_contract/SyntheticFieldsTest.java b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/integration/extended_contract/SyntheticFieldsTest.java index 211c4b334..20d7f1a21 100644 --- a/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/integration/extended_contract/SyntheticFieldsTest.java +++ b/equalsverifier-core/src/test/java/nl/jqno/equalsverifier/integration/extended_contract/SyntheticFieldsTest.java @@ -36,6 +36,11 @@ public void succeed_whenClassIsInstrumentedByCobertura_givenCoberturaDoesntMarkI EqualsVerifier.forClass(CoberturaContainer.class).verify(); } + @Test + public void succeed_whenScalacOptionCheckinitIsEnabled_givenScalaDoesntMarkItsFieldsSynthetic() { + EqualsVerifier.forClass(ScalaCheckinit.class).verify(); + } + static final class LambdaContainer { private static final Comparator COMPARATOR = (c1, c2) -> 0; // A lambda is a synthetic class @@ -152,4 +157,32 @@ public int hashCode() { return defaultHashCode(this); } } + + public static final class ScalaCheckinit { + + // CHECKSTYLE OFF: MemberName + // This field is generated by the Scala 2.x compiler when -Xcheckinit is switched on. + // However, it's not marked as 'synthetic', so EqualsVerifier doesn't ignore it automatically. + private volatile byte bitmap$init$0 = 0; + // CHECKSTYLE ON: MemberName + private final int i; + + public ScalaCheckinit(int i) { + this.i = i; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ScalaCheckinit)) { + return false; + } + ScalaCheckinit p = (ScalaCheckinit) obj; + return p.i == i; + } + + @Override + public int hashCode() { + return Objects.hash(i); + } + } }