Skip to content

Commit 0949a0a

Browse files
committed
Merge branch 'master' into issue-1455
2 parents be220e7 + 09b39c9 commit 0949a0a

File tree

6 files changed

+808
-26
lines changed

6 files changed

+808
-26
lines changed

buildSrc/src/main/groovy/nullaway.java-test-conventions.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ plugins {
2020
}
2121

2222
jacoco {
23-
toolVersion = "0.8.13"
23+
toolVersion = "0.8.14"
2424
}
2525

2626
// Do not generate reports for individual projects

gradle/libs.versions.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ support = "27.1.1"
55
wala = "1.6.12"
66
commons-cli = "1.4"
77
auto-service = "1.1.1"
8-
google-java-format = "1.30.0"
8+
google-java-format = "1.34.1"
99
android-gradle-plugin = "8.13.0"
1010
gradle-maven-publish-plugin = "0.34.0"
11-
spotless = "8.1.0"
12-
errorprone-plugin = "4.4.0"
11+
spotless = "8.2.1"
12+
errorprone-plugin = "5.0.0"
1313
shadow = "8.3.8"
1414
jmh = "0.7.3"
1515
versions-plugin = "0.53.0"
@@ -42,7 +42,7 @@ lombok = "1.18.38"
4242
spring-beans = "5.3.7"
4343
spring-context = "5.3.7"
4444
grpc-core = "1.15.1"
45-
mockito = "5.19.0"
45+
mockito = "5.21.0"
4646
javax-annotation = "1.3.2"
4747
assertj = "3.23.1"
4848
amazon-utils = "2.32.19"

nullaway/src/main/java/com/uber/nullaway/NullAway.java

Lines changed: 100 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@
124124
import org.checkerframework.nullaway.dataflow.cfg.node.MethodInvocationNode;
125125
import org.checkerframework.nullaway.javacutil.ElementUtils;
126126
import org.checkerframework.nullaway.javacutil.TreeUtils;
127+
import org.jetbrains.annotations.Contract;
127128
import org.jspecify.annotations.Nullable;
128129

