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];
 }
Ejemplo n.º 5
0
 /**
  * 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);
 }
Ejemplo n.º 6
0
  /**
   * 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);
    }
  }
Ejemplo n.º 7
0
  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);
          }
        }
      }
    }
  }