Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@ParametersAreNonnullByDefault should mean fields are too #308

Closed
Pr0methean opened this issue May 19, 2020 · 4 comments
Closed

@ParametersAreNonnullByDefault should mean fields are too #308

Pr0methean opened this issue May 19, 2020 · 4 comments
Labels

Comments

@Pr0methean
Copy link

Pr0methean commented May 19, 2020

What steps will reproduce the problem?

Create a class annotated with @ParametersAreNonnullByDefault and a final object field that's not @Nullable, and test it with EqualsVerifier.

What is the code that triggers this problem?

Class under test:

package io.github.pr0methean.betterrandom.util;

import java.util.Objects;
import javax.annotation.ParametersAreNonnullByDefault;

@ParametersAreNonnullByDefault
public class EqualsVerifierBugExample {
  private final String name;
  private final int age;

  public EqualsVerifierBugExample(String name, int age) {
    this.name = name;
    this.age = age;
  }

  @Override public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }
    EqualsVerifierBugExample that = (EqualsVerifierBugExample) o;
    return age == that.age && name.equals(that.name);
  }

  @Override public int hashCode() {
    return Objects.hash(name, age);
  }
}

Test:

package io.github.pr0methean.betterrandom.util;

import nl.jqno.equalsverifier.EqualsVerifier;
import org.testng.annotations.Test;

public class EqualsVerifierBugExampleTest {
  @Test public void testWithEqualsVerifier() {
    EqualsVerifier.forClass(EqualsVerifierBugExample.class).verify();
  }
}

What error message or stack trace does EqualsVerifier give?

java.lang.AssertionError: EqualsVerifier found a problem in class io.github.pr0methean.betterrandom.util.EqualsVerifierBugExample.
-> Non-nullity: equals throws NullPointerException on field name.

For more information, go to: http://www.jqno.nl/equalsverifier/errormessages

	at nl.jqno.equalsverifier.api.SingleTypeEqualsVerifierApi.verify(SingleTypeEqualsVerifierApi.java:269)
	at io.github.pr0methean.betterrandom.util.EqualsVerifierBugExampleTest.testWithEqualsVerifier(EqualsVerifierBugExampleTest.java:8)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:133)
	at org.testng.internal.TestInvoker.invokeMethod(TestInvoker.java:584)
	at org.testng.internal.TestInvoker.invokeTestMethod(TestInvoker.java:172)
	at org.testng.internal.MethodRunner.runInSequence(MethodRunner.java:46)
	at org.testng.internal.TestInvoker$MethodInvocationAgent.invoke(TestInvoker.java:804)
	at org.testng.internal.TestInvoker.invokeTestMethods(TestInvoker.java:145)
	at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:146)
	at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:128)
	at org.testng.TestRunner$$Lambda$187/0000000000000000.accept(Unknown Source)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
	at org.testng.TestRunner.privateRun(TestRunner.java:770)
	at org.testng.TestRunner.run(TestRunner.java:591)
	at org.testng.SuiteRunner.runTest(SuiteRunner.java:402)
	at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:396)
	at org.testng.SuiteRunner.privateRun(SuiteRunner.java:355)
	at org.testng.SuiteRunner.run(SuiteRunner.java:304)
	at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:53)
	at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:96)
	at org.testng.TestNG.runSuitesSequentially(TestNG.java:1180)
	at org.testng.TestNG.runSuitesLocally(TestNG.java:1102)
	at org.testng.TestNG.runSuites(TestNG.java:1032)
	at org.testng.TestNG.run(TestNG.java:1000)
	at com.intellij.rt.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:66)
	at com.intellij.rt.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:110)
Caused by: nl.jqno.equalsverifier.internal.exceptions.AssertionException
	at nl.jqno.equalsverifier.internal.util.Assert.fail(Assert.java:75)
	at nl.jqno.equalsverifier.internal.checkers.fieldchecks.NullPointerExceptionFieldCheck.npeThrown(NullPointerExceptionFieldCheck.java:71)
	at nl.jqno.equalsverifier.internal.checkers.fieldchecks.NullPointerExceptionFieldCheck.handle(NullPointerExceptionFieldCheck.java:60)
	at nl.jqno.equalsverifier.internal.checkers.fieldchecks.NullPointerExceptionFieldCheck.performTests(NullPointerExceptionFieldCheck.java:49)
	at nl.jqno.equalsverifier.internal.checkers.fieldchecks.NullPointerExceptionFieldCheck.execute(NullPointerExceptionFieldCheck.java:42)
	at nl.jqno.equalsverifier.internal.checkers.FieldInspector.check(FieldInspector.java:26)
	at nl.jqno.equalsverifier.internal.checkers.NullChecker.check(NullChecker.java:23)
	at nl.jqno.equalsverifier.api.SingleTypeEqualsVerifierApi.verifyWithoutExamples(SingleTypeEqualsVerifierApi.java:351)
	at nl.jqno.equalsverifier.api.SingleTypeEqualsVerifierApi.performVerification(SingleTypeEqualsVerifierApi.java:320)
	at nl.jqno.equalsverifier.api.SingleTypeEqualsVerifierApi.verify(SingleTypeEqualsVerifierApi.java:267)
	... 29 more
Caused by: java.lang.NullPointerException
	at io.github.pr0methean.betterrandom.util.EqualsVerifierBugExample.equals(EqualsVerifierBugExample.java:24)
	at nl.jqno.equalsverifier.internal.checkers.fieldchecks.NullPointerExceptionFieldCheck.lambda$performTests$1(NullPointerExceptionFieldCheck.java:49)
	at nl.jqno.equalsverifier.internal.checkers.fieldchecks.NullPointerExceptionFieldCheck$$Lambda$616/0000000000000000.run(Unknown Source)
	at nl.jqno.equalsverifier.internal.checkers.fieldchecks.NullPointerExceptionFieldCheck.handle(NullPointerExceptionFieldCheck.java:58)
	... 36 more

What did you expect?

EqualsVerifier recognizes @javax.annotation.ParametersAreNonnullByDefault and does not try to set any field to null unless it's annotated @Nullable.

Which version of EqualsVerifier are you using?

3.3

Please provide any additional information below.

Static analysis of the constructors would reveal that @Nullable immutable fields are always copied directly from @Nullable constructor parameters. For classes whose initialization isn't provably "trivial" in this sense, a workaround for the field/parameter disconnect might be to create the instances under test using constructors, only passing null to @Nullable parameters, or by cloning other instances that were created this way.

@jqno jqno added the accepted label May 20, 2020
@jqno
Copy link
Owner

jqno commented May 20, 2020

Hi, thanks for reporting this.

Indeed, EqualsVerifier doesn't support this annotation (yet). I wasn't familiar with it -- where does it come from? Do you have a Javadoc page or Maven coordinates or something, so that I can do some tests? I haven't been able to find that on google.

@Pr0methean
Copy link
Author

Pr0methean commented May 23, 2020 via email

@jqno
Copy link
Owner

jqno commented May 25, 2020

Thanks! I'll look into it.

@jqno
Copy link
Owner

jqno commented Jun 13, 2020

I've just released version 3.4, which adds support for @ParametersAreNonnullByDefault.

@jqno jqno closed this as completed Jun 13, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants