124124import org .checkerframework .nullaway .dataflow .cfg .node .MethodInvocationNode ;
125125import org .checkerframework .nullaway .javacutil .ElementUtils ;
126126import org .checkerframework .nullaway .javacutil .TreeUtils ;
127+ import org .jetbrains .annotations .Contract ;
127128import 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