public void evaluateNullAnnotations(Scope scope, TypeParameter parameter) { long nullTagBits = NullAnnotationMatching.validNullTagBits(this.tagBits); if (this.firstBound != null && this.firstBound.isValidBinding()) { long superNullTagBits = NullAnnotationMatching.validNullTagBits(this.firstBound.tagBits); if (superNullTagBits != 0L) { if (nullTagBits == 0L) { nullTagBits |= superNullTagBits; } else if (superNullTagBits != nullTagBits) { this.firstBound = nullMismatchOnBound(parameter, this.firstBound, superNullTagBits, nullTagBits, scope); } } } ReferenceBinding[] interfaces = this.superInterfaces; int length; if ((length = interfaces.length) != 0) { for (int i = length; --i >= 0; ) { ReferenceBinding resolveType = interfaces[i]; long superNullTagBits = NullAnnotationMatching.validNullTagBits(resolveType.tagBits); if (superNullTagBits != 0L) { if (nullTagBits == 0L) { nullTagBits |= superNullTagBits; } else if (superNullTagBits != nullTagBits) { interfaces[i] = (ReferenceBinding) nullMismatchOnBound( parameter, resolveType, superNullTagBits, nullTagBits, scope); } } interfaces[i] = resolveType; } } if (nullTagBits != 0) this.tagBits |= nullTagBits | TagBits.HasNullTypeAnnotation; }
/** Check null-ness of 'var' against a possible null annotation */ public static int checkAssignment( BlockScope currentScope, FlowContext flowContext, VariableBinding var, int nullStatus, Expression expression, TypeBinding providedType) { long lhsTagBits = 0L; boolean hasReported = false; if (currentScope.compilerOptions().sourceLevel < ClassFileConstants.JDK1_8) { lhsTagBits = var.tagBits & TagBits.AnnotationNullMASK; } else { if (expression instanceof ConditionalExpression && expression.isPolyExpression()) { // drill into both branches: ConditionalExpression ce = ((ConditionalExpression) expression); int status1 = NullAnnotationMatching.checkAssignment( currentScope, flowContext, var, ce.ifTrueNullStatus, ce.valueIfTrue, ce.valueIfTrue.resolvedType); int status2 = NullAnnotationMatching.checkAssignment( currentScope, flowContext, var, ce.ifFalseNullStatus, ce.valueIfFalse, ce.valueIfFalse.resolvedType); if (status1 == status2) return status1; return nullStatus; // if both branches disagree use the precomputed & merged nullStatus } lhsTagBits = var.type.tagBits & TagBits.AnnotationNullMASK; NullAnnotationMatching annotationStatus = analyse(var.type, providedType, nullStatus); if (annotationStatus.isDefiniteMismatch()) { currentScope .problemReporter() .nullityMismatchingTypeAnnotation(expression, providedType, var.type, annotationStatus); hasReported = true; } else if (annotationStatus.isUnchecked()) { flowContext.recordNullityMismatch( currentScope, expression, providedType, var.type, nullStatus); hasReported = true; } } if (lhsTagBits == TagBits.AnnotationNonNull && nullStatus != FlowInfo.NON_NULL) { if (!hasReported) flowContext.recordNullityMismatch( currentScope, expression, providedType, var.type, nullStatus); return FlowInfo.NON_NULL; } else if (lhsTagBits == TagBits.AnnotationNullable && nullStatus == FlowInfo.UNKNOWN) { // provided a legacy type? return FlowInfo.POTENTIALLY_NULL; // -> use more specific info from the annotation } return nullStatus; }
private long getReturnTypeNullnessTagBits(MethodBinding method, boolean useTypeAnnotations) { if (useTypeAnnotations) { if (method.returnType == null) return 0L; return NullAnnotationMatching.validNullTagBits(method.returnType.tagBits); } return method.tagBits & TagBits.AnnotationNullMASK; }
private Boolean getParameterNonNullness(MethodBinding method, int i, boolean useTypeAnnotations) { if (useTypeAnnotations) { TypeBinding parameter = method.parameters[i]; if (parameter != null) { long nullBits = NullAnnotationMatching.validNullTagBits(parameter.tagBits); if (nullBits != 0L) return Boolean.valueOf(nullBits == TagBits.AnnotationNonNull); } return null; } return (method.parameterNonNullness == null) ? null : method.parameterNonNullness[i]; }
/** * Check whether this type reference conforms to the null constraints defined for the * corresponding type variable. */ protected void checkNullConstraints( Scope scope, Substitution substitution, TypeBinding[] variables, int rank) { if (variables != null && variables.length > rank) { TypeBinding variable = variables[rank]; if (variable.hasNullTypeAnnotations()) { if (NullAnnotationMatching.analyse( variable, this.resolvedType, null, substitution, -1, null, CheckMode.BOUND_CHECK) .isAnyMismatch()) scope.problemReporter().nullityMismatchTypeArgument(variable, this.resolvedType, this); } } checkIllegalNullAnnotation(scope); }
/** * Complain if assigned expression is cast, but not actually used as such, e.g. Object o = (List) * object; */ public static void checkNeedForAssignedCast( BlockScope scope, TypeBinding expectedType, CastExpression rhs) { CompilerOptions compilerOptions = scope.compilerOptions(); if (compilerOptions.getSeverity(CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore) return; TypeBinding castedExpressionType = rhs.expression.resolvedType; // int i = (byte) n; // cast still had side effect // double d = (float) n; // cast to float is unnecessary if (castedExpressionType == null || rhs.resolvedType.isBaseType()) return; // if (castedExpressionType.id == T_null) return; // tolerate null expression cast if (castedExpressionType.isCompatibleWith(expectedType, scope)) { if (scope.environment().usesNullTypeAnnotations()) { // are null annotations compatible, too? if (NullAnnotationMatching.analyse(expectedType, castedExpressionType, -1).isAnyMismatch()) return; // already reported unchecked cast (nullness), say no more. } scope.problemReporter().unnecessaryCast(rhs); } }
public TypeBinding resolveType(BlockScope scope) { // compute a new constant if the cast is effective this.constant = Constant.NotAConstant; this.implicitConversion = TypeIds.T_undefined; boolean exprContainCast = false; TypeBinding castType = this.resolvedType = this.type.resolveType(scope); if (scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_8) { this.expression.setExpressionContext(CASTING_CONTEXT); if (this.expression instanceof FunctionalExpression) { this.expression.setExpectedType(this.resolvedType); this.bits |= ASTNode.DisableUnnecessaryCastCheck; } } if (this.expression instanceof CastExpression) { this.expression.bits |= ASTNode.DisableUnnecessaryCastCheck; exprContainCast = true; } TypeBinding expressionType = this.expression.resolveType(scope); if (this.expression instanceof MessageSend) { MessageSend messageSend = (MessageSend) this.expression; MethodBinding methodBinding = messageSend.binding; if (methodBinding != null && methodBinding.isPolymorphic()) { messageSend.binding = scope .environment() .updatePolymorphicMethodReturnType( (PolymorphicMethodBinding) methodBinding, castType); if (TypeBinding.notEquals(expressionType, castType)) { expressionType = castType; this.bits |= ASTNode.DisableUnnecessaryCastCheck; } } } if (castType != null) { if (expressionType != null) { boolean nullAnnotationMismatch = scope.compilerOptions().isAnnotationBasedNullAnalysisEnabled && NullAnnotationMatching.analyse(castType, expressionType, -1).isAnyMismatch(); boolean isLegal = checkCastTypesCompatibility(scope, castType, expressionType, this.expression); if (isLegal) { this.expression.computeConversion(scope, castType, expressionType); if ((this.bits & ASTNode.UnsafeCast) != 0) { // unsafe cast if (scope.compilerOptions().reportUnavoidableGenericTypeProblems || !(expressionType.isRawType() && this.expression.forcedToBeRaw(scope.referenceContext()))) { scope.problemReporter().unsafeCast(this, scope); } } else if (nullAnnotationMismatch) { // report null annotation issue at medium priority scope.problemReporter().unsafeNullnessCast(this, scope); } else { if (castType.isRawType() && scope.compilerOptions().getSeverity(CompilerOptions.RawTypeReference) != ProblemSeverities.Ignore) { scope.problemReporter().rawTypeReference(this.type, castType); } if ((this.bits & (ASTNode.UnnecessaryCast | ASTNode.DisableUnnecessaryCastCheck)) == ASTNode.UnnecessaryCast) { // unnecessary cast if (!isIndirectlyUsed()) // used for generic type inference or boxing ? scope.problemReporter().unnecessaryCast(this); } } } else { // illegal cast if ((castType.tagBits & TagBits.HasMissingType) == 0) { // no complaint if secondary error scope.problemReporter().typeCastError(this, castType, expressionType); } this.bits |= ASTNode.DisableUnnecessaryCastCheck; // disable further secondary diagnosis } } this.resolvedType = castType.capture( scope, this.type.sourceStart, this.type .sourceEnd); // make it unique, a cast expression shares source end with the // expression. if (exprContainCast) { checkNeedForCastCast(scope, this); } } return this.resolvedType; }
private TypeConstants.BoundCheckStatus internalBoundCheck( Substitution substitution, TypeBinding argumentType, Scope scope, ASTNode location) { if (argumentType == TypeBinding.NULL || TypeBinding.equalsEquals(argumentType, this)) { return BoundCheckStatus.OK; } boolean hasSubstitution = substitution != null; if (!(argumentType instanceof ReferenceBinding || argumentType.isArrayType())) return BoundCheckStatus.MISMATCH; // special case for re-entrant source types (selection, code assist, etc)... // can request additional types during hierarchy walk that are found as source types that also // 'need' to connect their hierarchy if (this.superclass == null) return BoundCheckStatus.OK; if (argumentType.kind() == Binding.WILDCARD_TYPE) { WildcardBinding wildcard = (WildcardBinding) argumentType; switch (wildcard.boundKind) { case Wildcard.EXTENDS: TypeBinding wildcardBound = wildcard.bound; if (TypeBinding.equalsEquals(wildcardBound, this)) return BoundCheckStatus.OK; boolean isArrayBound = wildcardBound.isArrayType(); if (!wildcardBound.isInterface()) { TypeBinding substitutedSuperType = hasSubstitution ? Scope.substitute(substitution, this.superclass) : this.superclass; if (substitutedSuperType.id != TypeIds.T_JavaLangObject) { if (isArrayBound) { if (!wildcardBound.isCompatibleWith(substitutedSuperType, scope)) return BoundCheckStatus.MISMATCH; } else { TypeBinding match = wildcardBound.findSuperTypeOriginatingFrom(substitutedSuperType); if (match != null) { if (substitutedSuperType.isProvablyDistinct(match)) { return BoundCheckStatus.MISMATCH; } } else { match = substitutedSuperType.findSuperTypeOriginatingFrom(wildcardBound); if (match != null) { if (match.isProvablyDistinct(wildcardBound)) { return BoundCheckStatus.MISMATCH; } } else { if (denotesRelevantSuperClass(wildcardBound) && denotesRelevantSuperClass(substitutedSuperType)) { // non-object real superclass should have produced a valid 'match' above return BoundCheckStatus.MISMATCH; } } } } } } boolean mustImplement = isArrayBound || ((ReferenceBinding) wildcardBound).isFinal(); for (int i = 0, length = this.superInterfaces.length; i < length; i++) { TypeBinding substitutedSuperType = hasSubstitution ? Scope.substitute(substitution, this.superInterfaces[i]) : this.superInterfaces[i]; if (isArrayBound) { if (!wildcardBound.isCompatibleWith(substitutedSuperType, scope)) return BoundCheckStatus.MISMATCH; } else { TypeBinding match = wildcardBound.findSuperTypeOriginatingFrom(substitutedSuperType); if (match != null) { if (substitutedSuperType.isProvablyDistinct(match)) { return BoundCheckStatus.MISMATCH; } } else if (mustImplement) { return BoundCheckStatus .MISMATCH; // cannot be extended further to satisfy missing bounds } } } break; case Wildcard.SUPER: // if the wildcard is lower-bounded by a type variable that has no relevant upper bound // there's nothing to check here (bug 282152): if (wildcard.bound.isTypeVariable() && ((TypeVariableBinding) wildcard.bound).superclass.id == TypeIds.T_JavaLangObject) break; return boundCheck(substitution, wildcard.bound, scope, location); case Wildcard.UNBOUND: break; } return BoundCheckStatus.OK; } boolean unchecked = false; boolean checkNullAnnotations = scope.environment().usesNullTypeAnnotations(); boolean haveReportedNullProblem = false; if (this.superclass.id != TypeIds.T_JavaLangObject) { TypeBinding substitutedSuperType = hasSubstitution ? Scope.substitute(substitution, this.superclass) : this.superclass; if (TypeBinding.notEquals(substitutedSuperType, argumentType)) { if (!argumentType.isCompatibleWith(substitutedSuperType, scope)) { return BoundCheckStatus.MISMATCH; } TypeBinding match = argumentType.findSuperTypeOriginatingFrom(substitutedSuperType); if (match != null) { // Enum#RAW is not a substitute for <E extends Enum<E>> (86838) if (match.isRawType() && substitutedSuperType.isBoundParameterizedType()) unchecked = true; } } if (location != null && checkNullAnnotations) { if (NullAnnotationMatching.analyse( this, argumentType, substitutedSuperType, substitution, -1, CheckMode.BOUND_CHECK) .isAnyMismatch()) { scope.problemReporter().nullityMismatchTypeArgument(this, argumentType, location); haveReportedNullProblem = true; } } } for (int i = 0, length = this.superInterfaces.length; i < length; i++) { TypeBinding substitutedSuperType = hasSubstitution ? Scope.substitute(substitution, this.superInterfaces[i]) : this.superInterfaces[i]; if (TypeBinding.notEquals(substitutedSuperType, argumentType)) { if (!argumentType.isCompatibleWith(substitutedSuperType, scope)) { return BoundCheckStatus.MISMATCH; } TypeBinding match = argumentType.findSuperTypeOriginatingFrom(substitutedSuperType); if (match != null) { // Enum#RAW is not a substitute for <E extends Enum<E>> (86838) if (match.isRawType() && substitutedSuperType.isBoundParameterizedType()) unchecked = true; } } if (location != null && checkNullAnnotations) { if (NullAnnotationMatching.analyse( this, argumentType, substitutedSuperType, substitution, -1, CheckMode.BOUND_CHECK) .isAnyMismatch()) { scope.problemReporter().nullityMismatchTypeArgument(this, argumentType, location); haveReportedNullProblem = true; } } } if (location != null && checkNullAnnotations && !haveReportedNullProblem) { long nullBits = this.tagBits & TagBits.AnnotationNullMASK; if (nullBits != 0 && nullBits != (argumentType.tagBits & TagBits.AnnotationNullMASK)) { scope.problemReporter().nullityMismatchTypeArgument(this, argumentType, location); haveReportedNullProblem = true; } } return unchecked ? BoundCheckStatus.UNCHECKED : haveReportedNullProblem ? BoundCheckStatus.NULL_PROBLEM : BoundCheckStatus.OK; }
/** * The main algorithm in this class. * * @param currentMethod focus method * @param srcMethod AST of 'currentMethod' if present * @param hasReturnNonNullDefault is a @NonNull default applicable for the return type of * currentMethod? * @param hasParameterNonNullDefault is a @NonNull default applicable for parameters of * currentMethod? * @param shouldComplain should we report any errors found? (see also comment about flows into * this method, below). * @param inheritedMethod one overridden method from a super type * @param allInheritedMethods look here to see if nonnull-unannotated conflict already exists in * one super type * @param scope provides context for error reporting etc. * @param inheritedNonNullnessInfos if non-null, this array of non-null elements is used for * interim recording of nullness information from inheritedMethod rather than prematurely * updating currentMethod. Index position 0 is used for the return type, positions i+1 for * argument i. */ void checkNullSpecInheritance( MethodBinding currentMethod, AbstractMethodDeclaration srcMethod, boolean hasReturnNonNullDefault, boolean hasParameterNonNullDefault, boolean shouldComplain, MethodBinding inheritedMethod, MethodBinding[] allInheritedMethods, Scope scope, InheritedNonNullnessInfo[] inheritedNonNullnessInfos) { // Note that basically two different flows lead into this method: // (1) during MethodVerifyer15.checkMethods() we want to report errors (against srcMethod or // against the current type) // In this case this method is directly called from MethodVerifier15 // (checkAgainstInheritedMethod / checkConcreteInheritedMethod) // (2) during on-demand invocation we are mainly interested in the side effects of copying // inherited null annotations // In this case this method is called via checkImplicitNullAnnotations from // - MessageSend.resolveType(..) // - SourceTypeBinding.createArgumentBindings(..) // - recursive calls within this class // Still we *might* want to complain about problems found (controlled by 'complain') if ((inheritedMethod.tagBits & TagBits.IsNullnessKnown) == 0) { // TODO (stephan): even here we may need to report problems? How to discriminate? this.buddyImplicitNullAnnotationsVerifier.checkImplicitNullAnnotations( inheritedMethod, null, false, scope); } boolean useTypeAnnotations = this.environment.usesNullTypeAnnotations(); long inheritedNullnessBits = getReturnTypeNullnessTagBits(inheritedMethod, useTypeAnnotations); long currentNullnessBits = getReturnTypeNullnessTagBits(currentMethod, useTypeAnnotations); boolean shouldInherit = this.inheritNullAnnotations; // return type: returnType: { if (currentMethod.returnType == null || currentMethod.returnType.isBaseType()) break returnType; // no nullness for primitive types if (currentNullnessBits == 0) { // unspecified, may fill in either from super or from default if (shouldInherit) { if (inheritedNullnessBits != 0) { if (hasReturnNonNullDefault) { // both inheritance and default: check for conflict? if (shouldComplain && inheritedNullnessBits == TagBits.AnnotationNullable) scope .problemReporter() .conflictingNullAnnotations( currentMethod, ((MethodDeclaration) srcMethod).returnType, inheritedMethod); // still use the inherited bits to avoid incompatibility } if (inheritedNonNullnessInfos != null && srcMethod != null) { recordDeferredInheritedNullness( scope, ((MethodDeclaration) srcMethod).returnType, inheritedMethod, Boolean.valueOf(inheritedNullnessBits == TagBits.AnnotationNonNull), inheritedNonNullnessInfos[0]); } else { // no need to defer, record this info now: applyReturnNullBits(currentMethod, inheritedNullnessBits); } break returnType; // compatible by construction, skip complain phase below } } if (hasReturnNonNullDefault && (!useTypeAnnotations || currentMethod.returnType .acceptsNonNullDefault())) { // conflict with inheritance already checked currentNullnessBits = TagBits.AnnotationNonNull; applyReturnNullBits(currentMethod, currentNullnessBits); } } if (shouldComplain) { if ((inheritedNullnessBits & TagBits.AnnotationNonNull) != 0 && currentNullnessBits != TagBits.AnnotationNonNull) { if (srcMethod != null) { scope .problemReporter() .illegalReturnRedefinition( srcMethod, inheritedMethod, this.environment.getNonNullAnnotationName()); break returnType; } else { scope .problemReporter() .cannotImplementIncompatibleNullness( currentMethod, inheritedMethod, useTypeAnnotations); return; } } if (useTypeAnnotations) { TypeBinding substituteReturnType = null; // for TVB identity checks inside NullAnnotationMatching.analyze() TypeVariableBinding[] typeVariables = inheritedMethod.original().typeVariables; if (typeVariables != null && currentMethod.returnType.id != TypeIds.T_void) { ParameterizedGenericMethodBinding substitute = this.environment.createParameterizedGenericMethod(currentMethod, typeVariables); substituteReturnType = substitute.returnType; } if (NullAnnotationMatching.analyse( inheritedMethod.returnType, currentMethod.returnType, substituteReturnType, 0, CheckMode.OVERRIDE) .isAnyMismatch()) { if (srcMethod != null) scope .problemReporter() .illegalReturnRedefinition( srcMethod, inheritedMethod, this.environment.getNonNullAnnotationName()); else scope .problemReporter() .cannotImplementIncompatibleNullness( currentMethod, inheritedMethod, useTypeAnnotations); return; } } } } // parameters: TypeBinding[] substituteParameters = null; // for TVB identity checks inside NullAnnotationMatching.analyze() if (shouldComplain) { TypeVariableBinding[] typeVariables = currentMethod.original().typeVariables; if (typeVariables != Binding.NO_TYPE_VARIABLES) { ParameterizedGenericMethodBinding substitute = this.environment.createParameterizedGenericMethod(inheritedMethod, typeVariables); substituteParameters = substitute.parameters; } } Argument[] currentArguments = srcMethod == null ? null : srcMethod.arguments; int length = 0; if (currentArguments != null) length = currentArguments.length; if (useTypeAnnotations) // need to look for type annotations on all parameters: length = currentMethod.parameters.length; else if (inheritedMethod.parameterNonNullness != null) length = inheritedMethod.parameterNonNullness.length; else if (currentMethod.parameterNonNullness != null) length = currentMethod.parameterNonNullness.length; parameterLoop: for (int i = 0; i < length; i++) { if (currentMethod.parameters[i].isBaseType()) continue; Argument currentArgument = currentArguments == null ? null : currentArguments[i]; Boolean inheritedNonNullNess = getParameterNonNullness(inheritedMethod, i, useTypeAnnotations); Boolean currentNonNullNess = getParameterNonNullness(currentMethod, i, useTypeAnnotations); if (currentNonNullNess == null) { // unspecified, may fill in either from super or from default if (inheritedNonNullNess != null) { if (shouldInherit) { if (hasParameterNonNullDefault) { // both inheritance and default: check for conflict? if (shouldComplain && inheritedNonNullNess == Boolean.FALSE && currentArgument != null) { scope .problemReporter() .conflictingNullAnnotations(currentMethod, currentArgument, inheritedMethod); } // still use the inherited info to avoid incompatibility } if (inheritedNonNullnessInfos != null && srcMethod != null) { recordDeferredInheritedNullness( scope, srcMethod.arguments[i].type, inheritedMethod, inheritedNonNullNess, inheritedNonNullnessInfos[i + 1]); } else { // no need to defer, record this info now: if (!useTypeAnnotations) recordArgNonNullness( currentMethod, length, i, currentArgument, inheritedNonNullNess); else recordArgNonNullness18( currentMethod, i, currentArgument, inheritedNonNullNess, this.environment); } continue; // compatible by construction, skip complain phase below } } if (hasParameterNonNullDefault) { // conflict with inheritance already checked currentNonNullNess = Boolean.TRUE; if (!useTypeAnnotations) recordArgNonNullness(currentMethod, length, i, currentArgument, Boolean.TRUE); else if (currentMethod.parameters[i].acceptsNonNullDefault()) recordArgNonNullness18( currentMethod, i, currentArgument, Boolean.TRUE, this.environment); else currentNonNullNess = null; // cancel if parameter doesn't accept the default } } if (shouldComplain) { char[][] annotationName; if (inheritedNonNullNess == Boolean.TRUE) { annotationName = this.environment.getNonNullAnnotationName(); } else { annotationName = this.environment.getNullableAnnotationName(); } if (inheritedNonNullNess != Boolean.TRUE // super parameter is not restricted to @NonNull && currentNonNullNess == Boolean.TRUE) // current parameter is restricted to @NonNull { // incompatible if (currentArgument != null) { scope .problemReporter() .illegalRedefinitionToNonNullParameter( currentArgument, inheritedMethod.declaringClass, (inheritedNonNullNess == null) ? null : this.environment.getNullableAnnotationName()); } else { scope .problemReporter() .cannotImplementIncompatibleNullness(currentMethod, inheritedMethod, false); } continue; } else if (currentNonNullNess == null) { // unannotated strictly conflicts only with inherited @Nullable if (inheritedNonNullNess == Boolean.FALSE) { if (currentArgument != null) { scope .problemReporter() .parameterLackingNullableAnnotation( currentArgument, inheritedMethod.declaringClass, annotationName); } else { scope .problemReporter() .cannotImplementIncompatibleNullness(currentMethod, inheritedMethod, false); } continue; } else if (inheritedNonNullNess == Boolean.TRUE) { // not strictly a conflict, but a configurable warning is given anyway: if (allInheritedMethods != null) { // avoid this optional warning if the conflict already existed in one supertype // (merging of two methods into one?) for (MethodBinding one : allInheritedMethods) if (TypeBinding.equalsEquals(inheritedMethod.declaringClass, one.declaringClass) && getParameterNonNullness(one, i, useTypeAnnotations) != Boolean.TRUE) continue parameterLoop; } scope .problemReporter() .parameterLackingNonnullAnnotation( currentArgument, inheritedMethod.declaringClass, annotationName); continue; } } if (useTypeAnnotations) { TypeBinding inheritedParameter = inheritedMethod.parameters[i]; TypeBinding substituteParameter = substituteParameters != null ? substituteParameters[i] : null; if (NullAnnotationMatching.analyse( currentMethod.parameters[i], inheritedParameter, substituteParameter, 0, CheckMode.OVERRIDE) .isAnyMismatch()) { if (currentArgument != null) scope .problemReporter() .illegalParameterRedefinition( currentArgument, inheritedMethod.declaringClass, inheritedParameter); else scope .problemReporter() .cannotImplementIncompatibleNullness(currentMethod, inheritedMethod, false); } } } } }