Package org.eclipse.jdt.internal.compiler.flow

Source Code of org.eclipse.jdt.internal.compiler.flow.FinallyFlowContext

/*******************************************************************************
* Copyright (c) 2000, 2013 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     IBM Corporation - initial API and implementation
*     Stephan Herrmann - Contributions for
*                bug 186342 - [compiler][null] Using annotations for null checking
*                bug 365519 - editorial cleanup after bug 186342 and bug 365387
*                bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK
*                bug 365859 - [compiler][null] distinguish warnings based on flow analysis vs. null annotations
*                bug 385626 - @NonNull fails across loop boundaries
*                bug 388996 - [compiler][resource] Incorrect 'potential resource leak'
*                bug 403147 - [compiler][null] FUP of bug 400761: consolidate interaction between unboxing, NPE, and deferred checking
*     Jesper S Moller - Contributions for
*                bug 404657 - [1.8][compiler] Analysis for effectively final variables fails to consider loops
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.flow;

import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.Reference;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.TagBits;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;

/**
* Reflects the context of code analysis, keeping track of enclosing
*  try statements, exception handlers, etc...
*/
public class FinallyFlowContext extends TryFlowContext {

  Reference[] finalAssignments;
  VariableBinding[] finalVariables;
  int assignCount;

  // the following three arrays are in sync regarding their indices:
  LocalVariableBinding[] nullLocals; // slots can be null for checkType == IN_UNBOXING
  ASTNode[] nullReferences;  // Expressions for null checking, Statements for resource analysis
                // cast to Expression is safe if corresponding nullCheckType != EXIT_RESOURCE
  int[] nullCheckTypes;
  int nullCount;
  // see also the related field FlowContext#expectedTypes

  // back reference to the flow context of the corresponding try statement
  public FlowContext tryContext;

