public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
    this.preAssertInitStateIndex = currentScope.methodScope().recordInitializationStates(flowInfo);

    Constant cst = this.assertExpression.optimizedBooleanConstant();
    if ((this.assertExpression.implicitConversion & TypeIds.UNBOXING) != 0) {
      this.assertExpression.checkNPE(currentScope, flowContext, flowInfo);
    }
    boolean isOptimizedTrueAssertion = cst != Constant.NotAConstant && cst.booleanValue() == true;
    boolean isOptimizedFalseAssertion = cst != Constant.NotAConstant && cst.booleanValue() == false;

    flowContext.tagBits |= FlowContext.HIDE_NULL_COMPARISON_WARNING;
    FlowInfo conditionFlowInfo =
        this.assertExpression.analyseCode(currentScope, flowContext, flowInfo.copy());
    flowContext.extendTimeToLiveForNullCheckedField(1); // survive this assert as a Statement
    flowContext.tagBits &= ~FlowContext.HIDE_NULL_COMPARISON_WARNING;
    UnconditionalFlowInfo assertWhenTrueInfo =
        conditionFlowInfo.initsWhenTrue().unconditionalInits();
    FlowInfo assertInfo = conditionFlowInfo.initsWhenFalse();
    if (isOptimizedTrueAssertion) {
      assertInfo.setReachMode(FlowInfo.UNREACHABLE_OR_DEAD);
    }

    if (this.exceptionArgument != null) {
      // only gets evaluated when escaping - results are not taken into account
      FlowInfo exceptionInfo =
          this.exceptionArgument.analyseCode(currentScope, flowContext, assertInfo.copy());

      if (isOptimizedTrueAssertion) {
        currentScope.problemReporter().fakeReachable(this.exceptionArgument);
      } else {
        flowContext.checkExceptionHandlers(
            currentScope.getJavaLangAssertionError(), this, exceptionInfo, currentScope);
      }
    }

    if (!isOptimizedTrueAssertion) {
      // add the assert support in the clinit
      manageSyntheticAccessIfNecessary(currentScope, flowInfo);
    }
    // account for potential AssertionError:
    flowContext.recordAbruptExit();
    if (isOptimizedFalseAssertion) {
      return flowInfo; // if assertions are enabled, the following code will be unreachable
      // change this if we need to carry null analysis results of the assert
      // expression downstream
    } else {
      CompilerOptions compilerOptions = currentScope.compilerOptions();
      if (!compilerOptions.includeNullInfoFromAsserts) {
        // keep just the initializations info, don't include assert's null info
        // merge initialization info's and then add back the null info from flowInfo to
        // make sure that the empty null info of assertInfo doesnt change flowInfo's null info.
        return ((flowInfo.nullInfoLessUnconditionalCopy())
                .mergedWith(assertInfo.nullInfoLessUnconditionalCopy()))
            .addNullInfoFrom(flowInfo);
      }
      return flowInfo
          .mergedWith(assertInfo.nullInfoLessUnconditionalCopy())
          .addInitializationsFrom(assertWhenTrueInfo.discardInitializationInfo());
      // keep the merge from the initial code for the definite assignment
      // analysis, tweak the null part to influence nulls downstream
    }
  }
  public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
    this.breakLabel = new BranchLabel();
    this.continueLabel = new BranchLabel();
    LoopingFlowContext loopingContext =
        new LoopingFlowContext(
            flowContext, flowInfo, this, this.breakLabel, this.continueLabel, currentScope);

    Constant cst = this.condition.constant;
    boolean isConditionTrue = cst != Constant.NotAConstant && cst.booleanValue() == true;
    cst = this.condition.optimizedBooleanConstant();
    boolean isConditionOptimizedTrue = cst != Constant.NotAConstant && cst.booleanValue() == true;
    boolean isConditionOptimizedFalse = cst != Constant.NotAConstant && cst.booleanValue() == false;

    int previousMode = flowInfo.reachMode();

    UnconditionalFlowInfo actionInfo = flowInfo.nullInfoLessUnconditionalCopy();
    // we need to collect the contribution to nulls of the coming paths through the
    // loop, be they falling through normally or branched to break, continue labels
    // or catch blocks
    if ((this.action != null) && !this.action.isEmptyBlock()) {
      actionInfo =
          this.action.analyseCode(currentScope, loopingContext, actionInfo).unconditionalInits();

      // code generation can be optimized when no need to continue in the loop
      if ((actionInfo.tagBits & loopingContext.initsOnContinue.tagBits & FlowInfo.UNREACHABLE)
          != 0) {
        this.continueLabel = null;
      }
    }
    /* Reset reach mode, to address following scenario.
     *   final blank;
     *   do { if (true) break; else blank = 0; } while(false);
     *   blank = 1; // may be initialized already
     */
    actionInfo.setReachMode(previousMode);

    LoopingFlowContext condLoopContext;
    FlowInfo condInfo =
        this.condition.analyseCode(
            currentScope,
            (condLoopContext =
                new LoopingFlowContext(flowContext, flowInfo, this, null, null, currentScope)),
            (this.action == null
                    ? actionInfo
                    : (actionInfo.mergedWith(loopingContext.initsOnContinue)))
                .copy());
    this.preConditionInitStateIndex =
        currentScope.methodScope().recordInitializationStates(actionInfo);
    if (!isConditionOptimizedFalse && this.continueLabel != null) {
      loopingContext.complainOnDeferredFinalChecks(currentScope, condInfo);
      condLoopContext.complainOnDeferredFinalChecks(currentScope, condInfo);
      loopingContext.complainOnDeferredNullChecks(
          currentScope,
          flowInfo
              .unconditionalCopy()
              .addPotentialNullInfoFrom(condInfo.initsWhenTrue().unconditionalInits()));
      condLoopContext.complainOnDeferredNullChecks(
          currentScope,
          actionInfo.addPotentialNullInfoFrom(condInfo.initsWhenTrue().unconditionalInits()));
    }

    // end of loop
    FlowInfo mergedInfo =
        FlowInfo.mergedOptimizedBranches(
            (loopingContext.initsOnBreak.tagBits & FlowInfo.UNREACHABLE) != 0
                ? loopingContext.initsOnBreak
                : flowInfo.unconditionalCopy().addInitializationsFrom(loopingContext.initsOnBreak),
            // recover upstream null info
            isConditionOptimizedTrue,
            (condInfo.tagBits & FlowInfo.UNREACHABLE) == 0
                ? flowInfo.addInitializationsFrom(condInfo.initsWhenFalse())
                : condInfo,
            // recover null inits from before condition analysis
            false, // never consider opt false case for DO loop, since break can always occur
                   // (47776)
            !isConditionTrue /*do{}while(true); unreachable(); */);
    this.mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo);
    return mergedInfo;
  }
  public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
    boolean nonStatic = !this.binding.isStatic();
    boolean wasInsideAssert =
        ((flowContext.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) != 0);
    flowInfo =
        this.receiver
            .analyseCode(currentScope, flowContext, flowInfo, nonStatic)
            .unconditionalInits();
    // recording the closing of AutoCloseable resources:
    boolean analyseResources = currentScope.compilerOptions().analyseResourceLeaks;
    if (analyseResources && CharOperation.equals(TypeConstants.CLOSE, this.selector)) {
      FakedTrackingVariable trackingVariable =
          FakedTrackingVariable.getCloseTrackingVariable(this.receiver);
      if (trackingVariable
          != null) { // null happens if receiver is not a local variable or not an AutoCloseable
        if (trackingVariable.methodScope == currentScope.methodScope()) {
          trackingVariable.markClose(flowInfo, flowContext);
        } else {
          trackingVariable.markClosedInNestedMethod();
        }
      }
    }
    if (nonStatic) {
      this.receiver.checkNPE(currentScope, flowContext, flowInfo);
      // https://bugs.eclipse.org/bugs/show_bug.cgi?id=318682
      if (this.receiver.isThis()) {
        // accessing non-static method without an object
        currentScope.resetDeclaringClassMethodStaticFlag(this.binding.declaringClass);
      }
    } else if (this.receiver.isThis()) {
      if ((this.receiver.bits & ASTNode.IsImplicitThis) == 0) {
        // explicit this receiver, not allowed in static context
        currentScope.resetEnclosingMethodStaticFlag();
      }
    }

    FlowInfo conditionFlowInfo;
    if (this.arguments != null) {
      int length = this.arguments.length;
      for (int i = 0; i < length; i++) {
        Expression argument = this.arguments[i];
        if ((argument.implicitConversion & TypeIds.UNBOXING) != 0) {
          argument.checkNPE(currentScope, flowContext, flowInfo);
        }
        if (this.receiver.resolvedType != null
            && this.receiver.resolvedType.id == TypeIds.T_OrgEclipseCoreRuntimeAssert
            && argument.resolvedType != null
            && argument.resolvedType.id == TypeIds.T_boolean) {
          Constant cst = argument.optimizedBooleanConstant();
          boolean isOptimizedTrueAssertion =
              cst != Constant.NotAConstant && cst.booleanValue() == true;
          boolean isOptimizedFalseAssertion =
              cst != Constant.NotAConstant && cst.booleanValue() == false;
          flowContext.tagBits |= FlowContext.HIDE_NULL_COMPARISON_WARNING;
          conditionFlowInfo = argument.analyseCode(currentScope, flowContext, flowInfo.copy());
          if (!wasInsideAssert) {
            flowContext.tagBits &= ~FlowContext.HIDE_NULL_COMPARISON_WARNING;
          }
          UnconditionalFlowInfo assertWhenTrueInfo =
              conditionFlowInfo.initsWhenTrue().unconditionalInits();
          FlowInfo assertInfo = conditionFlowInfo.initsWhenFalse();
          if (isOptimizedTrueAssertion) {
            assertInfo.setReachMode(FlowInfo.UNREACHABLE_OR_DEAD);
          }
          if (!isOptimizedFalseAssertion) {
            // if assertion is not false for sure, only then it makes sense to carry the flow info
            // ahead.
            // if the code does reach ahead, it means the assert didn't cause an exit, and so
            // the expression inside it shouldn't change the prior flowinfo
            // viz. org.eclipse.core.runtime.Assert.isLegal(false && o != null)

            // keep the merge from the initial code for the definite assignment
            // analysis, tweak the null part to influence nulls downstream
            flowInfo =
                flowInfo
                    .mergedWith(assertInfo.nullInfoLessUnconditionalCopy())
                    .addInitializationsFrom(assertWhenTrueInfo.discardInitializationInfo());
          }
        } else {
          flowInfo = argument.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits();
        }
        if (analyseResources) {
          // if argument is an AutoCloseable insert info that it *may* be closed (by the target
          // method, i.e.)
          flowInfo =
              FakedTrackingVariable.markPassedToOutside(currentScope, argument, flowInfo, false);
        }
      }
      analyseArguments(currentScope, flowContext, flowInfo, this.binding, this.arguments);
    }
    ReferenceBinding[] thrownExceptions;
    if ((thrownExceptions = this.binding.thrownExceptions) != Binding.NO_EXCEPTIONS) {
      if ((this.bits & ASTNode.Unchecked) != 0 && this.genericTypeArguments == null) {
        // https://bugs.eclipse.org/bugs/show_bug.cgi?id=277643, align with javac on JLS 15.12.2.6
        thrownExceptions =
            currentScope.environment().convertToRawTypes(this.binding.thrownExceptions, true, true);
      }
      // must verify that exceptions potentially thrown by this expression are caught in the method
      flowContext.checkExceptionHandlers(thrownExceptions, this, flowInfo.copy(), currentScope);
      // TODO (maxime) the copy above is needed because of a side effect into
      //               checkExceptionHandlers; consider protecting there instead of here;
      //               NullReferenceTest#test0510
    }
    manageSyntheticAccessIfNecessary(currentScope, flowInfo);
    return flowInfo;
  }