/**
   * The flowInfo corresponds to non-static field initialization infos. It may be unreachable
   * (155423), but still the explicit constructor call must be analysed as reachable, since it will
   * be generated in the end.
   */
  public void analyseCode(
      ClassScope classScope,
      InitializationFlowContext initializerFlowContext,
      FlowInfo flowInfo,
      int initialReachMode) {
    if (this.ignoreFurtherInvestigation) return;

    int nonStaticFieldInfoReachMode = flowInfo.reachMode();
    flowInfo.setReachMode(initialReachMode);

    checkUnused:
    {
      MethodBinding constructorBinding;
      if ((constructorBinding = this.binding) == null) break checkUnused;
      if (this.isDefaultConstructor) break checkUnused;
      if (constructorBinding.isUsed()) break checkUnused;
      if (constructorBinding.isPrivate()) {
        if ((this.binding.declaringClass.tagBits & TagBits.HasNonPrivateConstructor) == 0)
          break checkUnused; // tolerate as known pattern to block instantiation
      } else if ((this.binding.declaringClass.tagBits
              & (TagBits.IsAnonymousType | TagBits.IsLocalType))
          != TagBits.IsLocalType) {
        break checkUnused;
      }
      // complain unused
      this.scope.problemReporter().unusedPrivateConstructor(this);
    }

    // check constructor recursion, once all constructor got resolved
    if (isRecursive(null /*lazy initialized visited list*/)) {
      this.scope.problemReporter().recursiveConstructorInvocation(this.constructorCall);
    }

    try {
      ExceptionHandlingFlowContext constructorContext =
          new ExceptionHandlingFlowContext(
              initializerFlowContext.parent, this, null, this.scope, FlowInfo.DEAD_END);
      initializerFlowContext.checkInitializerExceptions(this.scope, constructorContext, flowInfo);

      // anonymous constructor can gain extra thrown exceptions from unhandled ones
      if (this.binding.declaringClass.isAnonymousType()) {
        ArrayList computedExceptions = constructorContext.extendedExceptions;
        if (computedExceptions != null) {
          int size;
          if ((size = computedExceptions.size()) > 0) {
            ReferenceBinding[] actuallyThrownExceptions;
            computedExceptions.toArray(actuallyThrownExceptions = new ReferenceBinding[size]);
          }
        }
      }

      // tag parameters as being set
      if (this.arguments != null) {
        for (int i = 0, count = this.arguments.length; i < count; i++) {
          flowInfo.markAsDefinitelyAssigned(this.arguments[i].binding);
        }
      }

      // propagate to constructor call
      if (this.constructorCall != null) {
        // if calling 'this(...)', then flag all non-static fields as definitely
        // set since they are supposed to be set inside other local constructor
        if (this.constructorCall.accessMode == ExplicitConstructorCall.This) {
          FieldBinding[] fields = this.binding.declaringClass.fields();
          for (int i = 0, count = fields.length; i < count; i++) {
            FieldBinding field;
            if (!(field = fields[i]).isStatic()) {
              flowInfo.markAsDefinitelyAssigned(field);
            }
          }
        }
        flowInfo = this.constructorCall.analyseCode(this.scope, constructorContext, flowInfo);
      }

      // reuse the reachMode from non static field info
      flowInfo.setReachMode(nonStaticFieldInfoReachMode);

      // propagate to statements
      if (this.statements != null) {
        boolean didAlreadyComplain = false;
        for (int i = 0, count = this.statements.length; i < count; i++) {
          Statement stat = this.statements[i];
          if (!stat.complainIfUnreachable(flowInfo, this.scope, didAlreadyComplain)) {
            flowInfo = stat.analyseCode(this.scope, constructorContext, flowInfo);
          } else {
            didAlreadyComplain = true;
          }
        }
      }
      // check for missing returning path
      this.needFreeReturn = (flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0;

      // reuse the initial reach mode for diagnosing missing blank finals
      flowInfo.setReachMode(initialReachMode);

      // check missing blank final field initializations
      if ((this.constructorCall != null)
          && (this.constructorCall.accessMode != ExplicitConstructorCall.This)) {
        flowInfo = flowInfo.mergedWith(constructorContext.initsOnReturn);
      }
      // check unreachable catch blocks
      constructorContext.complainIfUnusedExceptionHandlers(this);
    } catch (AbortMethod e) {
      this.ignoreFurtherInvestigation = true;
    }
  }