typetools / typetools/checker-framework

@RequiresNonNull ignored when taking a method reference

Open
#2,822 0 comments 0 reactions 1 assignee View on GitHub

@smillst is already working on this.

Since Nov 4, 2019.

Dominant language
Java
Stars
1.1k
Forks
440
Avg merge
1d 12h
Merged PRs (30d)
134

Description

Annotating a method with @RequiresNonNull("myField") and then calling that from a lambda or Runnable without an in-line null check fails the checker (expected). But taking a method reference to the same method doesn't fail (which it probably should).

This means the method can be called in an invalid state (with myField == null), meaning the annotation loses a lot of its power.

Demonstration (the problematic line is the first call to ExecutorService#submit inside run()):

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.RequiresNonNull;

public class RequiresNonNullOnMethodReference {

  @MonotonicNonNull private String monotonicField;

  private void run() {
    ExecutorService executorService = Executors.newSingleThreadExecutor();

    // Method reference with outer and no null check
    executorService.submit(this::myMethod);  // Passes check (but should throw error)
    if (monotonicField != null) {
      executorService.submit(this::myMethod);  // Passes check (not really expected, given the 
                                               // lambda & runnable equivalent below fail)
    }

    // Runnable with inner, outer and no null check
    executorService.submit(() -> myMethod());  // Fails check (expected)
    if (monotonicField != null) {
      executorService.submit(() -> myMethod());  // Fails check (mostly expected)
    }
    executorService.submit(
        () -> {
          if (monotonicField != null) {
            myMethod();  // Passes check (expected)
          }
        });

    // Runnable with inner, outer and no null check
    executorService.submit(
        new Runnable() {
          @Override
          public void run() {
            myMethod();  // Fails check (expected)
          }
        });
    if (monotonicField != null) {
      executorService.submit(
          new Runnable() {
            @Override
            public void run() {
              myMethod();  // Fails check (mostly expected)
            }
          });
    }
    executorService.submit(
        new Runnable() {
          @Override
          public void run() {
            if (monotonicField != null) {
              myMethod();  // Passes check (expected)
            }
          }
        });

  executorService.shutdown();
  }

  @RequiresNonNull("monotonicField")
  private void myMethod() {}
}

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.