/**
   * Check and fill in implicit annotations from overridden methods and from default. Precondition:
   * caller has checked whether annotation-based null analysis is enabled.
   */
  public void checkImplicitNullAnnotations(
      MethodBinding currentMethod,
      AbstractMethodDeclaration srcMethod,
      boolean complain,
      Scope scope) {
    // check inherited nullness from superclass and superInterfaces
    try {
      ReferenceBinding currentType = currentMethod.declaringClass;
      if (currentType.id == TypeIds.T_JavaLangObject) {
        return;
      }
      boolean usesTypeAnnotations = scope.environment().usesNullTypeAnnotations();
      boolean needToApplyReturnNonNullDefault =
          currentMethod.hasNonNullDefaultFor(
              Binding.DefaultLocationReturnType, usesTypeAnnotations);
      boolean needToApplyParameterNonNullDefault =
          currentMethod.hasNonNullDefaultFor(Binding.DefaultLocationParameter, usesTypeAnnotations);
      boolean needToApplyNonNullDefault =
          needToApplyReturnNonNullDefault | needToApplyParameterNonNullDefault;
      // compatibility & inheritance do not consider constructors / static methods:
      boolean isInstanceMethod = !currentMethod.isConstructor() && !currentMethod.isStatic();
      complain &= isInstanceMethod;
      if (!needToApplyNonNullDefault
          && !complain
          && !(this.inheritNullAnnotations && isInstanceMethod)) {
        return; // short cut, no work to be done
      }

      if (isInstanceMethod) {
        List superMethodList = new ArrayList();

        // need super types connected:
        if (currentType instanceof SourceTypeBinding
            && !currentType.isHierarchyConnected()
            && !currentType.isAnonymousType()) {
          ((SourceTypeBinding) currentType).scope.connectTypeHierarchy();
        }

        int paramLen = currentMethod.parameters.length;
        findAllOverriddenMethods(
            currentMethod.original(),
            currentMethod.selector,
            paramLen,
            currentType,
            new HashSet(),
            superMethodList);

        // prepare interim storage for nullness info so we don't pollute currentMethod before we
        // know its conflict-free:
        InheritedNonNullnessInfo[] inheritedNonNullnessInfos =
            new InheritedNonNullnessInfo[paramLen + 1]; // index 0 is for the return type
        for (int i = 0; i < paramLen + 1; i++)
          inheritedNonNullnessInfos[i] = new InheritedNonNullnessInfo();

        int length = superMethodList.size();
        for (int i = length; --i >= 0; ) {
          MethodBinding currentSuper = (MethodBinding) superMethodList.get(i);
          if ((currentSuper.tagBits & TagBits.IsNullnessKnown) == 0) {
            // recurse to prepare currentSuper
            checkImplicitNullAnnotations(
                currentSuper,
                null,
                false,
                scope); // TODO (stephan) complain=true if currentSuper is source method??
          }
          checkNullSpecInheritance(
              currentMethod,
              srcMethod,
              needToApplyReturnNonNullDefault,
              needToApplyParameterNonNullDefault,
              complain,
              currentSuper,
              null,
              scope,
              inheritedNonNullnessInfos);
          needToApplyNonNullDefault = false;
        }

        // transfer collected information into currentMethod:
        InheritedNonNullnessInfo info = inheritedNonNullnessInfos[0];
        if (!info.complained) {
          long tagBits = 0;
          if (info.inheritedNonNullness == Boolean.TRUE) {
            tagBits = TagBits.AnnotationNonNull;
          } else if (info.inheritedNonNullness == Boolean.FALSE) {
            tagBits = TagBits.AnnotationNullable;
          }
          if (tagBits != 0) {
            if (!usesTypeAnnotations) {
              currentMethod.tagBits |= tagBits;
            } else {
              if (!currentMethod.returnType.isBaseType()) {
                LookupEnvironment env = scope.environment();
                currentMethod.returnType =
                    env.createAnnotatedType(
                        currentMethod.returnType, env.nullAnnotationsFromTagBits(tagBits));
              }
            }
          }
        }
        for (int i = 0; i < paramLen; i++) {
          info = inheritedNonNullnessInfos[i + 1];
          if (!info.complained && info.inheritedNonNullness != null) {
            Argument currentArg = srcMethod == null ? null : srcMethod.arguments[i];
            if (!usesTypeAnnotations)
              recordArgNonNullness(
                  currentMethod, paramLen, i, currentArg, info.inheritedNonNullness);
            else
              recordArgNonNullness18(
                  currentMethod, i, currentArg, info.inheritedNonNullness, scope.environment());
          }
        }
      }
      if (needToApplyNonNullDefault) {
        if (!usesTypeAnnotations) currentMethod.fillInDefaultNonNullness(srcMethod);
        else currentMethod.fillInDefaultNonNullness18(srcMethod, scope.environment());
      }
    } finally {
      currentMethod.tagBits |= TagBits.IsNullnessKnown;
    }
  }