Skip to content

Commit

Permalink
Merge branch 'master' into jspecify-array-handling
Browse files Browse the repository at this point in the history
  • Loading branch information
armughan11 authored Oct 19, 2023
2 parents 6a19f36 + 8f270e2 commit d1e0cb2
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 14 deletions.
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
Changelog
=========
Version 0.10.15
---------------
* [IMPORTANT] Update minimum Error Prone version and Guava version (#843)
NullAway now requires Error Prone 2.10.0 or later
* Add Spring mock/testing annotations to excluded field annotation list (#757)
* Update to Checker Framework 3.39.0 (#839) [Support for JDK 21 constructs]
* Support for JSpecify's 0.3.0 annotation [experimental]
- Properly check generic method overriding in explicitly-typed anonymous classes (#808)
- JSpecify: handle incorrect method parameter nullability for method reference (#845)
- JSpecify: initial handling of generic enclosing types for inner classes (#837)
* Build / CI tooling for NullAway itself:
- Update Gradle and a couple of plugin versions (#832)
- Run recent JDK tests on JDK 21 (#834)
- Fix which JDKs are installed on CI (#835)
- Update to Error Prone 2.22.0 (#833)
- Ignore code coverage for method executed non-deterministically in tests (#838 and #844)
- Build NullAway with JSpecify mode enabled (#841)

Version 0.10.14
---------------
IMPORTANT: This version introduces EXPERIMENTAL JDK21 support.
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ org.gradle.caching=true
org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m

GROUP=com.uber.nullaway
VERSION_NAME=0.10.15-SNAPSHOT
VERSION_NAME=0.10.16-SNAPSHOT

POM_DESCRIPTION=A fast annotation-based null checker for Java

Expand Down
14 changes: 7 additions & 7 deletions nullaway/src/main/java/com/uber/nullaway/GenericsChecks.java
Original file line number Diff line number Diff line change
Expand Up @@ -702,11 +702,6 @@ public static void checkTypeParameterNullnessForMethodOverriding(
public static Nullness getGenericMethodReturnTypeNullness(
Symbol.MethodSymbol method, Symbol enclosingSymbol, VisitorState state, Config config) {
Type enclosingType = getTypeForSymbol(enclosingSymbol, state);
if (enclosingType == null) {
// we have no additional information from generics, so return NONNULL (presence of a @Nullable
// annotation should have been handled by the caller)
return Nullness.NONNULL;
}
return getGenericMethodReturnTypeNullness(method, enclosingType, state, config);
}

Expand Down Expand Up @@ -738,8 +733,13 @@ private static Type getTypeForSymbol(Symbol symbol, VisitorState state) {
}
}

private static Nullness getGenericMethodReturnTypeNullness(
Symbol.MethodSymbol method, Type enclosingType, VisitorState state, Config config) {
static Nullness getGenericMethodReturnTypeNullness(
Symbol.MethodSymbol method, @Nullable Type enclosingType, VisitorState state, Config config) {
if (enclosingType == null) {
// we have no additional information from generics, so return NONNULL (presence of a @Nullable
// annotation should have been handled by the caller)
return Nullness.NONNULL;
}
Type overriddenMethodType = state.getTypes().memberType(enclosingType, method);
verify(
overriddenMethodType instanceof ExecutableType,
Expand Down
25 changes: 19 additions & 6 deletions nullaway/src/main/java/com/uber/nullaway/NullAway.java
Original file line number Diff line number Diff line change
Expand Up @@ -957,7 +957,8 @@ private Description checkOverriding(
// if the super method returns nonnull, overriding method better not return nullable
// Note that, for the overriding method, the permissive default is non-null,
// but it's nullable for the overridden one.
if (overriddenMethodReturnsNonNull(overriddenMethod, overridingMethod.owner, state)
if (overriddenMethodReturnsNonNull(
overriddenMethod, overridingMethod.owner, memberReferenceTree, state)
&& getMethodReturnNullness(overridingMethod, state, Nullness.NONNULL)
.equals(Nullness.NULLABLE)
&& (memberReferenceTree == null
Expand Down Expand Up @@ -996,18 +997,30 @@ && getMethodReturnNullness(overridingMethod, state, Nullness.NONNULL)
}

private boolean overriddenMethodReturnsNonNull(
Symbol.MethodSymbol overriddenMethod, Symbol enclosingSymbol, VisitorState state) {
Symbol.MethodSymbol overriddenMethod,
Symbol enclosingSymbol,
@Nullable MemberReferenceTree memberReferenceTree,
VisitorState state) {
Nullness methodReturnNullness =
getMethodReturnNullness(overriddenMethod, state, Nullness.NULLABLE);
if (!methodReturnNullness.equals(Nullness.NONNULL)) {
return false;
}
// In JSpecify mode, for generic methods, we additionally need to check the return nullness
// using the type parameters from the type enclosing the overriding method
// using the type arguments from the type enclosing the overriding method
if (config.isJSpecifyMode()) {
return GenericsChecks.getGenericMethodReturnTypeNullness(
overriddenMethod, enclosingSymbol, state, config)
.equals(Nullness.NONNULL);
if (memberReferenceTree != null) {
// For a method reference, we get generic type arguments from javac's inferred type for the
// tree, which properly preserves type-use annotations
return GenericsChecks.getGenericMethodReturnTypeNullness(
overriddenMethod, ASTHelpers.getType(memberReferenceTree), state, config)
.equals(Nullness.NONNULL);
} else {
// Use the enclosing class of the overriding method to find generic type arguments
return GenericsChecks.getGenericMethodReturnTypeNullness(
overriddenMethod, enclosingSymbol, state, config)
.equals(Nullness.NONNULL);
}
}
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,106 @@ public void testForMethodReferenceInAnAssignment() {
.doTest();
}

@Test
public void testForMethodReferenceForClassFieldAssignment() {
makeHelper()
.addSourceLines(
"Test.java",
"package com.uber;",
"import org.jspecify.annotations.Nullable;",
"class Test {",
" interface A<T1 extends @Nullable Object> {",
" T1 function(Object o);",
" }",
" static @Nullable String foo(Object o) {",
" return o.toString();",
" }",
" // BUG: Diagnostic contains: referenced method returns @Nullable",
" A<String> positiveField = Test::foo;",
" A<@Nullable String> negativeField = Test::foo;",
"}")
.doTest();
}

@Test
public void testForMethodReferenceReturnTypeInAnAssignment() {
makeHelper()
.addSourceLines(
"Test.java",
"package com.uber;",
"import org.jspecify.annotations.Nullable;",
"class Test {",
" interface A<T1 extends @Nullable Object> {",
" T1 function(Object o);",
" }",
" static @Nullable String foo(Object o) {",
" return o.toString();",
" }",
" static void testPositive() {",
" // BUG: Diagnostic contains: referenced method returns @Nullable",
" A<String> p = Test::foo;",
" }",
" static void testNegative() {",
" A<@Nullable String> p = Test::foo;",
" }",
"}")
.doTest();
}

@Test
public void testForMethodReferenceWhenReturned() {
makeHelper()
.addSourceLines(
"Test.java",
"package com.uber;",
"import org.jspecify.annotations.Nullable;",
"class Test {",
" interface A<T1 extends @Nullable Object> {",
" T1 function(Object o);",
" }",
" static @Nullable String foo(Object o) {",
" return o.toString();",
" }",
" static A<String> testPositive() {",
" // BUG: Diagnostic contains: referenced method returns @Nullable",
" return Test::foo;",
" }",
" static A<@Nullable String> testNegative() {",
" return Test::foo;",
" }",
"}")
.doTest();
}

@Test
public void testForMethodReferenceAsMethodParameter() {
makeHelper()
.addSourceLines(
"Test.java",
"package com.uber;",
"import org.jspecify.annotations.Nullable;",
"class Test {",
" interface A<T1 extends @Nullable Object> {",
" T1 function(Object o);",
" }",
" static @Nullable String foo(Object o) {",
" return o.toString();",
" }",
" static void fooPositive(A<String> a) {",
" }",
" static void fooNegative(A<@Nullable String> a) {",
" }",
" static void testPositive() {",
" // BUG: Diagnostic contains: referenced method returns @Nullable",
" fooPositive(Test::foo);",
" }",
" static void testNegative() {",
" fooNegative(Test::foo);",
" }",
"}")
.doTest();
}

@Test
public void testForLambdasInAnAssignment() {
makeHelper()
Expand Down

0 comments on commit d1e0cb2

Please sign in to comment.