  public FinallyFlowContext(FlowContext parent, ASTNode associatedNode, ExceptionHandlingFlowContext tryContext) {
    super(parent, associatedNode);
    this.tryContext = tryContext;
  }

/**
* Given some contextual initialization info (derived from a try block or a catch block), this
* code will check that the subroutine context does not also initialize a final variable potentially set
* redundantly.
*/
public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) {

  // check redundant final assignments
  for (int i = 0; i < this.assignCount; i++) {
    VariableBinding variable = this.finalVariables[i];
    if (variable == null) continue;

    boolean complained = false; // remember if have complained on this final assignment
    if (variable instanceof FieldBinding) {
      // final field
      if (flowInfo.isPotentiallyAssigned((FieldBinding)variable)) {
        complained = true;
        scope.problemReporter().duplicateInitializationOfBlankFinalField((FieldBinding)variable, this.finalAssignments[i]);
      }
    } else {
      // final local variable
      if (flowInfo.isPotentiallyAssigned((LocalVariableBinding)variable)) {
        variable.tagBits &= ~TagBits.IsEffectivelyFinal;
        if (variable.isFinal()) {
          complained = true;
          scope.problemReporter().duplicateInitializationOfFinalLocal(
            (LocalVariableBinding) variable,
            this.finalAssignments[i]);
        }
      }
    }
    // any reference reported at this level is removed from the parent context
    // where it could also be reported again
    if (complained) {
      FlowContext currentContext = this.getLocalParent();
      while (currentContext != null) {
        //if (currentContext.isSubRoutine()) {
        currentContext.removeFinalAssignmentIfAny(this.finalAssignments[i]);
        //}
        currentContext = currentContext.getLocalParent();
      }
    }
  }

  // check inconsistent null checks
  if ((this.tagBits & FlowContext.DEFER_NULL_DIAGNOSTIC) != 0) { // within an enclosing loop, be conservative
    for (int i = 0; i < this.nullCount; i++) {
      ASTNode location = this.nullReferences[i];
      switch (this.nullCheckTypes[i] & ~HIDE_NULL_COMPARISON_WARNING_MASK) {
        case ASSIGN_TO_NONNULL:
          int nullStatus = flowInfo.nullStatus(this.nullLocals[i]);
          if (nullStatus != FlowInfo.NON_NULL) {
            this.parent.recordNullityMismatch(scope, (Expression) location,
                this.providedExpectedTypes[i][0], this.providedExpectedTypes[i][1], nullStatus);
          }
          break;
        case IN_UNBOXING:
          checkUnboxing(scope, (Expression) location, flowInfo);
          break;
        default:
          this.parent.recordUsingNullReference(scope, this.nullLocals[i],
              this.nullReferences[i]this.nullCheckTypes[i], flowInfo);
      }

    }
  }
  else { // no enclosing loop, be as precise as possible right now
    for (int i = 0; i < this.nullCount; i++) {
      ASTNode location = this.nullReferences[i];
      // final local variable
      LocalVariableBinding local = this.nullLocals[i];
      switch (this.nullCheckTypes[i] & ~HIDE_NULL_COMPARISON_WARNING_MASK) {
        case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL:
        case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL:
          if (flowInfo.isDefinitelyNonNull(local)) {
            if ((this.nullCheckTypes[i] & ~HIDE_NULL_COMPARISON_WARNING_MASK) == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) {
              if ((this.nullCheckTypes[i] & HIDE_NULL_COMPARISON_WARNING) == 0) {
                scope.problemReporter().localVariableRedundantCheckOnNonNull(local, location);
              }
            } else {
              scope.problemReporter().localVariableNonNullComparedToNull(local, location);
            }
            continue;
          }
          //$FALL-THROUGH$
        case CAN_ONLY_NULL | IN_COMPARISON_NULL:
        case CAN_ONLY_NULL | IN_COMPARISON_NON_NULL:
        case CAN_ONLY_NULL | IN_ASSIGNMENT:
        case CAN_ONLY_NULL | IN_INSTANCEOF:
          Expression expression = (Expression) location;
          if (flowInfo.isDefinitelyNull(local)) {
            switch(this.nullCheckTypes[i] & CONTEXT_MASK) {
              case FlowContext.IN_COMPARISON_NULL:
                if (((this.nullCheckTypes[i] & CHECK_MASK & ~HIDE_NULL_COMPARISON_WARNING_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
                  scope.problemReporter().localVariableNullReference(local, expression);
                  continue;
                }
                if ((this.nullCheckTypes[i] & HIDE_NULL_COMPARISON_WARNING) == 0) {
                  scope.problemReporter().localVariableRedundantCheckOnNull(local, expression);
                }
                continue;
              case FlowContext.IN_COMPARISON_NON_NULL:
                if (((this.nullCheckTypes[i] & CHECK_MASK & ~HIDE_NULL_COMPARISON_WARNING_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
                  scope.problemReporter().localVariableNullReference(local, expression);
                  continue;
                }
                scope.problemReporter().localVariableNullComparedToNonNull(local, expression);
                continue;
              case FlowContext.IN_ASSIGNMENT:
                scope.problemReporter().localVariableRedundantNullAssignment(local, expression);
                continue;
              case FlowContext.IN_INSTANCEOF:
                scope.problemReporter().localVariableNullInstanceof(local, expression);
                continue;
            }
          } else if (flowInfo.isPotentiallyNull(local)) {
            switch(this.nullCheckTypes[i] & CONTEXT_MASK) {
              case FlowContext.IN_COMPARISON_NULL:
                this.nullReferences[i] = null;
                if (((this.nullCheckTypes[i] & CHECK_MASK & ~HIDE_NULL_COMPARISON_WARNING_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
                  scope.problemReporter().localVariablePotentialNullReference(local, expression);
                  continue;
                }
                break;
              case FlowContext.IN_COMPARISON_NON_NULL:
                this.nullReferences[i] = null;
                if (((this.nullCheckTypes[i] & CHECK_MASK & ~HIDE_NULL_COMPARISON_WARNING_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
                  scope.problemReporter().localVariablePotentialNullReference(local, expression);
                  continue;
                }
                break;
            }
          }
          break;
        case MAY_NULL:
          if (flowInfo.isDefinitelyNull(local)) {
            scope.problemReporter().localVariableNullReference(local, location);
            continue;
          }
          if (flowInfo.isPotentiallyNull(local)) {
            scope.problemReporter().localVariablePotentialNullReference(local, location);
          }
          break;
        case ASSIGN_TO_NONNULL:
          int nullStatus = flowInfo.nullStatus(local);
          if (nullStatus != FlowInfo.NON_NULL) {
            char[][] annotationName = scope.environment().getNonNullAnnotationName();
            scope.problemReporter().nullityMismatch((Expression) location, this.providedExpectedTypes[i][0], this.providedExpectedTypes[i][1], nullStatus, annotationName);
          }
          break;
        case IN_UNBOXING:
          checkUnboxing(scope, (Expression) location, flowInfo)
          break;
        default:
          // should not happen
      }
    }
  }
}

  public String individualToString() {

    StringBuffer buffer = new StringBuffer("Finally flow context"); //$NON-NLS-1$
    buffer.append("[finalAssignments count - ").append(this.assignCount).append(']'); //$NON-NLS-1$
    buffer.append("[nullReferences count - ").append(this.nullCount).append(']'); //$NON-NLS-1$
    return buffer.toString();
  }

  public boolean isSubRoutine() {
    return true;
  }

  protected boolean recordFinalAssignment(
    VariableBinding binding,
    Reference finalAssignment) {
    if (this.assignCount == 0) {
      this.finalAssignments = new Reference[5];
      this.finalVariables = new VariableBinding[5];
    } else {
      if (this.assignCount == this.finalAssignments.length)
        System.arraycopy(
          this.finalAssignments,
          0,
          (this.finalAssignments = new Reference[this.assignCount * 2]),
          0,
          this.assignCount);
      System.arraycopy(
        this.finalVariables,
        0,
        (this.finalVariables = new VariableBinding[this.assignCount * 2]),
        0,
        this.assignCount);
    }
    this.finalAssignments[this.assignCount] = finalAssignment;
    this.finalVariables[this.assignCount++] = binding;
    return true;
  }

  public void recordUsingNullReference(Scope scope, LocalVariableBinding local,
      ASTNode location, int checkType, FlowInfo flowInfo) {
    if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0 && !flowInfo.isDefinitelyUnknown(local))  {
      // if reference is being recorded inside an assert, we will not raise redundant null check warnings
      checkType |= (this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING);
      int checkTypeWithoutHideNullWarning = checkType & ~FlowContext.HIDE_NULL_COMPARISON_WARNING_MASK;
      if ((this.tagBits & FlowContext.DEFER_NULL_DIAGNOSTIC) != 0) { // within an enclosing loop, be conservative
        switch (checkTypeWithoutHideNullWarning) {
          case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL:
          case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL:
          case CAN_ONLY_NULL | IN_COMPARISON_NULL:
          case CAN_ONLY_NULL | IN_COMPARISON_NON_NULL:
          case CAN_ONLY_NULL | IN_ASSIGNMENT:
          case CAN_ONLY_NULL | IN_INSTANCEOF:
            Expression reference = (Expression) location;
            if (flowInfo.cannotBeNull(local)) {
              if (checkTypeWithoutHideNullWarning == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) {
                if ((checkType & HIDE_NULL_COMPARISON_WARNING) == 0) {
                  scope.problemReporter().localVariableRedundantCheckOnNonNull(local, reference);
                }
                flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
              } else if (checkTypeWithoutHideNullWarning == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL)) {
                scope.problemReporter().localVariableNonNullComparedToNull(local, reference);
                flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
              }
              return;
            }
            if (flowInfo.canOnlyBeNull(local)) {
              switch(checkTypeWithoutHideNullWarning & CONTEXT_MASK) {
                case FlowContext.IN_COMPARISON_NULL:
                  if (((checkTypeWithoutHideNullWarning & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
                    scope.problemReporter().localVariableNullReference(local, reference);
                    return;
                  }
                  if ((checkType & HIDE_NULL_COMPARISON_WARNING) == 0) {
                    scope.problemReporter().localVariableRedundantCheckOnNull(local, reference);
                  }
                  flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
                  return;
                case FlowContext.IN_COMPARISON_NON_NULL:
                  if (((checkTypeWithoutHideNullWarning & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
                    scope.problemReporter().localVariableNullReference(local, reference);
                    return;
                  }
                  scope.problemReporter().localVariableNullComparedToNonNull(local, reference);
                  flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
                  return;
                case FlowContext.IN_ASSIGNMENT:
                  scope.problemReporter().localVariableRedundantNullAssignment(local, reference);
                  return;
                case FlowContext.IN_INSTANCEOF:
                  scope.problemReporter().localVariableNullInstanceof(local, reference);
                  return;
              }
            } else if (flowInfo.isPotentiallyNull(local)) {
              switch(checkTypeWithoutHideNullWarning & CONTEXT_MASK) {
                case FlowContext.IN_COMPARISON_NULL:
                  if (((checkTypeWithoutHideNullWarning & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
                    scope.problemReporter().localVariablePotentialNullReference(local, reference);
                    return;
                  }
                  break;
                case FlowContext.IN_COMPARISON_NON_NULL:
                  if (((checkTypeWithoutHideNullWarning & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
                    scope.problemReporter().localVariablePotentialNullReference(local, reference);
                    return;
                  }
                  break;
              }
            }
            break;
          case MAY_NULL :
            if (flowInfo.cannotBeNull(local)) {
              return;
            }
            if (flowInfo.canOnlyBeNull(local)) {
              scope.problemReporter().localVariableNullReference(local, location);
              return;
            }
            break;
          default:
            // never happens
        }
      }
      else { // no enclosing loop, be as precise as possible right now
        switch (checkTypeWithoutHideNullWarning) {
          case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL:
          case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL:
            if (flowInfo.isDefinitelyNonNull(local)) {
              if (checkTypeWithoutHideNullWarning == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) {
                if ((checkType & HIDE_NULL_COMPARISON_WARNING) == 0) {
                  scope.problemReporter().localVariableRedundantCheckOnNonNull(local, location);
                }
                flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
              } else {
                scope.problemReporter().localVariableNonNullComparedToNull(local, location);
                flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
              }
              return;
            }
            //$FALL-THROUGH$
          case CAN_ONLY_NULL | IN_COMPARISON_NULL:
          case CAN_ONLY_NULL | IN_COMPARISON_NON_NULL:
          case CAN_ONLY_NULL | IN_ASSIGNMENT:
          case CAN_ONLY_NULL | IN_INSTANCEOF:
            Expression reference = (Expression) location;
            if (flowInfo.isDefinitelyNull(local)) {
              switch(checkTypeWithoutHideNullWarning & CONTEXT_MASK) {
                case FlowContext.IN_COMPARISON_NULL:
                  if (((checkTypeWithoutHideNullWarning & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
                    scope.problemReporter().localVariableNullReference(local, reference);
                    return;
                  }
                  if ((checkType & HIDE_NULL_COMPARISON_WARNING) == 0) {
                    scope.problemReporter().localVariableRedundantCheckOnNull(local, reference);
                  }
                  flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
                  return;
                case FlowContext.IN_COMPARISON_NON_NULL:
                  if (((checkTypeWithoutHideNullWarning & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
                    scope.problemReporter().localVariableNullReference(local, reference);
                    return;
                  }
                  scope.problemReporter().localVariableNullComparedToNonNull(local, reference);
                  flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
                  return;
                case FlowContext.IN_ASSIGNMENT:
                  scope.problemReporter().localVariableRedundantNullAssignment(local, reference);
                  return;
                case FlowContext.IN_INSTANCEOF:
                  scope.problemReporter().localVariableNullInstanceof(local, reference);
                  return;
              }
            } else if (flowInfo.isPotentiallyNull(local)) {
              switch(checkTypeWithoutHideNullWarning & CONTEXT_MASK) {
                case FlowContext.IN_COMPARISON_NULL:
                  if (((checkTypeWithoutHideNullWarning & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
                    scope.problemReporter().localVariablePotentialNullReference(local, reference);
                    return;
                  }
                  break;
                case FlowContext.IN_COMPARISON_NON_NULL:
                  if (((checkTypeWithoutHideNullWarning & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
                    scope.problemReporter().localVariablePotentialNullReference(local, reference);
                    return;
                  }
                  break;
              }
            }
            break;
          case MAY_NULL :
            if (flowInfo.isDefinitelyNull(local)) {
              scope.problemReporter().localVariableNullReference(local, location);
              return;
            }
            if (flowInfo.isPotentiallyNull(local)) {
              scope.problemReporter().localVariablePotentialNullReference(local, location);
              return;
            }
            if (flowInfo.isDefinitelyNonNull(local)) {
              return; // shortcut: cannot be null
            }
            break;
          default:
            // never happens
        }
      }
      recordNullReference(local, location, checkType);
      // prepare to re-check with try/catch flow info
    }
  }

  void removeFinalAssignmentIfAny(Reference reference) {
    for (int i = 0; i < this.assignCount; i++) {
      if (this.finalAssignments[i] == reference) {
        this.finalAssignments[i] = null;
        this.finalVariables[i] = null;
        return;
      }
    }
  }

protected void recordNullReference(LocalVariableBinding local,
  ASTNode expression, int checkType) {
  if (this.nullCount == 0) {
    this.nullLocals = new LocalVariableBinding[5];
    this.nullReferences = new ASTNode[5];
    this.nullCheckTypes = new int[5];
  }
  else if (this.nullCount == this.nullLocals.length) {
    int newLength = this.nullCount * 2;
    System.arraycopy(this.nullLocals, 0,
      this.nullLocals = new LocalVariableBinding[newLength], 0,
      this.nullCount);
    System.arraycopy(this.nullReferences, 0,
      this.nullReferences = new ASTNode[newLength], 0,
      this.nullCount);
    System.arraycopy(this.nullCheckTypes, 0,
      this.nullCheckTypes = new int[newLength], 0,
      this.nullCount);
  }
  this.nullLocals[this.nullCount] = local;
  this.nullReferences[this.nullCount] = expression;
  this.nullCheckTypes[this.nullCount++] = checkType;
}
public void recordUnboxing(Scope scope, Expression expression, int nullStatus, FlowInfo flowInfo) {
  if (nullStatus == FlowInfo.NULL)
    super.recordUnboxing(scope, expression, nullStatus, flowInfo);
  else // defer checking:
    recordNullReference(null, expression, IN_UNBOXING);
}
protected boolean internalRecordNullityMismatch(Expression expression, TypeBinding providedType, int nullStatus, TypeBinding expectedType, int checkType) {
  // cf. decision structure inside FinallyFlowContext.recordUsingNullReference(..)
  if (nullStatus == FlowInfo.UNKNOWN ||
      ((this.tagBits & FlowContext.DEFER_NULL_DIAGNOSTIC) != 0 && nullStatus != FlowInfo.NULL)) {
    recordProvidedExpectedTypes(providedType, expectedType, this.nullCount);
    recordNullReference(expression.localVariableBinding(), expression, checkType);
    return true;
  }
  return false;
}
}
TOP

Related Classes of org.eclipse.jdt.internal.compiler.flow.FinallyFlowContext

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.