/** Check null-ness of 'var' against a possible null annotation */ protected int checkAssignmentAgainstNullAnnotation( BlockScope currentScope, FlowContext flowContext, VariableBinding var, int nullStatus, Expression expression, TypeBinding providedType) { int severity = 0; if ((var.tagBits & TagBits.AnnotationNonNull) != 0 && nullStatus != FlowInfo.NON_NULL) { flowContext.recordNullityMismatch( currentScope, expression, providedType, var.type, nullStatus); return FlowInfo.NON_NULL; } else if ((severity = findNullTypeAnnotationMismatch(var.type, providedType)) > 0) { currentScope .problemReporter() .nullityMismatchingTypeAnnotation( expression, providedType, var.type, severity == 1, currentScope.environment()); } else if ((var.tagBits & TagBits.AnnotationNullable) != 0 && nullStatus == FlowInfo.UNKNOWN) { // provided a legacy type? return FlowInfo.POTENTIALLY_NULL; // -> use more specific info from the annotation } return nullStatus; }
/** Analysing arguments of MessageSend, ExplicitConstructorCall, AllocationExpression. */ protected void analyseArguments( BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, MethodBinding methodBinding, Expression[] arguments) { // compare actual null-status against parameter annotations of the called method: if (arguments != null) { CompilerOptions compilerOptions = currentScope.compilerOptions(); boolean considerTypeAnnotations = compilerOptions.sourceLevel >= ClassFileConstants.JDK1_8 && compilerOptions.isAnnotationBasedNullAnalysisEnabled; boolean hasJDK15NullAnnotations = methodBinding.parameterNonNullness != null; int numParamsToCheck = methodBinding.parameters.length; if (considerTypeAnnotations || hasJDK15NullAnnotations) { // check if varargs need special treatment: boolean passThrough = false; if (methodBinding.isVarargs()) { int varArgPos = numParamsToCheck - 1; // this if-block essentially copied from generateArguments(..): if (numParamsToCheck == arguments.length) { TypeBinding varArgsType = methodBinding.parameters[varArgPos]; TypeBinding lastType = arguments[varArgPos].resolvedType; if (lastType == TypeBinding.NULL || (varArgsType.dimensions() == lastType.dimensions() && lastType.isCompatibleWith(varArgsType))) passThrough = true; // pass directly as-is } if (!passThrough) numParamsToCheck--; // with non-passthrough varargs last param is fed from individual // args -> don't check } } if (considerTypeAnnotations) { for (int i = 0; i < numParamsToCheck; i++) { TypeBinding expectedType = methodBinding.parameters[i]; Expression argument = arguments[i]; // prefer check based on type annotations: int severity = findNullTypeAnnotationMismatch(expectedType, argument.resolvedType); if (severity > 0) { // immediate reporting: currentScope .problemReporter() .nullityMismatchingTypeAnnotation( argument, argument.resolvedType, expectedType, severity == 1, currentScope.environment()); // next check flow-based null status against null JDK15-style annotations: } else if (hasJDK15NullAnnotations && methodBinding.parameterNonNullness[i] == Boolean.TRUE) { int nullStatus = argument.nullStatus( flowInfo, flowContext); // slight loss of precision: should also use the null info from // the receiver. if (nullStatus != FlowInfo.NON_NULL) // if required non-null is not provided flowContext.recordNullityMismatch( currentScope, argument, argument.resolvedType, expectedType, nullStatus); } } } else if (hasJDK15NullAnnotations) { for (int i = 0; i < numParamsToCheck; i++) { if (methodBinding.parameterNonNullness[i] == Boolean.TRUE) { TypeBinding expectedType = methodBinding.parameters[i]; Expression argument = arguments[i]; int nullStatus = argument.nullStatus( flowInfo, flowContext); // slight loss of precision: should also use the null info from // the receiver. if (nullStatus != FlowInfo.NON_NULL) // if required non-null is not provided flowContext.recordNullityMismatch( currentScope, argument, argument.resolvedType, expectedType, nullStatus); } } } } }