NullAway icon indicating copy to clipboard operation
NullAway copied to clipboard

JSpecify: some calls to constructor / super not handled correctly

Open msridhar opened this issue 8 months ago • 5 comments

Here's an example with subclasses:

import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

@NullMarked
class Foo {
  interface Supplier<T extends @Nullable Object> {
    T get();
  }
  static class SupplierImpl<T2 extends @Nullable Object> implements Supplier<T2> {
    Supplier<T2> impl;

    SupplierImpl(Supplier<T2> delegate) {
      impl = delegate;
    }

    @Override
    public T2 get() {
      return impl.get();
    }
  }

  static class ConcreteImpl extends SupplierImpl<@Nullable Foo> {
    ConcreteImpl(Supplier<@Nullable Foo> delegate) {
      super(delegate);
    }
  }
}

yields:

Foo.java:24: warning: [NullAway] Cannot pass parameter of type Supplier<@Nullable Foo>, as formal parameter has type Supplier<T2>, which has mismatched type parameter nullability
      super(delegate);
            ^
    (see http://t.uber.com/nullaway )

Or alternatively, replacing ConcreteImpl with:

  static void main() {
    Supplier<@Nullable Foo> sup = () -> null;
    new SupplierImpl<@Nullable Foo>(sup);
  }

yields:

Foo.java:24: warning: [NullAway] Cannot pass parameter of type Supplier<@Nullable Foo>, as formal parameter has type Supplier<T2>, which has mismatched type parameter nullability
    new SupplierImpl<@Nullable Foo>(sup);

Originally posted by @agrieve in #1209

msridhar avatar May 04 '25 17:05 msridhar

Another simple example (which I'm not sure is the same or not):

@NullMarked
class Foo {
  static void main() {
      FutureTask<@Nullable Void> task = new FutureTask<@Nullable Void>(() -> {}, null);
  }
}
Foo.java:8: warning: [NullAway] passing @Nullable parameter 'null' where @NonNull is required
      FutureTask<@Nullable Void> task = new FutureTask<>(() -> {}, null);

This is with a library model that sets:

   public ImmutableSetMultimap<String, Integer> typeVariablesWithNullableUpperBounds() {
        return new ImmutableSetMultimap.Builder<String, Integer>()
                .put("java.util.concurrent.FutureTask", 0).build();
  }

    public ImmutableSet<String> nullMarkedClasses() {
        return typeVariablesWithNullableUpperBounds().keySet();
    }

agrieve avatar May 05 '25 17:05 agrieve

Oh, happens without library model as well:

import org.chromium.build.annotations.Nullable;
import org.chromium.build.annotations.NullMarked;

@NullMarked
class Foo {
  static class Example<T extends @Nullable Object> {
    Example(T thing) {}
  }
  static void main() {
      Example<@Nullable Void> example = new Example<>(null);
  }
}

agrieve avatar May 05 '25 18:05 agrieve

Currently our handling of the diamond operator on constructor calls is limited / non-existent. So that might be the issue here? Sorry, we should document that better.

msridhar avatar May 05 '25 18:05 msridhar

This has the same warning if I fill in the generics:

  static void main() {
      Example<@Nullable Void> example = new Example<@Nullable Void>(null);
  }

agrieve avatar May 05 '25 18:05 agrieve

This has the same warning if I fill in the generics:

  static void main() {
      Example<@Nullable Void> example = new Example<@Nullable Void>(null);
  }

This might get fixed by #1210

msridhar avatar May 05 '25 20:05 msridhar

Modulo handling of the diamond operator, I think the issues from the examples here are fixed, so I'm going to close this one.

msridhar avatar Oct 03 '25 20:10 msridhar