129130
/**
@@ -808,6 +809,12 @@ private Description checkParamOverriding(
808809
(overridingMethod != null
809810
&& !codeAnnotationInfo.isSymbolUnannotated(overridingMethod, config, handler))
810811
|| lambdaExpressionTree != null;
812+
Type.MethodType jspecifyMemberReferenceMethodType = null;
813+
if (memberReferenceTree != null) {
814+
jspecifyMemberReferenceMethodType =
815+
genericsChecks.getMemberReferenceMethodType(
816+
memberReferenceTree, castToNonNull(overridingMethod), state);
817+
}
811818

812819
// Get argument nullability for the overridden method. If overriddenMethodArgNullnessMap[i] is
813820
// null, parameter i is treated as unannotated.
@@ -897,6 +904,14 @@ private Description checkParamOverriding(
897904
}
898905
int methodParamInd = i - startParam;
899906
VarSymbol paramSymbol = overridingParamSymbols.get(methodParamInd);
907+
boolean paramIsNonNull =
908+
paramOfOverridingMethodIsNonNull(
909+
paramSymbol,
910+
methodParamInd,
911+
overridingMethod,
912+
isOverridingMethodAnnotated,
913+
memberReferenceTree,
914+
jspecifyMemberReferenceMethodType);
900915
// in the case where we have a parameter of a lambda expression, we do
901916
// *not* force the parameter to be annotated with @Nullable; instead we "inherit"
902917
// nullability from the corresponding functional interface method.
@@ -906,7 +921,7 @@ private Description checkParamOverriding(
906921
lambdaExpressionTree != null
907922
&& NullabilityUtil.lambdaParamIsImplicitlyTyped(
908923
lambdaExpressionTree.getParameters().get(methodParamInd));
909-
if (!implicitlyTypedLambdaParam && paramIsNonNull(paramSymbol, isOverridingMethodAnnotated)) {
924+
if (!implicitlyTypedLambdaParam && paramIsNonNull) {
910925
String message =
911926
"parameter "
912927
+ paramSymbol.name.toString()
@@ -945,14 +960,94 @@ private Description checkParamOverriding(
945960
return Description.NO_MATCH;
946961
}
947962

948-
private boolean paramIsNonNull(VarSymbol paramSymbol, boolean isMethodAnnotated) {
963+
/**
964+
* Checks if the parameter of the overriding method is {@code @NonNull}
965+
*
966+
* @param paramSymbol the symbol for the parameter of the overriding method
967+
* @param methodParamInd the index of the parameter in the method signature of the overriding
968+
* method (adjusted for unbound member references)
969+
* @param overridingMethod if available, the symbol for the overriding method
970+
* @param isMethodAnnotated whether the overriding method is annotated
971+
* @param memberReferenceTree if the overriding method is a member reference, the tree for the
972+
* member reference; otherwise {@code null}
973+
* @param memberReferenceMethodType if the overriding method is a member reference, the method
974+
* type of the member reference after handling generics; otherwise {@code null}
975+
* @return true if the parameter of the overriding method is effectively {@code @NonNull}, false
976+
* otherwise
977+
*/
978+
private boolean paramOfOverridingMethodIsNonNull(
979+
VarSymbol paramSymbol,
980+
int methodParamInd,
981+
Symbol.@Nullable MethodSymbol overridingMethod,
982+
boolean isMethodAnnotated,
983+
@Nullable MemberReferenceTree memberReferenceTree,
984+
Type.@Nullable MethodType memberReferenceMethodType) {
985+
boolean result = false;
949986
if (isMethodAnnotated) {
950-
return !Nullness.hasNullableAnnotation(paramSymbol, config);
987+
result = !Nullness.hasNullableAnnotation(paramSymbol, config);
951988
} else if (config.acknowledgeRestrictiveAnnotations()) {
952989
// can still be @NonNull if there is a restrictive annotation
953-
return Nullness.hasNonNullAnnotation(paramSymbol, config);
990+
result = Nullness.hasNonNullAnnotation(paramSymbol, config);
991+
}
992+
if (result && memberReferenceMethodType != null) {
993+
// when the overriding method is a member reference, also check that the parameter is not
994+
// effectively @Nullable due to generics. memberReferenceMethodType should be the method type
995+
// of the member reference after handling generics
996+
com.sun.tools.javac.util.List<Type> parameterTypes =
997+
memberReferenceMethodType.getParameterTypes();
998+
int memberRefParamIndex =
999+
getMemberRefParamIndex(methodParamInd, overridingMethod, memberReferenceTree);
1000+
Type paramType = parameterTypes.get(memberRefParamIndex);
1001+
// if we have a method reference to a varargs method where varargs are passed individually
1002+
// (not as an array), and this parameter is the varargs
1003+
// parameter, then we need to check the nullability of the varargs element type
1004+
if (memberRefToVarargsPassedIndividually(overridingMethod, memberReferenceTree)
1005+
&& methodParamInd >= overridingMethod.getParameters().size() - 1) {
1006+
Verify.verify(
1007+
paramType.getKind() == TypeKind.ARRAY,
1008+
"Expected array type for varargs parameter in %s, got %s",
1009+
memberReferenceTree,
1010+
paramType);
1011+
paramType = ((Type.ArrayType) paramType).getComponentType();
1012+
}
1013+
result = !Nullness.hasNullableAnnotation(paramType.getAnnotationMirrors().stream(), config);
9541014
}
955-
return false;
1015+
return result;
1016+
}
1017+
1018+
/**
1019+
* Checks if we have a member reference to a varargs method where the varargs are passed
1020+
* individually rather than as an array
1021+
*
1022+
* @param referencedMethod if available, the symbol for the referenced method
1023+
* @param memberReferenceTree the tree for the member reference
1024+
* @return true if we have a member reference to a varargs method where the varargs are passed
1025+
* individually
1026+
*/
1027+
@Contract("null, _ -> false; _, null -> false")
1028+
private static boolean memberRefToVarargsPassedIndividually(
1029+
Symbol.@Nullable MethodSymbol referencedMethod,
1030+
@Nullable MemberReferenceTree memberReferenceTree) {
1031+
return memberReferenceTree != null
1032+
&& referencedMethod != null
1033+
&& referencedMethod.isVarArgs()
1034+
&& ((JCTree.JCMemberReference) memberReferenceTree).varargsElement != null;
1035+
}
1036+
1037+
private static int getMemberRefParamIndex(
1038+
int methodParamInd,
1039+
Symbol.@Nullable MethodSymbol overridingMethod,
1040+
@Nullable MemberReferenceTree memberReferenceTree) {
1041+
int memberRefParamIndex = methodParamInd;
1042+
if (memberRefToVarargsPassedIndividually(overridingMethod, memberReferenceTree)) {
1043+
// With varargs adaptation, one or more functional-interface parameters can map to the
1044+
// varargs element type of the referenced method.
1045+
int varargsParamIndex = overridingMethod.getParameters().size() - 1;
1046+
if (methodParamInd >= varargsParamIndex) {
1047+
memberRefParamIndex = varargsParamIndex;
1048+
}
1049+
}
1050+
return memberRefParamIndex;
9561051
}
9571052

9581053
static Trees getTreesInstance(VisitorState state) {

0 commit comments

Comments
 (0)