### Eclipse Workspace Patch 1.0 #P org.eclipse.jdt.core Index: compiler/org/eclipse/jdt/core/compiler/IProblem.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java,v retrieving revision 1.164 diff -u -r1.164 IProblem.java --- compiler/org/eclipse/jdt/core/compiler/IProblem.java 10 Jan 2006 21:01:07 -0000 1.164 +++ compiler/org/eclipse/jdt/core/compiler/IProblem.java 24 Jan 2006 09:56:37 -0000 @@ -688,6 +688,8 @@ int LocalVariableCannotBeNull = MethodRelated + 397; /** @since 3.1 */ int LocalVariableCanOnlyBeNull = MethodRelated + 398; + /** @since 3.2 */ + int LocalVariableMayBeNull = MethodRelated + 399; // method verifier problems int AbstractMethodMustBeImplemented = MethodRelated + 400; Index: compiler/org/eclipse/jdt/internal/compiler/ast/AND_AND_Expression.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AND_AND_Expression.java,v retrieving revision 1.31 diff -u -r1.31 AND_AND_Expression.java --- compiler/org/eclipse/jdt/internal/compiler/ast/AND_AND_Expression.java 18 Nov 2005 16:46:22 -0000 1.31 +++ compiler/org/eclipse/jdt/internal/compiler/ast/AND_AND_Expression.java 24 Jan 2006 09:56:37 -0000 @@ -49,7 +49,7 @@ // need to be careful of scenario: // (x && y) && !z, if passing the left info to the right, it would be // swapped by the ! - FlowInfo rightInfo = leftInfo.initsWhenTrue().unconditionalInits().copy(); + FlowInfo rightInfo = leftInfo.initsWhenTrue().unconditionalCopy(); rightInitStateIndex = currentScope.methodScope().recordInitializationStates(rightInfo); int previousMode = rightInfo.reachMode(); @@ -57,12 +57,11 @@ rightInfo.setReachMode(FlowInfo.UNREACHABLE); } rightInfo = right.analyseCode(currentScope, flowContext, rightInfo); - FlowInfo trueMergedInfo = rightInfo.initsWhenTrue().copy(); - rightInfo.setReachMode(previousMode); // reset after trueMergedInfo got extracted FlowInfo mergedInfo = FlowInfo.conditional( - trueMergedInfo, - leftInfo.initsWhenFalse().copy().unconditionalInits().mergedWith( - rightInfo.initsWhenFalse().copy().unconditionalInits())); + rightInfo.safeInitsWhenTrue(), + leftInfo.initsWhenFalse().unconditionalInits().mergedWith( + rightInfo.initsWhenFalse().setReachMode(previousMode).unconditionalInits())); + // reset after trueMergedInfo got extracted mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo); return mergedInfo; } Index: compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java,v retrieving revision 1.61 diff -u -r1.61 ASTNode.java --- compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java 19 Jan 2006 13:58:39 -0000 1.61 +++ compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java 24 Jan 2006 09:56:37 -0000 @@ -39,7 +39,7 @@ public final static int Bit15 = 0x4000; // is unnecessary cast (expression) | is varargs (type ref) public final static int Bit16 = 0x8000; // in javadoc comment (name ref, type ref, msg) public final static int Bit17 = 0x10000; // compound assigned (reference lhs) - public final static int Bit18 = 0x20000; + public final static int Bit18 = 0x20000; // non null (expression) public final static int Bit19 = 0x40000; public final static int Bit20 = 0x80000; public final static int Bit21 = 0x100000; @@ -158,6 +158,9 @@ // for array initializer public static final int IsAnnotationDefaultValue = Bit1; + // for null refenrence analysis + public static final int IsNonNull = Bit18; + public ASTNode() { super(); Index: compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java,v retrieving revision 1.57 diff -u -r1.57 AllocationExpression.java --- compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java 10 Jan 2006 14:37:27 -0000 1.57 +++ compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java 24 Jan 2006 09:56:37 -0000 @@ -164,7 +164,7 @@ */ public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) { - if (!flowInfo.isReachable()) return; + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) { ReferenceBinding allocatedTypeErasure = (ReferenceBinding) binding.declaringClass.erasure(); // perform some emulation work in case there is some and we are inside a local type only @@ -180,11 +180,12 @@ // request cascade of accesses } } + } } public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) { - if (!flowInfo.isReachable()) return; + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) { // if constructor from parameterized type got found, use the original constructor at codegen time this.codegenBinding = this.binding.original(); @@ -201,6 +202,7 @@ currentScope.problemReporter().needToEmulateMethodAccess(this.codegenBinding, this); } } + } } public StringBuffer printExpression(int indent, StringBuffer output) { Index: compiler/org/eclipse/jdt/internal/compiler/ast/ArrayReference.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ArrayReference.java,v retrieving revision 1.39 diff -u -r1.39 ArrayReference.java --- compiler/org/eclipse/jdt/internal/compiler/ast/ArrayReference.java 10 Jan 2006 14:37:27 -0000 1.39 +++ compiler/org/eclipse/jdt/internal/compiler/ast/ArrayReference.java 24 Jan 2006 09:56:37 -0000 @@ -27,34 +27,32 @@ sourceStart = rec.sourceStart; } - public FlowInfo analyseAssignment( +public FlowInfo analyseAssignment( BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, Assignment assignment, boolean compoundAssignment) { + // TODO (maxime) optimization: unconditionalInits is applied to all existing calls + if (assignment.expression == null) { + return analyseCode(currentScope, flowContext, flowInfo); + } + return assignment + .expression + .analyseCode( + currentScope, + flowContext, + analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()); +} - if (assignment.expression == null) { - return analyseCode(currentScope, flowContext, flowInfo).unconditionalInits(); - } - return assignment - .expression - .analyseCode( - currentScope, - flowContext, - analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()) - .unconditionalInits(); - } - - public FlowInfo analyseCode( +public FlowInfo analyseCode( BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { - - flowInfo = receiver.analyseCode(currentScope, flowContext, flowInfo); - receiver.checkNullStatus(currentScope, flowContext, flowInfo, FlowInfo.NON_NULL); - return position.analyseCode(currentScope, flowContext, flowInfo); - } + receiver.checkNPE(currentScope, flowContext, flowInfo, true); + flowInfo = receiver.analyseCode(currentScope, flowContext, flowInfo); + return position.analyseCode(currentScope, flowContext, flowInfo); +} public void generateAssignment( BlockScope currentScope, @@ -179,6 +177,10 @@ codeStream.arrayAtPut(this.resolvedType.id, false); } +public int nullStatus(FlowInfo flowInfo) { + return FlowInfo.UNKNOWN; +} + public StringBuffer printExpression(int indent, StringBuffer output) { receiver.printExpression(0, output).append('['); Index: compiler/org/eclipse/jdt/internal/compiler/ast/AssertStatement.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AssertStatement.java,v retrieving revision 1.46 diff -u -r1.46 AssertStatement.java --- compiler/org/eclipse/jdt/internal/compiler/ast/AssertStatement.java 13 Jan 2006 16:37:16 -0000 1.46 +++ compiler/org/eclipse/jdt/internal/compiler/ast/AssertStatement.java 24 Jan 2006 09:56:37 -0000 @@ -54,11 +54,12 @@ boolean isOptimizedTrueAssertion = cst != Constant.NotAConstant && cst.booleanValue() == true; boolean isOptimizedFalseAssertion = cst != Constant.NotAConstant && cst.booleanValue() == false; - FlowInfo assertInfo = flowInfo.copy(); + UnconditionalFlowInfo assertInfo = assertExpression. + analyseCode(currentScope, flowContext, flowInfo.copy()). + unconditionalInits(); if (isOptimizedTrueAssertion) { assertInfo.setReachMode(FlowInfo.UNREACHABLE); } - assertInfo = assertExpression.analyseCode(currentScope, flowContext, assertInfo).unconditionalInits(); if (exceptionArgument != null) { // only gets evaluated when escaping - results are not taken into account @@ -80,7 +81,7 @@ if (isOptimizedFalseAssertion) { return flowInfo; // if assertions are enabled, the following code will be unreachable } else { - return flowInfo.mergedWith(assertInfo.unconditionalInits()); + return flowInfo.mergedWith(assertInfo); } } @@ -163,7 +164,7 @@ public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) { - if (!flowInfo.isReachable()) return; + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) { // need assertion flag: $assertionsDisabled on outer most source clas // (in case of static member of interface, will use the outermost static member - bug 22334) @@ -186,6 +187,7 @@ break; } } + } } public StringBuffer printStatement(int tab, StringBuffer output) { Index: compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java,v retrieving revision 1.67 diff -u -r1.67 Assignment.java --- compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java 10 Jan 2006 14:37:27 -0000 1.67 +++ compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java 24 Jan 2006 09:56:37 -0000 @@ -36,34 +36,38 @@ this.sourceEnd = sourceEnd; } - public FlowInfo analyseCode( - BlockScope currentScope, - FlowContext flowContext, - FlowInfo flowInfo) { - // record setting a variable: various scenarii are possible, setting an array reference, - // a field reference, a blank final field reference, a field of an enclosing instance or - // just a local variable. - - LocalVariableBinding local = this.lhs.localVariableBinding(); - int nullStatus = this.expression.nullStatus(flowInfo); - if (local != null && nullStatus == FlowInfo.NULL) { - flowContext.recordUsingNullReference(currentScope, local, this.lhs, FlowInfo.NON_NULL, flowInfo); - } - flowInfo = ((Reference) lhs) - .analyseAssignment(currentScope, flowContext, flowInfo, this, false) - .unconditionalInits(); - if (local != null) { - switch(nullStatus) { - case FlowInfo.NULL : - flowInfo.markAsDefinitelyNull(local); - break; - case FlowInfo.NON_NULL : - flowInfo.markAsDefinitelyNonNull(local); - break; - } - } - return flowInfo; - } +public FlowInfo analyseCode( + BlockScope currentScope, + FlowContext flowContext, + FlowInfo flowInfo) { + // record setting a variable: various scenarii are possible, setting an array reference, + // a field reference, a blank final field reference, a field of an enclosing instance or + // just a local variable. + LocalVariableBinding local = this.lhs.localVariableBinding(); + int nullStatus = this.expression.nullStatus(flowInfo); + if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) { + if (nullStatus == FlowInfo.NULL) { + flowContext.recordUsingNullReference(currentScope, local, this.lhs, + FlowContext.CAN_ONLY_NULL, flowInfo); + } + } + flowInfo = ((Reference) lhs) + .analyseAssignment(currentScope, flowContext, flowInfo, this, false) + .unconditionalInits(); + if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) { + switch(nullStatus) { + case FlowInfo.NULL : + flowInfo.markAsDefinitelyNull(local); + break; + case FlowInfo.NON_NULL : + flowInfo.markAsDefinitelyNonNull(local); + break; + default: + flowInfo.markAsDefinitelyUnknown(local); + } + } + return flowInfo; +} void checkAssignmentEffect(BlockScope scope) { @@ -250,4 +254,8 @@ } visitor.endVisit(this, scope); } + +public LocalVariableBinding localVariableBinding() { + return lhs.localVariableBinding(); +} } Index: compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java,v retrieving revision 1.54 diff -u -r1.54 BinaryExpression.java --- compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java 10 Jan 2006 14:37:27 -0000 1.54 +++ compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java 24 Jan 2006 09:56:38 -0000 @@ -31,18 +31,17 @@ this.sourceEnd = right.sourceEnd; } - public FlowInfo analyseCode( +public FlowInfo analyseCode( BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { - - return right - .analyseCode( - currentScope, - flowContext, - left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()) - .unconditionalInits(); - } + left.checkNPE(currentScope, flowContext, flowInfo, false /* skip String */); + flowInfo = left.analyseCode(currentScope, flowContext, flowInfo). + unconditionalInits(); + right.checkNPE(currentScope, flowContext, flowInfo, false /* skip String */); + return right.analyseCode(currentScope, flowContext, flowInfo). + unconditionalInits(); +} public void computeConstant(BlockScope scope, int leftId, int rightId) { Index: compiler/org/eclipse/jdt/internal/compiler/ast/Clinit.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Clinit.java,v retrieving revision 1.40 diff -u -r1.40 Clinit.java --- compiler/org/eclipse/jdt/internal/compiler/ast/Clinit.java 10 Jan 2006 14:37:27 -0000 1.40 +++ compiler/org/eclipse/jdt/internal/compiler/ast/Clinit.java 24 Jan 2006 09:56:38 -0000 @@ -45,7 +45,8 @@ FlowInfo.DEAD_END); // check for missing returning path - this.needFreeReturn = flowInfo.isReachable(); + this.needFreeReturn = (flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0; + // check missing blank final field initializations flowInfo = flowInfo.mergedWith(staticInitializerFlowContext.initsOnReturn); Index: compiler/org/eclipse/jdt/internal/compiler/ast/CompoundAssignment.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CompoundAssignment.java,v retrieving revision 1.51 diff -u -r1.51 CompoundAssignment.java --- compiler/org/eclipse/jdt/internal/compiler/ast/CompoundAssignment.java 18 Nov 2005 16:46:21 -0000 1.51 +++ compiler/org/eclipse/jdt/internal/compiler/ast/CompoundAssignment.java 24 Jan 2006 09:56:38 -0000 @@ -34,13 +34,14 @@ this.operator = operator ; } - public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { - // record setting a variable: various scenarii are possible, setting an array reference, - // a field reference, a blank final field reference, a field of an enclosing instance or - // just a local variable. - - return ((Reference) lhs).analyseAssignment(currentScope, flowContext, flowInfo, this, true).unconditionalInits(); - } +public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, + FlowInfo flowInfo) { + // record setting a variable: various scenarii are possible, setting an array reference, + // a field reference, a blank final field reference, a field of an enclosing instance or + // just a local variable. + lhs.checkNPE(currentScope, flowContext, flowInfo, false /* skip String */); + return ((Reference) lhs).analyseAssignment(currentScope, flowContext, flowInfo, this, true).unconditionalInits(); +} public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) { @@ -56,9 +57,10 @@ codeStream.recordPositionsFrom(pc, this.sourceStart); } - public int nullStatus(FlowInfo flowInfo) { - return FlowInfo.NON_NULL; - } +public int nullStatus(FlowInfo flowInfo) { + return FlowInfo.NON_NULL; + // we may have complained on checkNPE, but we avoid duplicate error +} public String operatorToString() { switch (operator) { Index: compiler/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java,v retrieving revision 1.77 diff -u -r1.77 ConditionalExpression.java --- compiler/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java 13 Jan 2006 16:37:16 -0000 1.77 +++ compiler/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java 24 Jan 2006 09:56:38 -0000 @@ -40,11 +40,8 @@ sourceEnd = valueIfFalse.sourceEnd; } - public FlowInfo analyseCode( - BlockScope currentScope, - FlowContext flowContext, +public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { - Constant cst = this.condition.optimizedBooleanConstant(); boolean isConditionOptimizedTrue = cst != Constant.NotAConstant && cst.booleanValue() == true; boolean isConditionOptimizedFalse = cst != Constant.NotAConstant && cst.booleanValue() == false; @@ -84,16 +81,13 @@ boolean isValueIfFalseOptimizedTrue = cst != null && cst != Constant.NotAConstant && cst.booleanValue() == true; boolean isValueIfFalseOptimizedFalse = cst != null && cst != Constant.NotAConstant && cst.booleanValue() == false; - UnconditionalFlowInfo trueInfoWhenTrue = trueFlowInfo.initsWhenTrue().copy().unconditionalInits(); + UnconditionalFlowInfo trueInfoWhenTrue = trueFlowInfo.initsWhenTrue().unconditionalCopy(); + UnconditionalFlowInfo falseInfoWhenTrue = falseFlowInfo.initsWhenTrue().unconditionalCopy(); + UnconditionalFlowInfo trueInfoWhenFalse = trueFlowInfo.initsWhenFalse().unconditionalInits(); + UnconditionalFlowInfo falseInfoWhenFalse = falseFlowInfo.initsWhenFalse().unconditionalInits(); if (isValueIfTrueOptimizedFalse) trueInfoWhenTrue.setReachMode(FlowInfo.UNREACHABLE); - - UnconditionalFlowInfo falseInfoWhenTrue = falseFlowInfo.initsWhenTrue().copy().unconditionalInits(); if (isValueIfFalseOptimizedFalse) falseInfoWhenTrue.setReachMode(FlowInfo.UNREACHABLE); - - UnconditionalFlowInfo trueInfoWhenFalse = trueFlowInfo.initsWhenFalse().copy().unconditionalInits(); if (isValueIfTrueOptimizedTrue) trueInfoWhenFalse.setReachMode(FlowInfo.UNREACHABLE); - - UnconditionalFlowInfo falseInfoWhenFalse = falseFlowInfo.initsWhenFalse().copy().unconditionalInits(); if (isValueIfFalseOptimizedTrue) falseInfoWhenFalse.setReachMode(FlowInfo.UNREACHABLE); mergedInfo = @@ -271,6 +265,23 @@ // no implicit conversion for boolean values codeStream.updateLastRecordedEndPC(currentScope, codeStream.position); } + +public int nullStatus(FlowInfo flowInfo) { + Constant cst = this.condition.optimizedBooleanConstant(); + if (cst != Constant.NotAConstant) { + if (cst.booleanValue()) { + return valueIfTrue.nullStatus(flowInfo); + } + return valueIfFalse.nullStatus(flowInfo); + } + int ifTrueNullStatus = valueIfTrue.nullStatus(flowInfo), + ifFalseNullStatus = valueIfFalse.nullStatus(flowInfo); + if (ifTrueNullStatus == ifFalseNullStatus) { + return ifTrueNullStatus; + } + return FlowInfo.UNKNOWN; + // cannot decide which branch to take, and they disagree +} public Constant optimizedBooleanConstant() { Index: compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java,v retrieving revision 1.77 diff -u -r1.77 ConstructorDeclaration.java --- compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java 10 Jan 2006 14:37:27 -0000 1.77 +++ compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java 24 Jan 2006 09:56:38 -0000 @@ -112,7 +112,8 @@ } } // check for missing returning path - this.needFreeReturn = flowInfo.isReachable(); + this.needFreeReturn = (flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0; + // check missing blank final field initializations if ((constructorCall != null) Index: compiler/org/eclipse/jdt/internal/compiler/ast/ContinueStatement.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ContinueStatement.java,v retrieving revision 1.6 diff -u -r1.6 ContinueStatement.java --- compiler/org/eclipse/jdt/internal/compiler/ast/ContinueStatement.java 14 Oct 2005 22:26:02 -0000 1.6 +++ compiler/org/eclipse/jdt/internal/compiler/ast/ContinueStatement.java 24 Jan 2006 09:56:38 -0000 @@ -71,7 +71,7 @@ flowInfo.addInitializationsFrom(tryStatement.subRoutineInits); // collect inits } else if (traversedContext == targetContext) { // only record continue info once accumulated through subroutines, and only against target context - targetContext.recordContinueFrom(flowInfo); + targetContext.recordContinueFrom(flowContext, flowInfo); break; } } while ((traversedContext = traversedContext.parent) != null); Index: compiler/org/eclipse/jdt/internal/compiler/ast/DoStatement.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/DoStatement.java,v retrieving revision 1.45 diff -u -r1.45 DoStatement.java --- compiler/org/eclipse/jdt/internal/compiler/ast/DoStatement.java 13 Jan 2006 16:37:16 -0000 1.45 +++ compiler/org/eclipse/jdt/internal/compiler/ast/DoStatement.java 24 Jan 2006 09:56:38 -0000 @@ -46,6 +46,7 @@ LoopingFlowContext loopingContext = new LoopingFlowContext( flowContext, + flowInfo, this, breakLabel, continueLabel, @@ -59,12 +60,19 @@ int previousMode = flowInfo.reachMode(); - FlowInfo actionInfo = flowInfo.copy().unconditionalInits().discardNullRelatedInitializations(); + 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 ((action != null) && !action.isEmptyBlock()) { - actionInfo = action.analyseCode(currentScope, loopingContext, actionInfo); + actionInfo = action. + analyseCode(currentScope, loopingContext, actionInfo). + unconditionalInits(); // code generation can be optimized when no need to continue in the loop - if (!actionInfo.isReachable() && !loopingContext.initsOnContinue.isReachable()) { + if ((actionInfo.tagBits & + loopingContext.initsOnContinue.tagBits & + FlowInfo.UNREACHABLE) != 0) { continueLabel = null; } } @@ -75,22 +83,35 @@ */ actionInfo.setReachMode(previousMode); - actionInfo = + LoopingFlowContext condLoopContext; + FlowInfo condInfo = condition.analyseCode( currentScope, - loopingContext, + (condLoopContext = + new LoopingFlowContext(flowContext, flowInfo, this, null, + null, currentScope)), (action == null ? actionInfo - : (actionInfo.mergedWith(loopingContext.initsOnContinue)))); + : (actionInfo.mergedWith(loopingContext.initsOnContinue))).copy()); if (!isConditionOptimizedFalse && continueLabel != null) { - loopingContext.complainOnDeferredChecks(currentScope, actionInfo); + loopingContext.complainOnDeferredFinalChecks(currentScope, condInfo); + condLoopContext.complainOnDeferredFinalChecks(currentScope, condInfo); + UnconditionalFlowInfo checkFlowInfo; + loopingContext.complainOnDeferredNullChecks(currentScope, + checkFlowInfo = actionInfo. + addPotentialNullInfoFrom( + condInfo.initsWhenTrue().unconditionalInits())); + condLoopContext.complainOnDeferredNullChecks(currentScope, + checkFlowInfo); } // end of loop FlowInfo mergedInfo = FlowInfo.mergedOptimizedBranches( loopingContext.initsOnBreak, - isConditionOptimizedTrue, - actionInfo.initsWhenFalse().addInitializationsFrom(flowInfo), // recover null inits from before condition analysis + 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(); */); mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo); Index: compiler/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java,v retrieving revision 1.59 diff -u -r1.59 EqualExpression.java --- compiler/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java 10 Jan 2006 14:37:27 -0000 1.59 +++ compiler/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java 24 Jan 2006 09:56:38 -0000 @@ -22,84 +22,99 @@ public EqualExpression(Expression left, Expression right,int operator) { super(left,right,operator); } - public void checkNullComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse) { + private void checkNullComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse) { LocalVariableBinding local = this.left.localVariableBinding(); - if (local != null) { + if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) { checkVariableComparison(scope, flowContext, flowInfo, initsWhenTrue, initsWhenFalse, local, right.nullStatus(flowInfo), this.left); } local = this.right.localVariableBinding(); - if (local != null) { + if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) { checkVariableComparison(scope, flowContext, flowInfo, initsWhenTrue, initsWhenFalse, local, left.nullStatus(flowInfo), this.right); } } private void checkVariableComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse, LocalVariableBinding local, int nullStatus, Expression reference) { switch (nullStatus) { case FlowInfo.NULL : - flowContext.recordUsingNullReference(scope, local, reference, FlowInfo.NULL, flowInfo); + flowContext.recordUsingNullReference(scope, local, reference, + FlowContext.CAN_ONLY_NULL_NON_NULL, flowInfo); if (((this.bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) { - initsWhenTrue.markAsDefinitelyNull(local); // from thereon it is set - initsWhenFalse.markAsDefinitelyNonNull(local); // from thereon it is set + initsWhenTrue.markAsComparedEqualToNull(local); // from thereon it is set + initsWhenFalse.markAsComparedEqualToNonNull(local); // from thereon it is set } else { - initsWhenTrue.markAsDefinitelyNonNull(local); // from thereon it is set - initsWhenFalse.markAsDefinitelyNull(local); // from thereon it is set + initsWhenTrue.markAsComparedEqualToNonNull(local); // from thereon it is set + initsWhenFalse.markAsComparedEqualToNull(local); // from thereon it is set } break; case FlowInfo.NON_NULL : - flowContext.recordUsingNullReference(scope, local, reference, FlowInfo.NON_NULL, flowInfo); + flowContext.recordUsingNullReference(scope, local, reference, + FlowContext.CAN_ONLY_NULL, flowInfo); if (((this.bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) { - initsWhenTrue.markAsDefinitelyNonNull(local); // from thereon it is set + initsWhenTrue.markAsComparedEqualToNonNull(local); // from thereon it is set } break; } } public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { + FlowInfo result; if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) { if ((left.constant != Constant.NotAConstant) && (left.constant.typeID() == T_boolean)) { if (left.constant.booleanValue()) { // true == anything // this is equivalent to the right argument inits - return right.analyseCode(currentScope, flowContext, flowInfo); + result = right.analyseCode(currentScope, flowContext, flowInfo); } else { // false == anything // this is equivalent to the right argument inits negated - return right.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition(); + result = right.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition(); } - } - if ((right.constant != Constant.NotAConstant) && (right.constant.typeID() == T_boolean)) { + } + else if ((right.constant != Constant.NotAConstant) && (right.constant.typeID() == T_boolean)) { if (right.constant.booleanValue()) { // anything == true - // this is equivalent to the right argument inits - return left.analyseCode(currentScope, flowContext, flowInfo); + // this is equivalent to the left argument inits + result = left.analyseCode(currentScope, flowContext, flowInfo); } else { // anything == false // this is equivalent to the right argument inits negated - return left.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition(); + result = left.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition(); } + } + else { + result = right.analyseCode( + currentScope, flowContext, + left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()).unconditionalInits(); } - return right.analyseCode( - currentScope, flowContext, - left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()).unconditionalInits(); } else { //NOT_EQUAL : if ((left.constant != Constant.NotAConstant) && (left.constant.typeID() == T_boolean)) { if (!left.constant.booleanValue()) { // false != anything // this is equivalent to the right argument inits - return right.analyseCode(currentScope, flowContext, flowInfo); + result = right.analyseCode(currentScope, flowContext, flowInfo); } else { // true != anything // this is equivalent to the right argument inits negated - return right.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition(); + result = right.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition(); } } - if ((right.constant != Constant.NotAConstant) && (right.constant.typeID() == T_boolean)) { + else if ((right.constant != Constant.NotAConstant) && (right.constant.typeID() == T_boolean)) { if (!right.constant.booleanValue()) { // anything != false // this is equivalent to the right argument inits - return left.analyseCode(currentScope, flowContext, flowInfo); + result = left.analyseCode(currentScope, flowContext, flowInfo); } else { // anything != true // this is equivalent to the right argument inits negated - return left.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition(); + result = left.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition(); } - } - return right.analyseCode( - currentScope, flowContext, - left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()).asNegatedCondition().unconditionalInits(); + } + else { + result = right.analyseCode( + currentScope, flowContext, + left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()). + /* unneeded since we flatten it: asNegatedCondition(). */ + unconditionalInits(); + } + } + if (result instanceof UnconditionalFlowInfo && + (result.tagBits & FlowInfo.UNREACHABLE) == 0) { // the flow info is flat + result = FlowInfo.conditional(result, result.copy()); } + checkNullComparison(currentScope, flowContext, result, result.initsWhenTrue(), result.initsWhenFalse()); + return result; } public final void computeConstant(TypeBinding leftType, TypeBinding rightType) { Index: compiler/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java,v retrieving revision 1.50 diff -u -r1.50 ExplicitConstructorCall.java --- compiler/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java 10 Jan 2006 14:37:27 -0000 1.50 +++ compiler/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java 24 Jan 2006 09:56:38 -0000 @@ -178,7 +178,7 @@ void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) { ReferenceBinding superTypeErasure = (ReferenceBinding) binding.declaringClass.erasure(); - if (!flowInfo.isReachable()) return; + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) { // perform some emulation work in case there is some and we are inside a local type only if (superTypeErasure.isNestedType() && currentScope.enclosingSourceType().isLocalType()) { @@ -190,11 +190,12 @@ currentScope.propagateInnerEmulation(superTypeErasure, qualification != null); } } + } } public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) { - if (!flowInfo.isReachable()) return; + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) { // if constructor from parameterized type got found, use the original constructor at codegen time this.codegenBinding = this.binding.original(); @@ -210,6 +211,7 @@ currentScope.problemReporter().needToEmulateMethodAccess(this.codegenBinding, this); } } + } } public StringBuffer printStatement(int indent, StringBuffer output) { Index: compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java,v retrieving revision 1.92 diff -u -r1.92 Expression.java --- compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java 17 Jan 2006 18:44:23 -0000 1.92 +++ compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java 24 Jan 2006 09:56:38 -0000 @@ -481,31 +481,32 @@ } } - public void checkNullComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse) { - // do nothing by default - see EqualExpression - } - - public FlowInfo checkNullStatus(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, int nullStatus) { - - LocalVariableBinding local = this.localVariableBinding(); - if (local != null) { - switch(nullStatus) { - case FlowInfo.NULL : - flowContext.recordUsingNullReference(scope, local, this, FlowInfo.NULL, flowInfo); - flowInfo.markAsDefinitelyNull(local); // from thereon it is set - break; - case FlowInfo.NON_NULL : - flowContext.recordUsingNullReference(scope, local, this, FlowInfo.NON_NULL, flowInfo); - flowInfo.markAsDefinitelyNonNull(local); // from thereon it is set - break; - case FlowInfo.UNKNOWN : - break; - } +/** + * Check the local variable of this expression, if any, against potential NPEs + * given a flow context and an upstream flow info. If so, report the risk to + * the context. Marks the local as checked, which affects the flow info. + * @param scope the scope of the analysis + * @param flowContext the current flow context + * @param flowInfo the upstream flow info; caveat: may get modified + * @param checkString if true, a local variable of type String is checked; else + * it is skipped + */ +public void checkNPE(BlockScope scope, FlowContext flowContext, + FlowInfo flowInfo, boolean checkString) { + LocalVariableBinding local = this.localVariableBinding(); + if (local != null && + (local.type.tagBits & TagBits.IsBaseType) == 0 && + (checkString || local.type.id != T_JavaLangString)) { + if ((this.bits & IsNonNull) == 0) { + flowContext.recordUsingNullReference(scope, local, this, + FlowContext.MAY_NULL, flowInfo); } - return flowInfo; + flowInfo.markAsComparedEqualToNonNull(local); + // from thereon it is set } - - public boolean checkUnsafeCast(Scope scope, TypeBinding castType, TypeBinding expressionType, TypeBinding match, boolean isNarrowing) { +} + +public boolean checkUnsafeCast(Scope scope, TypeBinding castType, TypeBinding expressionType, TypeBinding match, boolean isNarrowing) { if (match == castType) { if (!isNarrowing) tagAsUnnecessaryCast(scope, castType); return true; @@ -795,9 +796,18 @@ return null; } +/** + * Mark this expression as being non null, per a NON-NULL or NN tag in the + * source code. + */ +public void markAsNonNull() { + this.bits |= IsNonNull; +} + public int nullStatus(FlowInfo flowInfo) { - if (this.constant != null && this.constant != Constant.NotAConstant) + if ((this.bits & IsNonNull) != 0 || + this.constant != null && this.constant != Constant.NotAConstant) return FlowInfo.NON_NULL; // constant expression cannot be null LocalVariableBinding local = localVariableBinding(); Index: compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java,v retrieving revision 1.98 diff -u -r1.98 FieldReference.java --- compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java 10 Jan 2006 14:37:27 -0000 1.98 +++ compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java 24 Jan 2006 09:56:39 -0000 @@ -98,7 +98,9 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, boolean valueRequired) { boolean nonStatic = !binding.isStatic(); receiver.analyseCode(currentScope, flowContext, flowInfo, nonStatic); - if (nonStatic) receiver.checkNullStatus(currentScope, flowContext, flowInfo, FlowInfo.NON_NULL); + if (nonStatic) { + receiver.checkNPE(currentScope, flowContext, flowInfo, true); + } if (valueRequired || currentScope.compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4) { manageSyntheticAccessIfNecessary(currentScope, flowInfo, true /*read-access*/); @@ -349,7 +351,8 @@ * No need to emulate access to protected fields since not implicitly accessed */ public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo, boolean isReadAccess) { - if (!flowInfo.isReachable()) return; + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0) return; + // if field from parameterized type got found, use the original field at codegen time this.codegenBinding = this.binding.original(); @@ -415,6 +418,10 @@ } } +public int nullStatus(FlowInfo flowInfo) { + return FlowInfo.UNKNOWN; +} + public Constant optimizedBooleanConstant() { switch (this.resolvedType.id) { case T_boolean : Index: compiler/org/eclipse/jdt/internal/compiler/ast/ForStatement.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ForStatement.java,v retrieving revision 1.54 diff -u -r1.54 ForStatement.java --- compiler/org/eclipse/jdt/internal/compiler/ast/ForStatement.java 13 Jan 2006 16:37:16 -0000 1.54 +++ compiler/org/eclipse/jdt/internal/compiler/ast/ForStatement.java 24 Jan 2006 09:56:39 -0000 @@ -83,38 +83,46 @@ // process the condition LoopingFlowContext condLoopContext = null; - FlowInfo condInfo = flowInfo.copy().unconditionalInits().discardNullRelatedInitializations(); + FlowInfo condInfo = flowInfo.nullInfoLessUnconditionalCopy(); if (condition != null) { if (!isConditionTrue) { condInfo = condition.analyseCode( scope, (condLoopContext = - new LoopingFlowContext(flowContext, this, null, null, scope)), + new LoopingFlowContext(flowContext, flowInfo, this, null, + null, scope)), condInfo); } } // process the action LoopingFlowContext loopingContext; - FlowInfo actionInfo; + UnconditionalFlowInfo actionInfo; if (action == null || (action.isEmptyBlock() && currentScope.compilerOptions().complianceLevel <= ClassFileConstants.JDK1_3)) { if (condLoopContext != null) - condLoopContext.complainOnDeferredChecks(scope, condInfo); + condLoopContext.complainOnDeferredFinalChecks(scope, condInfo); if (isConditionTrue) { + if (condLoopContext != null) { + condLoopContext.complainOnDeferredNullChecks(currentScope, + condInfo); + } return FlowInfo.DEAD_END; } else { if (isConditionFalse){ continueLabel = null; // for(;false;p()); } - actionInfo = condInfo.initsWhenTrue().copy().unconditionalInits().discardNullRelatedInitializations(); + actionInfo = condInfo.initsWhenTrue().unconditionalCopy(); loopingContext = - new LoopingFlowContext(flowContext, this, breakLabel, continueLabel, scope); + new LoopingFlowContext(flowContext, flowInfo, this, + breakLabel, continueLabel, scope); } - } else { + } + else { loopingContext = - new LoopingFlowContext(flowContext, this, breakLabel, continueLabel, scope); + new LoopingFlowContext(flowContext, flowInfo, this, breakLabel, + continueLabel, scope); FlowInfo initsWhenTrue = condInfo.initsWhenTrue(); condIfTrueInitStateIndex = currentScope.methodScope().recordInitializationStates(initsWhenTrue); @@ -122,38 +130,65 @@ if (isConditionFalse) { actionInfo = FlowInfo.DEAD_END; } else { - actionInfo = initsWhenTrue.copy().unconditionalInits().discardNullRelatedInitializations(); + actionInfo = initsWhenTrue.unconditionalCopy(); if (isConditionOptimizedFalse){ actionInfo.setReachMode(FlowInfo.UNREACHABLE); } } if (!this.action.complainIfUnreachable(actionInfo, scope, false)) { - actionInfo = action.analyseCode(scope, loopingContext, actionInfo); + actionInfo = action.analyseCode(scope, loopingContext, actionInfo). + unconditionalInits(); } // code generation can be optimized when no need to continue in the loop - if (!actionInfo.isReachable() && !loopingContext.initsOnContinue.isReachable()) { + if ((actionInfo.tagBits & + loopingContext.initsOnContinue.tagBits & + FlowInfo.UNREACHABLE) != 0) { continueLabel = null; - } else { - if (condLoopContext != null) - condLoopContext.complainOnDeferredChecks(scope, condInfo); - actionInfo = actionInfo.mergedWith(loopingContext.initsOnContinue.unconditionalInits()); - loopingContext.complainOnDeferredChecks(scope, actionInfo); + } + else { + if (condLoopContext != null) { + condLoopContext.complainOnDeferredFinalChecks(scope, + condInfo); + } + actionInfo = actionInfo.mergedWith(loopingContext.initsOnContinue); + loopingContext.complainOnDeferredFinalChecks(scope, + actionInfo); } } // for increments - FlowInfo exitBranch = condInfo.initsWhenFalse(); - exitBranch.addInitializationsFrom(flowInfo); // recover null inits from before condition analysis + FlowInfo exitBranch = flowInfo.copy(); + // recover null inits from before condition analysis + LoopingFlowContext incrementContext = null; if (continueLabel != null) { if (increments != null) { - LoopingFlowContext loopContext = - new LoopingFlowContext(flowContext, this, null, null, scope); + incrementContext = + new LoopingFlowContext(flowContext, flowInfo, this, null, + null, scope); + FlowInfo incrementInfo = actionInfo; for (int i = 0, count = increments.length; i < count; i++) { - actionInfo = increments[i].analyseCode(scope, loopContext, actionInfo); + incrementInfo = increments[i]. + analyseCode(scope, incrementContext, incrementInfo); } - loopContext.complainOnDeferredChecks(scope, actionInfo); + incrementContext.complainOnDeferredFinalChecks(scope, + actionInfo = incrementInfo.unconditionalInits()); } - exitBranch.addPotentialInitializationsFrom(actionInfo.unconditionalInits()); + exitBranch.addPotentialInitializationsFrom(actionInfo). + addInitializationsFrom(condInfo.initsWhenFalse()); + } + else { + exitBranch.addInitializationsFrom(condInfo.initsWhenFalse()); + } + // nulls checks + if (condLoopContext != null) { + condLoopContext.complainOnDeferredNullChecks(currentScope, + actionInfo); + } + loopingContext.complainOnDeferredNullChecks(currentScope, + actionInfo); + if (incrementContext != null) { + incrementContext.complainOnDeferredNullChecks(currentScope, + actionInfo); } //end of loop Index: compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java,v retrieving revision 1.29 diff -u -r1.29 ForeachStatement.java --- compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java 17 Jan 2006 18:44:23 -0000 1.29 +++ compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java 24 Jan 2006 09:56:39 -0000 @@ -17,6 +17,7 @@ import org.eclipse.jdt.internal.compiler.flow.FlowContext; import org.eclipse.jdt.internal.compiler.flow.FlowInfo; import org.eclipse.jdt.internal.compiler.flow.LoopingFlowContext; +import org.eclipse.jdt.internal.compiler.flow.UnconditionalFlowInfo; import org.eclipse.jdt.internal.compiler.impl.Constant; import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding; import org.eclipse.jdt.internal.compiler.lookup.Binding; @@ -80,9 +81,9 @@ continueLabel = new Label(); // process the element variable and collection + this.collection.checkNPE(currentScope, flowContext, flowInfo, true); flowInfo = this.elementVariable.analyseCode(scope, flowContext, flowInfo); - FlowInfo condInfo = flowInfo.copy().unconditionalInits().discardNullRelatedInitializations(); - condInfo = this.collection.analyseCode(scope, flowContext, condInfo); + FlowInfo condInfo = this.collection.analyseCode(scope, flowContext, flowInfo.copy()); // element variable will be assigned when iterating condInfo.markAsDefinitelyAssigned(this.elementVariable.binding); @@ -90,25 +91,32 @@ this.postCollectionInitStateIndex = currentScope.methodScope().recordInitializationStates(condInfo); // process the action - LoopingFlowContext loopingContext = new LoopingFlowContext(flowContext, this, breakLabel, continueLabel, scope); - FlowInfo actionInfo = condInfo.initsWhenTrue().copy(); + LoopingFlowContext loopingContext = + new LoopingFlowContext(flowContext, flowInfo, this, breakLabel, + continueLabel, scope); + UnconditionalFlowInfo actionInfo = + condInfo.nullInfoLessUnconditionalCopy(); FlowInfo exitBranch; if (!(action == null || (action.isEmptyBlock() && currentScope.compilerOptions().complianceLevel <= ClassFileConstants.JDK1_3))) { if (!this.action.complainIfUnreachable(actionInfo, scope, false)) { - actionInfo = action.analyseCode(scope, loopingContext, actionInfo); + actionInfo = action. + analyseCode(scope, loopingContext, actionInfo). + unconditionalCopy(); } // code generation can be optimized when no need to continue in the loop - exitBranch = condInfo.initsWhenFalse(); - exitBranch.addInitializationsFrom(flowInfo); // recover null inits from before condition analysis - if (!actionInfo.isReachable() && !loopingContext.initsOnContinue.isReachable()) { + exitBranch = flowInfo.unconditionalCopy(). + addInitializationsFrom(condInfo.initsWhenFalse()); + // TODO (maxime) no need to test when false: can optimize (same for action being unreachable above) + if ((actionInfo.tagBits & loopingContext.initsOnContinue.tagBits & + FlowInfo.UNREACHABLE) != 0) { continueLabel = null; } else { - actionInfo = actionInfo.mergedWith(loopingContext.initsOnContinue.unconditionalInits()); - loopingContext.complainOnDeferredChecks(scope, actionInfo); - exitBranch.addPotentialInitializationsFrom(actionInfo.unconditionalInits()); + actionInfo = actionInfo.mergedWith(loopingContext.initsOnContinue); + loopingContext.complainOnDeferredFinalChecks(scope, actionInfo); + exitBranch.addPotentialInitializationsFrom(actionInfo); } } else { exitBranch = condInfo.initsWhenFalse(); @@ -132,6 +140,8 @@ } } //end of loop + loopingContext.complainOnDeferredNullChecks(currentScope, actionInfo); + FlowInfo mergedInfo = FlowInfo.mergedOptimizedBranches( loopingContext.initsOnBreak, false, Index: compiler/org/eclipse/jdt/internal/compiler/ast/IfStatement.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/IfStatement.java,v retrieving revision 1.52 diff -u -r1.52 IfStatement.java --- compiler/org/eclipse/jdt/internal/compiler/ast/IfStatement.java 13 Jan 2006 16:37:16 -0000 1.52 +++ compiler/org/eclipse/jdt/internal/compiler/ast/IfStatement.java 24 Jan 2006 09:56:39 -0000 @@ -60,22 +60,22 @@ FlowInfo flowInfo) { // process the condition - flowInfo = condition.analyseCode(currentScope, flowContext, flowInfo); + FlowInfo conditionFlowInfo = + condition.analyseCode(currentScope, flowContext, flowInfo); Constant cst = this.condition.optimizedBooleanConstant(); boolean isConditionOptimizedTrue = cst != Constant.NotAConstant && cst.booleanValue() == true; boolean isConditionOptimizedFalse = cst != Constant.NotAConstant && cst.booleanValue() == false; // process the THEN part - FlowInfo thenFlowInfo = flowInfo.initsWhenTrue().copy(); + FlowInfo thenFlowInfo = conditionFlowInfo.safeInitsWhenTrue(); if (isConditionOptimizedFalse) { thenFlowInfo.setReachMode(FlowInfo.UNREACHABLE); } - FlowInfo elseFlowInfo = flowInfo.initsWhenFalse().copy(); + FlowInfo elseFlowInfo = conditionFlowInfo.initsWhenFalse(); if (isConditionOptimizedTrue) { elseFlowInfo.setReachMode(FlowInfo.UNREACHABLE); } - this.condition.checkNullComparison(currentScope, flowContext, flowInfo, thenFlowInfo, elseFlowInfo); if (this.thenStatement != null) { // Save info for code gen thenInitStateIndex = @@ -86,7 +86,7 @@ } } // code gen: optimizing the jump around the ELSE part - this.thenExit = !thenFlowInfo.isReachable(); + this.thenExit = (thenFlowInfo.tagBits & FlowInfo.UNREACHABLE) != 0; // process the ELSE part if (this.elseStatement != null) { @@ -107,11 +107,11 @@ // merge THEN & ELSE initializations FlowInfo mergedInfo = FlowInfo.mergedOptimizedBranches( - thenFlowInfo, - isConditionOptimizedTrue, - elseFlowInfo, - isConditionOptimizedFalse, - true /*if(true){ return; } fake-reachable(); */); + thenFlowInfo, + isConditionOptimizedTrue, + elseFlowInfo, + isConditionOptimizedFalse, + true /*if(true){ return; } fake-reachable(); */); mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo); return mergedInfo; } Index: compiler/org/eclipse/jdt/internal/compiler/ast/InstanceOfExpression.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/InstanceOfExpression.java,v retrieving revision 1.45 diff -u -r1.45 InstanceOfExpression.java --- compiler/org/eclipse/jdt/internal/compiler/ast/InstanceOfExpression.java 10 Jan 2006 14:37:27 -0000 1.45 +++ compiler/org/eclipse/jdt/internal/compiler/ast/InstanceOfExpression.java 24 Jan 2006 09:56:39 -0000 @@ -31,17 +31,23 @@ this.sourceEnd = type.sourceEnd; } - public FlowInfo analyseCode( +public FlowInfo analyseCode( BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { - - flowInfo = expression - .analyseCode(currentScope, flowContext, flowInfo) - .unconditionalInits(); - expression.checkNullStatus(currentScope, flowContext, flowInfo, FlowInfo.NON_NULL); - return flowInfo; + LocalVariableBinding local = this.expression.localVariableBinding(); + if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) { + flowContext.recordUsingNullReference(currentScope, local, + this.expression, FlowContext.CAN_ONLY_NULL, flowInfo); + flowInfo = expression.analyseCode(currentScope, flowContext, flowInfo). + unconditionalInits(); + FlowInfo initsWhenFalse = flowInfo.copy(); + flowInfo.markAsComparedEqualToNonNull(local); + return FlowInfo.conditional(flowInfo, initsWhenFalse); } + return expression.analyseCode(currentScope, flowContext, flowInfo). + unconditionalInits(); +} /** * Code generation for instanceOfExpression Index: compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java,v retrieving revision 1.53 diff -u -r1.53 LocalDeclaration.java --- compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java 10 Jan 2006 14:37:27 -0000 1.53 +++ compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java 24 Jan 2006 09:56:39 -0000 @@ -32,25 +32,22 @@ this.declarationEnd = sourceEnd; } - public FlowInfo analyseCode( - BlockScope currentScope, - FlowContext flowContext, +public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { - - // record variable initialization if any - if (flowInfo.isReachable()) { - bits |= IsLocalDeclarationReachable; // only set if actually reached - } - if (this.initialization == null) - return flowInfo; - - int nullStatus = this.initialization.nullStatus(flowInfo); - flowInfo = - this.initialization - .analyseCode(currentScope, flowContext, flowInfo) - .unconditionalInits(); - - flowInfo.markAsDefinitelyAssigned(binding); + // record variable initialization if any + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) { + bits |= IsLocalDeclarationReachable; // only set if actually reached + } + if (this.initialization == null) { + return flowInfo; + } + int nullStatus = this.initialization.nullStatus(flowInfo); + flowInfo = + this.initialization + .analyseCode(currentScope, flowContext, flowInfo) + .unconditionalInits(); + flowInfo.markAsDefinitelyAssigned(binding); + if ((this.binding.type.tagBits & TagBits.IsBaseType) == 0) { switch(nullStatus) { case FlowInfo.NULL : flowInfo.markAsDefinitelyNull(this.binding); @@ -58,9 +55,12 @@ case FlowInfo.NON_NULL : flowInfo.markAsDefinitelyNonNull(this.binding); break; + default: + flowInfo.markAsDefinitelyUnknown(this.binding); } - return flowInfo; } + return flowInfo; +} public void checkModifiers() { Index: compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java,v retrieving revision 1.107 diff -u -r1.107 MessageSend.java --- compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java 10 Jan 2006 14:37:27 -0000 1.107 +++ compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java 24 Jan 2006 09:56:39 -0000 @@ -42,7 +42,9 @@ boolean nonStatic = !binding.isStatic(); flowInfo = receiver.analyseCode(currentScope, flowContext, flowInfo, nonStatic).unconditionalInits(); - if (nonStatic) receiver.checkNullStatus(currentScope, flowContext, flowInfo, FlowInfo.NON_NULL); + if (nonStatic) { + receiver.checkNPE(currentScope, flowContext, flowInfo, true); + } if (arguments != null) { int length = arguments.length; @@ -53,7 +55,10 @@ ReferenceBinding[] thrownExceptions; if ((thrownExceptions = binding.thrownExceptions) != Binding.NO_EXCEPTIONS) { // must verify that exceptions potentially thrown by this expression are caught in the method - flowContext.checkExceptionHandlers(thrownExceptions, this, flowInfo, currentScope); + 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; @@ -169,7 +174,7 @@ } public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo){ - if (!flowInfo.isReachable()) return; + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0) return; // if method from parameterized type got found, use the original method at codegen time this.codegenBinding = this.binding.original(); Index: compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java,v retrieving revision 1.52 diff -u -r1.52 MethodDeclaration.java --- compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java 10 Jan 2006 14:37:27 -0000 1.52 +++ compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java 24 Jan 2006 09:56:39 -0000 @@ -90,7 +90,8 @@ // check for missing returning path TypeBinding returnTypeBinding = binding.returnType; if ((returnTypeBinding == TypeBinding.VOID) || isAbstract()) { - this.needFreeReturn = flowInfo.isReachable(); + this.needFreeReturn = + (flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0; } else { if (flowInfo != FlowInfo.DEAD_END) { scope.problemReporter().shouldReturn(returnTypeBinding, this); Index: compiler/org/eclipse/jdt/internal/compiler/ast/OR_OR_Expression.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/OR_OR_Expression.java,v retrieving revision 1.30 diff -u -r1.30 OR_OR_Expression.java --- compiler/org/eclipse/jdt/internal/compiler/ast/OR_OR_Expression.java 18 Nov 2005 16:46:21 -0000 1.30 +++ compiler/org/eclipse/jdt/internal/compiler/ast/OR_OR_Expression.java 24 Jan 2006 09:56:39 -0000 @@ -50,7 +50,7 @@ // need to be careful of scenario: // (x || y) || !z, if passing the left info to the right, it would be swapped by the ! - FlowInfo rightInfo = leftInfo.initsWhenFalse().unconditionalInits().copy(); + FlowInfo rightInfo = leftInfo.initsWhenFalse().unconditionalCopy(); rightInitStateIndex = currentScope.methodScope().recordInitializationStates(rightInfo); @@ -59,14 +59,11 @@ rightInfo.setReachMode(FlowInfo.UNREACHABLE); } rightInfo = right.analyseCode(currentScope, flowContext, rightInfo); - FlowInfo falseMergedInfo = rightInfo.initsWhenFalse().copy(); - rightInfo.setReachMode(previousMode); // reset after falseMergedInfo got extracted - FlowInfo mergedInfo = FlowInfo.conditional( // merging two true initInfos for such a negative case: if ((t && (b = t)) || f) r = b; // b may not have been initialized - leftInfo.initsWhenTrue().copy().unconditionalInits().mergedWith( - rightInfo.initsWhenTrue().copy().unconditionalInits()), - falseMergedInfo); + leftInfo.initsWhenTrue().unconditionalInits().mergedWith( + rightInfo.safeInitsWhenTrue().setReachMode(previousMode).unconditionalInits()), + rightInfo.initsWhenFalse()); mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo); return mergedInfo; Index: compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java,v retrieving revision 1.73 diff -u -r1.73 QualifiedAllocationExpression.java --- compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java 10 Jan 2006 14:37:27 -0000 1.73 +++ compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java 24 Jan 2006 09:56:39 -0000 @@ -161,7 +161,7 @@ */ public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) { - if (!flowInfo.isReachable()) return; + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) { ReferenceBinding allocatedTypeErasure = (ReferenceBinding) binding.declaringClass.erasure(); // perform some extra emulation work in case there is some and we are inside a local type only @@ -175,6 +175,7 @@ currentScope.propagateInnerEmulation(allocatedTypeErasure, enclosingInstance != null); } } + } } public StringBuffer printExpression(int indent, StringBuffer output) { Index: compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java,v retrieving revision 1.102 diff -u -r1.102 QualifiedNameReference.java --- compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java 10 Jan 2006 14:37:27 -0000 1.102 +++ compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java 24 Jan 2006 09:56:39 -0000 @@ -93,12 +93,12 @@ .isDefinitelyAssigned(localBinding = (LocalVariableBinding) binding)) { currentScope.problemReporter().uninitializedLocalVariable(localBinding, this); } - if (flowInfo.isReachable()) { + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) { localBinding.useFlag = LocalVariableBinding.USED; } else if (localBinding.useFlag == LocalVariableBinding.UNUSED) { localBinding.useFlag = LocalVariableBinding.FAKE_USED; } - this.checkNullStatus(currentScope, flowContext, flowInfo, FlowInfo.NON_NULL); + checkNPE(currentScope, flowContext, flowInfo, true); } if (needValue) { @@ -252,12 +252,12 @@ .isDefinitelyAssigned(localBinding = (LocalVariableBinding) binding)) { currentScope.problemReporter().uninitializedLocalVariable(localBinding, this); } - if (flowInfo.isReachable()) { + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) { localBinding.useFlag = LocalVariableBinding.USED; } else if (localBinding.useFlag == LocalVariableBinding.UNUSED) { localBinding.useFlag = LocalVariableBinding.FAKE_USED; } - this.checkNullStatus(currentScope, flowContext, flowInfo, FlowInfo.NON_NULL); + checkNPE(currentScope, flowContext, flowInfo, true); } if (needValue) { manageEnclosingInstanceAccessIfNecessary(currentScope, flowInfo); @@ -304,7 +304,25 @@ bits |= Binding.FIELD; return getOtherFieldBindings(scope); } - + +public void checkNPE(BlockScope scope, FlowContext flowContext, + FlowInfo flowInfo, boolean checkString) { + // cannot override localVariableBinding because this would project o.m onto o when + // analysing assignments + if ((bits & RestrictiveFlagMASK) == Binding.LOCAL) { + LocalVariableBinding local = (LocalVariableBinding) this.binding; + if (local != null && + (local.type.tagBits & TagBits.IsBaseType) == 0 && + (checkString || local.type.id != T_JavaLangString)) { + if ((this.bits & IsNonNull) == 0) { + flowContext.recordUsingNullReference(scope, local, this, + FlowContext.MAY_NULL, flowInfo); + } + flowInfo.markAsComparedEqualToNonNull(local); + // from thereon it is set + } + } +} /** * @see org.eclipse.jdt.internal.compiler.ast.Expression#computeConversion(org.eclipse.jdt.internal.compiler.lookup.Scope, org.eclipse.jdt.internal.compiler.lookup.TypeBinding, org.eclipse.jdt.internal.compiler.lookup.TypeBinding) */ @@ -779,9 +797,9 @@ ? type.capture(scope, this.sourceEnd) : type; } - + public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) { - if (!flowInfo.isReachable()) return; + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) { //If inlinable field, forget the access emulation, the code gen will directly target it if (((bits & DepthMASK) == 0) || (constant != Constant.NotAConstant)) { return; @@ -789,6 +807,7 @@ if ((bits & RestrictiveFlagMASK) == Binding.LOCAL) { currentScope.emulateOuterAccess((LocalVariableBinding) binding); } + } } /** * index is <0 to denote write access emulation @@ -799,8 +818,8 @@ TypeBinding lastReceiverType, int index, FlowInfo flowInfo) { - - if (!flowInfo.isReachable()) return; + + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0) return; // index == 0 denotes the first fieldBinding, index > 0 denotes one of the 'otherBindings', index < 0 denotes a write access (to last binding) if (fieldBinding.constant() != Constant.NotAConstant) return; @@ -855,6 +874,11 @@ } } } + +public int nullStatus(FlowInfo flowInfo) { + return FlowInfo.UNKNOWN; +} + public Constant optimizedBooleanConstant() { switch (this.resolvedType.id) { Index: compiler/org/eclipse/jdt/internal/compiler/ast/Reference.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Reference.java,v retrieving revision 1.26 diff -u -r1.26 Reference.java --- compiler/org/eclipse/jdt/internal/compiler/ast/Reference.java 10 Jan 2006 14:37:27 -0000 1.26 +++ compiler/org/eclipse/jdt/internal/compiler/ast/Reference.java 24 Jan 2006 09:56:39 -0000 @@ -12,7 +12,6 @@ import org.eclipse.jdt.internal.compiler.codegen.*; import org.eclipse.jdt.internal.compiler.flow.*; -import org.eclipse.jdt.internal.compiler.impl.Constant; import org.eclipse.jdt.internal.compiler.lookup.*; public abstract class Reference extends Expression { @@ -68,20 +67,4 @@ public abstract void generateCompoundAssignment(BlockScope currentScope, CodeStream codeStream, Expression expression, int operator, int assignmentImplicitConversion, boolean valueRequired); public abstract void generatePostIncrement(BlockScope currentScope, CodeStream codeStream, CompoundAssignment postIncrement, boolean valueRequired); - -public int nullStatus(FlowInfo flowInfo) { - - if (this.constant != null && this.constant != Constant.NotAConstant) - return FlowInfo.NON_NULL; // constant expression cannot be null - - LocalVariableBinding local = localVariableBinding(); - if (local != null) { - if (flowInfo.isDefinitelyNull(local)) - return FlowInfo.NULL; - if (flowInfo.isDefinitelyNonNull(local)) - return FlowInfo.NON_NULL; - } - return FlowInfo.UNKNOWN; -} - } Index: compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java,v retrieving revision 1.85 diff -u -r1.85 SingleNameReference.java --- compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java 10 Jan 2006 14:37:27 -0000 1.85 +++ compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java 24 Jan 2006 09:56:40 -0000 @@ -34,7 +34,7 @@ } public FlowInfo analyseAssignment(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, Assignment assignment, boolean isCompound) { - boolean isReachable = flowInfo.isReachable(); + boolean isReachable = (flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0; // compound assignment extra work if (isCompound) { // check the variable part is initialized if blank final switch (bits & RestrictiveFlagMASK) { @@ -163,7 +163,7 @@ if (!flowInfo.isDefinitelyAssigned(localBinding = (LocalVariableBinding) binding)) { currentScope.problemReporter().uninitializedLocalVariable(localBinding, this); } - if (flowInfo.isReachable()) { + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) { localBinding.useFlag = LocalVariableBinding.USED; } else if (localBinding.useFlag == LocalVariableBinding.UNUSED) { localBinding.useFlag = LocalVariableBinding.FAKE_USED; @@ -641,17 +641,18 @@ public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) { - if (!flowInfo.isReachable()) return; + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) { //If inlinable field, forget the access emulation, the code gen will directly target it if (((bits & DepthMASK) == 0) || (constant != Constant.NotAConstant)) return; if ((bits & RestrictiveFlagMASK) == Binding.LOCAL) { currentScope.emulateOuterAccess((LocalVariableBinding) binding); } + } } public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo, boolean isReadAccess) { - if (!flowInfo.isReachable()) return; + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0) return; //If inlinable field, forget the access emulation, the code gen will directly target it if (constant != Constant.NotAConstant) @@ -695,6 +696,28 @@ } } } + +public int nullStatus(FlowInfo flowInfo) { + if (this.constant != null && this.constant != Constant.NotAConstant) { + return FlowInfo.NON_NULL; // constant expression cannot be null + } + switch (bits & RestrictiveFlagMASK) { + case Binding.FIELD : // reading a field + return FlowInfo.UNKNOWN; + case Binding.LOCAL : // reading a local variable + LocalVariableBinding local = (LocalVariableBinding) this.binding; + if (local != null) { + if (flowInfo.isDefinitelyNull(local)) + return FlowInfo.NULL; + if (flowInfo.isDefinitelyNonNull(local)) + return FlowInfo.NON_NULL; + return FlowInfo.UNKNOWN; + } + } + return FlowInfo.NON_NULL; +// REVIEW should never get here? +} + /** * @see org.eclipse.jdt.internal.compiler.ast.Expression#postConversionType(Scope) */ Index: compiler/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java,v retrieving revision 1.62 diff -u -r1.62 SwitchStatement.java --- compiler/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java 13 Jan 2006 16:37:16 -0000 1.62 +++ compiler/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java 24 Jan 2006 09:56:40 -0000 @@ -62,11 +62,11 @@ if ((caseIndex < caseCount) && (statement == cases[caseIndex])) { // statement is a case this.scope.enclosingCase = cases[caseIndex]; // record entering in a switch case block caseIndex++; - caseInits = caseInits.mergedWith(flowInfo.copy().unconditionalInits()); + caseInits = caseInits.mergedWith(flowInfo.unconditionalInits()); didAlreadyComplain = false; // reset complaint } else if (statement == defaultCase) { // statement is the default case this.scope.enclosingCase = defaultCase; // record entering in a switch case block - caseInits = caseInits.mergedWith(flowInfo.copy().unconditionalInits()); + caseInits = caseInits.mergedWith(flowInfo.unconditionalInits()); didAlreadyComplain = false; // reset complaint } if (!statement.complainIfUnreachable(caseInits, scope, didAlreadyComplain)) { Index: compiler/org/eclipse/jdt/internal/compiler/ast/SynchronizedStatement.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SynchronizedStatement.java,v retrieving revision 1.39 diff -u -r1.39 SynchronizedStatement.java --- compiler/org/eclipse/jdt/internal/compiler/ast/SynchronizedStatement.java 13 Jan 2006 16:37:16 -0000 1.39 +++ compiler/org/eclipse/jdt/internal/compiler/ast/SynchronizedStatement.java 24 Jan 2006 09:56:40 -0000 @@ -56,7 +56,7 @@ expression.analyseCode(scope, flowContext, flowInfo)); // optimizing code gen - this.blockExit = !flowInfo.isReachable(); + this.blockExit = (flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0; return flowInfo; } Index: compiler/org/eclipse/jdt/internal/compiler/ast/TryStatement.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TryStatement.java,v retrieving revision 1.86 diff -u -r1.86 TryStatement.java --- compiler/org/eclipse/jdt/internal/compiler/ast/TryStatement.java 13 Jan 2006 16:37:16 -0000 1.86 +++ compiler/org/eclipse/jdt/internal/compiler/ast/TryStatement.java 24 Jan 2006 09:56:40 -0000 @@ -85,7 +85,7 @@ .analyseCode( currentScope, finallyContext = new FinallyFlowContext(flowContext, finallyBlock), - flowInfo.copy().unconditionalInits().discardNullRelatedInitializations()) + flowInfo.nullInfoLessUnconditionalCopy()) .unconditionalInits(); if (subInfo == FlowInfo.DEAD_END) { isSubRoutineEscaping = true; @@ -108,7 +108,7 @@ tryBlockExit = false; } else { tryInfo = tryBlock.analyseCode(currentScope, handlingContext, flowInfo.copy()); - tryBlockExit = !tryInfo.isReachable(); + tryBlockExit = (tryInfo.tagBits & FlowInfo.UNREACHABLE) != 0; } // check unreachable catch blocks @@ -121,13 +121,17 @@ for (int i = 0; i < catchCount; i++) { // keep track of the inits that could potentially have led to this exception handler (for final assignments diagnosis) FlowInfo catchInfo = - flowInfo - .copy() - .unconditionalInits() + flowInfo.unconditionalCopy(). + addPotentialInitializationsFrom( + handlingContext.initsOnException( + caughtExceptionTypes[i])) .addPotentialInitializationsFrom( - handlingContext.initsOnException(caughtExceptionTypes[i]).unconditionalInits()) - .addPotentialInitializationsFrom(tryInfo.unconditionalInits()) - .addPotentialInitializationsFrom(handlingContext.initsOnReturn); + tryInfo.nullInfoLessUnconditionalCopy()) + // remove null info to protect point of + // exception null info + .addPotentialInitializationsFrom( + handlingContext.initsOnReturn. + nullInfoLessUnconditionalCopy()); // catch var is always set LocalVariableBinding catchArg = catchArguments[i].binding; @@ -149,7 +153,8 @@ currentScope, catchContext, catchInfo); - catchExits[i] = !catchInfo.isReachable(); + catchExits[i] = + (catchInfo.tagBits & FlowInfo.UNREACHABLE) != 0; tryInfo = tryInfo.mergedWith(catchInfo.unconditionalInits()); } } @@ -162,10 +167,16 @@ // we also need to check potential multiple assignments of final variables inside the finally block // need to include potential inits from returns inside the try/catch parts - 1GK2AOF - finallyContext.complainOnDeferredChecks( - tryInfo.isReachable() - ? (tryInfo.addPotentialInitializationsFrom(insideSubContext.initsOnReturn)) - : insideSubContext.initsOnReturn, + finallyContext/* NN null with subRoutineStartLabel, which returns */.complainOnDeferredChecks( + (tryInfo.tagBits & FlowInfo.UNREACHABLE) == 0 + ? flowInfo.unconditionalCopy(). + addPotentialInitializationsFrom(tryInfo). + // lighten the influence of the try block, which may have + // exited at any point + addPotentialInitializationsFrom( + insideSubContext/* NN null with subRoutineStartLabel, which returns */. + initsOnReturn) + : insideSubContext.initsOnReturn, currentScope); if (subInfo == FlowInfo.DEAD_END) { mergedInitStateIndex = Index: compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java,v retrieving revision 1.112 diff -u -r1.112 TypeDeclaration.java --- compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java 10 Jan 2006 14:37:27 -0000 1.112 +++ compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java 24 Jan 2006 09:56:40 -0000 @@ -201,7 +201,7 @@ if (ignoreFurtherInvestigation) return flowInfo; try { - if (flowInfo.isReachable()) { + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) { bits |= IsReachable; LocalTypeBinding localType = (LocalTypeBinding) binding; localType.setConstantPoolName(currentScope.compilationUnitScope().computeConstantPoolName(localType)); @@ -244,7 +244,7 @@ if (ignoreFurtherInvestigation) return; try { - if (flowInfo.isReachable()) { + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) { bits |= IsReachable; LocalTypeBinding localType = (LocalTypeBinding) binding; localType.setConstantPoolName(currentScope.compilationUnitScope().computeConstantPoolName(localType)); @@ -634,7 +634,7 @@ * Common flow analysis for all types * */ - public void internalAnalyseCode(FlowContext flowContext, FlowInfo flowInfo) { + private void internalAnalyseCode(FlowContext flowContext, FlowInfo flowInfo) { if ((this.binding.isPrivate()/* || (this.binding.tagBits & (TagBits.IsAnonymousType|TagBits.IsLocalType)) == TagBits.IsLocalType*/) && !this.binding.isUsed()) { if (!scope.referenceCompilationUnit().compilationResult.hasSyntaxError) { @@ -644,13 +644,13 @@ InitializationFlowContext initializerContext = new InitializationFlowContext(null, this, initializerScope); InitializationFlowContext staticInitializerContext = new InitializationFlowContext(null, this, staticInitializerScope); - FlowInfo nonStaticFieldInfo = flowInfo.copy().unconditionalInits().discardFieldInitializations(); - FlowInfo staticFieldInfo = flowInfo.copy().unconditionalInits().discardFieldInitializations(); + FlowInfo nonStaticFieldInfo = flowInfo.unconditionalFieldLessCopy(); + FlowInfo staticFieldInfo = flowInfo.unconditionalFieldLessCopy(); if (fields != null) { for (int i = 0, count = fields.length; i < count; i++) { FieldDeclaration field = fields[i]; if (field.isStatic()) { - if (!staticFieldInfo.isReachable()) + if ((staticFieldInfo.tagBits & FlowInfo.UNREACHABLE) != 0) field.bits &= ~ASTNode.IsReachable; /*if (field.isField()){ @@ -670,7 +670,7 @@ staticFieldInfo = FlowInfo.initial(maxFieldCount).setReachMode(FlowInfo.UNREACHABLE); } } else { - if (!nonStaticFieldInfo.isReachable()) + if ((nonStaticFieldInfo.tagBits & FlowInfo.UNREACHABLE) != 0) field.bits &= ~ASTNode.IsReachable; /*if (field.isField()){ @@ -699,7 +699,7 @@ } } if (methods != null) { - UnconditionalFlowInfo outerInfo = flowInfo.copy().unconditionalInits().discardFieldInitializations(); + UnconditionalFlowInfo outerInfo = flowInfo.unconditionalFieldLessCopy(); FlowInfo constructorInfo = nonStaticFieldInfo.unconditionalInits().discardNonFieldInitializations().addInitializationsFrom(outerInfo); for (int i = 0, count = methods.length; i < count; i++) { AbstractMethodDeclaration method = methods[i]; @@ -748,7 +748,7 @@ */ public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) { - if (!flowInfo.isReachable()) return; + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0) return; NestedTypeBinding nestedType = (NestedTypeBinding) binding; MethodScope methodScope = currentScope.methodScope(); @@ -800,9 +800,10 @@ */ public void manageEnclosingInstanceAccessIfNecessary(ClassScope currentScope, FlowInfo flowInfo) { - if (!flowInfo.isReachable()) return; + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) { NestedTypeBinding nestedType = (NestedTypeBinding) binding; nestedType.addSyntheticArgumentAndField(binding.enclosingType()); + } } /** Index: compiler/org/eclipse/jdt/internal/compiler/ast/UnaryExpression.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/UnaryExpression.java,v retrieving revision 1.39 diff -u -r1.39 UnaryExpression.java --- compiler/org/eclipse/jdt/internal/compiler/ast/UnaryExpression.java 10 Jan 2006 14:37:27 -0000 1.39 +++ compiler/org/eclipse/jdt/internal/compiler/ast/UnaryExpression.java 24 Jan 2006 09:56:40 -0000 @@ -27,19 +27,20 @@ this.bits |= operator << OperatorSHIFT; // encode operator } - public FlowInfo analyseCode( +public FlowInfo analyseCode( BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { - - if (((bits & OperatorMASK) >> OperatorSHIFT) == NOT) { - return this.expression - .analyseCode(currentScope, flowContext, flowInfo) - .asNegatedCondition(); - } else { - return this.expression.analyseCode(currentScope, flowContext, flowInfo); - } + this.expression.checkNPE(currentScope, flowContext, flowInfo, true); + if (((bits & OperatorMASK) >> OperatorSHIFT) == NOT) { + return this.expression. + analyseCode(currentScope, flowContext, flowInfo). + asNegatedCondition(); + } else { + return this.expression. + analyseCode(currentScope, flowContext, flowInfo); } +} public Constant optimizedBooleanConstant() { Index: compiler/org/eclipse/jdt/internal/compiler/ast/WhileStatement.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/WhileStatement.java,v retrieving revision 1.54 diff -u -r1.54 WhileStatement.java --- compiler/org/eclipse/jdt/internal/compiler/ast/WhileStatement.java 13 Jan 2006 16:37:16 -0000 1.54 +++ compiler/org/eclipse/jdt/internal/compiler/ast/WhileStatement.java 24 Jan 2006 09:56:40 -0000 @@ -55,11 +55,15 @@ preCondInitStateIndex = currentScope.methodScope().recordInitializationStates(flowInfo); LoopingFlowContext condLoopContext; - FlowInfo condInfo = flowInfo.copy().unconditionalInits().discardNullRelatedInitializations(); + FlowInfo condInfo = 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 condInfo = this.condition.analyseCode( currentScope, (condLoopContext = - new LoopingFlowContext(flowContext, this, null, null, currentScope)), + new LoopingFlowContext(flowContext, flowInfo, this, null, + null, currentScope)), condInfo); LoopingFlowContext loopingContext; @@ -67,17 +71,22 @@ FlowInfo exitBranch; if (action == null || (action.isEmptyBlock() && currentScope.compilerOptions().complianceLevel <= ClassFileConstants.JDK1_3)) { - condLoopContext.complainOnDeferredChecks(currentScope, condInfo); + condLoopContext.complainOnDeferredFinalChecks(currentScope, + condInfo); + condLoopContext.complainOnDeferredNullChecks(currentScope, + condInfo.unconditionalInits()); if (isConditionTrue) { return FlowInfo.DEAD_END; } else { - FlowInfo mergedInfo = condInfo.initsWhenFalse().unconditionalInits(); + FlowInfo mergedInfo = condInfo.initsWhenFalse(); if (isConditionOptimizedTrue){ mergedInfo.setReachMode(FlowInfo.UNREACHABLE); } mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo); - return mergedInfo; + return flowInfo.unconditionalInits(). + addPotentialNullInfoFrom( + condInfo.initsWhenFalse().unconditionalInits()); } } else { // in case the condition was inlined to false, record the fact that there is no way to reach any @@ -85,6 +94,7 @@ loopingContext = new LoopingFlowContext( flowContext, + flowInfo, this, breakLabel, continueLabel, @@ -108,15 +118,28 @@ } // code generation can be optimized when no need to continue in the loop - exitBranch = condInfo.initsWhenFalse(); - exitBranch.addInitializationsFrom(flowInfo); // recover null inits from before condition analysis - if (!actionInfo.isReachable() && !loopingContext.initsOnContinue.isReachable()) { + exitBranch = flowInfo.copy(); + // need to start over from flowInfo so as to get null inits + + if ((actionInfo.tagBits & + loopingContext.initsOnContinue.tagBits & + FlowInfo.UNREACHABLE) != 0) { continueLabel = null; + exitBranch.addInitializationsFrom(condInfo.initsWhenFalse()); } else { - condLoopContext.complainOnDeferredChecks(currentScope, condInfo); + condLoopContext.complainOnDeferredFinalChecks(currentScope, + condInfo); actionInfo = actionInfo.mergedWith(loopingContext.initsOnContinue.unconditionalInits()); - loopingContext.complainOnDeferredChecks(currentScope, actionInfo); - exitBranch.addPotentialInitializationsFrom(actionInfo.unconditionalInits()); + condLoopContext.complainOnDeferredNullChecks(currentScope, + actionInfo); + loopingContext.complainOnDeferredFinalChecks(currentScope, + actionInfo); + loopingContext.complainOnDeferredNullChecks(currentScope, + actionInfo); + exitBranch. + addPotentialInitializationsFrom( + actionInfo.unconditionalInits()). + addInitializationsFrom(condInfo.initsWhenFalse()); } } Index: compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java,v retrieving revision 1.121 diff -u -r1.121 CodeStream.java --- compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java 20 Jan 2006 03:44:05 -0000 1.121 +++ compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java 24 Jan 2006 09:56:41 -0000 @@ -4549,7 +4549,7 @@ bCodeStream[classFileOffset++] = Opcodes.OPC_ireturn; } public boolean isDefinitelyAssigned(Scope scope, int initStateIndex, LocalVariableBinding local) { - // Dependant of UnconditionalFlowInfo.isDefinitelyAssigned(..) + // Mirror of UnconditionalFlowInfo.isDefinitelyAssigned(..) if (initStateIndex == -1) return false; if (local.isArgument) { Index: compiler/org/eclipse/jdt/internal/compiler/flow/ConditionalFlowInfo.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/ConditionalFlowInfo.java,v retrieving revision 1.23 diff -u -r1.23 ConditionalFlowInfo.java --- compiler/org/eclipse/jdt/internal/compiler/flow/ConditionalFlowInfo.java 23 Feb 2005 02:47:29 -0000 1.23 +++ compiler/org/eclipse/jdt/internal/compiler/flow/ConditionalFlowInfo.java 24 Jan 2006 09:56:41 -0000 @@ -83,52 +83,20 @@ && initsWhenFalse.isDefinitelyAssigned(local); } - /** - * Check status of definite non-null assignment for a field. - */ - public boolean isDefinitelyNonNull(FieldBinding field) { - - return initsWhenTrue.isDefinitelyNonNull(field) - && initsWhenFalse.isDefinitelyNonNull(field); - } - - /** - * Check status of definite non-null assignment for a local variable. - */ - public boolean isDefinitelyNonNull(LocalVariableBinding local) { - - return initsWhenTrue.isDefinitelyNonNull(local) - && initsWhenFalse.isDefinitelyNonNull(local); - } +public boolean isDefinitelyNonNull(LocalVariableBinding local) { + return initsWhenTrue.isDefinitelyNonNull(local) + && initsWhenFalse.isDefinitelyNonNull(local); +} - /** - * Check status of definite null assignment for a field. - */ - public boolean isDefinitelyNull(FieldBinding field) { - - return initsWhenTrue.isDefinitelyNull(field) - && initsWhenFalse.isDefinitelyNull(field); - } - - /** - * Check status of definite null assignment for a local variable. - */ - public boolean isDefinitelyNull(LocalVariableBinding local) { - - return initsWhenTrue.isDefinitelyNull(local) - && initsWhenFalse.isDefinitelyNull(local); - } +public boolean isDefinitelyNull(LocalVariableBinding local) { + return initsWhenTrue.isDefinitelyNull(local) + && initsWhenFalse.isDefinitelyNull(local); +} - public int reachMode(){ - return unconditionalInits().reachMode(); - } - - public boolean isReachable(){ - - return unconditionalInits().isReachable(); - //should maybe directly be: false - } - +public boolean isDefinitelyUnknown(LocalVariableBinding local) { + return initsWhenTrue.isDefinitelyUnknown(local) + && initsWhenFalse.isDefinitelyUnknown(local); +} /** * Check status of potential assignment for a field. */ @@ -147,6 +115,36 @@ || initsWhenFalse.isPotentiallyAssigned(local); } +public boolean isPotentiallyNull(LocalVariableBinding local) { + return initsWhenTrue.isPotentiallyNull(local) + || initsWhenFalse.isPotentiallyNull(local); +} + +public boolean isPotentiallyUnknown(LocalVariableBinding local) { + return initsWhenTrue.isPotentiallyUnknown(local) + || initsWhenFalse.isPotentiallyUnknown(local); +} + +public boolean isProtectedNonNull(LocalVariableBinding local) { + return initsWhenTrue.isProtectedNonNull(local) + && initsWhenFalse.isProtectedNonNull(local); +} + +public boolean isProtectedNull(LocalVariableBinding local) { + return initsWhenTrue.isProtectedNull(local) + && initsWhenFalse.isProtectedNull(local); +} + +public void markAsComparedEqualToNonNull(LocalVariableBinding local) { + initsWhenTrue.markAsComparedEqualToNonNull(local); + initsWhenFalse.markAsComparedEqualToNonNull(local); +} + +public void markAsComparedEqualToNull(LocalVariableBinding local) { + initsWhenTrue.markAsComparedEqualToNull(local); + initsWhenFalse.markAsComparedEqualToNull(local); +} + /** * Record a field got definitely assigned. */ @@ -201,50 +199,60 @@ initsWhenFalse.markAsDefinitelyNull(local); } - /** - * Clear the initialization info for a field - */ - public void markAsDefinitelyNotAssigned(FieldBinding field) { - - initsWhenTrue.markAsDefinitelyNotAssigned(field); - initsWhenFalse.markAsDefinitelyNotAssigned(field); - } - - /** - * Clear the initialization info for a local variable - */ - public void markAsDefinitelyNotAssigned(LocalVariableBinding local) { - - initsWhenTrue.markAsDefinitelyNotAssigned(local); - initsWhenFalse.markAsDefinitelyNotAssigned(local); - } - - public FlowInfo setReachMode(int reachMode) { - - initsWhenTrue.setReachMode(reachMode); - initsWhenFalse.setReachMode(reachMode); - return this; - } - - /** - * Converts conditional receiver into inconditional one, updated in the following way: - */ - public UnconditionalFlowInfo mergedWith(UnconditionalFlowInfo otherInits) { - - return unconditionalInits().mergedWith(otherInits); - } +public void markAsDefinitelyUnknown(LocalVariableBinding local) { + initsWhenTrue.markAsDefinitelyUnknown(local); + initsWhenFalse.markAsDefinitelyUnknown(local); +} + +public FlowInfo setReachMode(int reachMode) { + if (reachMode == REACHABLE) { + this.tagBits &= ~UNREACHABLE; + } + else { + this.tagBits |= UNREACHABLE; + } + initsWhenTrue.setReachMode(reachMode); + initsWhenFalse.setReachMode(reachMode); + return this; +} +public UnconditionalFlowInfo mergedWith(UnconditionalFlowInfo otherInits) { + return unconditionalInits().mergedWith(otherInits); +} + +public UnconditionalFlowInfo nullInfoLessUnconditionalCopy() { + return unconditionalInitsWithoutSideEffect(). + nullInfoLessUnconditionalCopy(); +} public String toString() { return "FlowInfo"; //$NON-NLS-1$ //$NON-NLS-3$ //$NON-NLS-2$ } - - public UnconditionalFlowInfo unconditionalInits() { - - return initsWhenTrue.unconditionalInits().copy() - .mergedWith(initsWhenFalse.unconditionalInits()); - } + +public FlowInfo safeInitsWhenTrue() { + return initsWhenTrue; +} + +public UnconditionalFlowInfo unconditionalCopy() { + return initsWhenTrue.unconditionalCopy(). + mergedWith(initsWhenFalse.unconditionalInits()); +} + +public UnconditionalFlowInfo unconditionalFieldLessCopy() { + return initsWhenTrue.unconditionalFieldLessCopy(). + mergedWith(initsWhenFalse.unconditionalFieldLessCopy()); + // should never happen, hence suboptimal does not hurt +} + +public UnconditionalFlowInfo unconditionalInits() { + return initsWhenTrue.unconditionalInits(). + mergedWith(initsWhenFalse.unconditionalInits()); +} + +public UnconditionalFlowInfo unconditionalInitsWithoutSideEffect() { + // cannot do better here than unconditionalCopy - but still a different + // operation for UnconditionalFlowInfo + return initsWhenTrue.unconditionalCopy(). + mergedWith(initsWhenFalse.unconditionalInits()); +} } Index: compiler/org/eclipse/jdt/internal/compiler/flow/ExceptionHandlingFlowContext.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/ExceptionHandlingFlowContext.java,v retrieving revision 1.34 diff -u -r1.34 ExceptionHandlingFlowContext.java --- compiler/org/eclipse/jdt/internal/compiler/flow/ExceptionHandlingFlowContext.java 10 Jan 2006 14:37:28 -0000 1.34 +++ compiler/org/eclipse/jdt/internal/compiler/flow/ExceptionHandlingFlowContext.java 24 Jan 2006 09:56:41 -0000 @@ -62,7 +62,7 @@ int cacheIndex = i / BitCacheSize, bitMask = 1 << (i % BitCacheSize); if (handledExceptions[i].isUncheckedException(true)) { isReached[cacheIndex] |= bitMask; - this.initsOnExceptions[i] = flowInfo.copy().unconditionalInits(); + this.initsOnExceptions[i] = flowInfo.unconditionalCopy(); } else { this.initsOnExceptions[i] = FlowInfo.DEAD_END; } @@ -168,20 +168,21 @@ this.isReached[cacheIndex] |= bitMask; initsOnExceptions[index] = - initsOnExceptions[index] == FlowInfo.DEAD_END - ? flowInfo.copy().unconditionalInits() - : initsOnExceptions[index].mergedWith(flowInfo.copy().unconditionalInits()); + (initsOnExceptions[index].tagBits & FlowInfo.UNREACHABLE) == 0 ? + initsOnExceptions[index].mergedWith(flowInfo): + flowInfo.unconditionalCopy(); } - public void recordReturnFrom(FlowInfo flowInfo) { - - if (!flowInfo.isReachable()) return; - if (initsOnReturn == FlowInfo.DEAD_END) { - initsOnReturn = flowInfo.copy().unconditionalInits(); - } else { - initsOnReturn = initsOnReturn.mergedWith(flowInfo.copy().unconditionalInits()); +public void recordReturnFrom(UnconditionalFlowInfo flowInfo) { + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) { + if ((initsOnReturn.tagBits & FlowInfo.UNREACHABLE) == 0) { + initsOnReturn = initsOnReturn.mergedWith(flowInfo); + } + else { + initsOnReturn = (UnconditionalFlowInfo) flowInfo.copy(); } } +} /* * Compute a merged list of unhandled exception types (keeping only the most generic ones). Index: compiler/org/eclipse/jdt/internal/compiler/flow/FinallyFlowContext.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FinallyFlowContext.java,v retrieving revision 1.17 diff -u -r1.17 FinallyFlowContext.java --- compiler/org/eclipse/jdt/internal/compiler/flow/FinallyFlowContext.java 23 Feb 2005 02:47:29 -0000 1.17 +++ compiler/org/eclipse/jdt/internal/compiler/flow/FinallyFlowContext.java 24 Jan 2006 09:56:41 -0000 @@ -16,6 +16,7 @@ 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.VariableBinding; /** @@ -28,77 +29,124 @@ VariableBinding[] finalVariables; int assignCount; + LocalVariableBinding[] nullLocals; Expression[] nullReferences; - int[] nullStatus; + int[] nullCheckTypes; int nullCount; public FinallyFlowContext(FlowContext parent, ASTNode associatedNode) { super(parent, associatedNode); } - /** - * 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) { +/** + * 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; - // check redundant final assignments - for (int i = 0; i < assignCount; i++) { - VariableBinding variable = 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, finalAssignments[i]); - } - } else { - // final local variable - if (flowInfo.isPotentiallyAssigned((LocalVariableBinding) variable)) { - complained = true; - scope.problemReporter().duplicateInitializationOfFinalLocal( - (LocalVariableBinding) variable, - 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 = parent; - while (currentContext != null) { - //if (currentContext.isSubRoutine()) { - currentContext.removeFinalAssignmentIfAny(finalAssignments[i]); - //} - currentContext = currentContext.parent; - } + 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, finalAssignments[i]); + } + } else { + // final local variable + if (flowInfo.isPotentiallyAssigned((LocalVariableBinding) variable)) { + complained = true; + scope.problemReporter().duplicateInitializationOfFinalLocal( + (LocalVariableBinding) variable, + this.finalAssignments[i]); } } - - // check inconsistent null checks - for (int i = 0; i < nullCount; i++) { - Expression expression = nullReferences[i]; - if (expression == null) continue; + // any reference reported at this level is removed from the parent context + // where it could also be reported again + if (complained) { + FlowContext currentContext = this.parent; + while (currentContext != null) { + //if (currentContext.isSubRoutine()) { + currentContext.removeFinalAssignmentIfAny(this.finalAssignments[i]); + //} + currentContext = currentContext.parent; + } + } + } + + // check inconsistent null checks + if (this.deferNullDiagnostic) { // within an enclosing loop, be conservative + for (int i = 0; i < this.nullCount; i++) { + Expression expression = this.nullReferences[i]; + LocalVariableBinding local = this.nullLocals[i]; + switch (this.nullCheckTypes[i]) { + case CAN_ONLY_NULL_NON_NULL : + case CAN_ONLY_NULL: + if (flowInfo.isProtectedNonNull(local)) { + if (nullCheckTypes[i] == CAN_ONLY_NULL_NON_NULL) { + scope.problemReporter().localVariableCannotBeNull(local, expression); + } + return; + } + if (flowInfo.isProtectedNull(local)) { + scope.problemReporter().localVariableCanOnlyBeNull(local, expression); + return; + } + break; + case MAY_NULL : + if (flowInfo.isProtectedNonNull(local)) { + return; + } + if (flowInfo.isProtectedNull(local)) { + scope.problemReporter().localVariableCanOnlyBeNull(local, expression); + return; + } + break; + default: + // never happens + } + this.parent.recordUsingNullReference(scope, local, expression, + this.nullCheckTypes[i], flowInfo); + } + } + else { // no enclosing loop, be as precise as possible right now + for (int i = 0; i < this.nullCount; i++) { + Expression expression = this.nullReferences[i]; // final local variable - LocalVariableBinding local = expression.localVariableBinding(); - switch (nullStatus[i]) { - case FlowInfo.NULL : + LocalVariableBinding local = this.nullLocals[i]; + switch (this.nullCheckTypes[i]) { + case CAN_ONLY_NULL_NON_NULL : + if (flowInfo.isDefinitelyNonNull(local)) { + scope.problemReporter().localVariableCannotBeNull(local, expression); + return; + } + case CAN_ONLY_NULL: if (flowInfo.isDefinitelyNull(local)) { - nullReferences[i] = null; - this.parent.recordUsingNullReference(scope, local, expression, nullStatus[i], flowInfo); + scope.problemReporter().localVariableCanOnlyBeNull(local, expression); + return; } break; - case FlowInfo.NON_NULL : - if (flowInfo.isDefinitelyNonNull(local)) { - nullReferences[i] = null; - this.parent.recordUsingNullReference(scope, local, expression, nullStatus[i], flowInfo); + case MAY_NULL : + if (flowInfo.isDefinitelyNull(local)) { + scope.problemReporter().localVariableCanOnlyBeNull(local, expression); + return; + } + if (flowInfo.isPotentiallyNull(local)) { + scope.problemReporter().localVariableMayBeNull(local, expression); + return; } break; + default: + // should not happen } } } +} public String individualToString() { @@ -138,6 +186,72 @@ return true; } + public void recordUsingNullReference(Scope scope, LocalVariableBinding local, + Expression reference, int checkType, FlowInfo flowInfo) { + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) { + if (deferNullDiagnostic) { // within an enclosing loop, be conservative + switch (checkType) { + case CAN_ONLY_NULL_NON_NULL : + case CAN_ONLY_NULL: + if (flowInfo.isProtectedNonNull(local)) { + if (checkType == CAN_ONLY_NULL_NON_NULL) { + scope.problemReporter().localVariableCannotBeNull(local, reference); + } + return; + } + if (flowInfo.isProtectedNull(local)) { + scope.problemReporter().localVariableCanOnlyBeNull(local, reference); + return; + } + break; + case MAY_NULL : + if (flowInfo.isProtectedNonNull(local)) { + return; + } + if (flowInfo.isProtectedNull(local)) { + scope.problemReporter().localVariableCanOnlyBeNull(local, reference); + return; + } + break; + default: + // never happens + } + } + else { // no enclosing loop, be as precise as possible right now + switch (checkType) { + case CAN_ONLY_NULL_NON_NULL : + if (flowInfo.isDefinitelyNonNull(local)) { + scope.problemReporter().localVariableCannotBeNull(local, reference); + return; + } + case CAN_ONLY_NULL: + if (flowInfo.isDefinitelyNull(local)) { + scope.problemReporter().localVariableCanOnlyBeNull(local, reference); + return; + } + break; + case MAY_NULL : + if (flowInfo.isDefinitelyNull(local)) { + scope.problemReporter().localVariableCanOnlyBeNull(local, reference); + return; + } + if (flowInfo.isPotentiallyNull(local)) { + scope.problemReporter().localVariableMayBeNull(local, reference); + return; + } + if (flowInfo.isDefinitelyNonNull(local)) { + return; // shortcut: cannot be null + } + break; + default: + // never happens + } + } + recordNullReference(local, reference, checkType); + // prepare to re-check with try/catch flow info + } + } + void removeFinalAssignmentIfAny(Reference reference) { for (int i = 0; i < assignCount; i++) { if (finalAssignments[i] == reference) { @@ -148,18 +262,27 @@ } } - protected boolean recordNullReference(Expression expression, int status) { - if (nullCount == 0) { - nullReferences = new Expression[5]; - nullStatus = new int[5]; - } else { - if (nullCount == nullReferences.length) { - System.arraycopy(nullReferences, 0, nullReferences = new Expression[nullCount * 2], 0, nullCount); - System.arraycopy(nullStatus, 0, nullStatus = new int[nullCount * 2], 0, nullCount); - } - } - nullReferences[nullCount] = expression; - nullStatus[nullCount++] = status; - return true; +protected void recordNullReference(LocalVariableBinding local, + Expression expression, int status) { + if (this.nullCount == 0) { + this.nullLocals = new LocalVariableBinding[5]; + this.nullReferences = new Expression[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 Expression[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++] = status; +} } Index: compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java,v retrieving revision 1.46 diff -u -r1.46 FlowContext.java --- compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java 10 Jan 2006 14:37:28 -0000 1.46 +++ compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java 24 Jan 2006 09:56:41 -0000 @@ -36,12 +36,15 @@ public ASTNode associatedNode; public FlowContext parent; - + boolean deferNullDiagnostic, preemptNullDiagnostic; + // preempt marks looping contexts public final static FlowContext NotContinuableContext = new FlowContext(null, null); public FlowContext(FlowContext parent, ASTNode associatedNode) { this.parent = parent; this.associatedNode = associatedNode; + deferNullDiagnostic = parent != null && + (parent.deferNullDiagnostic || parent.preemptNullDiagnostic); } public Label breakLabel() { @@ -164,7 +167,7 @@ traversedContext.recordReturnFrom(flowInfo.unconditionalInits()); if (traversedContext.associatedNode instanceof TryStatement){ TryStatement tryStatement = (TryStatement) traversedContext.associatedNode; - flowInfo = flowInfo.copy().addInitializationsFrom(tryStatement.subRoutineInits); + flowInfo = flowInfo.addInitializationsFrom(tryStatement.subRoutineInits); } traversedContext = traversedContext.parent; } @@ -257,7 +260,7 @@ traversedContext.recordReturnFrom(flowInfo.unconditionalInits()); if (traversedContext.associatedNode instanceof TryStatement){ TryStatement tryStatement = (TryStatement) traversedContext.associatedNode; - flowInfo = flowInfo.copy().addInitializationsFrom(tryStatement.subRoutineInits); + flowInfo = flowInfo.addInitializationsFrom(tryStatement.subRoutineInits); } traversedContext = traversedContext.parent; } @@ -404,7 +407,7 @@ // default implementation: do nothing } -public void recordContinueFrom(FlowInfo flowInfo) { +public void recordContinueFrom(FlowContext innerFlowContext, FlowInfo flowInfo) { // default implementation: do nothing } @@ -412,17 +415,26 @@ return true; // keep going } -protected boolean recordNullReference(Expression expression, int status) { - return false; // keep going +/** + * Record a null reference for use by deferred checks. Only looping or + * finally contexts really record that information. + * @param local the local variable involved in the check + * @param expression the expression within which local lays + * @param status the status against which the check must be performed; one of + * {@link #CAN_ONLY_NULL CAN_ONLY_NULL}, {@link #CAN_ONLY_NULL_NON_NULL + * CAN_ONLY_NULL_NON_NULL}, {@link #MAY_NULL MAY_NULL} + */ +protected void recordNullReference(LocalVariableBinding local, + Expression expression, int status) { + // default implementation: do nothing } -public void recordReturnFrom(FlowInfo flowInfo) { +public void recordReturnFrom(UnconditionalFlowInfo flowInfo) { // default implementation: do nothing } public void recordSettingFinal(VariableBinding variable, Reference finalReference, FlowInfo flowInfo) { - if (!flowInfo.isReachable()) return; - + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) { // for initialization inside looping statement that effectively loops FlowContext context = this; while (context != null) { @@ -431,36 +443,76 @@ } context = context.parent; } + } } -public void recordUsingNullReference(Scope scope, LocalVariableBinding local, Expression reference, int status, FlowInfo flowInfo) { - if (!flowInfo.isReachable()) return; - - switch (status) { - case FlowInfo.NULL : +public static final int + CAN_ONLY_NULL_NON_NULL = 20, + // check against null and non null, with definite values -- comparisons + CAN_ONLY_NULL = 21, + // check against null, with definite values -- assignment to null + MAY_NULL = 22; + // check against null, with potential values -- NPE guard + +/** + * Record a null reference for use by deferred checks. Only looping or + * finally contexts really record that information. The context may + * emit an error immediately depending on the status of local against + * flowInfo and its nature (only looping of finally contexts defer part + * of the checks; nonetheless, contexts that are nested into a looping or a + * finally context get affected and delegate some checks to their enclosing + * context). + * @param scope the scope into which the check is performed + * @param local the local variable involved in the check + * @param reference the expression within which local lays + * @param checkType the status against which the check must be performed; one + * of {@link #CAN_ONLY_NULL CAN_ONLY_NULL}, {@link #CAN_ONLY_NULL_NON_NULL + * CAN_ONLY_NULL_NON_NULL}, {@link #MAY_NULL MAY_NULL} + * @param flowInfo the flow info at the check point; deferring contexts will + * perform supplementary checks against flow info instances that cannot + * be known at the time of calling this method (they are influenced by + * code that follows the current point) + */ +public void recordUsingNullReference(Scope scope, LocalVariableBinding local, + Expression reference, int checkType, FlowInfo flowInfo) { + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0 || + flowInfo.isDefinitelyUnknown(local)) { + return; + } + switch (checkType) { + case CAN_ONLY_NULL_NON_NULL : + if (flowInfo.isDefinitelyNonNull(local)) { + scope.problemReporter().localVariableCannotBeNull(local, reference); + return; + } + else if (flowInfo.isPotentiallyUnknown(local)) { + return; + } + case CAN_ONLY_NULL: if (flowInfo.isDefinitelyNull(local)) { scope.problemReporter().localVariableCanOnlyBeNull(local, reference); return; - } else if (flowInfo.isDefinitelyNonNull(local)) { - scope.problemReporter().localVariableCannotBeNull(local, reference); + } + else if (flowInfo.isPotentiallyUnknown(local)) { return; } break; - case FlowInfo.NON_NULL : + case MAY_NULL : if (flowInfo.isDefinitelyNull(local)) { - scope.problemReporter().localVariableCanOnlyBeNull(local, reference); + scope.problemReporter().localVariableCanOnlyBeNull(local, reference); + return; + } + if (flowInfo.isPotentiallyNull(local)) { + scope.problemReporter().localVariableMayBeNull(local, reference); return; } break; + default: + // never happens } - - // for initialization inside looping statement that effectively loops - FlowContext context = this; - while (context != null) { - if (context.recordNullReference(reference, status)) { - return; // no need to keep going - } - context = context.parent; + if (parent != null) { + parent.recordUsingNullReference(scope, local, reference, checkType, + flowInfo); } } Index: compiler/org/eclipse/jdt/internal/compiler/flow/FlowInfo.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowInfo.java,v retrieving revision 1.27 diff -u -r1.27 FlowInfo.java --- compiler/org/eclipse/jdt/internal/compiler/flow/FlowInfo.java 23 Feb 2005 02:47:29 -0000 1.27 +++ compiler/org/eclipse/jdt/internal/compiler/flow/FlowInfo.java 24 Jan 2006 09:56:41 -0000 @@ -15,8 +15,10 @@ public abstract class FlowInfo { + public int tagBits; // REACHABLE by default public final static int REACHABLE = 0; public final static int UNREACHABLE = 1; + public final static int NULL_FLAG_MASK = 2; public final static int UNKNOWN = 0; public final static int NULL = 1; @@ -25,11 +27,30 @@ public static final UnconditionalFlowInfo DEAD_END; // Represents a dead branch status of initialization static { DEAD_END = new UnconditionalFlowInfo(); - DEAD_END.reachMode = UNREACHABLE; + DEAD_END.tagBits = UNREACHABLE; } - abstract public FlowInfo addInitializationsFrom(FlowInfo otherInits); - abstract public FlowInfo addPotentialInitializationsFrom(FlowInfo otherInits); +/** + * Add other inits to this flow info, then return this. The operation semantics + * are to match as closely as possible the application to this flow info of all + * the operations that resulted into otherInits. + * @param otherInits other inits to add to this + * @return this, modified according to otherInits information + */ +abstract public FlowInfo addInitializationsFrom(FlowInfo otherInits); + + +/** + * Compose other inits over this flow info, then return this. The operation + * semantics are to wave into this flow info the consequences of a possible + * path into the operations that resulted into otherInits. The fact that this + * path may be left unexecuted under peculiar conditions results into less + * specific results than {@link #addInitializationsFrom(FlowInfo) + * addInitializationsFrom}. + * @param otherInits other inits to compose over this + * @return this, modified according to otherInits information + */ +abstract public FlowInfo addPotentialInitializationsFrom(FlowInfo otherInits); public FlowInfo asNegatedCondition() { @@ -42,6 +63,10 @@ return new ConditionalFlowInfo(initsWhenTrue, initsWhenFalse); } +/** + * Return a deep copy of the current instance. + * @return a deep copy of this flow info + */ abstract public FlowInfo copy(); public static UnconditionalFlowInfo initial(int maxFieldCount) { @@ -49,9 +74,27 @@ info.maxFieldCount = maxFieldCount; return info; } - - abstract public FlowInfo initsWhenFalse(); - + +/** + * Return the flow info that would result from the path associated to the + * value false for the condition expression that generated this flow info. + * May be this flow info if it is not an instance of {@link + * ConditionalFlowInfo}. May have a side effect on subparts of this flow + * info (subtrees get merged). + * @return the flow info associated to the false branch of the condition + * that generated this flow info + */ +abstract public FlowInfo initsWhenFalse(); + +/** + * Return the flow info that would result from the path associated to the + * value true for the condition expression that generated this flow info. + * May be this flow info if it is not an instance of {@link + * ConditionalFlowInfo}. May have a side effect on subparts of this flow + * info (subtrees get merged). + * @return the flow info associated to the true branch of the condition + * that generated this flow info + */ abstract public FlowInfo initsWhenTrue(); /** @@ -64,25 +107,26 @@ */ public abstract boolean isDefinitelyAssigned(LocalVariableBinding local); - /** - * Check status of definite null assignment for a field. - */ - abstract public boolean isDefinitelyNonNull(FieldBinding field); - - /** - * Check status of definite null assignment for a local. - */ +/** + * Check status of definite non-null value for a given local variable. + * @param local the variable to ckeck + * @return true iff local is definitely non null for this flow info + */ public abstract boolean isDefinitelyNonNull(LocalVariableBinding local); - /** - * Check status of definite null assignment for a field. - */ - abstract public boolean isDefinitelyNull(FieldBinding field); - - /** - * Check status of definite null assignment for a local. - */ - public abstract boolean isDefinitelyNull(LocalVariableBinding local); +/** + * Check status of definite null value for a given local variable. + * @param local the variable to ckeck + * @return true iff local is definitely null for this flow info + */ +public abstract boolean isDefinitelyNull(LocalVariableBinding local); + +/** + * Check status of definite unknown value for a given local variable. + * @param local the variable to ckeck + * @return true iff local is definitely unknown for this flow info + */ +public abstract boolean isDefinitelyUnknown(LocalVariableBinding local); /** * Check status of potential assignment for a field. @@ -95,7 +139,44 @@ abstract public boolean isPotentiallyAssigned(LocalVariableBinding field); - abstract public boolean isReachable(); +/** + * Check status of potential null assignment for a local. + */ +public abstract boolean isPotentiallyNull(LocalVariableBinding local); + +/** + * Return true if the given local may have been assigned to an unknown value. + * @param local the local to check + * @return true if the given local may have been assigned to an unknown value + */ +public abstract boolean isPotentiallyUnknown(LocalVariableBinding local); + +/** + * Return true if the given local is protected by a test against a non null + * value. + * @param local the local to check + * @return true if the given local is protected by a test against a non null + */ +public abstract boolean isProtectedNonNull(LocalVariableBinding local); + +/** + * Return true if the given local is protected by a test against null. + * @param local the local to check + * @return true if the given local is protected by a test against null + */ +public abstract boolean isProtectedNull(LocalVariableBinding local); + +/** + * Record that a local variable got checked to be non null. + * @param local the checked local variable + */ +abstract public void markAsComparedEqualToNonNull(LocalVariableBinding local); + +/** + * Record that a local variable got checked to be null. + * @param local the checked local variable + */ +abstract public void markAsComparedEqualToNull(LocalVariableBinding local); /** * Record a field got definitely assigned. @@ -118,7 +199,7 @@ abstract public void markAsDefinitelyNull(LocalVariableBinding local); /** - * Record a field got definitely assigned. + * Record a field got definitely assigned to null. */ abstract public void markAsDefinitelyNull(FieldBinding field); @@ -127,52 +208,99 @@ */ abstract public void markAsDefinitelyAssigned(LocalVariableBinding local); - /** - * Clear the initialization info for a field - */ - abstract public void markAsDefinitelyNotAssigned(FieldBinding field); - - /** - * Clear the initialization info for a local variable - */ - abstract public void markAsDefinitelyNotAssigned(LocalVariableBinding local); - - /** - * Merge branches using optimized boolean conditions - */ - public static FlowInfo mergedOptimizedBranches(FlowInfo initsWhenTrue, boolean isOptimizedTrue, FlowInfo initsWhenFalse, boolean isOptimizedFalse, boolean allowFakeDeadBranch) { - FlowInfo mergedInfo; - if (isOptimizedTrue){ - if (initsWhenTrue == FlowInfo.DEAD_END && allowFakeDeadBranch) { - mergedInfo = initsWhenFalse.setReachMode(FlowInfo.UNREACHABLE); - } else { - mergedInfo = initsWhenTrue.addPotentialInitializationsFrom(initsWhenFalse); - } - - } else if (isOptimizedFalse) { - if (initsWhenFalse == FlowInfo.DEAD_END && allowFakeDeadBranch) { - mergedInfo = initsWhenTrue.setReachMode(FlowInfo.UNREACHABLE); - } else { - mergedInfo = initsWhenFalse.addPotentialInitializationsFrom(initsWhenTrue); - } - - } else { - mergedInfo = initsWhenTrue.unconditionalInits().mergedWith(initsWhenFalse.unconditionalInits()); +/** + * Record a local got definitely assigned to an unknown value. + */ +abstract public void markAsDefinitelyUnknown(LocalVariableBinding local); + +/** + * Merge branches using optimized boolean conditions + */ +public static UnconditionalFlowInfo mergedOptimizedBranches( + FlowInfo initsWhenTrue, boolean isOptimizedTrue, + FlowInfo initsWhenFalse, boolean isOptimizedFalse, + boolean allowFakeDeadBranch) { + UnconditionalFlowInfo mergedInfo; + if (isOptimizedTrue){ + if (initsWhenTrue == FlowInfo.DEAD_END && allowFakeDeadBranch) { + mergedInfo = initsWhenFalse.setReachMode(FlowInfo.UNREACHABLE). + unconditionalInits(); + } + else { + mergedInfo = + initsWhenTrue.addPotentialInitializationsFrom(initsWhenFalse. + nullInfoLessUnconditionalCopy()). + unconditionalInits(); } - return mergedInfo; + } + else if (isOptimizedFalse) { + if (initsWhenFalse == FlowInfo.DEAD_END && allowFakeDeadBranch) { + mergedInfo = initsWhenTrue.setReachMode(FlowInfo.UNREACHABLE). + unconditionalInits(); + } + else { + mergedInfo = + initsWhenFalse.addPotentialInitializationsFrom(initsWhenTrue. + nullInfoLessUnconditionalCopy()). + unconditionalInits(); + } + } + else { + mergedInfo = initsWhenTrue. + mergedWith(initsWhenFalse.unconditionalInits()); } + return mergedInfo; +} - abstract public int reachMode(); - - abstract public FlowInfo setReachMode(int reachMode); - - /** - * Returns the receiver updated in the following way: - */ - abstract public UnconditionalFlowInfo mergedWith(UnconditionalFlowInfo otherInits); +/** + * Return REACHABLE if this flow info is reachable, UNREACHABLE + * else. + * @return REACHABLE if this flow info is reachable, UNREACHABLE + * else + */ +public int reachMode() { + return this.tagBits & UNREACHABLE; +} + +/** + * Return a flow info that carries the same information as the result of + * {@link #initsWhenTrue() initsWhenTrue}, but warrantied to be different + * from this.
+ * Caveat: side effects on the result may affect components of this. + * @return the result of initsWhenTrue or a copy of it + */ +abstract public FlowInfo safeInitsWhenTrue(); + +/** + * Set this flow info reach mode and return this. + * @param reachMode one of {@link #REACHABLE REACHABLE} or {@link #UNREACHABLE UNREACHABLE} + * @return this, with the reach mode set to reachMode + */ +abstract public FlowInfo setReachMode(int reachMode); + +/** + * Return the intersection of this and otherInits, that is + * one of: + * otherInits is not affected, and is not returned either (no + * need to protect the result). + * @param otherInits the flow info to merge with this + * @return the intersection of this and otherInits. + */ +abstract public UnconditionalFlowInfo mergedWith( + UnconditionalFlowInfo otherInits); + +/** + * Return a copy of this unconditional flow info, deprived from its null + * info. {@link #DEAD_END DEAD_END} is returned unmodified. + * @return a copy of this unconditional flow info deprived from its null info + */ +abstract public UnconditionalFlowInfo nullInfoLessUnconditionalCopy(); public String toString(){ @@ -182,5 +310,38 @@ return super.toString(); } - abstract public UnconditionalFlowInfo unconditionalInits(); +/** + * Return a new flow info that holds the same information as this would after + * a call to unconditionalInits, but leaving this info unaffected. Moreover, + * the result can be modified without affecting this. + * @return a new flow info carrying this unconditional flow info + */ +abstract public UnconditionalFlowInfo unconditionalCopy(); + +/** + * Return a new flow info that holds the same information as this would after + * a call to {@link #unconditionalInits() unconditionalInits} followed by the + * erasure of fields specific information, but leaving this flow info unaffected. + * @return a new flow info carrying the unconditional flow info for local variables + */ +abstract public UnconditionalFlowInfo unconditionalFieldLessCopy(); + +/** + * Return a flow info that merges the possible paths of execution described by + * this flow info. In case of an unconditional flow info, return this. In case + * of a conditional flow info, merge branches recursively. Caveat: this may + * be affected, and modifying the result may affect this. + * @return a flow info that merges the possible paths of execution described by + * this + */ +abstract public UnconditionalFlowInfo unconditionalInits(); + +/** + * Return a new flow info that holds the same information as this would after + * a call to {@link #unconditionalInits() unconditionalInits}, but leaving + * this info unaffected. Side effects on the result might affect this though + * (consider it as read only). + * @return a flow info carrying this unconditional flow info + */ +abstract public UnconditionalFlowInfo unconditionalInitsWithoutSideEffect(); } Index: compiler/org/eclipse/jdt/internal/compiler/flow/InsideSubRoutineFlowContext.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/InsideSubRoutineFlowContext.java,v retrieving revision 1.15 diff -u -r1.15 InsideSubRoutineFlowContext.java --- compiler/org/eclipse/jdt/internal/compiler/flow/InsideSubRoutineFlowContext.java 23 Feb 2005 02:47:29 -0000 1.15 +++ compiler/org/eclipse/jdt/internal/compiler/flow/InsideSubRoutineFlowContext.java 24 Jan 2006 09:56:41 -0000 @@ -47,13 +47,13 @@ return (SubRoutineStatement)associatedNode; } - public void recordReturnFrom(FlowInfo flowInfo) { - - if (!flowInfo.isReachable()) return; - if (initsOnReturn == FlowInfo.DEAD_END) { - initsOnReturn = flowInfo.copy().unconditionalInits(); - } else { - initsOnReturn = initsOnReturn.mergedWith(flowInfo.copy().unconditionalInits()); - } +public void recordReturnFrom(UnconditionalFlowInfo flowInfo) { + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) { + if (initsOnReturn == FlowInfo.DEAD_END) { + initsOnReturn = (UnconditionalFlowInfo) flowInfo.copy(); + } else { + initsOnReturn = initsOnReturn.mergedWith(flowInfo); } + } +} } Index: compiler/org/eclipse/jdt/internal/compiler/flow/LoopingFlowContext.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/LoopingFlowContext.java,v retrieving revision 1.30 diff -u -r1.30 LoopingFlowContext.java --- compiler/org/eclipse/jdt/internal/compiler/flow/LoopingFlowContext.java 23 Feb 2005 02:47:29 -0000 1.30 +++ compiler/org/eclipse/jdt/internal/compiler/flow/LoopingFlowContext.java 24 Jan 2006 09:56:42 -0000 @@ -28,82 +28,165 @@ public Label continueLabel; public UnconditionalFlowInfo initsOnContinue = FlowInfo.DEAD_END; + private UnconditionalFlowInfo upstreamNullFlowInfo; + private LoopingFlowContext innerFlowContexts[] = null; + private UnconditionalFlowInfo innerFlowInfos[] = null; + private int innerFlowContextsNb = 0; + Reference finalAssignments[]; VariableBinding finalVariables[]; int assignCount = 0; + LocalVariableBinding[] nullLocals; Expression[] nullReferences; - int[] nullStatus; + int[] nullCheckTypes; int nullCount; Scope associatedScope; public LoopingFlowContext( FlowContext parent, + FlowInfo upstreamNullFlowInfo, ASTNode associatedNode, Label breakLabel, Label continueLabel, Scope associatedScope) { super(parent, associatedNode, breakLabel); + preemptNullDiagnostic = true; + // children will defer to this, which may defer to its own parent this.continueLabel = continueLabel; this.associatedScope = associatedScope; + this.upstreamNullFlowInfo = upstreamNullFlowInfo.unconditionalCopy(); } - - public void complainOnDeferredChecks(BlockScope scope, FlowInfo flowInfo) { - - // complain on final assignments in loops - for (int i = 0; i < assignCount; i++) { - VariableBinding variable = finalVariables[i]; - if (variable == null) continue; - boolean complained = false; // remember if have complained on this final assignment - if (variable instanceof FieldBinding) { - if (flowInfo.isPotentiallyAssigned((FieldBinding) variable)) { - complained = true; - scope.problemReporter().duplicateInitializationOfBlankFinalField( - (FieldBinding) variable, - finalAssignments[i]); - } - } else { - if (flowInfo.isPotentiallyAssigned((LocalVariableBinding) variable)) { - complained = true; - scope.problemReporter().duplicateInitializationOfFinalLocal( - (LocalVariableBinding) variable, - finalAssignments[i]); - } + +/** + * Perform deferred checks relative to final variables duplicate initialization + * of lack of initialization. + * @param scope the scope to which this context is associated + * @param flowInfo the flow info against which checks must be performed + */ +public void complainOnDeferredFinalChecks(BlockScope scope, FlowInfo flowInfo) { + // complain on final assignments in loops + for (int i = 0; i < assignCount; i++) { + VariableBinding variable = finalVariables[i]; + if (variable == null) continue; + boolean complained = false; // remember if have complained on this final assignment + if (variable instanceof FieldBinding) { + if (flowInfo.isPotentiallyAssigned((FieldBinding) variable)) { + complained = true; + scope.problemReporter().duplicateInitializationOfBlankFinalField( + (FieldBinding) variable, + finalAssignments[i]); } - // any reference reported at this level is removed from the parent context where it - // could also be reported again - if (complained) { - FlowContext context = parent; - while (context != null) { - context.removeFinalAssignmentIfAny(finalAssignments[i]); - context = context.parent; - } + } else { + if (flowInfo.isPotentiallyAssigned((LocalVariableBinding) variable)) { + complained = true; + scope.problemReporter().duplicateInitializationOfFinalLocal( + (LocalVariableBinding) variable, + finalAssignments[i]); } } - // check inconsistent null checks - for (int i = 0; i < nullCount; i++) { - Expression expression = nullReferences[i]; - if (expression == null) continue; + // any reference reported at this level is removed from the parent context where it + // could also be reported again + if (complained) { + FlowContext context = parent; + while (context != null) { + context.removeFinalAssignmentIfAny(finalAssignments[i]); + context = context.parent; + } + } + } +} + +/** + * Perform deferred checks relative to the null status of local variables. + * @param scope the scope to which this context is associated + * @param flowInfo the flow info against which checks must be performed + */ +public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo flowInfo) { + for (int i = 0 ; i < this.innerFlowContextsNb ; i++) { + this.upstreamNullFlowInfo. + addPotentialNullInfoFrom( + this.innerFlowContexts[i].upstreamNullFlowInfo). + addPotentialNullInfoFrom(this.innerFlowInfos[i]); + } + this.innerFlowContextsNb = 0; + flowInfo = this.upstreamNullFlowInfo. + addPotentialNullInfoFrom( + flowInfo.unconditionalInitsWithoutSideEffect()); + if (this.deferNullDiagnostic) { + // check only immutable null checks on innermost looping context + for (int i = 0; i < this.nullCount; i++) { + LocalVariableBinding local = this.nullLocals[i]; + Expression expression = this.nullReferences[i]; // final local variable - LocalVariableBinding local = expression.localVariableBinding(); - switch (nullStatus[i]) { - case FlowInfo.NULL : + switch (this.nullCheckTypes[i]) { + case CAN_ONLY_NULL_NON_NULL : + if (flowInfo.isDefinitelyNonNull(local)) { + this.nullReferences[i] = null; + scope.problemReporter().localVariableCannotBeNull(local, expression); + continue; + } + case CAN_ONLY_NULL : if (flowInfo.isDefinitelyNull(local)) { - nullReferences[i] = null; - this.parent.recordUsingNullReference(scope, local, expression, nullStatus[i], flowInfo); + this.nullReferences[i] = null; + scope.problemReporter().localVariableCanOnlyBeNull(local, expression); + continue; } break; - case FlowInfo.NON_NULL : + case MAY_NULL: + if (flowInfo.isDefinitelyNull(local)) { + this.nullReferences[i] = null; + scope.problemReporter().localVariableCanOnlyBeNull(local, expression); + continue; + } + break; + default: + // never happens + } + this.parent.recordUsingNullReference(scope, local, expression, + this.nullCheckTypes[i], flowInfo); + } + } + else { + // check inconsistent null checks on outermost looping context + for (int i = 0; i < this.nullCount; i++) { + Expression expression = this.nullReferences[i]; + // final local variable + LocalVariableBinding local = this.nullLocals[i]; + switch (this.nullCheckTypes[i]) { + case CAN_ONLY_NULL_NON_NULL : if (flowInfo.isDefinitelyNonNull(local)) { - nullReferences[i] = null; - this.parent.recordUsingNullReference(scope, local, expression, nullStatus[i], flowInfo); + this.nullReferences[i] = null; + scope.problemReporter().localVariableCannotBeNull(local, expression); + continue; + } + case CAN_ONLY_NULL : + if (flowInfo.isDefinitelyNull(local)) { + this.nullReferences[i] = null; + scope.problemReporter().localVariableCanOnlyBeNull(local, expression); + continue; } break; + case MAY_NULL: + if (flowInfo.isDefinitelyNull(local)) { + this.nullReferences[i] = null; + scope.problemReporter().localVariableCanOnlyBeNull(local, expression); + continue; + } + if (flowInfo.isPotentiallyNull(local)) { + this.nullReferences[i] = null; + scope.problemReporter().localVariableMayBeNull(local, expression); + continue; + } + break; + default: + // never happens } - } + } } - +} + public Label continueLabel() { return continueLabel; } @@ -125,15 +208,45 @@ return initsOnContinue != FlowInfo.DEAD_END; } - public void recordContinueFrom(FlowInfo flowInfo) { - - if (!flowInfo.isReachable()) return; - if (initsOnContinue == FlowInfo.DEAD_END) { - initsOnContinue = flowInfo.copy().unconditionalInits(); - } else { - initsOnContinue = initsOnContinue.mergedWith(flowInfo.copy().unconditionalInits()); +public void recordContinueFrom(FlowContext innerFlowContext, FlowInfo flowInfo) { + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) { + if ((initsOnContinue.tagBits & FlowInfo.UNREACHABLE) == 0) { + initsOnContinue = initsOnContinue. + mergedWith(flowInfo.unconditionalInitsWithoutSideEffect()); + } + else { + initsOnContinue = flowInfo.unconditionalCopy(); + } + FlowContext inner = innerFlowContext; + while (inner != this && !(inner instanceof LoopingFlowContext)) { + inner = inner.parent; + } + if (inner == this) { + this.upstreamNullFlowInfo. + addPotentialNullInfoFrom( + flowInfo.unconditionalInitsWithoutSideEffect()); + } + else { + int length = 0; + if (this.innerFlowContexts == null) { + this.innerFlowContexts = new LoopingFlowContext[5]; + this.innerFlowInfos = new UnconditionalFlowInfo[5]; + } + else if (this.innerFlowContextsNb == + (length = this.innerFlowContexts.length) - 1) { + System.arraycopy(this.innerFlowContexts, 0, + (this.innerFlowContexts = new LoopingFlowContext[length + 5]), + 0, length); + System.arraycopy(this.innerFlowInfos, 0, + (this.innerFlowInfos= new UnconditionalFlowInfo[length + 5]), + 0, length); } + this.innerFlowContexts[this.innerFlowContextsNb] = (LoopingFlowContext) inner; + this.innerFlowInfos[this.innerFlowContextsNb++] = + flowInfo.unconditionalInitsWithoutSideEffect(); } + } +} protected boolean recordFinalAssignment( VariableBinding binding, @@ -170,20 +283,68 @@ return true; } - protected boolean recordNullReference(Expression expression, int status) { - if (nullCount == 0) { - nullReferences = new Expression[5]; - nullStatus = new int[5]; - } else { - if (nullCount == nullReferences.length) { - System.arraycopy(nullReferences, 0, nullReferences = new Expression[nullCount * 2], 0, nullCount); - System.arraycopy(nullStatus, 0, nullStatus = new int[nullCount * 2], 0, nullCount); +protected void recordNullReference(LocalVariableBinding local, + Expression expression, int status) { + if (nullCount == 0) { + nullLocals = new LocalVariableBinding[5]; + nullReferences = new Expression[5]; + nullCheckTypes = new int[5]; + } + else if (nullCount == nullLocals.length) { + System.arraycopy(nullLocals, 0, + nullLocals = new LocalVariableBinding[nullCount * 2], 0, nullCount); + System.arraycopy(nullReferences, 0, + nullReferences = new Expression[nullCount * 2], 0, nullCount); + System.arraycopy(nullCheckTypes, 0, + nullCheckTypes = new int[nullCount * 2], 0, nullCount); + } + nullLocals[nullCount] = local; + nullReferences[nullCount] = expression; + nullCheckTypes[nullCount++] = status; +} + +public void recordUsingNullReference(Scope scope, LocalVariableBinding local, + Expression reference, int checkType, FlowInfo flowInfo) { + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0 || + flowInfo.isDefinitelyUnknown(local)) { + return; + } + switch (checkType) { + case CAN_ONLY_NULL_NON_NULL : + case CAN_ONLY_NULL: + if (flowInfo.isDefinitelyNonNull(local)) { + if (checkType == CAN_ONLY_NULL_NON_NULL) { + scope.problemReporter().localVariableCannotBeNull(local, reference); + } + return; } - } - nullReferences[nullCount] = expression; - nullStatus[nullCount++] = status; - return true; - } + if (flowInfo.isDefinitelyNull(local)) { + scope.problemReporter().localVariableCanOnlyBeNull(local, reference); + return; + } + if (flowInfo.isPotentiallyUnknown(local)) { + return; + } + recordNullReference(local, reference, checkType); + return; + case MAY_NULL : + if (flowInfo.isDefinitelyNonNull(local)) { + return; + } + if (flowInfo.isDefinitelyNull(local)) { + scope.problemReporter().localVariableCanOnlyBeNull(local, reference); + return; + } + if (flowInfo.isPotentiallyNull(local)) { + scope.problemReporter().localVariableMayBeNull(local, reference); + return; + } + recordNullReference(local, reference, checkType); + return; + default: + // never happens + } +} void removeFinalAssignmentIfAny(Reference reference) { for (int i = 0; i < assignCount; i++) { Index: compiler/org/eclipse/jdt/internal/compiler/flow/SwitchFlowContext.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/SwitchFlowContext.java,v retrieving revision 1.26 diff -u -r1.26 SwitchFlowContext.java --- compiler/org/eclipse/jdt/internal/compiler/flow/SwitchFlowContext.java 14 Oct 2005 22:43:00 -0000 1.26 +++ compiler/org/eclipse/jdt/internal/compiler/flow/SwitchFlowContext.java 24 Jan 2006 09:56:42 -0000 @@ -42,10 +42,11 @@ } public void recordBreakFrom(FlowInfo flowInfo) { - if (initsOnBreak == FlowInfo.DEAD_END) { - initsOnBreak = flowInfo.copy().unconditionalInits(); - } else { - initsOnBreak = initsOnBreak.mergedWith(flowInfo.copy().unconditionalInits()); + if ((initsOnBreak.tagBits & FlowInfo.UNREACHABLE) == 0) { + initsOnBreak = initsOnBreak.mergedWith(flowInfo.unconditionalInits()); + } + else { + initsOnBreak = flowInfo.unconditionalCopy(); } } } Index: compiler/org/eclipse/jdt/internal/compiler/flow/UnconditionalFlowInfo.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/UnconditionalFlowInfo.java,v retrieving revision 1.44 diff -u -r1.44 UnconditionalFlowInfo.java --- compiler/org/eclipse/jdt/internal/compiler/flow/UnconditionalFlowInfo.java 18 Nov 2005 16:46:23 -0000 1.44 +++ compiler/org/eclipse/jdt/internal/compiler/flow/UnconditionalFlowInfo.java 24 Jan 2006 09:56:42 -0000 @@ -10,10 +10,12 @@ *******************************************************************************/ package org.eclipse.jdt.internal.compiler.flow; +import org.eclipse.jdt.internal.core.Assert.AssertionFailedException; // for coverage tests import org.eclipse.jdt.internal.compiler.impl.Constant; import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; +import org.eclipse.jdt.internal.compiler.lookup.TagBits; /** * Record initialization status during definite assignment analysis @@ -21,808 +23,1700 @@ * No caching of pre-allocated instances. */ public class UnconditionalFlowInfo extends FlowInfo { + // Coverage tests + // Coverage tests need that the code be instrumented. The following flag + // controls whether the instrumented code is compiled in or not, and whether + // the coverage tests methods run or not. + public final static boolean coverageTestFlag = false; + // never release with the coverageTestFlag set to true + public static int coverageTestId; - public long definiteInits; public long potentialInits; - public long extraDefiniteInits[]; - public long extraPotentialInits[]; - public long definiteNulls; - public long definiteNonNulls; - public long extraDefiniteNulls[]; - public long extraDefiniteNonNulls[]; - - public int reachMode; // by default + public long nullAssignmentStatusBit1; + public long nullAssignmentStatusBit2; + // 0 0 is potential (bit 1 is leftmost here) + // 1 0 is assigned + // 0 1 is protected null (aka if (o == null) { // here o protected null...) + // 1 1 is protected non null + public long nullAssignmentValueBit1; + public long nullAssignmentValueBit2; + // information only relevant for potential and assigned + // 0 0 is start -- nothing known at all + // 0 1 is assigned non null or potential anything but null + // 1 0 is assigned null or potential null + // 1 1 is potential null and potential anything but null or definite unknown + // REVIEW consider reintroducing the difference between potential non null and potential + // REVIEW unknown; if this is done, rename to nullAssignmentBit[1-4] since the semantics + // REVIEW would be ever less clear + // REVIEW went public in order to grant access to tests; do not like it... + + public static final int extraLength = 6; + public long extra[][]; + // extra bit fields for larger numbers of fields/variables + // extra[0] holds definiteInits values, extra[1] potentialInits, etc. + // lifecycle is extra == null or else all extra[]'s are allocated + // arrays which have the same size - public int maxFieldCount; + public int maxFieldCount; // limit between fields and locals // Constants public static final int BitCacheSize = 64; // 64 bits in a long. - UnconditionalFlowInfo() { - this.reachMode = REACHABLE; - } - - // unions of both sets of initialization - used for try/finally - public FlowInfo addInitializationsFrom(FlowInfo inits) { +public FlowInfo addInitializationsFrom(FlowInfo inits) { + if (this == DEAD_END) + return this; + if (inits == DEAD_END) + return this; + UnconditionalFlowInfo otherInits = inits.unconditionalInits(); - if (this == DEAD_END) - return this; - - UnconditionalFlowInfo otherInits = inits.unconditionalInits(); - if (otherInits == DEAD_END) - return this; - - // union of definitely assigned variables, - definiteInits |= otherInits.definiteInits; - // union of potentially set ones - potentialInits |= otherInits.potentialInits; - - // union of definitely null variables, - definiteNulls = (definiteNulls | otherInits.definiteNulls) & ~otherInits.definiteNonNulls; - // union of definitely non null variables, - definiteNonNulls = (definiteNonNulls | otherInits.definiteNonNulls) & ~otherInits.definiteNulls; - // fix-up null/non-null infos since cannot overlap: + --> - - // treating extra storage - if (extraDefiniteInits != null) { - if (otherInits.extraDefiniteInits != null) { + // union of definitely assigned variables, + this.definiteInits |= otherInits.definiteInits; + // union of potentially set ones + this.potentialInits |= otherInits.potentialInits; + // combine null information + // note: we may have both forms of protection (null and non null) + // coming with otherInits, because of loops + boolean considerNulls = (otherInits.tagBits & NULL_FLAG_MASK) != 0; + long a1, na1, a2, na2, a3, a4, na4, b1, b2, nb2, b3, nb3, b4, nb4; + // REVIEW does an inner declaration save stack space? does duplicate declaration waste time? + if (considerNulls) { + if ((this.tagBits & NULL_FLAG_MASK) == 0) { + this.nullAssignmentStatusBit1 = otherInits.nullAssignmentStatusBit1; + this.nullAssignmentStatusBit2 = otherInits.nullAssignmentStatusBit2; + this.nullAssignmentValueBit1 = otherInits.nullAssignmentValueBit1; + this.nullAssignmentValueBit2 = otherInits.nullAssignmentValueBit2; + if (coverageTestFlag && coverageTestId == 1) { + this.nullAssignmentValueBit2 = ~0; + } + } + else { + // TODO (maxime) indent as follows: + /* + * a + * | (b + * & c) + * + */ + // REVIEW indentation example + this.nullAssignmentStatusBit1 = + (b1 = otherInits.nullAssignmentStatusBit1) + | ((a1 = this.nullAssignmentStatusBit1) + & (((nb2 = ~(b2 = otherInits.nullAssignmentStatusBit2)) + & (nb3 = ~(b3 = otherInits.nullAssignmentValueBit1)) + & ((nb4 = ~(b4 = otherInits.nullAssignmentValueBit2)) + | ((a2 = this.nullAssignmentStatusBit2) + ^ (a4 = this.nullAssignmentValueBit2)))) + | nb4 & (na2 = ~a2) & (na4 = ~a4))); + this.nullAssignmentStatusBit2 = + (b1 & b2) + | (~b1 + & ((((na1 = ~a1) | a4) & b2) + | (a2 + & (b2 + | (a1 & (na4 = ~a4) & nb2 & nb3) + | ((~(a3 = this.nullAssignmentValueBit1) & nb3) + | (na1 & na4)) + & nb4)))); + this.nullAssignmentValueBit1 = + nb2 & b3 | + ~b1 & ((a1 & na2 & na4 | na1 & a3) & (nb2 | nb4) | + a1 & na2 & a3 & nb2 | + (a1 | a2 | na4) & b3); + this.nullAssignmentValueBit2 = + b4 | + a4 & (nb2 & nb3 | ~(b1 ^ b2)); + if (coverageTestFlag && coverageTestId == 2) { + this.nullAssignmentValueBit2 = ~0; + } + } + this.tagBits |= NULL_FLAG_MASK; // in all cases - avoid forgetting extras + } + // treating extra storage + if (this.extra != null || otherInits.extra != null) { + int mergeLimit = 0, copyLimit = 0; + if (this.extra != null) { + if (otherInits.extra != null) { // both sides have extra storage - int i = 0, length, otherLength; - if ((length = extraDefiniteInits.length) < (otherLength = otherInits.extraDefiniteInits.length)) { - // current storage is shorter -> grow current (could maybe reuse otherInits extra storage?) - System.arraycopy(extraDefiniteInits, 0, (extraDefiniteInits = new long[otherLength]), 0, length); - System.arraycopy(extraPotentialInits, 0, (extraPotentialInits = new long[otherLength]), 0, length); - System.arraycopy(extraDefiniteNulls, 0, (extraDefiniteNulls = new long[otherLength]), 0, length); - System.arraycopy(extraDefiniteNonNulls, 0, (extraDefiniteNonNulls = new long[otherLength]), 0, length); - for (; i < length; i++) { - extraDefiniteInits[i] |= otherInits.extraDefiniteInits[i]; - extraPotentialInits[i] |= otherInits.extraPotentialInits[i]; - extraDefiniteNulls[i] = (extraDefiniteNulls[i] | otherInits.extraDefiniteNulls[i]) & ~otherInits.extraDefiniteNonNulls[i]; - extraDefiniteNonNulls[i] = (extraDefiniteNonNulls[i] | otherInits.extraDefiniteNonNulls[i]) & ~otherInits.extraDefiniteNulls[i]; + int length, otherLength; + if ((length = this.extra[0].length) < + (otherLength = otherInits.extra[0].length)) { + if (coverageTestFlag && coverageTestId == 3) { + throw new AssertionFailedException("COVERAGE 3"); //$NON-NLS-1$ } - for (; i < otherLength; i++) { - extraPotentialInits[i] = otherInits.extraPotentialInits[i]; + // current storage is shorter -> grow current + for (int j = 0; j < extraLength; j++) { + System.arraycopy(this.extra[j], 0, + (this.extra[j] = new long[otherLength]), 0, length); } + mergeLimit = length; + copyLimit = otherLength; } else { + if (coverageTestFlag && coverageTestId == 4) { + throw new AssertionFailedException("COVERAGE 4"); //$NON-NLS-1$ + } // current storage is longer - for (; i < otherLength; i++) { - extraDefiniteInits[i] |= otherInits.extraDefiniteInits[i]; - extraPotentialInits[i] |= otherInits.extraPotentialInits[i]; - extraDefiniteNulls[i] = (extraDefiniteNulls[i] | otherInits.extraDefiniteNulls[i]) & ~otherInits.extraDefiniteNonNulls[i]; - extraDefiniteNonNulls[i] = (extraDefiniteNonNulls[i] | otherInits.extraDefiniteNonNulls[i]) & ~otherInits.extraDefiniteNulls[i]; - } - for (; i < length; i++) { - extraDefiniteInits[i] = 0; - extraDefiniteNulls[i] = 0; - extraDefiniteNonNulls[i] = 0; - } - } - } else { - // no extra storage on otherInits - } - } else - if (otherInits.extraDefiniteInits != null) { - // no storage here, but other has extra storage. - int otherLength; - System.arraycopy(otherInits.extraDefiniteInits, 0, (extraDefiniteInits = new long[otherLength = otherInits.extraDefiniteInits.length]), 0, otherLength); - System.arraycopy(otherInits.extraPotentialInits, 0, (extraPotentialInits = new long[otherLength]), 0, otherLength); - System.arraycopy(otherInits.extraDefiniteNulls, 0, (extraDefiniteNulls = new long[otherLength]), 0, otherLength); - System.arraycopy(otherInits.extraDefiniteNonNulls, 0, (extraDefiniteNonNulls = new long[otherLength]), 0, otherLength); + mergeLimit = otherLength; + } + } + } + else if (otherInits.extra != null) { + // no storage here, but other has extra storage. + // shortcut regular copy because array copy is better + int otherLength; + this.extra = new long[extraLength][]; + System.arraycopy(otherInits.extra[0], 0, + (this.extra[0] = new long[otherLength = + otherInits.extra[0].length]), 0, otherLength); + System.arraycopy(otherInits.extra[1], 0, + (this.extra[1] = new long[otherLength]), 0, otherLength); + if (considerNulls) { + for (int j = 2; j < extraLength; j++) { + System.arraycopy(otherInits.extra[j], 0, + (this.extra[j] = new long[otherLength]), 0, otherLength); + } + if (coverageTestFlag && coverageTestId == 5) { + this.extra[5][otherLength - 1] = ~0; + } + } + else { + for (int j = 2; j < extraLength; j++) { + this.extra[j] = new long[otherLength]; + } + if (coverageTestFlag && coverageTestId == 6) { + this.extra[5][otherLength - 1] = ~0; + } } - return this; - } - - // unions of both sets of initialization - used for try/finally - public FlowInfo addPotentialInitializationsFrom(FlowInfo inits) { - - if (this == DEAD_END){ - return this; } - - UnconditionalFlowInfo otherInits = inits.unconditionalInits(); - if (otherInits == DEAD_END){ - return this; - } - // union of potentially set ones - this.potentialInits |= otherInits.potentialInits; - // also merge null check information (affected by potential inits) - this.definiteNulls &= otherInits.definiteNulls; - this.definiteNonNulls &= otherInits.definiteNonNulls; - - // treating extra storage - if (this.extraDefiniteInits != null) { - if (otherInits.extraDefiniteInits != null) { - // both sides have extra storage - int i = 0, length, otherLength; - if ((length = this.extraDefiniteInits.length) < (otherLength = otherInits.extraDefiniteInits.length)) { - // current storage is shorter -> grow current (could maybe reuse otherInits extra storage?) - System.arraycopy(this.extraDefiniteInits, 0, (this.extraDefiniteInits = new long[otherLength]), 0, length); - System.arraycopy(this.extraPotentialInits, 0, (this.extraPotentialInits = new long[otherLength]), 0, length); - System.arraycopy(this.extraDefiniteNulls, 0, (this.extraDefiniteNulls = new long[otherLength]), 0, length); - System.arraycopy(this.extraDefiniteNonNulls, 0, (this.extraDefiniteNonNulls = new long[otherLength]), 0, length); - while (i < length) { - this.extraPotentialInits[i] |= otherInits.extraPotentialInits[i]; - this.extraDefiniteNulls[i] &= otherInits.extraDefiniteNulls[i]; - this.extraDefiniteNonNulls[i] &= otherInits.extraDefiniteNonNulls[i++]; - } - while (i < otherLength) { - this.extraPotentialInits[i] = otherInits.extraPotentialInits[i]; - this.extraDefiniteNulls[i] &= otherInits.extraDefiniteNulls[i]; - this.extraDefiniteNonNulls[i] &= otherInits.extraDefiniteNonNulls[i++]; + int i = 0; + for (; i < mergeLimit; i++) { + this.extra[0][i] |= otherInits.extra[0][i]; + this.extra[1][i] |= otherInits.extra[1][i]; + if (considerNulls) { // could consider pushing the test outside the loop + if (this.extra[2][i] == 0 && + this.extra[3][i] == 0 && + this.extra[4][i] == 0 && + this.extra[5][i] == 0) { + for (int j = 2; j < extraLength; j++) { + this.extra[j][i] = otherInits.extra[j][i]; } - } else { - // current storage is longer - while (i < otherLength) { - this.extraPotentialInits[i] |= otherInits.extraPotentialInits[i]; - this.extraDefiniteNulls[i] &= otherInits.extraDefiniteNulls[i]; - this.extraDefiniteNonNulls[i] &= otherInits.extraDefiniteNonNulls[i++]; + if (coverageTestFlag && coverageTestId == 7) { + this.extra[5][i] = ~0; } } + else { + this.extra[2][i] = + (b1 = otherInits.extra[2][i]) | + (a1 = this.extra[2][i]) & + ((nb2 = ~(b2 = otherInits.extra[3][i])) & + (nb3 = ~(b3 = otherInits.extra[4][i])) & + ((nb4 = ~(b4 = otherInits.extra[5][i])) | + ((a2 = this.extra[3][i]) ^ + (a4 = this.extra[5][i]))) | + nb4 & (na2 = ~a2) & (na4 = ~a4)); + this.extra[3][i] = + b1 & b2 | + ~b1 & (((na1 = ~a1) | a4) & b2 | + a2 & (b2 | + a1 & (na4 = ~a4) & nb2 & nb3 | + (~(a3 = this.extra[4][i]) & nb3 | na1 & na4) & nb4)); + this.extra[4][i] = + nb2 & b3 | + ~b1 & ((a1 & na2 & na4 | na1 & a3) & (nb2 | nb4) | + a1 & na2 & a3 & nb2 | + (a1 | a2 | na4) & b3); + this.extra[5][i] = + b4 | + a4 & (nb2 & nb3 | ~(b1 ^ b2)); + if (coverageTestFlag && coverageTestId == 8) { + this.extra[5][i] = ~0; + } + } } - } else - if (otherInits.extraDefiniteInits != null) { - // no storage here, but other has extra storage. - int otherLength; - this.extraDefiniteInits = new long[otherLength = otherInits.extraDefiniteInits.length]; - System.arraycopy(otherInits.extraPotentialInits, 0, (this.extraPotentialInits = new long[otherLength]), 0, otherLength); - this.extraDefiniteNulls = new long[otherLength]; - this.extraDefiniteNonNulls = new long[otherLength]; + } + for (; i < copyLimit; i++) { + this.extra[0][i] = otherInits.extra[0][i]; + this.extra[1][i] = otherInits.extra[1][i]; + if (considerNulls) { + for (int j = 2; j < extraLength; j++) { + this.extra[j][i] = otherInits.extra[j][i]; + } + if (coverageTestFlag && coverageTestId == 9) { + this.extra[5][i] = ~0; + } } - return this; - } - - /** - * Answers a copy of the current instance - */ - public FlowInfo copy() { - - // do not clone the DeadEnd - if (this == DEAD_END) - return this; - - // look for an unused preallocated object - UnconditionalFlowInfo copy = new UnconditionalFlowInfo(); - - // copy slots - copy.definiteInits = this.definiteInits; - copy.potentialInits = this.potentialInits; - copy.definiteNulls = this.definiteNulls; - copy.definiteNonNulls = this.definiteNonNulls; - copy.reachMode = this.reachMode; - copy.maxFieldCount = this.maxFieldCount; - - if (this.extraDefiniteInits != null) { - int length; - System.arraycopy(this.extraDefiniteInits, 0, (copy.extraDefiniteInits = new long[length = extraDefiniteInits.length]), 0, length); - System.arraycopy(this.extraPotentialInits, 0, (copy.extraPotentialInits = new long[length]), 0, length); - System.arraycopy(this.extraDefiniteNulls, 0, (copy.extraDefiniteNulls = new long[length]), 0, length); - System.arraycopy(this.extraDefiniteNonNulls, 0, (copy.extraDefiniteNonNulls = new long[length]), 0, length); } - return copy; } - - public UnconditionalFlowInfo discardFieldInitializations(){ - - int limit = this.maxFieldCount; - - if (limit < BitCacheSize) { - long mask = (1L << limit)-1; - this.definiteInits &= ~mask; - this.potentialInits &= ~mask; - this.definiteNulls &= ~mask; - this.definiteNonNulls &= ~mask; - return this; - } + return this; +} - this.definiteInits = 0; - this.potentialInits = 0; - this.definiteNulls = 0; - this.definiteNonNulls = 0; - - // use extra vector - if (extraDefiniteInits == null) { - return this; // if vector not yet allocated, then not initialized - } - int vectorIndex, length = this.extraDefiniteInits.length; - if ((vectorIndex = (limit / BitCacheSize) - 1) >= length) { - return this; // not enough room yet - } - for (int i = 0; i < vectorIndex; i++) { - this.extraDefiniteInits[i] = 0L; - this.extraPotentialInits[i] = 0L; - this.extraDefiniteNulls[i] = 0L; - this.extraDefiniteNonNulls[i] = 0L; - } - long mask = (1L << (limit % BitCacheSize))-1; - this.extraDefiniteInits[vectorIndex] &= ~mask; - this.extraPotentialInits[vectorIndex] &= ~mask; - this.extraDefiniteNulls[vectorIndex] &= ~mask; - this.extraDefiniteNonNulls[vectorIndex] &= ~mask; +public FlowInfo addPotentialInitializationsFrom(FlowInfo inits) { + if (this == DEAD_END){ + return this; + } + if (inits == DEAD_END){ return this; } + UnconditionalFlowInfo otherInits = inits.unconditionalInits(); + // union of potentially set ones + this.potentialInits |= otherInits.potentialInits; + // treating extra storage + if (this.extra != null) { + if (otherInits.extra != null) { + // both sides have extra storage + int i = 0, length, otherLength; + if ((length = this.extra[0].length) < (otherLength = otherInits.extra[0].length)) { + // current storage is shorter -> grow current + for (int j = 0; j < extraLength; j++) { + System.arraycopy(this.extra[j], 0, + (this.extra[j] = new long[otherLength]), 0, length); + } + for (; i < length; i++) { + this.extra[1][i] |= otherInits.extra[1][i]; + } + for (; i < otherLength; i++) { + this.extra[1][i] = otherInits.extra[1][i]; + } + } + else { + // current storage is longer + for (; i < otherLength; i++) { + this.extra[1][i] |= otherInits.extra[1][i]; + } + } + } + } + else if (otherInits.extra != null) { + // no storage here, but other has extra storage. + int otherLength = otherInits.extra[0].length; + this.extra = new long[extraLength][]; + for (int j = 0; j < extraLength; j++) { + this.extra[j] = new long[otherLength]; + } + System.arraycopy(otherInits.extra[1], 0, this.extra[1], 0, + otherLength); + } + this.addPotentialNullInfoFrom(otherInits); + // REVIEW inline? + return this; +} - public UnconditionalFlowInfo discardNonFieldInitializations(){ - - int limit = this.maxFieldCount; - - if (limit < BitCacheSize) { - long mask = (1L << limit)-1; - this.definiteInits &= mask; - this.potentialInits &= mask; - this.definiteNulls &= mask; - this.definiteNonNulls &= mask; - return this; - } - // use extra vector - if (extraDefiniteInits == null) { - return this; // if vector not yet allocated, then not initialized +/** + * Compose other inits over this flow info, then return this. The operation + * semantics are to wave into this flow info the consequences upon null + * information of a possible path into the operations that resulted into + * otherInits. The fact that this path may be left unexecuted under peculiar + * conditions results into less specific results than + * {@link #addInitializationsFrom(FlowInfo) addInitializationsFrom}; moreover, + * only the null information is affected. + * @param otherInits other null inits to compose over this + * @return this, modified according to otherInits information + */ +public UnconditionalFlowInfo addPotentialNullInfoFrom( + UnconditionalFlowInfo otherInits) { + if ((this.tagBits & UNREACHABLE) != 0 || + (otherInits.tagBits & UNREACHABLE) != 0 || + (otherInits.tagBits & NULL_FLAG_MASK) == 0) { + return this; + } + // if we get here, otherInits has some null info + boolean thisHasNulls = (this.tagBits & NULL_FLAG_MASK) != 0; + if (thisHasNulls) { + long a1, a2, na2, a3, na3, a4, na4, b1, nb1, b2, nb2, b3, nb3, b4, nb4; + this.nullAssignmentStatusBit1 = + ((a1 = this.nullAssignmentStatusBit1) & + (na4 = ~(a4 = this.nullAssignmentValueBit2)) & + ((na3 = ~(a3 = this.nullAssignmentValueBit1)) | + (a2 = this.nullAssignmentStatusBit2)) | + a2 & na3 & a4) & + (nb3 = ~(b3 = otherInits.nullAssignmentValueBit1)) & + ((b2 = otherInits.nullAssignmentStatusBit2) | + (nb4 = ~(b4 = otherInits.nullAssignmentValueBit2))) | + a1 & (na2 = ~a2) & + (a4 & ((nb1 = ~(b1 = otherInits.nullAssignmentStatusBit1)) & + nb3 | b1 & + (b4 | b2)) | + na4 & (nb1 & (((nb2 = ~b2) & nb4 | b2) & nb3 | b3 & nb4) | + b1 & nb4 & (nb2 | nb3))); + this.nullAssignmentStatusBit2 = + a2 & (~a1 & na4 & nb4 | + a1 & na3 & nb3 & (nb1 & (nb2 & nb4 | b2) | + b1 & (nb4 |b2 & b4))); + this.nullAssignmentValueBit1 = + a3 | + b1 & nb2 & nb4 | + nb1 & b3 | + a1 & na2 & (b1 & b3 | nb1 & b4); +// b1 & (~b2 & ~b4 | a1 & ~a2 & b3) | +// ~b1 & (b3 | a1 & ~a2 & b4); -- same op nb + this.nullAssignmentValueBit2 = + a4 & (na2 | a2 & na3) | + b4 & (nb2 | b2 & nb3); + if (coverageTestFlag && coverageTestId == 15) { + this.nullAssignmentValueBit2 = ~0; + } + // extra storage management + if (otherInits.extra != null) { + int mergeLimit = 0, copyLimit = 0; + int otherLength = otherInits.extra[0].length; + if (this.extra == null) { + this.extra = new long[extraLength][]; + for (int j = 0; j < extraLength; j++) { + this.extra[j] = new long[otherLength]; + } + copyLimit = otherLength; + if (coverageTestFlag && coverageTestId == 16) { + this.extra[2][0] = ~0; thisHasNulls = true; + } + } + else { + mergeLimit = otherLength; + if (mergeLimit > this.extra[0].length) { + copyLimit = mergeLimit; + mergeLimit = this.extra[0].length; + for (int j = 0; j < extraLength; j++) { + System.arraycopy(this.extra[j], 0, + this.extra[j] = new long[otherLength], 0, + mergeLimit); + } + } + int i; + for (i = 0; i < mergeLimit; i++) { + this.extra[2][i] = + ((a1 = this.extra[2][i]) & + (na4 = ~(a4 = this.extra[5][i])) & + ((na3 = ~(a3 = this.extra[4][i])) | + (a2 = this.extra[3][i])) | + a2 & na3 & a4) & + (nb3 = ~(b3 = otherInits.extra[4][i])) & + ((b2 = otherInits.extra[3][i]) | + (nb4 = ~(b4 = otherInits.extra[5][i]))) | + a1 & (na2 = ~a2) & + (a4 & ((nb1 = ~(b1 = otherInits.extra[2][i])) & + nb3 | b1 & + (b4 | b2)) | + na4 & (nb1 & (((nb2 = ~b2) & nb4 | b2) & nb3 | b3 & nb4) | + b1 & nb4 & (nb2 | nb3))); + this.extra[3][i] = + a2 & (~a1 & na4 & nb4 | + a1 & na3 & nb3 & (nb1 & (nb2 & nb4 | b2) | + b1 & (nb4 |b2 & b4))); + this.extra[4][i] = + a3 | + b1 & nb2 & nb4 | + nb1 & b3 | + a1 & na2 & (b1 & b3 | nb1 & b4); + this.extra[5][i] = + a4 & (na2 | a2 & na3) | + b4 & (nb2 | b2 & nb3); + if (coverageTestFlag && coverageTestId == 17) { + this.nullAssignmentValueBit2 = ~0; + } + } + for (; i < copyLimit; i++) { + if (otherInits.extra[4][i] != 0 || + otherInits.extra[5][i] != 0) { + this.tagBits |= NULL_FLAG_MASK; + this.extra[4][i] = + otherInits.extra[4][i] & + ~(otherInits.extra[2][i] & + ~otherInits.extra[3][i] & + otherInits.extra[5][i]); + this.extra[5][i] = + otherInits.extra[5][i]; + if (coverageTestFlag && coverageTestId == 18) { + this.extra[5][i] = ~0; + } + } + } + } } - int vectorIndex, length = this.extraDefiniteInits.length; - if ((vectorIndex = (limit / BitCacheSize) - 1) >= length) { - return this; // not enough room yet + } + else { + if (otherInits.nullAssignmentValueBit1 != 0 || + otherInits.nullAssignmentValueBit2 != 0) { + // add potential values + this.nullAssignmentValueBit1 = + otherInits.nullAssignmentValueBit1 & + ~(otherInits.nullAssignmentStatusBit1 & + ~otherInits.nullAssignmentStatusBit2 & + otherInits.nullAssignmentValueBit2); // exclude assigned unknown + this.nullAssignmentValueBit2 = + otherInits.nullAssignmentValueBit2; + thisHasNulls = + this.nullAssignmentValueBit1 != 0 || + this.nullAssignmentValueBit2 != 0; + if (coverageTestFlag && coverageTestId == 10) { + this.nullAssignmentValueBit2 = ~0; + } } - long mask = (1L << (limit % BitCacheSize))-1; - this.extraDefiniteInits[vectorIndex] &= mask; - this.extraPotentialInits[vectorIndex] &= mask; - this.extraDefiniteNulls[vectorIndex] &= mask; - this.extraDefiniteNonNulls[vectorIndex] &= mask; - for (int i = vectorIndex+1; i < length; i++) { - this.extraDefiniteInits[i] = 0L; - this.extraPotentialInits[i] = 0L; - this.extraDefiniteNulls[i] = 0L; - this.extraDefiniteNonNulls[i] = 0L; + // extra storage management + if (otherInits.extra != null) { + int mergeLimit = 0, copyLimit = 0; + int otherLength = otherInits.extra[0].length; + if (this.extra == null) { + copyLimit = otherLength; + // cannot happen when called from addPotentialInitializationsFrom + this.extra = new long[extraLength][]; + for (int j = 0; j < extraLength; j++) { + this.extra[j] = new long[otherLength]; + } + if (coverageTestFlag && coverageTestId == 11) { + this.extra[5][0] = ~0; this.tagBits |= NULL_FLAG_MASK; + } + } + else { + mergeLimit = otherLength; + if (mergeLimit > this.extra[0].length) { + copyLimit = mergeLimit; + mergeLimit = this.extra[0].length; + System.arraycopy(this.extra[0], 0, + this.extra[0] = new long[otherLength], 0, + mergeLimit); + System.arraycopy(this.extra[1], 0, + this.extra[1] = new long[otherLength], 0, + mergeLimit); + for (int j = 2; j < extraLength; j++) { + this.extra[j] = new long[otherLength]; + } + if (coverageTestFlag && coverageTestId == 12) { + throw new AssertionFailedException("COVERAGE 12"); //$NON-NLS-1$ + } + } + } + int i; + for (i = 0; i < mergeLimit; i++) { + if (otherInits.extra[4][i] != 0 || + otherInits.extra[5][i] != 0) { + this.extra[4][i] |= + otherInits.extra[4][i] & + ~(otherInits.extra[2][i] & + ~otherInits.extra[3][i] & + otherInits.extra[5][i]); + this.extra[5][i] |= + otherInits.extra[5][i]; + thisHasNulls = thisHasNulls || + this.extra[4][i] != 0 || + this.extra[5][i] != 0; + if (coverageTestFlag && coverageTestId == 13) { + this.extra[5][i] = ~0; + } + } + } + for (; i < copyLimit; i++) { + if (otherInits.extra[4][i] != 0 || + otherInits.extra[5][i] != 0) { + this.extra[4][i] = + otherInits.extra[4][i] & + ~(otherInits.extra[2][i] & + ~otherInits.extra[3][i] & + otherInits.extra[5][i]); + this.extra[5][i] = + otherInits.extra[5][i]; + thisHasNulls = thisHasNulls || + this.extra[4][i] != 0 || + this.extra[5][i] != 0; + if (coverageTestFlag && coverageTestId == 14) { + this.extra[5][i] = ~0; + } + } + } } - return this; } - - public UnconditionalFlowInfo discardNullRelatedInitializations(){ - - this.definiteNulls = 0; - this.definiteNonNulls = 0; - - int length = this.extraDefiniteInits == null ? 0 : this.extraDefiniteInits.length; - for (int i = 0; i < length; i++) { - this.extraDefiniteNulls[i] = 0L; - this.extraDefiniteNonNulls[i] = 0L; - } - return this; + if (thisHasNulls) { + this.tagBits |= NULL_FLAG_MASK; } - - public FlowInfo initsWhenFalse() { - - return this; + else { + this.tagBits &= NULL_FLAG_MASK; } - - public FlowInfo initsWhenTrue() { - + return this; +} + +public FlowInfo copy() { + // do not clone the DeadEnd + if (this == DEAD_END) { return this; } - - /** - * Check status of definite assignment at a given position. - * It deals with the dual representation of the InitializationInfo2: - * bits for the first 64 entries, then an array of booleans. - */ - final private boolean isDefinitelyAssigned(int position) { - - // Dependant of CodeStream.isDefinitelyAssigned(..) - // id is zero-based - if (position < BitCacheSize) { - return (definiteInits & (1L << position)) != 0; // use bits + UnconditionalFlowInfo copy = new UnconditionalFlowInfo(); + // copy slots + copy.definiteInits = this.definiteInits; + copy.potentialInits = this.potentialInits; + boolean hasNullInfo = (this.tagBits & NULL_FLAG_MASK) != 0; + if (hasNullInfo) { + copy.nullAssignmentStatusBit1 = this.nullAssignmentStatusBit1; + copy.nullAssignmentStatusBit2 = this.nullAssignmentStatusBit2; + copy.nullAssignmentValueBit1 = this.nullAssignmentValueBit1; + copy.nullAssignmentValueBit2 = this.nullAssignmentValueBit2; + } + copy.tagBits = this.tagBits; + copy.maxFieldCount = this.maxFieldCount; + if (this.extra != null) { + int length; + copy.extra = new long[extraLength][]; + System.arraycopy(this.extra[0], 0, + (copy.extra[0] = new long[length = this.extra[0].length]), 0, + length); + System.arraycopy(this.extra[1], 0, + (copy.extra[1] = new long[length]), 0, length); + if (hasNullInfo) { + for (int j = 2; j < extraLength; j++) { + System.arraycopy(this.extra[j], 0, + (copy.extra[j] = new long[length]), 0, length); + } } - // use extra vector - if (extraDefiniteInits == null) - return false; // if vector not yet allocated, then not initialized - int vectorIndex; - if ((vectorIndex = (position / BitCacheSize) - 1) >= extraDefiniteInits.length) - return false; // if not enough room in vector, then not initialized - return ((extraDefiniteInits[vectorIndex]) & (1L << (position % BitCacheSize))) != 0; - } - - /** - * Check status of definite non-null assignment at a given position. - * It deals with the dual representation of the InitializationInfo2: - * bits for the first 64 entries, then an array of booleans. - */ - final private boolean isDefinitelyNonNull(int position) { - - // Dependant of CodeStream.isDefinitelyAssigned(..) - // id is zero-based - if (position < BitCacheSize) { - return (definiteNonNulls & (1L << position)) != 0; // use bits + else { + for (int j = 2; j < extraLength; j++) { + copy.extra[j] = new long[length]; + } } - // use extra vector - if (extraDefiniteNonNulls == null) - return false; // if vector not yet allocated, then not initialized - int vectorIndex; - if ((vectorIndex = (position / BitCacheSize) - 1) >= extraDefiniteNonNulls.length) - return false; // if not enough room in vector, then not initialized - return ((extraDefiniteNonNulls[vectorIndex]) & (1L << (position % BitCacheSize))) != 0; - } - - /** - * Check status of definite null assignment at a given position. - * It deals with the dual representation of the InitializationInfo2: - * bits for the first 64 entries, then an array of booleans. - */ - final private boolean isDefinitelyNull(int position) { - - // Dependant of CodeStream.isDefinitelyAssigned(..) - // id is zero-based - if (position < BitCacheSize) { - return (definiteNulls & (1L << position)) != 0; // use bits + } + return copy; +} + +/** + * Remove local variables information from this flow info and return this. + * @return this, deprived from any local variable information + */ +public UnconditionalFlowInfo discardNonFieldInitializations() { + int limit = this.maxFieldCount; + if (limit < BitCacheSize) { + long mask = (1L << limit)-1; + this.definiteInits &= mask; + this.potentialInits &= mask; + this.nullAssignmentStatusBit1 &= mask; + this.nullAssignmentStatusBit2 &= mask; + this.nullAssignmentValueBit1 &= mask; + this.nullAssignmentValueBit2 &= mask; + } + // use extra vector + if (this.extra == null) { + return this; // if vector not yet allocated, then not initialized + } + int vectorIndex, length = this.extra[0].length; + if ((vectorIndex = (limit / BitCacheSize) - 1) >= length) { + return this; // not enough room yet + } + if (vectorIndex >= 0) { + // else we only have complete non field array items left + long mask = (1L << (limit % BitCacheSize))-1; + for (int j = 0; j < extraLength; j++) { + this.extra[j][vectorIndex] &= mask; } - // use extra vector - if (extraDefiniteNulls == null) - return false; // if vector not yet allocated, then not initialized - int vectorIndex; - if ((vectorIndex = (position / BitCacheSize) - 1) >= extraDefiniteNulls.length) - return false; // if not enough room in vector, then not initialized - return ((extraDefiniteNulls[vectorIndex]) & (1L << (position % BitCacheSize))) != 0; - } - - /** - * Check status of definite assignment for a field. - */ - final public boolean isDefinitelyAssigned(FieldBinding field) { - - // Dependant of CodeStream.isDefinitelyAssigned(..) - // We do not want to complain in unreachable code - if ((this.reachMode & UNREACHABLE) != 0) - return true; - return isDefinitelyAssigned(field.id); } - - /** - * Check status of definite assignment for a local. - */ - final public boolean isDefinitelyAssigned(LocalVariableBinding local) { - - // Dependant of CodeStream.isDefinitelyAssigned(..) - // We do not want to complain in unreachable code - if ((this.reachMode & UNREACHABLE) != 0) - return true; - - // final constants are inlined, and thus considered as always initialized - if (local.constant() != Constant.NotAConstant) { - return true; + for (int i = vectorIndex + 1; i < length; i++) { + for (int j = 0; j < extraLength; j++) { + this.extra[j][i] = 0; } - return isDefinitelyAssigned(local.id + maxFieldCount); } - - /** - * Check status of definite non-null assignment for a field. - */ - final public boolean isDefinitelyNonNull(FieldBinding field) { - - // Dependant of CodeStream.isDefinitelyAssigned(..) - // We do not want to complain in unreachable code - if ((this.reachMode & UNREACHABLE) != 0) - return false; - return isDefinitelyNonNull(field.id); + return this; +} + +public FlowInfo initsWhenFalse() { + return this; +} + +public FlowInfo initsWhenTrue() { + return this; +} + +/** + * Check status of definite assignment at a given position. + * It deals with the dual representation of the InitializationInfo2: + * bits for the first 64 entries, then an array of booleans. + */ +final private boolean isDefinitelyAssigned(int position) { + if (position < BitCacheSize) { + // use bits + return (this.definiteInits & (1L << position)) != 0; + } + // use extra vector + if (this.extra == null) + return false; // if vector not yet allocated, then not initialized + int vectorIndex; + if ((vectorIndex = (position / BitCacheSize) - 1) + >= this.extra[0].length) { + return false; // if not enough room in vector, then not initialized } - - /** - * Check status of definite non-null assignment for a local. - */ - final public boolean isDefinitelyNonNull(LocalVariableBinding local) { - - // Dependant of CodeStream.isDefinitelyAssigned(..) - // We do not want to complain in unreachable code - if ((this.reachMode & UNREACHABLE) != 0) - return false; - // final constants are inlined, and thus considered as always initialized - if (local.constant() != Constant.NotAConstant) { - return true; - } - return isDefinitelyNonNull(local.id + maxFieldCount); - } - - /** - * Check status of definite null assignment for a field. - */ - final public boolean isDefinitelyNull(FieldBinding field) { - - // Dependant of CodeStream.isDefinitelyAssigned(..) - // We do not want to complain in unreachable code - if ((this.reachMode & UNREACHABLE) != 0) - return false; - return isDefinitelyNull(field.id); + return ((this.extra[0][vectorIndex]) & + (1L << (position % BitCacheSize))) != 0; +} + +final public boolean isDefinitelyAssigned(FieldBinding field) { + // Mirrored in CodeStream.isDefinitelyAssigned(..) + // do not want to complain in unreachable code + if ((this.tagBits & UNREACHABLE) != 0) { + return true; } - - /** - * Check status of definite null assignment for a local. - */ - final public boolean isDefinitelyNull(LocalVariableBinding local) { - - // Dependant of CodeStream.isDefinitelyAssigned(..) - // We do not want to complain in unreachable code - if ((this.reachMode & UNREACHABLE) != 0) - return false; - return isDefinitelyNull(local.id + maxFieldCount); + return isDefinitelyAssigned(field.id); +} + +final public boolean isDefinitelyAssigned(LocalVariableBinding local) { + // do not want to complain in unreachable code + if ((this.tagBits & UNREACHABLE) != 0) { + return true; + } + // final constants are inlined, and thus considered as always initialized + if (local.constant() != Constant.NotAConstant) { + return true; } + return isDefinitelyAssigned(local.id + this.maxFieldCount); +} + +final public boolean isDefinitelyNonNull(LocalVariableBinding local) { + // do not want to complain in unreachable code + if ((this.tagBits & UNREACHABLE) != 0 || + (this.tagBits & NULL_FLAG_MASK) == 0) { + return false; + } + if ((local.type.tagBits & TagBits.IsBaseType) != 0 || + local.constant() != Constant.NotAConstant) { + // REVIEW only true if local is of a non object type, hence + // REVIEW second test is useless? + return true; + } + int position = local.id + this.maxFieldCount; + long mask; + if (position < BitCacheSize) { // use bits + return + (this.nullAssignmentStatusBit2 & + (mask = 1L << position)) != 0 ? + (this.nullAssignmentStatusBit1 & mask) != 0 : + (this.nullAssignmentStatusBit1 & + this.nullAssignmentValueBit2 & mask) != 0 && + (this.nullAssignmentValueBit1 & mask) == 0; + } + // use extra vector + if (this.extra == null) { + return false; // if vector not yet allocated, then not initialized + } + int vectorIndex; + if ((vectorIndex = (position / BitCacheSize) - 1) + >= this.extra[0].length) { + return false; // if not enough room in vector, then not initialized + } + return + (this.extra[3][vectorIndex] & + (mask = 1L << (position % BitCacheSize))) != 0 ? + (this.extra[2][vectorIndex] & mask) != 0 : + (this.extra[2][vectorIndex] & + this.extra[5][vectorIndex] & mask) != 0 && + (this.extra[4][vectorIndex] & mask) == 0; +} - public boolean isReachable() { - - return this.reachMode == REACHABLE; +final public boolean isDefinitelyNull(LocalVariableBinding local) { + // do not want to complain in unreachable code + if ((this.tagBits & UNREACHABLE) != 0 || + (this.tagBits & NULL_FLAG_MASK) == 0 || + (local.type.tagBits & TagBits.IsBaseType) != 0) { + return false; + } + int position = local.id + this.maxFieldCount; + long mask; + if (position < BitCacheSize) { // use bits + return + (this.nullAssignmentStatusBit2 & (mask = 1L << position)) != 0 ? + (this.nullAssignmentStatusBit1 & mask) == 0 : + (this.nullAssignmentStatusBit1 & + this.nullAssignmentValueBit1 & mask) != 0 && + (this.nullAssignmentValueBit2 & mask) == 0; + } + // use extra vector + if (this.extra == null) { + return false; // if vector not yet allocated, then not initialized + } + int vectorIndex; + if ((vectorIndex = (position / BitCacheSize) - 1) >= + this.extra[0].length) { + return false; // if not enough room in vector, then not initialized + } + return + (this.extra[3][vectorIndex] & + (mask = 1L << (position % BitCacheSize))) != 0 ? + (this.extra[2][vectorIndex] & mask) == 0 : + (this.extra[2][vectorIndex] & + this.extra[4][vectorIndex] & mask) != 0 && + (this.extra[5][vectorIndex] & mask) == 0; +} + +final public boolean isDefinitelyUnknown(LocalVariableBinding local) { + // do not want to complain in unreachable code + if ((this.tagBits & UNREACHABLE) != 0 || + (this.tagBits & NULL_FLAG_MASK) == 0) { + return false; + } + int position = local.id + this.maxFieldCount; + long mask; + if (position < BitCacheSize) { // use bits + return + (this.nullAssignmentStatusBit2 & (mask = 1L << position)) != 0 ? + false : + (this.nullAssignmentStatusBit1 & + this.nullAssignmentValueBit1 & + this.nullAssignmentValueBit2 & mask) != 0; + } + // use extra vector + if (this.extra == null) { + return false; // if vector not yet allocated, then not initialized + } + int vectorIndex; + if ((vectorIndex = (position / BitCacheSize) - 1) >= + this.extra[0].length) { + return false; // if not enough room in vector, then not initialized + } + return + (this.extra[3][vectorIndex] & + (mask = 1L << (position % BitCacheSize))) != 0 ? + false : + (this.extra[2][vectorIndex] & + this.extra[4][vectorIndex] & + this.extra[5][vectorIndex] & + mask) != 0; +} + +/** + * Check status of potential assignment at a given position. + */ +final private boolean isPotentiallyAssigned(int position) { + // id is zero-based + if (position < BitCacheSize) { + // use bits + return (this.potentialInits & (1L << position)) != 0; + } + // use extra vector + if (this.extra == null) { + return false; // if vector not yet allocated, then not initialized + } + int vectorIndex; + if ((vectorIndex = (position / BitCacheSize) - 1) + >= this.extra[0].length) { + return false; // if not enough room in vector, then not initialized } - - /** - * Check status of potential assignment at a given position. - * It deals with the dual representation of the InitializationInfo3: - * bits for the first 64 entries, then an array of booleans. - */ - final private boolean isPotentiallyAssigned(int position) { - - // id is zero-based - if (position < BitCacheSize) { + return ((this.extra[1][vectorIndex]) & + (1L << (position % BitCacheSize))) != 0; +} + +/** + * REVIEW wrong comment? + * Check status of definite assignment for a field. + */ +final public boolean isPotentiallyAssigned(FieldBinding field) { + return isPotentiallyAssigned(field.id); +} + +/** + * Check status of potential assignment for a local. + */ +final public boolean isPotentiallyAssigned(LocalVariableBinding local) { + // final constants are inlined, and thus considered as always initialized + if (local.constant() != Constant.NotAConstant) { + return true; + } + return isPotentiallyAssigned(local.id + this.maxFieldCount); +} + +// REVIEW should rename this -- what we do is that we ask if there is a reasonable +// REVIEW expectation that the variable be null at this point; which means that +// REVIEW we add the protected null case, to augment diagnostics, but we do not +// REVIEW really check that someone deliberately has assigned to null on a given +// REVIEW path +final public boolean isPotentiallyNull(LocalVariableBinding local) { + if ((this.tagBits & NULL_FLAG_MASK) == 0 || + (local.type.tagBits & TagBits.IsBaseType) != 0) { + return false; + } + int position; + long mask; + if ((position = local.id + this.maxFieldCount) < BitCacheSize) { + // use bits + return + (this.nullAssignmentStatusBit2 & (mask = 1L << position)) != 0 ? + (this.nullAssignmentStatusBit1 & mask) == 0 : // protected null + (this.nullAssignmentValueBit1 & mask) != 0 && // null bit set and + ((this.nullAssignmentStatusBit1 & mask) == 0 || // (potential or + (this.nullAssignmentValueBit2 & mask) == 0); + // assigned, but not unknown) + } + // use extra vector + if (this.extra == null) { + return false; // if vector not yet allocated, then not initialized + } + int vectorIndex; + if ((vectorIndex = (position / BitCacheSize) - 1) >= + this.extra[0].length) { + return false; // if not enough room in vector, then not initialized + } + return + (this.extra[3][vectorIndex] & + (mask = 1L << (position % BitCacheSize))) != 0 ? + (this.extra[2][vectorIndex] & mask) == 0 : + (this.extra[4][vectorIndex] & mask) != 0 && + ((this.extra[2][vectorIndex] & mask) == 0 || + (this.extra[5][vectorIndex] & mask) == 0); +} + +final public boolean isPotentiallyUnknown(LocalVariableBinding local) { + // do not want to complain in unreachable code + if ((this.tagBits & UNREACHABLE) != 0 || + (this.tagBits & NULL_FLAG_MASK) == 0) { + return false; + } + int position = local.id + this.maxFieldCount; + long mask; + if (position < BitCacheSize) { // use bits + return + (this.nullAssignmentStatusBit2 & (mask = 1L << position)) != 0 ? + false : + ((this.nullAssignmentStatusBit1 & + this.nullAssignmentValueBit1 | + ~this.nullAssignmentStatusBit1 & + ~this.nullAssignmentValueBit1) & + this.nullAssignmentValueBit2 & mask) != 0; + } + // use extra vector + if (this.extra == null) { + return false; // if vector not yet allocated, then not initialized + } + int vectorIndex; + if ((vectorIndex = (position / BitCacheSize) - 1) >= + this.extra[0].length) { + return false; // if not enough room in vector, then not initialized + } + return + (this.extra[3][vectorIndex] & + (mask = 1L << (position % BitCacheSize))) != 0 ? + false : + ((this.extra[2][vectorIndex] & + this.extra[4][vectorIndex] | + ~this.extra[2][vectorIndex] & + ~this.extra[4][vectorIndex]) & + this.extra[5][vectorIndex] & + mask) != 0; +} + +final public boolean isProtectedNonNull(LocalVariableBinding local) { + if ((this.tagBits & NULL_FLAG_MASK) == 0 || + (local.type.tagBits & TagBits.IsBaseType) != 0) { + return false; + } + int position; + if ((position = local.id + this.maxFieldCount) < BitCacheSize) { + // use bits + return (this.nullAssignmentStatusBit1 & + this.nullAssignmentStatusBit2 & (1L << position)) != 0; + } + // use extra vector + if (this.extra == null) { + return false; // if vector not yet allocated, then not initialized + } + int vectorIndex; + if ((vectorIndex = (position / BitCacheSize) - 1) >= + this.extra[0].length) { + return false; // if not enough room in vector, then not initialized + } + return (this.extra[4][vectorIndex] & + this.extra[5][vectorIndex] & + (1L << (position % BitCacheSize))) != 0; +} + +final public boolean isProtectedNull(LocalVariableBinding local) { + if ((this.tagBits & NULL_FLAG_MASK) == 0 || + (local.type.tagBits & TagBits.IsBaseType) != 0) { + return false; + } + int position; + if ((position = local.id + this.maxFieldCount) < BitCacheSize) { + // use bits + return (~this.nullAssignmentStatusBit1 & + this.nullAssignmentStatusBit2 & (1L << position)) != 0; + } + // use extra vector + if (this.extra == null) { + return false; // if vector not yet allocated, then not initialized + } + int vectorIndex; + if ((vectorIndex = (position / BitCacheSize) - 1) >= + this.extra[0].length) { + return false; // if not enough room in vector, then not initialized + } + return (~this.extra[4][vectorIndex] & + this.extra[5][vectorIndex] & + (1L << (position % BitCacheSize))) != 0; +} + +public void markAsComparedEqualToNonNull(LocalVariableBinding local) { + // protected from non-object locals in calling methods + if (this != DEAD_END) { + this.tagBits |= NULL_FLAG_MASK; + int position; + long mask; + // position is zero-based + if ((position = local.id + this.maxFieldCount) < BitCacheSize) { // use bits - return (potentialInits & (1L << position)) != 0; + if (((mask = 1L << position) & // leave assigned non null unchanged + this.nullAssignmentStatusBit1 & + ~this.nullAssignmentStatusBit2 & + ~this.nullAssignmentValueBit1 & + this.nullAssignmentValueBit2) == 0) { + // set protected non null + this.nullAssignmentStatusBit1 |= mask; + this.nullAssignmentStatusBit2 |= mask; + // clear potential null + this.nullAssignmentValueBit1 &= ~mask; + if (coverageTestFlag && coverageTestId == 19) { + this.nullAssignmentValueBit2 = ~0; + } + } + if (coverageTestFlag && coverageTestId == 20) { + this.nullAssignmentValueBit2 = ~0; + } + } + else { + // use extra vector + int vectorIndex = (position / BitCacheSize) - 1; + if (this.extra == null) { + int length = vectorIndex + 1; + this.extra = new long[extraLength][]; + for (int j = 0; j < extraLength; j++) { + this.extra[j] = new long[length]; + } + if (coverageTestFlag && coverageTestId == 21) { + throw new AssertionFailedException("COVERAGE 21"); //$NON-NLS-1$ + } + } + else { + int oldLength; + if (vectorIndex >= (oldLength = this.extra[0].length)) { + int newLength = vectorIndex + 1; + for (int j = 0; j < extraLength; j++) { + System.arraycopy(this.extra[j], 0, + (this.extra[j] = new long[newLength]), 0, + oldLength); + } + if (coverageTestFlag && coverageTestId == 22) { + throw new AssertionFailedException("COVERAGE 22"); //$NON-NLS-1$ + } + } + } + if (((mask = 1L << (position % BitCacheSize)) & + this.extra[2][vectorIndex] & + ~this.extra[3][vectorIndex] & + ~this.extra[4][vectorIndex] & + this.extra[5][vectorIndex]) == 0) { + this.extra[2][vectorIndex] |= mask; + this.extra[3][vectorIndex] |= mask; + this.extra[4][vectorIndex] &= ~mask; + if (coverageTestFlag && coverageTestId == 23) { + this.extra[5][vectorIndex] = ~0; + } + } } - // use extra vector - if (extraPotentialInits == null) - return false; // if vector not yet allocated, then not initialized - int vectorIndex; - if ((vectorIndex = (position / BitCacheSize) - 1) >= extraPotentialInits.length) - return false; // if not enough room in vector, then not initialized - return ((extraPotentialInits[vectorIndex]) & (1L << (position % BitCacheSize))) != 0; } - - /** - * Check status of definite assignment for a field. - */ - final public boolean isPotentiallyAssigned(FieldBinding field) { - - return isPotentiallyAssigned(field.id); - } - - /** - * Check status of potential assignment for a local. - */ - final public boolean isPotentiallyAssigned(LocalVariableBinding local) { - - // final constants are inlined, and thus considered as always initialized - if (local.constant() != Constant.NotAConstant) { - return true; +} + +// REVIEW javadoc policy? +public void markAsComparedEqualToNull(LocalVariableBinding local) { + // protected from non-object locals in calling methods + if (this != DEAD_END) { + this.tagBits |= NULL_FLAG_MASK; + int position; + long mask, unknownAssigned; + // position is zero-based + if ((position = local.id + this.maxFieldCount) < BitCacheSize) { + // use bits + mask = 1L << position; + if ((mask & // leave assigned null unchanged + this.nullAssignmentStatusBit1 & + ~this.nullAssignmentStatusBit2 & + this.nullAssignmentValueBit1 & + ~this.nullAssignmentValueBit2) == 0) { + unknownAssigned = this.nullAssignmentStatusBit1 & + ~this.nullAssignmentStatusBit2 & + this.nullAssignmentValueBit1 & + this.nullAssignmentValueBit2; + // set protected + this.nullAssignmentStatusBit2 |= mask; + this.nullAssignmentStatusBit1 &= (mask = ~mask); + // protected is null + this.nullAssignmentValueBit1 &= mask | ~unknownAssigned; + this.nullAssignmentValueBit2 &= mask; + // clear potential anything but null + // REVIEW coûts relatifs d'un assignment et d'une négation? + if (coverageTestFlag && coverageTestId == 24) { + this.nullAssignmentValueBit2 = ~0; + } + } + if (coverageTestFlag && coverageTestId == 25) { + this.nullAssignmentValueBit2 = ~0; + } + } + else { + // use extra vector + int vectorIndex = (position / BitCacheSize) - 1; + mask = 1L << (position % BitCacheSize); + if (this.extra == null) { + int length = vectorIndex + 1; + this.extra = new long[extraLength][]; + for (int j = 0; j < extraLength; j++) { + this.extra[j] = new long[length ]; + } + if (coverageTestFlag && coverageTestId == 26) { + throw new AssertionFailedException("COVERAGE 26"); //$NON-NLS-1$ + } + } + else { + int oldLength; + if (vectorIndex >= (oldLength = this.extra[0].length)) { + int newLength = vectorIndex + 1; + for (int j = 0; j < extraLength; j++) { + System.arraycopy(this.extra[j], 0, + (this.extra[j] = new long[newLength]), 0, + oldLength); + } + if (coverageTestFlag && coverageTestId == 27) { + throw new AssertionFailedException("COVERAGE 27"); //$NON-NLS-1$ + } + } + } + if ((mask & + this.extra[2][vectorIndex] & + ~this.extra[3][vectorIndex] & + this.extra[4][vectorIndex] & + ~this.extra[5][vectorIndex]) == 0) { + unknownAssigned = this.extra[2][vectorIndex] & + ~this.extra[3][vectorIndex] & + this.extra[4][vectorIndex] & + this.extra[5][vectorIndex]; + this.extra[3][vectorIndex] |= mask; + this.extra[2][vectorIndex] &= (mask = ~mask); + this.extra[4][vectorIndex] &= mask | ~unknownAssigned; + this.extra[5][vectorIndex] &= mask; + if (coverageTestFlag && coverageTestId == 28) { + this.extra[5][vectorIndex] = ~0; + } + } } - return isPotentiallyAssigned(local.id + maxFieldCount); } +} + +/** + * Record a definite assignment at a given position. + * REVIEW wrong comment? + * It deals with the dual representation of the InitializationInfo2: + * bits for the first 64 entries, then an array of booleans. + */ +final private void markAsDefinitelyAssigned(int position) { - /** - * Record a definite assignment at a given position. - * It deals with the dual representation of the InitializationInfo2: - * bits for the first 64 entries, then an array of booleans. - */ - final private void markAsDefinitelyAssigned(int position) { - - if (this != DEAD_END) { - - // position is zero-based - if (position < BitCacheSize) { - // use bits - long mask; - definiteInits |= (mask = 1L << position); - potentialInits |= mask; - definiteNulls &= ~mask; - definiteNonNulls &= ~mask; - } else { - // use extra vector - int vectorIndex = (position / BitCacheSize) - 1; - if (extraDefiniteInits == null) { - int length; - extraDefiniteInits = new long[length = vectorIndex + 1]; - extraPotentialInits = new long[length]; - extraDefiniteNulls = new long[length]; - extraDefiniteNonNulls = new long[length]; - } else { - int oldLength; // might need to grow the arrays - if (vectorIndex >= (oldLength = extraDefiniteInits.length)) { - System.arraycopy(extraDefiniteInits, 0, (extraDefiniteInits = new long[vectorIndex + 1]), 0, oldLength); - System.arraycopy(extraPotentialInits, 0, (extraPotentialInits = new long[vectorIndex + 1]), 0, oldLength); - System.arraycopy(extraDefiniteNulls, 0, (extraDefiniteNulls = new long[vectorIndex + 1]), 0, oldLength); - System.arraycopy(extraDefiniteNonNulls, 0, (extraDefiniteNonNulls = new long[vectorIndex + 1]), 0, oldLength); + if (this != DEAD_END) { + // position is zero-based + if (position < BitCacheSize) { + // use bits + long mask; + this.definiteInits |= (mask = 1L << position); + this.potentialInits |= mask; + } + else { + // use extra vector + int vectorIndex = (position / BitCacheSize) - 1; + if (this.extra == null) { + int length = vectorIndex + 1; + this.extra = new long[extraLength][]; + for (int j = 0; j < extraLength; j++) { + this.extra[j] = new long[length]; + } + } + else { + int oldLength; // might need to grow the arrays + if (vectorIndex >= (oldLength = this.extra[0].length)) { + for (int j = 0; j < extraLength; j++) { + System.arraycopy(this.extra[j], 0, + (this.extra[j] = new long[vectorIndex + 1]), 0, + oldLength); } } - long mask; - extraDefiniteInits[vectorIndex] |= (mask = 1L << (position % BitCacheSize)); - extraPotentialInits[vectorIndex] |= mask; - extraDefiniteNulls[vectorIndex] &= ~mask; - extraDefiniteNonNulls[vectorIndex] &= ~mask; } + long mask; + this.extra[0][vectorIndex] |= + (mask = 1L << (position % BitCacheSize)); + this.extra[1][vectorIndex] |= mask; } } - - /** - * Record a field got definitely assigned. - */ - public void markAsDefinitelyAssigned(FieldBinding field) { - if (this != DEAD_END) - markAsDefinitelyAssigned(field.id); - } - - /** - * Record a local got definitely assigned. - */ - public void markAsDefinitelyAssigned(LocalVariableBinding local) { - if (this != DEAD_END) - markAsDefinitelyAssigned(local.id + maxFieldCount); - } - - /** - * Record a definite non-null assignment at a given position. - * It deals with the dual representation of the InitializationInfo2: - * bits for the first 64 entries, then an array of booleans. - */ - final private void markAsDefinitelyNonNull(int position) { - - if (this != DEAD_END) { - - // position is zero-based - if (position < BitCacheSize) { - // use bits - long mask; - definiteNonNulls |= (mask = 1L << position); - definiteNulls &= ~mask; - } else { - // use extra vector - int vectorIndex = (position / BitCacheSize) - 1; - long mask; - extraDefiniteNonNulls[vectorIndex] |= (mask = 1L << (position % BitCacheSize)); - extraDefiniteNulls[vectorIndex] &= ~mask; - } +} + +public void markAsDefinitelyAssigned(FieldBinding field) { + if (this != DEAD_END) + markAsDefinitelyAssigned(field.id); +} + +public void markAsDefinitelyAssigned(LocalVariableBinding local) { + if (this != DEAD_END) + markAsDefinitelyAssigned(local.id + this.maxFieldCount); +} + +/** + * Record a definite non-null assignment at a given position. + */ +final private void markAsDefinitelyNonNull(int position) { + // DEAD_END guarded above + this.tagBits |= NULL_FLAG_MASK; + long mask; + // position is zero-based + if (position < BitCacheSize) { + // use bits + this.nullAssignmentStatusBit1 |= (mask = 1L << position); + this.nullAssignmentValueBit2 |= mask; // set non null + this.nullAssignmentStatusBit2 &= ~mask; // clear protection + this.nullAssignmentValueBit1 &= ~mask; // clear null + if (coverageTestFlag && coverageTestId == 29) { + this.nullAssignmentStatusBit1 = 0; + } + } + else { + // use extra vector + int vectorIndex = (position / BitCacheSize) - 1; + // REVIEW seems to be guarded + this.extra[2][vectorIndex] |= + (mask = 1L << (position % BitCacheSize)); + this.extra[5][vectorIndex] |= mask; + this.extra[3][vectorIndex] &= ~mask; + this.extra[4][vectorIndex] &= ~mask; + if (coverageTestFlag && coverageTestId == 30) { + this.extra[5][vectorIndex] = ~0; } } +} - /** - * Record a field got definitely assigned to non-null value. - */ - public void markAsDefinitelyNonNull(FieldBinding field) { - if (this != DEAD_END) - markAsDefinitelyNonNull(field.id); +public void markAsDefinitelyNonNull(FieldBinding field) { + if (this != DEAD_END) { + markAsDefinitelyNonNull(field.id); } - - /** - * Record a local got definitely assigned to non-null value. - */ - public void markAsDefinitelyNonNull(LocalVariableBinding local) { - if (this != DEAD_END) - markAsDefinitelyNonNull(local.id + maxFieldCount); - } - - /** - * Record a definite null assignment at a given position. - * It deals with the dual representation of the InitializationInfo2: - * bits for the first 64 entries, then an array of booleans. - */ - final private void markAsDefinitelyNull(int position) { - - if (this != DEAD_END) { - - // position is zero-based - if (position < BitCacheSize) { - // use bits - long mask; - definiteNulls |= (mask = 1L << position); - definiteNonNulls &= ~mask; - } else { - // use extra vector - int vectorIndex = (position / BitCacheSize) - 1; - long mask; - extraDefiniteNulls[vectorIndex] |= (mask = 1L << (position % BitCacheSize)); - extraDefiniteNonNulls[vectorIndex] &= ~mask; - } +} + +public void markAsDefinitelyNonNull(LocalVariableBinding local) { + // protected from non-object locals in calling methods + if (this != DEAD_END) { + markAsDefinitelyNonNull(local.id + this.maxFieldCount); + } +} + +/** + * Record a definite null assignment at a given position. + */ +final private void markAsDefinitelyNull(int position) { + // DEAD_END guarded above + this.tagBits |= NULL_FLAG_MASK; + long mask; + if (position < BitCacheSize) { + // use bits + this.nullAssignmentStatusBit1 |= (mask = 1L << position); // set assignment + this.nullAssignmentStatusBit2 &= ~mask; // clear protection + this.nullAssignmentValueBit1 |= mask; // set null + this.nullAssignmentValueBit2 &= ~mask; // clear non null + if (coverageTestFlag && coverageTestId == 31) { + this.nullAssignmentValueBit2 = ~0; + } + } + else { + // use extra vector + int vectorIndex = (position / BitCacheSize) - 1; + // REVIEW seems to be guarded + this.extra[2][vectorIndex] |= + (mask = 1L << (position % BitCacheSize)); + this.extra[3][vectorIndex] &= ~mask; + this.extra[4][vectorIndex] |= mask; + this.extra[5][vectorIndex] &= ~mask; + if (coverageTestFlag && coverageTestId == 32) { + this.extra[5][vectorIndex] = ~0; } } +} - /** - * Record a field got definitely assigned to null. - */ - public void markAsDefinitelyNull(FieldBinding field) { - if (this != DEAD_END) - markAsDefinitelyAssigned(field.id); +public void markAsDefinitelyNull(FieldBinding field) { + if (this != DEAD_END) { + markAsDefinitelyNull(field.id); } - - /** - * Record a local got definitely assigned to null. - */ - public void markAsDefinitelyNull(LocalVariableBinding local) { - if (this != DEAD_END) - markAsDefinitelyNull(local.id + maxFieldCount); +} + +public void markAsDefinitelyNull(LocalVariableBinding local) { + // protected from non-object locals in calling methods + if (this != DEAD_END) { + markAsDefinitelyNull(local.id + this.maxFieldCount); } - - /** - * Clear initialization information at a given position. - * It deals with the dual representation of the InitializationInfo2: - * bits for the first 64 entries, then an array of booleans. - */ - final private void markAsDefinitelyNotAssigned(int position) { - if (this != DEAD_END) { - - // position is zero-based - if (position < BitCacheSize) { - // use bits - long mask; - definiteInits &= ~(mask = 1L << position); - potentialInits &= ~mask; - definiteNulls &= ~mask; - definiteNonNulls &= ~mask; - } else { - // use extra vector - int vectorIndex = (position / BitCacheSize) - 1; - if (extraDefiniteInits == null) { - return; // nothing to do, it was not yet set - } - // might need to grow the arrays - if (vectorIndex >= extraDefiniteInits.length) { - return; // nothing to do, it was not yet set - } - long mask; - extraDefiniteInits[vectorIndex] &= ~(mask = 1L << (position % BitCacheSize)); - extraPotentialInits[vectorIndex] &= ~mask; - extraDefiniteNulls[vectorIndex] &= ~mask; - extraDefiniteNonNulls[vectorIndex] &= ~mask; +} + +/** + * Mark a local as having been assigned to an unknown value. + * @param local the local to mark + */ +// PREMATURE may try to get closer to markAsDefinitelyAssigned, but not +// obvious +public void markAsDefinitelyUnknown(LocalVariableBinding local) { + // protected from non-object locals in calling methods + if (this != DEAD_END) { + this.tagBits |= NULL_FLAG_MASK; + long mask; + int position; + // position is zero-based + if ((position = local.id + this.maxFieldCount) < BitCacheSize) { + // use bits + this.nullAssignmentValueBit1 |= (mask = 1L << position); + this.nullAssignmentValueBit2 |= mask; + // set unknown + this.nullAssignmentStatusBit1 |= mask; + // set assignment + this.nullAssignmentStatusBit2 &= ~mask; + // clear protection + if (coverageTestFlag && coverageTestId == 33) { + this.nullAssignmentValueBit2 = ~0; + } + } + else { + // use extra vector + int vectorIndex = (position / BitCacheSize) - 1; + // REVIEW seems to be guarded + this.extra[4][vectorIndex] |= + (mask = 1L << (position % BitCacheSize)); + this.extra[5][vectorIndex] |= mask; + this.extra[2][vectorIndex] |= mask; + this.extra[3][vectorIndex] &= ~mask; + if (coverageTestFlag && coverageTestId == 34) { + this.extra[5][vectorIndex] = ~0; } } } - - /** - * Clear the initialization info for a field - */ - public void markAsDefinitelyNotAssigned(FieldBinding field) { - - if (this != DEAD_END) - markAsDefinitelyNotAssigned(field.id); - } - - /** - * Clear the initialization info for a local variable - */ - - public void markAsDefinitelyNotAssigned(LocalVariableBinding local) { - - if (this != DEAD_END) - markAsDefinitelyNotAssigned(local.id + maxFieldCount); - } - - /** - * Returns the receiver updated in the following way:
    - *
  • intersection of definitely assigned variables, - *
  • union of potentially assigned variables. - *
- */ - public UnconditionalFlowInfo mergedWith(UnconditionalFlowInfo otherInits) { - - if (this == DEAD_END) return otherInits; - if (otherInits == DEAD_END) return this; - - if ((this.reachMode & UNREACHABLE) != (otherInits.reachMode & UNREACHABLE)){ - if ((this.reachMode & UNREACHABLE) != 0){ - return otherInits; - } - return this; +} + +public UnconditionalFlowInfo mergedWith(UnconditionalFlowInfo otherInits) { + if ((otherInits.tagBits & UNREACHABLE) != 0 && this != DEAD_END) { + if (coverageTestFlag && coverageTestId == 35) { + throw new AssertionFailedException("COVERAGE 35"); //$NON-NLS-1$ } - - // if one branch is not fake reachable, then the merged one is reachable - this.reachMode &= otherInits.reachMode; - - // intersection of definitely assigned variables, - this.definiteInits &= otherInits.definiteInits; - // union of potentially set ones - this.potentialInits |= otherInits.potentialInits; - // intersection of definitely null variables, - this.definiteNulls &= otherInits.definiteNulls; - // intersection of definitely non-null variables, - this.definiteNonNulls &= otherInits.definiteNonNulls; - - // treating extra storage - if (this.extraDefiniteInits != null) { - if (otherInits.extraDefiniteInits != null) { + // DEAD_END + unreachable other -> other + return this; + } + if ((this.tagBits & UNREACHABLE) != 0) { + if (coverageTestFlag && coverageTestId == 36) { + throw new AssertionFailedException("COVERAGE 36"); //$NON-NLS-1$ + } + return (UnconditionalFlowInfo) otherInits.copy(); // make sure otherInits won't be affected + } + + // intersection of definitely assigned variables, + this.definiteInits &= otherInits.definiteInits; + // union of potentially set ones + this.potentialInits |= otherInits.potentialInits; + + // null combinations + boolean otherHasNulls = (otherInits.tagBits & NULL_FLAG_MASK) != 0, + thisHasNulls = false; + long a1, a2, na2, a3, na3, a4, na4, b1, nb1, b2, nb2, b3, nb3, b4, nb4; + if (otherHasNulls) { + this.nullAssignmentStatusBit1 = + (a1 = this.nullAssignmentStatusBit1) & + (b1 = otherInits.nullAssignmentStatusBit1) & ( + (nb4 = ~(b4 = otherInits.nullAssignmentValueBit2)) & + ((b2 = otherInits.nullAssignmentStatusBit2) & + (nb3 = ~(b3 = otherInits.nullAssignmentValueBit1)) & + (na3 = ~(a3 = this.nullAssignmentValueBit1)) & + ((a2 = this.nullAssignmentStatusBit2) & + (na4 = ~(a4 = this.nullAssignmentValueBit2)) | a4) | + (na2 = ~a2) & a3 & na4 & (nb2 = ~b2) & b3 ) | + b4 & (na3 & nb3 & (na4 & a2 | a4) | + na2 & a4 & nb2)); + this.nullAssignmentStatusBit2 = + a2 & b2 & ~(a1 ^ b1) & (na3 & nb3 | na4 & nb4) | + a1 & b1 & (a2 ^ b2) & na3 & nb3 | + (a1 & na2 & (nb1 = ~b1) & b2 | ~a1 & a2 & b1 & nb2) & na4 & nb4; + this.nullAssignmentValueBit1 = + b1 & nb2 & nb4 | + ~a1 & (a3 | + a2 & na3 & (b1 | nb2)) | + (a1 | na2) & nb1 & b2 & nb3 | + nb1 & b3 | + a1 & na2 & (na4 | + b1 & nb2 & (a3 | b3)); + this.nullAssignmentValueBit2 = + a4 | b4; + if (coverageTestFlag && coverageTestId == 37) { + this.nullAssignmentValueBit2 = ~0; + } + } + else { + // tune potentials + this.nullAssignmentValueBit1 = + ~(~this.nullAssignmentStatusBit1 & + ~this.nullAssignmentStatusBit2 & + ~this.nullAssignmentValueBit1) & + ~(this.nullAssignmentStatusBit1 & + (this.nullAssignmentStatusBit2 | this.nullAssignmentValueBit2)); + // reset assignment and protected + this.nullAssignmentStatusBit1 = + this.nullAssignmentStatusBit2 = 0; + if (coverageTestFlag && coverageTestId == 38) { + this.nullAssignmentValueBit2 = ~0; + } + } + thisHasNulls = this.nullAssignmentStatusBit1 != 0 || + this.nullAssignmentStatusBit2 != 0 || + this.nullAssignmentValueBit1 != 0 || + this.nullAssignmentValueBit2 != 0; + + // treating extra storage + if (this.extra != null || otherInits.extra != null) { + int mergeLimit = 0, copyLimit = 0, resetLimit = 0; + if (this.extra != null) { + if (otherInits.extra != null) { // both sides have extra storage - int i = 0, length, otherLength; - if ((length = this.extraDefiniteInits.length) < (otherLength = otherInits.extraDefiniteInits.length)) { - // current storage is shorter -> grow current (could maybe reuse otherInits extra storage?) - System.arraycopy(this.extraDefiniteInits, 0, (this.extraDefiniteInits = new long[otherLength]), 0, length); - System.arraycopy(this.extraPotentialInits, 0, (this.extraPotentialInits = new long[otherLength]), 0, length); - System.arraycopy(this.extraDefiniteNulls, 0, (this.extraDefiniteNulls = new long[otherLength]), 0, length); - System.arraycopy(this.extraDefiniteNonNulls, 0, (this.extraDefiniteNonNulls = new long[otherLength]), 0, length); - while (i < length) { - this.extraDefiniteInits[i] &= otherInits.extraDefiniteInits[i]; - this.extraPotentialInits[i] |= otherInits.extraPotentialInits[i]; - this.extraDefiniteNulls[i] &= otherInits.extraDefiniteNulls[i]; - this.extraDefiniteNonNulls[i] &= otherInits.extraDefiniteNonNulls[i++]; + int length, otherLength; + if ((length = this.extra[0].length) < + (otherLength = otherInits.extra[0].length)) { + // current storage is shorter -> grow current + for (int j = 0; j < extraLength; j++) { + System.arraycopy(this.extra[j], 0, + (this.extra[j] = new long[otherLength]), 0, length); } - while (i < otherLength) { - this.extraPotentialInits[i] = otherInits.extraPotentialInits[i++]; + mergeLimit = length; + copyLimit = otherLength; + if (coverageTestFlag && coverageTestId == 39) { + throw new AssertionFailedException("COVERAGE 39"); //$NON-NLS-1$ } - } else { + } + else { // current storage is longer - while (i < otherLength) { - this.extraDefiniteInits[i] &= otherInits.extraDefiniteInits[i]; - this.extraPotentialInits[i] |= otherInits.extraPotentialInits[i]; - this.extraDefiniteNulls[i] &= otherInits.extraDefiniteNulls[i]; - this.extraDefiniteNonNulls[i] &= otherInits.extraDefiniteNonNulls[i++]; - } - while (i < length) { - this.extraDefiniteInits[i] = 0; - this.extraDefiniteNulls[i] = 0; - this.extraDefiniteNonNulls[i++] = 0; - } - } - } else { - // no extra storage on otherInits - int i = 0, length = this.extraDefiniteInits.length; - while (i < length) { - this.extraDefiniteInits[i] = 0; - this.extraDefiniteNulls[i] = 0; - this.extraDefiniteNonNulls[i++] = 0; - } - } - } else - if (otherInits.extraDefiniteInits != null) { - // no storage here, but other has extra storage. - int otherLength; - this.extraDefiniteInits = new long[otherLength = otherInits.extraDefiniteInits.length]; - System.arraycopy(otherInits.extraPotentialInits, 0, (this.extraPotentialInits = new long[otherLength]), 0, otherLength); - this.extraDefiniteNulls = new long[otherLength]; - this.extraDefiniteNonNulls = new long[otherLength]; + mergeLimit = otherLength; + resetLimit = length; + if (coverageTestFlag && coverageTestId == 40) { + throw new AssertionFailedException("COVERAGE 40"); //$NON-NLS-1$ + } + } + } + else { + resetLimit = this.extra[0].length; + if (coverageTestFlag && coverageTestId == 41) { + throw new AssertionFailedException("COVERAGE 41"); //$NON-NLS-1$ + } } - return this; + } + else if (otherInits.extra != null) { + // no storage here, but other has extra storage. + int otherLength = otherInits.extra[0].length; + this.extra = new long[extraLength][]; + for (int j = 0; j < extraLength; j++) { + this.extra[j] = new long[otherLength]; + } + System.arraycopy(otherInits.extra[1], 0, + this.extra[1], 0, otherLength); + copyLimit = otherLength; + if (coverageTestFlag && coverageTestId == 42) { + throw new AssertionFailedException("COVERAGE 42"); //$NON-NLS-1$ + } + } + int i; + if (otherHasNulls) { + for (i = 0; i < mergeLimit; i++) { + this.extra[2][i] = + (a1 = this.extra[2][i]) & + (b1 = otherInits.extra[2][i]) & ( + (nb4 = ~(b4 = otherInits.extra[5][i])) & + ((b2 = otherInits.extra[3][i]) & + (nb3 = ~(b3 = otherInits.extra[4][i])) & + (na3 = ~(a3 = this.extra[4][i])) & + ((a2 = this.extra[3][i]) & + (na4 = ~(a4 = this.extra[5][i])) | a4) | + (na2 = ~a2) & a3 & na4 & (nb2 = ~b2) & b3 ) | + b4 & (na3 & nb3 & (na4 & a2 | a4) | + na2 & a4 & nb2)); + this.extra[3][i] = + a2 & b2 & ~(a1 ^ b1) & (na3 & nb3 | na4 & nb4) | + a1 & b1 & (a2 ^ b2) & na3 & nb3 | + (a1 & na2 & (nb1 = ~b1) & b2 | ~a1 & a2 & b1 & nb2) & na4 & nb4; + this.extra[4][i] = + b1 & nb2 & nb4 | + ~a1 & (a3 | + a2 & na3 & (b1 | nb2)) | + (a1 | na2) & nb1 & b2 & nb3 | + nb1 & b3 | + a1 & na2 & (na4 | + b1 & nb2 & (a3 | b3)); + this.extra[5][i] = + a4 | b4; + thisHasNulls = thisHasNulls || + this.extra[5][i] != 0 || + this.extra[2][i] != 0 || + this.extra[3][i] != 0 || + this.extra[4][i] != 0; + if (coverageTestFlag && coverageTestId == 43) { + this.extra[5][i] = ~0; + } + } + } + else { + for (i = 0; i < mergeLimit; i++) { + this.extra[0][i] &= + otherInits.extra[0][i]; + this.extra[1][i] |= + otherInits.extra[1][i]; + this.extra[4][i] = + ~(~this.extra[2][i] & + ~this.extra[3][i] & + ~this.extra[4][i]) & + ~(this.extra[2][i] & + (this.extra[3][i] | + this.extra[5][i])); + this.extra[2][i] = + this.extra[3][i] = 0; + thisHasNulls = thisHasNulls || + this.extra[4][i] != 0 || + this.extra[5][i] != 0; + if (coverageTestFlag && coverageTestId == 44) { + this.extra[5][i] = ~0; + } + } + } + for (; i < copyLimit; i++) { + this.extra[1][i] = otherInits.extra[1][i]; + this.extra[4][i] = + ~(~otherInits.extra[2][i] & + ~otherInits.extra[3][i] & + ~otherInits.extra[4][i]) & + ~(otherInits.extra[2][i] & + (otherInits.extra[3][i] | + otherInits.extra[5][i])); + this.extra[5][i] = otherInits.extra[5][i]; + thisHasNulls = thisHasNulls || + this.extra[4][i] != 0 || + this.extra[5][i] != 0; + if (coverageTestFlag && coverageTestId == 45) { + this.extra[5][i] = ~0; + } + } + for (; i < resetLimit; i++) { + this.extra[4][i] = + ~(~this.extra[2][i] & + ~this.extra[3][i] & + ~this.extra[4][i]) & + ~(this.extra[2][i] & + (this.extra[3][i] | + this.extra[5][i])); + this.extra[0][i] = + this.extra[2][i] = + this.extra[3][i] = 0; + thisHasNulls = thisHasNulls || + this.extra[4][i] != 0 || + this.extra[5][i] != 0; + if (coverageTestFlag && coverageTestId == 46) { + this.extra[5][i] = ~0; + } + } } - - /* - * Answer the total number of fields in enclosing types of a given type - */ - static int numberOfEnclosingFields(ReferenceBinding type){ - - int count = 0; + if (thisHasNulls) { + this.tagBits |= NULL_FLAG_MASK; + } + else { + this.tagBits &= ~NULL_FLAG_MASK; + } + return this; +} + +/* + * Answer the total number of fields in enclosing types of a given type + */ +static int numberOfEnclosingFields(ReferenceBinding type){ + int count = 0; + type = type.enclosingType(); + while(type != null) { + count += type.fieldCount(); type = type.enclosingType(); - while(type != null) { - count += type.fieldCount(); - type = type.enclosingType(); - } - return count; } - - public int reachMode(){ - return this.reachMode; + return count; +} + +public UnconditionalFlowInfo nullInfoLessUnconditionalCopy() { + if (this == DEAD_END) { + return this; } - - public FlowInfo setReachMode(int reachMode) { - - if (this == DEAD_END) return this; // cannot modify DEAD_END - - // reset optional inits when becoming unreachable - if ((this.reachMode & UNREACHABLE) == 0 && (reachMode & UNREACHABLE) != 0) { + UnconditionalFlowInfo copy = new UnconditionalFlowInfo(); + copy.definiteInits = this.definiteInits; + copy.potentialInits = this.potentialInits; + copy.tagBits = this.tagBits & ~NULL_FLAG_MASK; + copy.maxFieldCount = this.maxFieldCount; + if (this.extra != null) { + int length; + copy.extra = new long[extraLength][]; + System.arraycopy(this.extra[0], 0, + (copy.extra[0] = + new long[length = this.extra[0].length]), 0, length); + System.arraycopy(this.extra[1], 0, + (copy.extra[1] = new long[length]), 0, length); + for (int j = 2; j < extraLength; j++) { + copy.extra[j] = new long[length]; + } + } + return copy; +} + +public FlowInfo safeInitsWhenTrue() { + return copy(); +} + +public FlowInfo setReachMode(int reachMode) { + if (reachMode == REACHABLE && this != DEAD_END) { // cannot modify DEAD_END + this.tagBits &= ~UNREACHABLE; + } + else { + if ((this.tagBits & UNREACHABLE) == 0) { + // reset optional inits when becoming unreachable + // see InitializationTest#test090 (and others) this.potentialInits = 0; - if (this.extraPotentialInits != null){ - for (int i = 0, length = this.extraPotentialInits.length; i < length; i++){ - this.extraPotentialInits[i] = 0; + if (this.extra != null) { + for (int i = 0, length = this.extra[0].length; + i < length; i++) { + this.extra[1][i] = 0; } } } - this.reachMode = reachMode; - - return this; + this.tagBits |= UNREACHABLE; } + return this; +} - public String toString(){ - - if (this == DEAD_END){ - return "FlowInfo.DEAD_END"; //$NON-NLS-1$ - } - return "FlowInfo"; //$NON-NLS-1$ +public String toString(){ + // PREMATURE consider printing bit fields as 0001 0001 1000 0001... + if (this == DEAD_END){ + return "FlowInfo.DEAD_END"; //$NON-NLS-1$ + } + if ((this.tagBits & NULL_FLAG_MASK) != 0) { + if (this.extra == null) { + return "FlowInfo"; //$NON-NLS-1$ + } + else { + String def = "FlowInfo 3 ? + 3 : + this.extra[0].length; + i < ceil; i++) { + def += "," + this.extra[0][i]; //$NON-NLS-1$ + pot += "," + this.extra[1][i]; //$NON-NLS-1$ + nullS1 += "," + this.extra[2][i]; //$NON-NLS-1$ + nullS2 += "," + this.extra[3][i]; //$NON-NLS-1$ + nullV1 += "," + this.extra[4][i]; //$NON-NLS-1$ + nullV2 += "," + this.extra[5][i]; //$NON-NLS-1$ + } + if (ceil < this.extra[0].length) { + def += ",..."; //$NON-NLS-1$ + pot += ",..."; //$NON-NLS-1$ + nullS1 += ",..."; //$NON-NLS-1$ + nullS2 += ",..."; //$NON-NLS-1$ + nullV1 += ",..."; //$NON-NLS-1$ + nullV2 += ",..."; //$NON-NLS-1$ + } + return def + pot + + "], reachable:" + ((this.tagBits & UNREACHABLE) == 0) //$NON-NLS-1$ + + nullS1 + nullS2 + nullV1 + nullV2 + + "]>"; //$NON-NLS-1$ + } + } + else { + if (this.extra == null) { + return "FlowInfo"; //$NON-NLS-1$ + } + else { + String def = "FlowInfo 3 ? + 3 : + this.extra[0].length; + i < ceil; i++) { + def += "," + this.extra[0][i]; //$NON-NLS-1$ + pot += "," + this.extra[1][i]; //$NON-NLS-1$ + } + if (ceil < this.extra[0].length) { + def += ",..."; //$NON-NLS-1$ + pot += ",..."; //$NON-NLS-1$ + } + return def + pot + + "], reachable:" + ((this.tagBits & UNREACHABLE) == 0) //$NON-NLS-1$ + + ", no null info>"; //$NON-NLS-1$ + } } +} + +public UnconditionalFlowInfo unconditionalCopy() { + return (UnconditionalFlowInfo) copy(); +} - public UnconditionalFlowInfo unconditionalInits() { - - // also see conditional inits, where it requests them to merge - return this; +public UnconditionalFlowInfo unconditionalFieldLessCopy() { + // TODO (maxime) may consider leveraging null contribution verification as it is done in copy + UnconditionalFlowInfo copy = new UnconditionalFlowInfo(); + copy.tagBits = this.tagBits; + copy.maxFieldCount = this.maxFieldCount; + int limit = this.maxFieldCount; + if (limit < BitCacheSize) { + long mask; + copy.definiteInits = this.definiteInits & (mask = ~((1L << limit)-1)); + copy.potentialInits = this.potentialInits & mask; + copy.nullAssignmentStatusBit1 = this.nullAssignmentStatusBit1 & mask; + copy.nullAssignmentStatusBit2 = this.nullAssignmentStatusBit2 & mask; + copy.nullAssignmentValueBit1 = this.nullAssignmentValueBit1 & mask; + copy.nullAssignmentValueBit2 = this.nullAssignmentValueBit2 & mask; + } + // use extra vector + if (this.extra == null) { + return copy; // if vector not yet allocated, then not initialized + } + int vectorIndex, length, copyStart; + if ((vectorIndex = (limit / BitCacheSize) - 1) >= + (length = this.extra[0].length)) { + return copy; // not enough room yet + } + long mask; + copy.extra = new long[extraLength][]; + if ((copyStart = vectorIndex + 1) < length) { + int copyLength = length - copyStart; + for (int j = 0; j < extraLength; j++) { + System.arraycopy(this.extra[j], copyStart, + (copy.extra[j] = new long[length]), copyStart, + copyLength); + } + } + else if (vectorIndex >= 0) { + for (int j = 0; j < extraLength; j++) { + copy.extra[j] = new long[length]; + } + } + if (vectorIndex >= 0) { + mask = ~((1L << (limit % BitCacheSize))-1); + for (int j = 0; j < extraLength; j++) { + copy.extra[j][vectorIndex] = + this.extra[j][vectorIndex] & mask; + } } + return copy; +} + +public UnconditionalFlowInfo unconditionalInits() { + // also see conditional inits, where it requests them to merge + return this; +} + +public UnconditionalFlowInfo unconditionalInitsWithoutSideEffect() { + return this; +} } Index: compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java,v retrieving revision 1.155 diff -u -r1.155 CompilerOptions.java --- compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java 20 Jan 2006 03:44:05 -0000 1.155 +++ compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java 24 Jan 2006 09:56:42 -0000 @@ -1001,6 +1001,8 @@ case 'n' : if ("nls".equals(warningToken)) //$NON-NLS-1$ return NonExternalizedString; + if ("null".equals(warningToken)) //$NON-NLS-1$ + return NullReference; break; case 's' : if ("serial".equals(warningToken)) //$NON-NLS-1$ Index: compiler/org/eclipse/jdt/internal/compiler/lookup/ExtraCompilerModifiers.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ExtraCompilerModifiers.java,v retrieving revision 1.2 diff -u -r1.2 ExtraCompilerModifiers.java --- compiler/org/eclipse/jdt/internal/compiler/lookup/ExtraCompilerModifiers.java 21 Dec 2005 16:03:43 -0000 1.2 +++ compiler/org/eclipse/jdt/internal/compiler/lookup/ExtraCompilerModifiers.java 24 Jan 2006 09:56:42 -0000 @@ -18,7 +18,7 @@ // those constants are depending upon ClassFileConstants (relying that classfiles only use the 16 lower bits) final int AccJustFlag = 0xFFFF;// 16 lower bits - // bit17 - free + // bit17 - used below // bit18 - use by ClassFileConstants.AccAnnotationDefault final int AccRestrictedAccess = ASTNode.Bit19; final int AccFromClassFile = ASTNode.Bit20; @@ -38,4 +38,6 @@ final int AccOverriding = ASTNode.Bit29; // record fact a method overrides another one final int AccImplementing = ASTNode.Bit30; // record fact a method implements another one (it is concrete and overrides an abstract one) final int AccGenericSignature = ASTNode.Bit31; // record fact a type/method/field involves generics in its signature (and need special signature attr) + final int AccNotNull = ASTNode.Bit32; // record fact a local/method/parameter cannot evaluate to null + final int AccNullable = ASTNode.Bit17; // record fact a local/method/parameter can evaluate to null } Index: compiler/org/eclipse/jdt/internal/compiler/lookup/MethodScope.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodScope.java,v retrieving revision 1.57 diff -u -r1.57 MethodScope.java --- compiler/org/eclipse/jdt/internal/compiler/lookup/MethodScope.java 10 Jan 2006 14:37:28 -0000 1.57 +++ compiler/org/eclipse/jdt/internal/compiler/lookup/MethodScope.java 24 Jan 2006 09:56:42 -0000 @@ -395,10 +395,11 @@ public final int recordInitializationStates(FlowInfo flowInfo) { - if (!flowInfo.isReachable()) return -1; + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0) return -1; - UnconditionalFlowInfo unconditionalFlowInfo = flowInfo.unconditionalInits(); - long[] extraInits = unconditionalFlowInfo.extraDefiniteInits; + UnconditionalFlowInfo unconditionalFlowInfo = flowInfo.unconditionalInitsWithoutSideEffect(); + long[] extraInits = unconditionalFlowInfo.extra == null ? + null : unconditionalFlowInfo.extra[0]; long inits = unconditionalFlowInfo.definiteInits; checkNextEntry : for (int i = lastIndex; --i >= 0;) { if (definiteInits[i] == inits) { Index: compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java,v retrieving revision 1.123 diff -u -r1.123 SourceTypeBinding.java --- compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java 18 Jan 2006 20:16:26 -0000 1.123 +++ compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java 24 Jan 2006 09:56:43 -0000 @@ -553,6 +553,7 @@ // check @Deprecated annotation if ((this.getAnnotationTagBits() & TagBits.AnnotationDeprecated) != 0) { this.modifiers |= ClassFileConstants.AccDeprecated; + // REVIEW already done in getAnnotationsTagBits } ReferenceBinding enclosingType = this.enclosingType(); if (enclosingType != null && enclosingType.isViewedAsDeprecated() && !this.isDeprecated()) @@ -1108,6 +1109,8 @@ if (this.scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5) { if ((field.getAnnotationTagBits() & TagBits.AnnotationDeprecated) != 0) field.modifiers |= ClassFileConstants.AccDeprecated; + // REVIEW in the case of fields, getAnnotationTagBits does not mark, whereas it + // does it for types } if (isViewedAsDeprecated() && !field.isDeprecated()) field.modifiers |= ExtraCompilerModifiers.AccDeprecatedImplicitly; Index: compiler/org/eclipse/jdt/internal/compiler/parser/AbstractCommentParser.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/AbstractCommentParser.java,v retrieving revision 1.53 diff -u -r1.53 AbstractCommentParser.java --- compiler/org/eclipse/jdt/internal/compiler/parser/AbstractCommentParser.java 10 Jan 2006 11:05:02 -0000 1.53 +++ compiler/org/eclipse/jdt/internal/compiler/parser/AbstractCommentParser.java 24 Jan 2006 09:56:43 -0000 @@ -53,6 +53,8 @@ // Results protected long inheritedPositions; protected boolean deprecated; + public boolean notNull; // TODO (maxime) replace with other style of multi-return check + public boolean nullable; protected Object returnStatement; // Positions @@ -140,6 +142,8 @@ this.returnStatement = null; this.inheritedPositions = -1; this.deprecated = false; + this.notNull = false; + this.nullable = false; this.lastLinePtr = getLineNumber(javadocEnd); this.lineEnd = (this.linePtr == this.lastLinePtr) ? this.javadocEnd: this.scanner.getLineEnd(this.linePtr) - 1; this.textStart = -1; Index: compiler/org/eclipse/jdt/internal/compiler/parser/JavadocParser.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/JavadocParser.java,v retrieving revision 1.47 diff -u -r1.47 JavadocParser.java --- compiler/org/eclipse/jdt/internal/compiler/parser/JavadocParser.java 17 Nov 2005 18:52:09 -0000 1.47 +++ compiler/org/eclipse/jdt/internal/compiler/parser/JavadocParser.java 24 Jan 2006 09:56:43 -0000 @@ -81,7 +81,7 @@ this.index = javadocStart +3; // scan line per line, since tags must be at beginning of lines only - this.deprecated = false; + this.deprecated = this.notNull = this.nullable = false; nextLine : for (int line = firstLineNumber; line <= lastLineNumber; line++) { int lineStart = line == firstLineNumber ? javadocStart + 3 // skip leading /** @@ -103,7 +103,9 @@ continue nextCharacter; case '@' : parseSimpleTag(); - if (this.tagValue == TAG_DEPRECATED_VALUE) { + if (this.tagValue == TAG_DEPRECATED_VALUE || + this.tagValue == TAG_NOT_NULL_VALUE || + this.tagValue == TAG_NULLABLE_VALUE) { if (this.abort) break nextCharacter; } } @@ -346,6 +348,37 @@ } } break; + case 'n': + switch (readChar()) { + case 'o': + if ((readChar() == 't') && + (readChar() == 'N') && (readChar() == 'u') && + (readChar() == 'l') && (readChar() == 'l')) { + // ensure the tag is properly ended: either followed by a space, a tab, line end or asterisk. + char c = readChar(); + if (Character.isWhitespace(c) || c == '*') { + this.abort = true; + this.notNull = true; + this.tagValue = TAG_NOT_NULL_VALUE; + } + } + break; + case 'u': + if ((readChar() == 'l') && + (readChar() == 'l') && (readChar() == 'a') && + (readChar() == 'b') && (readChar() == 'l') && + (readChar() == 'e')) { + // ensure the tag is properly ended: either followed by a space, a tab, line end or asterisk. + char c = readChar(); + if (Character.isWhitespace(c) || c == '*') { + this.abort = true; + this.nullable = true; + this.tagValue = TAG_NULLABLE_VALUE; + } + } + break; + } + break; } } @@ -479,6 +512,18 @@ } } break; + case 'n': + if (length == TAG_NOT_NULL_LENGTH && CharOperation.equals(TAG_NOT_NULL, tagName)) { + this.notNull = true; + valid = true; + this.tagValue = TAG_NOT_NULL_VALUE; + } + else if (length == TAG_NULLABLE_LENGTH && CharOperation.equals(TAG_NULLABLE, tagName)) { + this.nullable = true; + valid = true; + this.tagValue = TAG_NULLABLE_VALUE; + } + break; case 'p': if (length == TAG_PARAM_LENGTH && CharOperation.equals(TAG_PARAM, tagName)) { this.tagValue = TAG_PARAM_VALUE; Index: compiler/org/eclipse/jdt/internal/compiler/parser/JavadocTagConstants.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/JavadocTagConstants.java,v retrieving revision 1.7 diff -u -r1.7 JavadocTagConstants.java --- compiler/org/eclipse/jdt/internal/compiler/parser/JavadocTagConstants.java 13 Jan 2006 16:37:16 -0000 1.7 +++ compiler/org/eclipse/jdt/internal/compiler/parser/JavadocTagConstants.java 24 Jan 2006 09:56:43 -0000 @@ -39,6 +39,8 @@ public static final char[] TAG_SINCE = "since".toCharArray(); //$NON-NLS-1$ public static final char[] TAG_VERSION = "version".toCharArray(); //$NON-NLS-1$ public static final char[] TAG_CATEGORY = "category".toCharArray(); //$NON-NLS-1$ + public static final char[] TAG_NOT_NULL = "notNull".toCharArray(); //$NON-NLS-1$ + public static final char[] TAG_NULLABLE = "nullable".toCharArray(); //$NON-NLS-1$ // tags lengthes public static final int TAG_DEPRECATED_LENGTH = TAG_DEPRECATED.length; @@ -52,7 +54,8 @@ public static final int TAG_INHERITDOC_LENGTH = TAG_INHERITDOC.length; public static final int TAG_VALUE_LENGTH = TAG_VALUE.length; public static final int TAG_CATEGORY_LENGTH = TAG_CATEGORY.length; - + public static final int TAG_NOT_NULL_LENGTH = TAG_NOT_NULL.length; + public static final int TAG_NULLABLE_LENGTH = TAG_NULLABLE.length; // tags value public static final int NO_TAG_VALUE = 0; @@ -67,6 +70,8 @@ public static final int TAG_INHERITDOC_VALUE = 9; public static final int TAG_VALUE_VALUE = 10; public static final int TAG_CATEGORY_VALUE = 11; + public static final int TAG_NOT_NULL_VALUE = 50; // JDT custom + public static final int TAG_NULLABLE_VALUE = 51; // JDT custom public static final int TAG_OTHERS_VALUE = 100; // tags expected positions Index: compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java,v retrieving revision 1.332 diff -u -r1.332 Parser.java --- compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java 20 Jan 2006 11:08:55 -0000 1.332 +++ compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java 24 Jan 2006 09:56:45 -0000 @@ -1026,8 +1026,13 @@ is zeroed when a copy of modifiers-buffer is push onto the this.astStack. */ - if ((this.modifiers & flag) != 0){ // duplicate modifier + if ((this.modifiers & flag) != 0 // duplicate modifier + || ((this.modifiers & ExtraCompilerModifiers.AccNotNull) != 0 // conflicting flags + && (flag & ExtraCompilerModifiers.AccNullable) != 0) + || ((flag & ExtraCompilerModifiers.AccNotNull) != 0 // conflicting flags + && (this.modifiers & ExtraCompilerModifiers.AccNullable) != 0)) { this.modifiers |= ExtraCompilerModifiers.AccAlternateModifierProblem; + // TODO (maxime) check error message } this.modifiers |= flag; @@ -1059,6 +1064,12 @@ if (this.javadocParser.checkDeprecation(lastComment)) { checkAndSetModifiers(ClassFileConstants.AccDeprecated); } + if (this.javadocParser.notNull) { + checkAndSetModifiers(ExtraCompilerModifiers.AccNotNull); + } + if (this.javadocParser.nullable) { // no else on purpose + checkAndSetModifiers(ExtraCompilerModifiers.AccNullable); + } this.javadoc = this.javadocParser.docComment; // null if check javadoc is not activated if (currentElement == null) this.lastJavadocEnd = commentEnd; } @@ -8240,6 +8251,11 @@ (int) (this.identifierPositionStack[this.identifierPtr + 1] >> 32), // sourceStart (int) this.identifierPositionStack[this.identifierPtr + length]); // sourceEnd } + + if (this.scanner.gotNonNullTag) { + ref.markAsNonNull(); + } + return ref; } protected NameReference getUnspecifiedReferenceOptimized() { @@ -8260,6 +8276,9 @@ this.identifierPositionStack[this.identifierPtr--]); ref.bits &= ~ASTNode.RestrictiveFlagMASK; ref.bits |= Binding.LOCAL | Binding.FIELD; + if (this.scanner.gotNonNullTag) { + ref.markAsNonNull(); + } return ref; } @@ -8281,6 +8300,9 @@ (int) this.identifierPositionStack[this.identifierPtr + length]); // sourceEnd ref.bits &= ~ASTNode.RestrictiveFlagMASK; ref.bits |= Binding.LOCAL | Binding.FIELD; + if (this.scanner.gotNonNullTag) { + ref.markAsNonNull(); + } return ref; } public void goForBlockStatementsopt() { @@ -8428,6 +8450,10 @@ final boolean checkNLS = this.options.getSeverity(CompilerOptions.NonExternalizedString) != ProblemSeverities.Ignore; this.checkExternalizeStrings = checkNLS; this.scanner.checkNonExternalizedStringLiterals = initializeNLS && checkNLS; + this.scanner.checkNullReferences = + this.options.getSeverity(CompilerOptions.NullReference) != + ProblemSeverities.Ignore; + this.scanner.gotNonNullTag = false; resetModifiers(); @@ -8461,7 +8487,9 @@ this.options.complianceLevel /*complianceLevel*/, this.options.taskTags/*taskTags*/, this.options.taskPriorites/*taskPriorities*/, - this.options.isTaskCaseSensitive/*taskCaseSensitive*/); + this.options.isTaskCaseSensitive/*taskCaseSensitive*/, + this.options.getSeverity(CompilerOptions.NullReference) != + ProblemSeverities.Ignore /* checkNullReferences */); } public void jumpOverMethodBody() { //on diet parsing.....do not buffer method statements @@ -8544,6 +8572,7 @@ } } } + /* * Move checkpoint location (current implementation is moving it by one token) * @@ -9314,6 +9343,10 @@ Increase the total number of identifier in the stack. identifierPtr points on the next top */ + if (this.scanner.gotNonNullTag && this.identifierPtr < 0) { + this.scanner.gotNonNullTag = false; + } + int stackLength = this.identifierStack.length; if (++this.identifierPtr >= stackLength) { System.arraycopy( @@ -9853,6 +9886,7 @@ protected boolean resumeOnSyntaxError() { this.checkExternalizeStrings = false; this.scanner.checkNonExternalizedStringLiterals = false; + // REVIEW don't know if we should reset checkNullReferences here as well... /* request recovery initialization */ if (this.currentElement == null){ // Reset javadoc before restart parsing after recovery Index: compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java,v retrieving revision 1.167 diff -u -r1.167 Scanner.java --- compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java 17 Jan 2006 18:44:23 -0000 1.167 +++ compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java 24 Jan 2006 09:56:46 -0000 @@ -155,6 +155,10 @@ private NLSTag[] nlsTags = null; protected int nlsTagsPtr; public boolean checkNonExternalizedStringLiterals; + + // support for tagging references as non null + boolean checkNullReferences; + public boolean gotNonNullTag; // generic support public boolean returnOnlyGreater = false; @@ -198,7 +202,8 @@ long complianceLevel, char[][] taskTags, char[][] taskPriorities, - boolean isTaskCaseSensitive) { + boolean isTaskCaseSensitive, + boolean checkNullReferences) { this.eofPosition = Integer.MAX_VALUE; this.tokenizeComments = tokenizeComments; @@ -209,6 +214,28 @@ this.taskTags = taskTags; this.taskPriorities = taskPriorities; this.isTaskCaseSensitive = isTaskCaseSensitive; + this.checkNullReferences = checkNullReferences; +} + +public Scanner( + boolean tokenizeComments, + boolean tokenizeWhiteSpace, + boolean checkNonExternalizedStringLiterals, + long sourceLevel, + long complianceLevel, + char[][] taskTags, + char[][] taskPriorities, + boolean isTaskCaseSensitive) { + this( + tokenizeComments, + tokenizeWhiteSpace, + checkNonExternalizedStringLiterals, + sourceLevel, + complianceLevel, + taskTags, + taskPriorities, + isTaskCaseSensitive, + false); } public Scanner( @@ -228,7 +255,8 @@ sourceLevel, taskTags, taskPriorities, - isTaskCaseSensitive); + isTaskCaseSensitive, + false); } public final boolean atEnd() { @@ -237,6 +265,72 @@ return this.source.length == this.currentPosition; } + +// Check presence of non null tags. +private final void checkNonNullTag(int commentStart, int commentEnd) { + for (int i = commentStart + 2, state = 0; state < 9;) { + if (i >= commentEnd || i >= this.eofPosition) { + return; + } + char currentChar = this.source[i++]; + switch (state) { + case 0: // start + if (currentChar == 'N') { + state++; + } + else if (!CharOperation.isWhitespace(currentChar)) { + return; + } + continue; + case 1: // got a N + switch (currentChar) { + case 'O': + state++; + continue; + case 'N': + state = 8; + continue; + default: + return; + } + // never get there + case 2: // got NO + case 4: // got NON- + if (currentChar == 'N') { + state++; + continue; + } + return; + case 3: // got NON + if (currentChar == '-') { + state++; + continue; + } + return; + case 5: // got NON-N + if (currentChar == 'U') { + state++; + continue; + } + return; + case 6: // got NON-NU + case 7: // got NON-NUL + if (currentChar == 'L') { + state++; + continue; + } + return; + case 8: // got NON-NULL or NN + if (currentChar != '*' && !CharOperation.isWhitespace(currentChar)) { + return; + } + state = 9; // got a marker + } + } + // got a non null marker + this.gotNonNullTag = true; +} + // chech presence of task: tags // TODO (frederic) see if we need to take unicode characters into account... public void checkTaskTag(int commentStart, int commentEnd) throws InvalidInputException { @@ -1282,6 +1376,10 @@ } recordComment(TokenNameCOMMENT_LINE); if (this.taskTags != null) checkTaskTag(this.startPosition, this.currentPosition); + if (this.checkNullReferences) { + checkNonNullTag(this.startPosition, + this.currentPosition); + } if ((this.currentCharacter == '\r') || (this.currentCharacter == '\n')) { if (this.checkNonExternalizedStringLiterals) { parseTags(); @@ -1301,6 +1399,10 @@ this.currentPosition--; recordComment(TokenNameCOMMENT_LINE); if (this.taskTags != null) checkTaskTag(this.startPosition, this.currentPosition); + if (this.checkNullReferences) { + checkNonNullTag(this.startPosition, + this.currentPosition); + } if (this.checkNonExternalizedStringLiterals) { parseTags(); } @@ -1406,6 +1508,10 @@ recordComment(token); this.commentTagStarts[this.commentPtr] = firstTag; if (this.taskTags != null) checkTaskTag(this.startPosition, this.currentPosition); + if (this.checkNullReferences) { + checkNonNullTag(this.startPosition, + this.currentPosition); + } if (this.tokenizeComments) { /* if (isJavadoc) @@ -1738,7 +1844,7 @@ getNextUnicodeChar(); } } - recordComment(TokenNameCOMMENT_LINE); + recordComment(TokenNameCOMMENT_LINE); // REVIEW why do we record comments while jumping over (method bodies)? if (this.recordLineSeparator && ((this.currentCharacter == '\r') || (this.currentCharacter == '\n'))) { if (this.checkNonExternalizedStringLiterals) { Index: compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java,v retrieving revision 1.279 diff -u -r1.279 ProblemReporter.java --- compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java 17 Jan 2006 18:44:23 -0000 1.279 +++ compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java 24 Jan 2006 09:56:47 -0000 @@ -1418,6 +1418,7 @@ case IProblem.LocalVariableCannotBeNull : case IProblem.LocalVariableCanOnlyBeNull : + case IProblem.LocalVariableMayBeNull : return CompilerOptions.NullReference; case IProblem.BoxingConversion : @@ -4049,6 +4050,15 @@ local.sourceEnd); } } +public void localVariableMayBeNull(LocalVariableBinding local, ASTNode location) { + String[] arguments = new String[] {new String(local.name)}; + this.handle( + IProblem.LocalVariableMayBeNull, + arguments, + arguments, + location.sourceStart, + location.sourceEnd); +} public void methodMustOverride(AbstractMethodDeclaration method) { MethodBinding binding = method.binding; this.handle( Index: compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties,v retrieving revision 1.192 diff -u -r1.192 messages.properties --- compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties 4 Jan 2006 16:06:21 -0000 1.192 +++ compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties 24 Jan 2006 09:56:47 -0000 @@ -324,6 +324,7 @@ 396 = Illegal modifier for the variable {0}; only final is permitted 397 = The variable {0} cannot be null; it was either set to a non-null value or assumed to be non-null when last used 398 = The variable {0} can only be null; it was either set to null or checked for null when last used +399 = The variable {0} may be null 400 = The type {3} must implement the inherited abstract method {2}.{0}({1}) 401 = Cannot override the final method from {0} Index: dom/org/eclipse/jdt/core/dom/DocCommentParser.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/DocCommentParser.java,v retrieving revision 1.28 diff -u -r1.28 DocCommentParser.java --- dom/org/eclipse/jdt/core/dom/DocCommentParser.java 17 Nov 2005 18:52:09 -0000 1.28 +++ dom/org/eclipse/jdt/core/dom/DocCommentParser.java 24 Jan 2006 09:56:47 -0000 @@ -436,6 +436,20 @@ } createTag(); break; + case 'n': + if (length == TAG_NOT_NULL_LENGTH && CharOperation.equals(TAG_NOT_NULL, tagName)) { + this.notNull = true; + this.tagValue = TAG_NOT_NULL_VALUE; + } + else if (length == TAG_NULLABLE_LENGTH && CharOperation.equals(TAG_NULLABLE, tagName)) { + this.nullable = true; + this.tagValue = TAG_NULLABLE_VALUE; + } + else { + this.tagValue = TAG_OTHERS_VALUE; + } + createTag(); + break; case 'p': if (length == TAG_PARAM_LENGTH && CharOperation.equals(TAG_PARAM, tagName)) { this.tagValue = TAG_PARAM_VALUE; Index: eval/org/eclipse/jdt/internal/eval/CodeSnippetAllocationExpression.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetAllocationExpression.java,v retrieving revision 1.33 diff -u -r1.33 CodeSnippetAllocationExpression.java --- eval/org/eclipse/jdt/internal/eval/CodeSnippetAllocationExpression.java 13 Jan 2006 16:37:16 -0000 1.33 +++ eval/org/eclipse/jdt/internal/eval/CodeSnippetAllocationExpression.java 24 Jan 2006 09:56:47 -0000 @@ -115,10 +115,11 @@ // not supported yet } public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) { - if (!flowInfo.isReachable()) return; + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) { // if constructor from parameterized type got found, use the original constructor at codegen time this.codegenBinding = this.binding.original(); + } } public TypeBinding resolveType(BlockScope scope) { // Propagate the type checking to the arguments, and check if the constructor is defined. Index: eval/org/eclipse/jdt/internal/eval/CodeSnippetFieldReference.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetFieldReference.java,v retrieving revision 1.38 diff -u -r1.38 CodeSnippetFieldReference.java --- eval/org/eclipse/jdt/internal/eval/CodeSnippetFieldReference.java 13 Jan 2006 16:37:16 -0000 1.38 +++ eval/org/eclipse/jdt/internal/eval/CodeSnippetFieldReference.java 24 Jan 2006 09:56:47 -0000 @@ -291,7 +291,8 @@ public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo, boolean isReadAccess){ // The private access will be managed through the code generation - if (!flowInfo.isReachable()) return; + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0) return; + // if field from parameterized type got found, use the original field at codegen time if (this.binding instanceof ParameterizedFieldBinding) { ParameterizedFieldBinding parameterizedField = (ParameterizedFieldBinding) this.binding; Index: eval/org/eclipse/jdt/internal/eval/CodeSnippetMessageSend.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetMessageSend.java,v retrieving revision 1.48 diff -u -r1.48 CodeSnippetMessageSend.java --- eval/org/eclipse/jdt/internal/eval/CodeSnippetMessageSend.java 13 Jan 2006 16:37:16 -0000 1.48 +++ eval/org/eclipse/jdt/internal/eval/CodeSnippetMessageSend.java 24 Jan 2006 09:56:47 -0000 @@ -162,7 +162,7 @@ } public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) { - if (!flowInfo.isReachable()) return; + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) { // if method from parameterized type got found, use the original method at codegen time this.codegenBinding = this.binding.original(); @@ -193,7 +193,8 @@ } // Post 1.4.0 target, array clone() invocations are qualified with array type // This is handled in array type #clone method binding resolution (see Scope and UpdatedMethodBinding) - } + } + } } public TypeBinding resolveType(BlockScope scope) { // Answer the signature return type Index: eval/org/eclipse/jdt/internal/eval/CodeSnippetQualifiedNameReference.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetQualifiedNameReference.java,v retrieving revision 1.49 diff -u -r1.49 CodeSnippetQualifiedNameReference.java --- eval/org/eclipse/jdt/internal/eval/CodeSnippetQualifiedNameReference.java 13 Jan 2006 16:37:16 -0000 1.49 +++ eval/org/eclipse/jdt/internal/eval/CodeSnippetQualifiedNameReference.java 24 Jan 2006 09:56:48 -0000 @@ -499,7 +499,7 @@ int index, FlowInfo flowInfo) { - if (!flowInfo.isReachable()) return; + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0) return; // if the binding declaring class is not visible, need special action // for runtime compatibility on 1.2 VMs : change the declaring class of the binding Index: eval/org/eclipse/jdt/internal/eval/CodeSnippetSingleNameReference.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetSingleNameReference.java,v retrieving revision 1.45 diff -u -r1.45 CodeSnippetSingleNameReference.java --- eval/org/eclipse/jdt/internal/eval/CodeSnippetSingleNameReference.java 13 Jan 2006 16:37:16 -0000 1.45 +++ eval/org/eclipse/jdt/internal/eval/CodeSnippetSingleNameReference.java 24 Jan 2006 09:56:48 -0000 @@ -67,7 +67,7 @@ if (!flowInfo.isDefinitelyAssigned(localBinding = (LocalVariableBinding) this.binding)) { currentScope.problemReporter().uninitializedLocalVariable(localBinding, this); } - if (flowInfo.isReachable()) { + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) { localBinding.useFlag = LocalVariableBinding.USED; } else if (localBinding.useFlag == LocalVariableBinding.UNUSED) { localBinding.useFlag = LocalVariableBinding.FAKE_USED; @@ -567,7 +567,7 @@ return; } - if (!flowInfo.isReachable()) return; + if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0) return; //If inlinable field, forget the access emulation, the code gen will directly target it if (this.constant != Constant.NotAConstant) return; Index: model/org/eclipse/jdt/core/JavaCore.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java,v retrieving revision 1.524 diff -u -r1.524 JavaCore.java --- model/org/eclipse/jdt/core/JavaCore.java 17 Jan 2006 18:44:23 -0000 1.524 +++ model/org/eclipse/jdt/core/JavaCore.java 24 Jan 2006 09:56:49 -0000 @@ -3154,8 +3154,9 @@ IClasspathAttribute[] extraAttributes, boolean isExported) { - if (containerPath == null) Assert.isTrue(false, "Container path cannot be null"); //$NON-NLS-1$ - if (containerPath.segmentCount() < 1) { + if (containerPath == null) { + Assert.isTrue(false, "Container path cannot be null"); //$NON-NLS-1$ + } else if (containerPath.segmentCount() < 1) { Assert.isTrue( false, "Illegal classpath container path: \'" + containerPath.makeRelative().toString() + "\', must have at least one segment (containerID+hints)"); //$NON-NLS-1$//$NON-NLS-2$ Index: model/org/eclipse/jdt/internal/compiler/DocumentElementParser.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/DocumentElementParser.java,v retrieving revision 1.21 diff -u -r1.21 DocumentElementParser.java --- model/org/eclipse/jdt/internal/compiler/DocumentElementParser.java 17 Jan 2006 18:44:23 -0000 1.21 +++ model/org/eclipse/jdt/internal/compiler/DocumentElementParser.java 24 Jan 2006 09:56:49 -0000 @@ -10,25 +10,11 @@ *******************************************************************************/ package org.eclipse.jdt.internal.compiler; -/* - * A document element parser extracts structural information - * from a piece of source, providing detailed source positions info. - * - * also see @IDocumentElementRequestor - * - * The structural investigation includes: - * - the package statement - * - import statements - * - top-level types: package member, member types (member types of member types...) - * - fields - * - methods - * - * Any (parsing) problem encountered is also provided. - */ import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.env.*; import org.eclipse.jdt.internal.compiler.impl.*; +import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers; import org.eclipse.jdt.core.compiler.*; import org.eclipse.jdt.internal.compiler.ast.*; import org.eclipse.jdt.internal.compiler.parser.*; @@ -77,6 +63,8 @@ /* persisting javadoc positions */ pushOnIntArrayStack(this.getJavaDocPositions()); boolean deprecated = false; + boolean notNull = false; + boolean nullable = false; int lastCommentIndex = -1; int commentPtr = scanner.commentPtr; @@ -93,11 +81,19 @@ } deprecated = this.javadocParser.checkDeprecation(lastCommentIndex); + notNull = this.javadocParser.notNull; + nullable = this.javadocParser.nullable; break nextComment; } if (deprecated) { checkAndSetModifiers(ClassFileConstants.AccDeprecated); } + if (notNull) { + checkAndSetModifiers(ExtraCompilerModifiers.AccNotNull); + } + if (nullable) { // no else on purpose + checkAndSetModifiers(ExtraCompilerModifiers.AccNullable); + } // modify the modifier source start to point at the first comment if (commentPtr >= 0) { declarationSourceStart = scanner.commentStarts[0]; Index: model/org/eclipse/jdt/internal/compiler/SourceElementParser.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/SourceElementParser.java,v retrieving revision 1.55 diff -u -r1.55 SourceElementParser.java --- model/org/eclipse/jdt/internal/compiler/SourceElementParser.java 25 Nov 2005 16:44:45 -0000 1.55 +++ model/org/eclipse/jdt/internal/compiler/SourceElementParser.java 24 Jan 2006 09:56:49 -0000 @@ -157,6 +157,12 @@ if (this.javadocParser.checkDeprecation(lastComment)) { checkAndSetModifiers(ClassFileConstants.AccDeprecated); } + if (this.javadocParser.notNull) { + checkAndSetModifiers(ExtraCompilerModifiers.AccNotNull); + } + if (this.javadocParser.nullable) { // no else on purpose + checkAndSetModifiers(ExtraCompilerModifiers.AccNullable); + } this.javadoc = this.javadocParser.docComment; // null if check javadoc is not activated if (currentElement == null) this.lastJavadocEnd = commentEnd; } Index: model/org/eclipse/jdt/internal/compiler/SourceJavadocParser.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/SourceJavadocParser.java,v retrieving revision 1.5 diff -u -r1.5 SourceJavadocParser.java --- model/org/eclipse/jdt/internal/compiler/SourceJavadocParser.java 5 Jan 2006 08:12:31 -0000 1.5 +++ model/org/eclipse/jdt/internal/compiler/SourceJavadocParser.java 24 Jan 2006 09:56:49 -0000 @@ -133,6 +133,35 @@ } } break; + case 'n': // perhaps @notNull or @nullable tag? + switch (readChar()) { + case 'o': + if ((readChar() == 't') && + (readChar() == 'N') && (readChar() == 'u') && + (readChar() == 'l') && (readChar() == 'l')) { + // ensure the tag is properly ended: either followed by a space, a tab, line end or asterisk. + char c = readChar(); + if (Character.isWhitespace(c) || c == '*') { + this.notNull = true; + this.tagValue = TAG_NOT_NULL_VALUE; + } + } + break; + case 'u': + if ((readChar() == 'l') && + (readChar() == 'l') && (readChar() == 'a') && + (readChar() == 'b') && (readChar() == 'l') && + (readChar() == 'e')) { + // ensure the tag is properly ended: either followed by a space, a tab, line end or asterisk. + char c = readChar(); + if (Character.isWhitespace(c) || c == '*') { + this.nullable = true; + this.tagValue = TAG_NULLABLE_VALUE; + } + } + break; + } + break; } } Index: model/org/eclipse/jdt/internal/core/util/CommentRecorderParser.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/CommentRecorderParser.java,v retrieving revision 1.17 diff -u -r1.17 CommentRecorderParser.java --- model/org/eclipse/jdt/internal/core/util/CommentRecorderParser.java 20 Oct 2005 13:26:50 -0000 1.17 +++ model/org/eclipse/jdt/internal/core/util/CommentRecorderParser.java 24 Jan 2006 09:56:49 -0000 @@ -13,6 +13,7 @@ import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; +import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers; import org.eclipse.jdt.internal.compiler.parser.Parser; import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities; @@ -48,6 +49,8 @@ } boolean deprecated = false; boolean checkDeprecated = false; + boolean notNull = false; + boolean nullable = false; int lastCommentIndex = -1; //since jdk1.2 look only in the last java doc comment... @@ -67,12 +70,20 @@ // do not report problem before last parsed comment while recovering code... this.javadocParser.reportProblems = this.currentElement == null || commentSourceEnd > this.lastJavadocEnd; deprecated = this.javadocParser.checkDeprecation(lastCommentIndex); + notNull = this.javadocParser.notNull; + nullable = this.javadocParser.nullable; this.javadoc = this.javadocParser.docComment; break nextComment; } if (deprecated) { checkAndSetModifiers(ClassFileConstants.AccDeprecated); } + if (notNull) { + checkAndSetModifiers(ExtraCompilerModifiers.AccNotNull); + } + if (nullable) { // no else on purpose + checkAndSetModifiers(ExtraCompilerModifiers.AccNullable); + } // modify the modifier source start to point at the first comment if (lastCommentIndex >= 0 && checkDeprecated) { this.modifiersSourceStart = this.scanner.commentStarts[lastCommentIndex]; #P org.eclipse.jdt.core.tests Index: Eclipse Java Tests Compiler/org/eclipse/jdt/tests/compiler/regression/ConformTest.java =================================================================== RCS file: /data/cvs/eclipse/org.eclipse.jdt.core.tests/Eclipse Java Tests Compiler/org/eclipse/jdt/tests/compiler/regression/ConformTest.java,v retrieving revision 1.131 diff -u -r1.131 ConformTest.java --- Eclipse Java Tests Compiler/org/eclipse/jdt/tests/compiler/regression/ConformTest.java 13 Jan 2006 16:37:38 -0000 1.131 +++ Eclipse Java Tests Compiler/org/eclipse/jdt/tests/compiler/regression/ConformTest.java 24 Jan 2006 09:56:54 -0000 @@ -24,12 +24,13 @@ super(name); } public static Test suite() { + if (false) { TestSuite ts; //some of the tests depend on the order of this suite. ts = new TestSuite(); - ts.addTest(new ConformTest("test089")); - return new RegressionTestSetup(ts, COMPLIANCE_1_6); + ts.addTest(new ConformTest("test246")); + return new RegressionTestSetup(ts, COMPLIANCE_1_4); } return setupSuite(testClass()); } @@ -948,7 +949,8 @@ "SUCCESS"); } public void test067() { - if (this.complianceLevel.compareTo(COMPLIANCE_1_4) >= 0) return; // TODO (philippe) test is failing in 1.4 or 1.5 compliance mode + if (COMPLIANCE_1_4.equals(this.complianceLevel) + || COMPLIANCE_1_5.equals(this.complianceLevel)) return; // TODO (philippe) test is failing in 1.4 or 1.5 compliance mode this.runConformTest(new String[] { "p/F.java", "package p;\n" + @@ -3555,7 +3557,7 @@ */ public void test211() { String expectedResult; - if (this.complianceLevel.compareTo(COMPLIANCE_1_5) >= 0) { + if (COMPLIANCE_1_5.equals(this.complianceLevel)) { expectedResult = "SUCCESS"; } else { expectedResult = "SUCCESS"; @@ -6092,14 +6094,7 @@ new String[] { "X.java", "public class X {\n" + - " void foo (int n) {\n" + - " synchronized (this) {\n" + - " switch (n) {\n" + - " case 1:\n" + - " throw new NullPointerException();\n" + - " }\n" + - " }\n" + - " }\n" + + "\n" + " public static void main(String args[]) {\n" + " try {\n" + " new X().foo(1);\n" + @@ -6107,6 +6102,15 @@ " System.out.println(\"SUCCESS\"); \n" + " }\n" + " } \n" + + "\n" + + "void foo (int n) {\n" + + " synchronized (this) {\n" + + " switch (n) {\n" + + " case 1:\n" + + " throw new NullPointerException();\n" + + " }\n" + + " }\n" + + "}\n" + "}\n", }, "SUCCESS", @@ -6117,7 +6121,7 @@ null); // custom requestor String expectedOutput = - " // Method descriptor #15 (I)V\n" + + " // Method descriptor #20 (I)V\n" + " // Stack: 2, Locals: 3\n" + " void foo(int n);\n" + " 0 aload_0 [this]\n" + @@ -6127,9 +6131,9 @@ " 4 iload_1 [n]\n" + " 5 tableswitch default: 32\n" + " case 1: 24\n" + - " 24 new java.lang.NullPointerException [16]\n" + + " 24 new java.lang.NullPointerException [41]\n" + " 27 dup\n" + - " 28 invokespecial java.lang.NullPointerException() [18]\n" + + " 28 invokespecial java.lang.NullPointerException() [43]\n" + " 31 athrow\n" + " 32 aload_2\n" + " 33 monitorexit\n" + @@ -6142,11 +6146,11 @@ " [pc: 4, pc: 34] -> 37 when : any\n" + " [pc: 37, pc: 39] -> 37 when : any\n" + " Line numbers:\n" + - " [pc: 0, line: 3]\n" + - " [pc: 4, line: 4]\n" + - " [pc: 24, line: 6]\n" + - " [pc: 32, line: 3]\n" + - " [pc: 40, line: 9]\n" + + " [pc: 0, line: 12]\n" + + " [pc: 4, line: 13]\n" + + " [pc: 24, line: 15]\n" + + " [pc: 32, line: 12]\n" + + " [pc: 40, line: 18]\n" + " Local variable table:\n" + " [pc: 0, pc: 41] local: this index: 0 type: X\n" + " [pc: 0, pc: 41] local: n index: 1 type: int\n"; Index: Eclipse Java Tests Compiler/org/eclipse/jdt/tests/compiler/regression/InitializationTest.java =================================================================== RCS file: /data/cvs/eclipse/org.eclipse.jdt.core.tests/Eclipse Java Tests Compiler/org/eclipse/jdt/tests/compiler/regression/InitializationTest.java,v retrieving revision 1.92 diff -u -r1.92 InitializationTest.java --- Eclipse Java Tests Compiler/org/eclipse/jdt/tests/compiler/regression/InitializationTest.java 14 Oct 2005 22:43:21 -0000 1.92 +++ Eclipse Java Tests Compiler/org/eclipse/jdt/tests/compiler/regression/InitializationTest.java 24 Jan 2006 09:56:54 -0000 @@ -11,9 +11,19 @@ public InitializationTest(String name) { super(name); } -public static Test suite() { - return setupSuite(testClass()); -} + // Static initializer to specify tests subset using TESTS_* static variables + // All specified tests which does not belong to the class are skipped... + // Only the highest compliance level is run; add the VM argument + // -Dcompliance=1.4 (for example) to lower it if needed + static { +// TESTS_NAMES = new String[] { "test011" }; +// TESTS_NUMBERS = new int[] { 58 }; +// TESTS_RANGE = new int[] { 231, 240 }; + } + public static Test suite() { + return buildTestSuite(testClass()); + } + public void test001() { this.runConformTest( new String[] { Index: Eclipse Java Tests Compiler/org/eclipse/jdt/tests/compiler/regression/InnerClassTest.java =================================================================== RCS file: /data/cvs/eclipse/org.eclipse.jdt.core.tests/Eclipse Java Tests Compiler/org/eclipse/jdt/tests/compiler/regression/InnerClassTest.java,v retrieving revision 1.30 diff -u -r1.30 InnerClassTest.java --- Eclipse Java Tests Compiler/org/eclipse/jdt/tests/compiler/regression/InnerClassTest.java 13 Jan 2006 16:37:38 -0000 1.30 +++ Eclipse Java Tests Compiler/org/eclipse/jdt/tests/compiler/regression/InnerClassTest.java 24 Jan 2006 09:56:54 -0000 @@ -4,19 +4,11 @@ import org.eclipse.jdt.core.tests.compiler.regression.*; public class InnerClassTest extends AbstractRegressionTest { -static { - // Names of tests to run: can be "testBugXXXX" or "BugXXXX") -// TESTS_NAMES = new String[] { "Bug58069" }; - // Numbers of tests to run: "test" will be run for each number of this array -// TESTS_NUMBERS = new int[] { 183 }; - // Range numbers of tests to run: all tests between "test" and "test" will be run for { first, last } -// TESTS_RANGE = new int[] { 85, -1 }; -} public InnerClassTest(String name) { super(name); } public static Test suite() { - return buildTestSuite(testClass()); + return setupSuite(testClass()); } public void test001() { this.runConformTest(new String[] { @@ -8827,7 +8819,7 @@ " }\n" + " public static void main( String args[] ) {\n" + " Test02 t = new Test02();\n" + - " Test02B b = new Test02B( t ); // Doesn\'t work. MUST provide enclosing instance with t.new B().\n" + + " Test02B b = new Test02B( t ); // Dosen\'t work. MUST provide enclosing instance with t.new B().\n" + " System.out.println( b.varTest02B );\n" + " }\n" + "}", @@ -8839,7 +8831,7 @@ "Illegal enclosing instance specification for type Object\n" + "----------\n" + "2. ERROR in InnerClassTests\\PassingParameters\\Test02.java (at line 30)\n" + - " Test02B b = new Test02B( t ); // Doesn\'t work. MUST provide enclosing instance with t.new B().\n" + + " Test02B b = new Test02B( t ); // Dosen\'t work. MUST provide enclosing instance with t.new B().\n" + " ^^^^^^^^^^^^^^^^\n" + "No enclosing instance of type Test02 is accessible. Must qualify the allocation with an enclosing instance of type Test02 (e.g. x.new A() where x is an instance of Test02).\n" + "----------\n" Index: Eclipse Java Tests Compiler/org/eclipse/jdt/tests/compiler/regression/InnerEmulationTest.java =================================================================== RCS file: /data/cvs/eclipse/org.eclipse.jdt.core.tests/Eclipse Java Tests Compiler/org/eclipse/jdt/tests/compiler/regression/InnerEmulationTest.java,v retrieving revision 1.83 diff -u -r1.83 InnerEmulationTest.java --- Eclipse Java Tests Compiler/org/eclipse/jdt/tests/compiler/regression/InnerEmulationTest.java 3 Dec 2005 03:52:11 -0000 1.83 +++ Eclipse Java Tests Compiler/org/eclipse/jdt/tests/compiler/regression/InnerEmulationTest.java 24 Jan 2006 09:56:54 -0000 @@ -2791,7 +2791,7 @@ " public static void main(String[] args) { \n"+ " System.out.print(\"SUCCESS\"); \n"+ " } \n"+ - "}" + "} \n" }, "SUCCESS"); } @@ -4542,7 +4542,7 @@ "bar"); // ensure synthetic access method got generated for enclosing field String expectedOutput = - " // Method descriptor #6 ()V\n" + + " // Method descriptor #6 ()V\n" + " // Stack: 1, Locals: 1\n" + " X$Z();\n" + " 0 aload_0 [this]\n" + Index: Eclipse Java Tests Compiler/org/eclipse/jdt/tests/compiler/regression/NegativeTest.java =================================================================== RCS file: /data/cvs/eclipse/org.eclipse.jdt.core.tests/Eclipse Java Tests Compiler/org/eclipse/jdt/tests/compiler/regression/NegativeTest.java,v retrieving revision 1.279 diff -u -r1.279 NegativeTest.java --- Eclipse Java Tests Compiler/org/eclipse/jdt/tests/compiler/regression/NegativeTest.java 13 Jan 2006 16:37:38 -0000 1.279 +++ Eclipse Java Tests Compiler/org/eclipse/jdt/tests/compiler/regression/NegativeTest.java 24 Jan 2006 09:56:55 -0000 @@ -9,15 +9,6 @@ import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; public class NegativeTest extends AbstractRegressionTest { -// Use this static initializer to specify subset for tests -// All specified tests which does not belong to the class are skipped... -static { -// TESTS_PREFIX = "testBug95521"; -// TESTS_NAMES = new String[] { "testBug83127a" }; -// TESTS_NUMBERS = new int[] { 218 }; -// TESTS_RANGE = new int[] { 23, -1 }; -} - public NegativeTest(String name) { super(name); } Index: Eclipse Java Tests Compiler/org/eclipse/jdt/tests/compiler/regression/RunPrivateCompilerTests.java =================================================================== RCS file: /data/cvs/eclipse/org.eclipse.jdt.core.tests/Eclipse Java Tests Compiler/org/eclipse/jdt/tests/compiler/regression/RunPrivateCompilerTests.java,v retrieving revision 1.2 diff -u -r1.2 RunPrivateCompilerTests.java --- Eclipse Java Tests Compiler/org/eclipse/jdt/tests/compiler/regression/RunPrivateCompilerTests.java 13 Jan 2006 16:37:38 -0000 1.2 +++ Eclipse Java Tests Compiler/org/eclipse/jdt/tests/compiler/regression/RunPrivateCompilerTests.java 24 Jan 2006 09:56:55 -0000 @@ -40,9 +40,6 @@ if ((possibleComplianceLevels & AbstractCompilerTest.F_1_5) != 0) { all.addTest(AbstractCompilerTest.suiteForComplianceLevel(AbstractCompilerTest.COMPLIANCE_1_5, RegressionTestSetup.class, standardTests)); } - if ((possibleComplianceLevels & AbstractCompilerTest.F_1_6) != 0) { - all.addTest(AbstractCompilerTest.suiteForComplianceLevel(AbstractCompilerTest.COMPLIANCE_1_6, RegressionTestSetup.class, standardTests)); - } return all; } } #P org.eclipse.jdt.core.tests.compiler Index: src/org/eclipse/jdt/core/tests/compiler/regression/AssignmentTest.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AssignmentTest.java,v retrieving revision 1.25 diff -u -r1.25 AssignmentTest.java --- src/org/eclipse/jdt/core/tests/compiler/regression/AssignmentTest.java 4 Jan 2006 16:06:24 -0000 1.25 +++ src/org/eclipse/jdt/core/tests/compiler/regression/AssignmentTest.java 24 Jan 2006 09:57:07 -0000 @@ -129,433 +129,8 @@ "The assignment to variable next has no effect\n" + "----------\n"); } -/* - * check null/non-null reference diagnosis - */ -public void test003() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " X foo(X x) {\n" + - " x.foo(null);\n" + - " if (x == null) {\n" + - " x.foo(null);\n" + - " }\n" + - " return this;\n" + - " }\n" + - "}\n", - }, - "----------\n" + - "1. ERROR in X.java (at line 4)\n" + - " if (x == null) {\n" + - " ^\n" + - "The variable x cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + - "----------\n" + - "2. ERROR in X.java (at line 5)\n" + - " x.foo(null);\n" + - " ^\n" + - "The variable x can only be null; it was either set to null or checked for null when last used\n" + - "----------\n"); -} -public void test004() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " X foo(X x) {\n" + - " x.foo(null); // 0\n" + - " if (x != null) { // 1\n" + - " if (x == null) { // 2\n" + - " x.foo(null); // 3\n" + - " } else if (x instanceof X) { // 4\n" + - " x.foo(null); // 5 \n" + - " } else if (x != null) { // 6\n" + - " x.foo(null); // 7\n" + - " }\n" + - " x.foo(null); // 8\n" + - " }\n" + - " return this;\n" + - " }\n" + - "}\n", - }, - "----------\n" + - "1. ERROR in X.java (at line 4)\n" + - " if (x != null) { // 1\n" + - " ^\n" + - "The variable x cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + - "----------\n" + - "2. ERROR in X.java (at line 5)\n" + - " if (x == null) { // 2\n" + - " ^\n" + - "The variable x cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + - "----------\n" + - "3. ERROR in X.java (at line 6)\n" + - " x.foo(null); // 3\n" + - " ^\n" + - "The variable x can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" + - "4. ERROR in X.java (at line 9)\n" + - " } else if (x != null) { // 6\n" + - " ^\n" + - "The variable x cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + - "----------\n"); -} -public void test005() { - this.runConformTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo(Class c) {\n" + - " if (c.isArray() ) {\n" + - " } else if (c == java.lang.String.class ) {\n" + - " }\n" + - " }\n" + - "}\n", - }, - ""); -} -public void test006() { - this.runConformTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo(X x) {\n" + - " if (x == this)\n" + - " return;\n" + - " x.foo(this);\n" + - " }\n" + - "}\n", - }, - ""); -} -public void test007() { - this.runConformTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo(X x, X x2) {\n" + - " if (x != null)\n" + - " return;\n" + - " x = x2;\n" + - " if (x == null) {\n" + - " }\n" + - " }\n" + - "}\n", - }, - ""); -} -public void test008() { - this.runConformTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo(X x, X x2) {\n" + - " if (x != null)\n" + - " return;\n" + - " try {\n" + - " x = x2;\n" + - " } catch(Exception e) {}\n" + - " if (x == null) {\n" + - " }\n" + - " }\n" + - "}\n", - }, - ""); -} -// TODO (philippe) reenable once fixed -public void _test009() { - this.runNegativeTest( - new String[] { - "X.java", - "import java.io.File;\n" + - "\n" + - "public class X {\n" + - " boolean check(String name) { return true; }\n" + - " Class bar(String name) throws ClassNotFoundException { return null; }\n" + - " File baz(String name) { return null; }\n" + - " \n" + - " public Class foo(String name, boolean resolve) throws ClassNotFoundException {\n" + - " \n" + - " Class c = bar(name);\n" + - " if (c != null)\n" + - " return c;\n" + - " if (check(name)) {\n" + - " try {\n" + - " c= bar(name);\n" + - " return c;\n" + - " } catch (ClassNotFoundException e) {\n" + - " // keep searching\n" + - " // only path to here left c unassigned from try block, means it was assumed to be null\n" + - " }\n" + - " }\n" + - " if (c == null) {// should complain: c can only be null\n" + - " File file= baz(name);\n" + - " if (file == null)\n" + - " throw new ClassNotFoundException();\n" + - " }\n" + - " return c;\n" + - " }\n" + - "\n" + - "}\n", - }, - "----------\n" + - "1. ERROR in X.java (at line 22)\n" + - " if (c == null) {// should complain: c can only be null\n" + - " ^\n" + - "The variable c can only be null; it was either set to null or checked for null when last used\n" + - "----------\n"); -} -public void test010() { - this.runConformTest( - new String[] { - "X.java", - "public class X {\n" + - "\n" + - " X itself() { return this; }\n" + - "\n" + - " void bar() {\n" + - " X itself = this.itself();\n" + - " if (this == itself) {\n" + - " System.out.println(itself.toString()); //1\n" + - " } else {\n" + - " System.out.println(itself.toString()); //2\n" + - " }\n" + - " }\n" + - "}\n", - }, - ""); -} -public void test011() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - "\n" + - " X itself() { return this; }\n" + - "\n" + - " void bar() {\n" + - " X itself = this.itself();\n" + - " if (this == itself) {\n" + - " X other = (X)itself;\n" + - " if (other != null) {\n" + - " }\n" + - " if (other == null) {\n" + - " }\n" + - " }\n" + - " }\n" + - "}\n", - }, - "----------\n" + - "1. ERROR in X.java (at line 9)\n" + - " if (other != null) {\n" + - " ^^^^^\n" + - "The variable other cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + - "----------\n"); -} -public void test012() { - this.runConformTest( - new String[] { - "X.java", - "public class X {\n" + - " \n" + - " void foo() {\n" + - " Object o = null;\n" + - " do {\n" + - " if (o == null) return;\n" + - " o = bar();\n" + - " } while (true);\n" + - " }\n" + - " X bar() { \n" + - " return null; \n" + - " }\n" + - "}", - }, - ""); -} -public void test013() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo(X x) {\n" + - " if (x == this) {\n" + - " if (x == null) {\n" + - " x.foo(this);\n" + - " }\n" + - " }\n" + - " }\n" + - "}\n", - }, - "----------\n" + - "1. ERROR in X.java (at line 4)\n" + - " if (x == null) {\n" + - " ^\n" + - "The variable x cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + - "----------\n" + - "2. ERROR in X.java (at line 5)\n" + - " x.foo(this);\n" + - " ^\n" + - "The variable x can only be null; it was either set to null or checked for null when last used\n" + - "----------\n"); -} -public void test014() { - this.runConformTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo(X x) {\n" + - " x = null;\n" + - " try {\n" + - " x = this;\n" + - " } finally {\n" + - " if (x != null) {\n" + - " x.foo(null);\n" + - " }\n" + - " }\n" + - " }\n" + - "}\n", - }, - ""); -} -public void test015() { - this.runConformTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Object o = null;\n" + - " int i = 1;\n" + - " switch (i) {\n" + - " case 1:\n" + - " o = new Object();\n" + - " break;\n" + - " }\n" + - " if (o != null)\n" + - " o.toString();\n" + - " }\n" + - "}\n", - }, - ""); -} -public void test016() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo(X x) {\n" + - " x = null;\n" + - " try {\n" + - " x = null;\n" + - " } finally {\n" + - " if (x != null) {\n" + - " x.foo(null);\n" + - " }\n" + - " }\n" + - " }\n" + - "}\n", - }, - "----------\n" + - "1. ERROR in X.java (at line 5)\n" + - " x = null;\n" + - " ^\n" + - "The variable x can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" + - "2. ERROR in X.java (at line 7)\n" + - " if (x != null) {\n" + - " ^\n" + - "The variable x can only be null; it was either set to null or checked for null when last used\n" + - "----------\n"); -} -public void test017() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo(X x) {\n" + - " x = this;\n" + - " try {\n" + - " x = null;\n" + - " } finally {\n" + - " if (x == null) {\n" + - " x.foo(null);\n" + - " }\n" + - " }\n" + - " }\n" + - "}\n", - }, - "----------\n" + - "1. ERROR in X.java (at line 7)\n" + - " if (x == null) {\n" + - " ^\n" + - "The variable x can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" + - "2. ERROR in X.java (at line 8)\n" + - " x.foo(null);\n" + - " ^\n" + - "The variable x can only be null; it was either set to null or checked for null when last used\n" + - "----------\n"); -} -public void test018() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " \n" + - " void foo() {\n" + - " Object o = null;\n" + - " do {\n" + - " if (o != null) return;\n" + - " o = null;\n" + - " } while (true);\n" + - " }\n" + - " X bar() { \n" + - " return null; \n" + - " }\n" + - "}", - }, - "----------\n" + - "1. ERROR in X.java (at line 6)\r\n" + - " if (o != null) return;\r\n" + - " ^\n" + - "The variable o can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" + - "2. ERROR in X.java (at line 7)\r\n" + - " o = null;\r\n" + - " ^\n" + - "The variable o can only be null; it was either set to null or checked for null when last used\n" + - "----------\n"); -} -public void test019() { - this.runConformTest( - new String[] { - "X.java", - "public class X {\n" + - " public static final char[] replaceOnCopy(\n" + - " char[] array,\n" + - " char toBeReplaced,\n" + - " char replacementChar) {\n" + - " \n" + - " char[] result = null;\n" + - " for (int i = 0, length = array.length; i < length; i++) {\n" + - " char c = array[i];\n" + - " if (c == toBeReplaced) {\n" + - " if (result == null) {\n" + - " result = new char[length];\n" + - " System.arraycopy(array, 0, result, 0, i);\n" + - " }\n" + - " result[i] = replacementChar;\n" + - " } else if (result != null) {\n" + - " result[i] = c;\n" + - " }\n" + - " }\n" + - " if (result == null) return array;\n" + - " return result;\n" + - " }\n" + - "}\n", - }, - ""); -} + +// final multiple assignment public void test020() { this.runNegativeTest( new String[] { @@ -582,320 +157,8 @@ "The final local variable v may already have been assigned\n" + "----------\n"); } -public void test021() { - this.runConformTest( - new String[] { - "X.java", - "public class X {\n" + - " int kind;\n" + - " X parent;\n" + - " Object[] foo() { return null; }\n" + - " private void findTypeParameters(X scope) {\n" + - " Object[] typeParameters = null;\n" + - " while (scope != null) {\n" + - " typeParameters = null;\n" + - " switch (scope.kind) {\n" + - " case 0 :\n" + - " typeParameters = foo();\n" + - " break;\n" + - " case 1 :\n" + - " typeParameters = foo();\n" + - " break;\n" + - " case 2 :\n" + - " return;\n" + - " }\n" + - " if(typeParameters != null) {\n" + - " foo();\n" + - " }\n" + - " scope = scope.parent;\n" + - " }\n" + - " }\n" + - "}\n", - }, - ""); -} -public void test022() { - this.runConformTest( - new String[] { - "X.java", - "public class X {\n" + - " \n" + - " boolean bool() { return true; }\n" + - " void doSomething() {}\n" + - " \n" + - " void foo() {\n" + - " Object progressJob = null;\n" + - " while (bool()) {\n" + - " if (bool()) {\n" + - " if (progressJob != null)\n" + - " progressJob = null;\n" + - " doSomething();\n" + - " }\n" + - " try {\n" + - " if (progressJob == null) {\n" + - " progressJob = new Object();\n" + - " }\n" + - " } finally {\n" + - " doSomething();\n" + - " }\n" + - " }\n" + - " }\n" + - "}", - }, - ""); -} -public void test023() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - "\n" + - " void foo() {\n" + - " Object o = new Object();\n" + - " while (this != null) {\n" + - " try {\n" + - " o = null;\n" + - " break;\n" + - " } finally {\n" + - " o = new Object();\n" + - " }\n" + - " }\n" + - " if (o == null) return;\n" + - " }\n" + - "}\n", - }, - "----------\n" + - "1. ERROR in X.java (at line 13)\n" + - " if (o == null) return;\n" + - " ^\n" + - "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + - "----------\n"); -} -public void test024() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " \n" + - " boolean bool() { return true; }\n" + - " void doSomething() {}\n" + - " \n" + - " void foo() {\n" + - " Object progressJob = null;\n" + - " while (bool()) {\n" + - " if (progressJob != null)\n" + - " progressJob = null;\n" + - " doSomething();\n" + - " try {\n" + - " if (progressJob == null) {\n" + - " progressJob = new Object();\n" + - " }\n" + - " } finally {\n" + - " doSomething();\n" + - " }\n" + - " }\n" + - " }\n" + - "}", - }, - "----------\n" + - "1. ERROR in X.java (at line 13)\n" + - " if (progressJob == null) {\n" + - " ^^^^^^^^^^^\n" + - "The variable progressJob can only be null; it was either set to null or checked for null when last used\n" + - "----------\n"); -} -public void test025() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " \n" + - " void foo() {\n" + - " Object o;\n" + - " try {\n" + - " o = null;\n" + - " } finally {\n" + - " o = new Object();\n" + - " }\n" + - " if (o == null) return;\n" + - " }\n" + - "}\n", - }, - "----------\n" + - "1. ERROR in X.java (at line 10)\n" + - " if (o == null) return;\n" + - " ^\n" + - "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + - "----------\n"); -} -// TODO (philippe) reenable once fixed -public void _test026() { - this.runConformTest( - new String[] { - "X.java", - "public class X {\n" + - " \n" + - " public static void main(String[] args) {\n" + - " Object o;\n" + - " try {\n" + - " o = null;\n" + - " } finally {\n" + - " if (args == null) o = new Object();\n" + - " }\n" + - " if (o == null) System.out.println(\"SUCCESS\");\n" + - " }\n" + - "}\n", - }, - "SUCCESS"); -} -// TODO (philippe) reenable once fixed -public void _test027() { - this.runConformTest( - new String[] { - "X.java", - "public class X {\n" + - " boolean b;\n" + - " void foo() {\n" + - " Object o = null;\n" + - " while (b) {\n" + - " try {\n" + - " o = null;\n" + - " } finally {\n" + - " if (o == null) \n" + - " o = new Object();\n" + - " }\n" + - " }\n" + - " if (o == null) return;\n" + - " }\n" + - "}\n", - }, - ""); -} -// TODO (philippe) reenable once fixed -public void _test028() { - this.runConformTest( - new String[] { - "X.java", - "public class X {\n" + - " boolean b;\n" + - " void foo() {\n" + - " Object o = null;\n" + - " while (b) {\n" + - " try {\n" + - " o = null;\n" + - " break;\n" + - " } finally {\n" + - " if (o == null) \n" + - " o = new Object();\n" + - " }\n" + - " }\n" + - " if (o == null) return;\n" + - " }\n" + - "}\n", - }, - ""); -} -public void test029() { - this.runConformTest( - new String[] { - "X.java", - "public class X {\n" + - " public static void main(String[] args) {\n" + - " Object o = null;\n" + - " int i = 0;\n" + - " while (i++ < 2) {\n" + - " try {\n" + - " if (i == 2) return;\n" + - " o = null;\n" + - " } finally {\n" + - " if (i == 2) System.out.println(o);\n" + - " if (o == null) \n" + - " o = \"SUCCESS\";\n" + - " }\n" + - " }\n" + - " if (o == null) return;\n" + - " }\n" + - "}\n", - }, - "SUCCESS"); -} -public void test030() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " \n" + - " void foo() {\n" + - " Object a = null;\n" + - " while (true) {\n" + - " a = null;\n" + - " if (a == null) {\n" + - " System.out.println();\n" + - " }\n" + - " a = new Object();\n" + - " break;\n" + - " }\n" + - " }\n" + - "}\n", - }, - "----------\n" + - "1. ERROR in X.java (at line 7)\n" + - " if (a == null) {\n" + - " ^\n" + - "The variable a can only be null; it was either set to null or checked for null when last used\n" + - "----------\n"); -} -// TODO (philippe) reenable once fixed -public void _test031() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " \n" + - " void foo() {\n" + - " Object a = null;\n" + - " while (true) {\n" + - " a = null;\n" + - " if (a == null) {\n" + - " System.out.println();\n" + - " }\n" + - " a = new Object();\n" + - " break;\n" + - " }\n" + - " if (a == null) {\n" + - " System.out.println();\n" + - " }\n" + - " }\n" + - "}\n", - }, - "----------\n" + - "1. ERROR in X.java (at line 7)\n" + - " if (a == null) {\n" + - " ^\n" + - "The variable a can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" + - "2. ERROR in X.java (at line 13)\n" + - " if (a == null) {\n" + - " ^\n" + - "The variable a cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + - "----------\n"); -} -public void test032() { - this.runConformTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Object o1 = this;\n" + - " Object o3;\n" + - " while (o1 != null && (o3 = o1) != null) {\n" + - " o1 = o3;\n" + - " }\n" + - " }\n" + - "}\n", - }, - ""); -} + +// null part has been repeated into NullReferenceTest#test1033 public void test033() { this.runNegativeTest( new String[] { @@ -917,17 +180,23 @@ "}\n", }, "----------\n" + - "1. ERROR in X.java (at line 9)\n" + + "1. ERROR in X.java (at line 7)\n" + + " }while(a!=null);\n" + + " ^\n" + + "The variable a cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n" + + "2. ERROR in X.java (at line 9)\n" + " if(a!=null)\n" + " ^\n" + - "The variable a cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "The variable a can only be null; it was either set to null or checked for null when last used\n" + "----------\n" + - "2. ERROR in X.java (at line 13)\n" + + "3. ERROR in X.java (at line 13)\n" + " System.out.println(a+b);\n" + " ^\n" + "The local variable b may not have been initialized\n" + "----------\n"); } + //https://bugs.eclipse.org/bugs/show_bug.cgi?id=84215 //TODO (philippe) should move to InitializationTest suite public void test034() { @@ -1293,7 +562,7 @@ " {\n" + " try\n" + " {\n" + - " rs.toString();\n" + + " rs.toString();\n" + " }\n" + " catch (Exception ex)\n" + " {\n" + @@ -1357,10 +626,15 @@ "}\n", }, "----------\n" + - "1. ERROR in X.java (at line 8)\r\n" + - " if (o == null) {\r\n" + + "1. ERROR in X.java (at line 7)\n" + + " } while (o != null);\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n" + + "2. ERROR in X.java (at line 8)\n" + + " if (o == null) {\n" + " ^\n" + - "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + "----------\n"); } //https://bugs.eclipse.org/bugs/show_bug.cgi?id=93588 Index: src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java,v retrieving revision 1.35 diff -u -r1.35 BatchCompilerTest.java --- src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java 24 Jan 2006 09:05:43 -0000 1.35 +++ src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java 24 Jan 2006 09:57:08 -0000 @@ -2530,6 +2530,55 @@ false); } +// null ref option +public void test044(){ + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o = null;\n" + + " o.toString();\n" + + " }\n" + + "}"}, + "\"" + OUTPUT_DIR + File.separator + "X.java\"" + + " -1.5 -g -preserveAllLocals" + + " -bootclasspath " + JRE_HOME_DIR + "/lib/rt.jar" + + " -cp " + JRE_HOME_DIR + "/lib/jce.jar" + + " -warn:+null" + + " -proceedOnError -referenceInfo -d \"" + OUTPUT_DIR + "\"", + "", + "----------\n" + + "1. WARNING in ---OUTPUT_DIR_PLACEHOLDER---" + File.separator + "X.java\n" + + " (at line 4)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n" + + "1 problem (1 warning)", true); +} + +// null ref option +public void test045(){ + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o = null;\n" + + " o.toString();\n" + + " }\n" + + "}"}, + "\"" + OUTPUT_DIR + File.separator + "X.java\"" + + " -1.5 -g -preserveAllLocals" + + " -bootclasspath " + JRE_HOME_DIR + "/lib/rt.jar" + + " -cp " + JRE_HOME_DIR + "/lib/jce.jar" + + " -warn:-null" // contrast with test036 + + " -proceedOnError -referenceInfo -d \"" + OUTPUT_DIR + "\"", + "", + "", true); +} + public static Class testClass() { return BatchCompilerTest.class; } Index: src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceTest.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceTest.java,v retrieving revision 1.4 diff -u -r1.4 NullReferenceTest.java --- src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceTest.java 9 Sep 2005 13:20:34 -0000 1.4 +++ src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceTest.java 24 Jan 2006 09:57:13 -0000 @@ -12,2527 +12,8991 @@ import java.util.Map; +import org.eclipse.jdt.internal.compiler.flow.FlowInfo; +import org.eclipse.jdt.internal.compiler.flow.UnconditionalFlowInfo; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; +import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; +import org.eclipse.jdt.internal.core.Assert.AssertionFailedException; +import junit.framework.AssertionFailedError; import junit.framework.Test; public class NullReferenceTest extends AbstractRegressionTest { - public NullReferenceTest(String name) { - super(name); - } +public NullReferenceTest(String name) { + super(name); +} // Static initializer to specify tests subset using TESTS_* static variables - // All specified tests which does not belong to the class are skipped... - // Only the highest compliance level is run; add the VM argument - // -Dcompliance=1.4 (for example) to lower it if needed - static { -// TESTS_NAMES = new String[] { "test011" }; -// TESTS_NUMBERS = new int[] { 2 }; -// TESTS_RANGE = new int[] { 231, 240 }; - } - public static Test suite() { - return buildTestSuite(testClass()); - } - - public static Class testClass() { - return NullReferenceTest.class; - } + // All specified tests which does not belong to the class are skipped... + // Only the highest compliance level is run; add the VM argument + // -Dcompliance=1.4 (for example) to lower it if needed + static { +// TESTS_NAMES = new String[] { "test011" }; +// TESTS_NUMBERS = new int[] { 729 }; +// TESTS_NUMBERS = new int[] { 2999 }; +// TESTS_RANGE = new int[] { 2050, -1 }; +// TESTS_RANGE = new int[] { 1, 2049 }; +// TESTS_RANGE = new int[] { 449, 451 }; +// TESTS_RANGE = new int[] { 900, 999 }; + } - // Augment problem detection settings - protected Map getCompilerOptions() { - Map defaultOptions = super.getCompilerOptions(); - defaultOptions.put(CompilerOptions.OPTION_ReportNullReference, CompilerOptions.WARNING); - defaultOptions.put(CompilerOptions.OPTION_ReportNoEffectAssignment, CompilerOptions.WARNING); - return defaultOptions; - } - - // null analysis -- simple case for local - public void test0001_simple_local() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Object o = null;\n" + - " o.toString();\n" + - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 4)\n" + - " o.toString();\n" + - " ^\n" + - "The variable o can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); - } - - // null analysis -- simple case for field - // despite the fact that a separate thread may update the field, - // a comprehensive warning policy could point this case as potentially - // harmful -- this is not the current design, thow; it takes a - // conservative approach and leaves fields out of the analysis altogether - // TODO (maxime) reset diagnostic once supported - public void test0002_simple_field() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " Object o;\n" + - " void foo() {\n" + - " o = null;\n" + - " o.toString();\n" + - " }\n" + - "}\n"}, +public static Test suite() { + return buildTestSuite(testClass()); +} + +public static Class testClass() { + return NullReferenceTest.class; +} + +// Augment problem detection settings +protected Map getCompilerOptions() { + Map defaultOptions = super.getCompilerOptions(); +// defaultOptions.put(CompilerOptions.OPTION_ReportNullReference, CompilerOptions.WARNING); + defaultOptions.put(CompilerOptions.OPTION_ReportNullReference, CompilerOptions.ERROR); +// defaultOptions.put(CompilerOptions.OPTION_ReportNoEffectAssignment, CompilerOptions.WARNING); + return defaultOptions; +} + +// null analysis -- simple case for local +public void test0001_simple_local() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o = null;\n" + + " o.toString();\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- simple case for field +// the current design leaves fields out of the analysis altogether +public void test0002_simple_field() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " Object o;\n" + + " void foo() {\n" + + " o = null;\n" + + " o.toString();\n" + + " }\n" + + "}\n"}, + "" +// "----------\n" + +// "1. ERROR in X.java (at line 5)\n" + +// " o.toString();\n" + +// " ^\n" + +// "The field o is likely null; it was either set to null or checked for null when last used\n" + +// "----------\n" + ); +} + +// null analysis -- simple case for parameter +public void test0003_simple_parameter() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o) {\n" + + " o = null;\n" + + " o.toString();\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- final local +public void test0004_final_local() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " final Object o = null;\n" + + " o.toString();\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- final local +public void test0005_final_local() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " final Object o;\n" + + " o.toString();\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " o.toString();\n" + + " ^\n" + + "The local variable o may not have been initialized\n" + + // hides the null related message, but complains once, which is good + "----------\n"); +} + +// null analysis -- final local +public void test0006_final_local() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " final Object o = null;\n" + + " if (o != null) { /* */ }\n" + // complain + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " if (o != null) { /* */ }\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- local with member +public void test0007_local_with_member() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " Object m;\n" + + " void foo() {\n" + + " X x = null;\n" + + " x.m.toString();\n" + // complain + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " x.m.toString();\n" + + " ^^^\n" + + "The variable x can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- local with member +// REVIEW le diagnostic montre x.m au lieu de x ; noter que c'est aussi le cas pour +// REVIEW le message "n'a pas été initialisé" ; ouvrir un bug dédié ? +// REVIEW la cause est que QualifiedNameReference est l'ASTNode concerné, et que l'on +// REVIEW n'a pas de support pour en montrer une sous-partie +public void test0008_local_with_member() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " Object m;\n" + + " void foo() {\n" + + " X x = null;\n" + + " System.out.println(x.m);\n" + // complain + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " System.out.println(x.m);\n" + + " ^^^\n" + + "The variable x can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- local with member +public void test0009_local_with_member() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " Object m;\n" + + " void foo(X x) {\n" + + " x.m.toString();\n" + // quiet + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- field +public void test0010_field_with_method_call() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " Object o;\n" + + " void foo() {\n" + + " o = null;\n" + + " bar();\n" + // defuses null by side effect + " o.toString();\n" + + " }\n" + + " void bar() {\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- field +public void test0011_field_with_method_call() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " static Object o;\n" + + " void foo() {\n" + + " o = null;\n" + + " bar();\n" + // defuses null by side effect + " o.toString();\n" + + " }\n" + + " static void bar() {\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- field +public void test0012_field_with_method_call() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " Object o;\n" + + " void foo() {\n" + + " o = null;\n" + + " bar();\n" + + " o.toString();\n" + + " }\n" + + " static void bar() {\n" + + " }\n" + + "}\n"}, + "" // still ok because the class may hold a pointer to this + ); +} + +// null analysis -- field +public void test0013_field_with_method_call() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " static Object o;\n" + + " void foo() {\n" + + " o = null;\n" + + " bar();\n" + + " o.toString();\n" + + " }\n" + + " void bar() {\n" + + " }\n" + + "}\n"}, + "" // still ok because this may place a static call upon X + ); +} + +// null analysis -- field +public void test0014_field_with_explicit_this_access() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " Object o;\n" + + " void foo() {\n" + + " o = null;\n" + + " this.o.toString();\n" + + " }\n" + + "}\n"}, "" -// "----------\n" + -// "1. WARNING in X.java (at line 5)\n" + -// " o.toString();\n" + -// " ^\n" + -// "The field o is likely null; it was either set to null or checked for null when last used\n" + -// "----------\n" - ); - } +// "----------\n" + +// "1. ERROR in X.java (at line 5)\n" + +// " this.o.toString();\n" + +// " ^^^^^^\n" + +// "The field o is likely null; it was either set to null or checked for null when last used\n" + +// "----------\n" + ); +} - // null analysis -- simple case for parameter - public void test0003_simple_parameter() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo(Object o) {\n" + - " o = null;\n" + - " o.toString();\n" + - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 4)\n" + - " o.toString();\n" + - " ^\n" + - "The variable o can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); - } +// null analysis -- field +public void test0015_field_with_explicit_this_access() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " Object o;\n" + + " void foo() {\n" + + " this.o = null;\n" + + " o.toString();\n" + + " }\n" + + "}\n"}, + "" +// "----------\n" + +// "1. ERROR in X.java (at line 5)\n" + +// " o.toString();\n" + +// " ^\n" + +// "The field o is likely null; it was either set to null or checked for null when last used\n" + +// "----------\n" + ); +} - // null analysis -- field - public void test0004_field_with_method_call() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " Object o;\n" + - " void foo() {\n" + - " o = null;\n" + - " bar();\n" + // defuses null by side effect - " o.toString();\n" + - " }\n" + - " void bar() {\n" + - " }\n" + - "}\n"}, - "" - ); - } +// null analysis -- field +public void test0016_field_of_another_object() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " Object o;\n" + + " void foo() {\n" + + " X other = new X();\n" + + " other.o = null;\n" + + " other.o.toString();\n" + + " }\n" + + "}\n"}, + ""); +} - // null analysis -- field - public void test0005_field_with_method_call() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " static Object o;\n" + - " void foo() {\n" + - " o = null;\n" + - " bar();\n" + // defuses null by side effect - " o.toString();\n" + - " }\n" + - " static void bar() {\n" + - " }\n" + - "}\n"}, - "" - ); - } +// null analysis -- field +public void test0017_field_of_another_object() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " Object o;\n" + + " void foo() {\n" + + " X other = this;\n" + + " o = null;\n" + + " other.o.toString();\n" + + " }\n" + + "}\n"}, + ""); +} - // null analysis -- field - public void test0006_field_with_method_call() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " Object o;\n" + - " void foo() {\n" + - " o = null;\n" + - " bar();\n" + - " o.toString();\n" + - " }\n" + - " static void bar() {\n" + - " }\n" + - "}\n"}, - "" // still ok because the class may hold a pointer to this - ); - } +// null analysis -- field +public void test0018_field_of_enclosing_object() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " Object o;\n" + + " public class Y {\n" + + " void foo() {\n" + + " X.this.o = null;\n" + + " X.this.o.toString();\n" + // complain + " }\n" + + " }\n" + + "}\n"}, + "" +// "----------\n" + +// "1. ERROR in X.java (at line 6)\n" + +// " X.this.o.toString();\n" + +// " ^^^^^^^^\n" + +// "The field o is likely null; it was either set to null or checked for null when last used\n" + +// "----------\n" + ); +} - // null analysis -- field - public void test0007_field_with_method_call() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " static Object o;\n" + - " void foo() {\n" + - " o = null;\n" + - " bar();\n" + - " o.toString();\n" + - " }\n" + - " void bar() {\n" + - " }\n" + - "}\n"}, - "" // still ok because this may place a static call upon X - ); - } - - // null analysis -- field - // TODO (maxime) reset diagnostic once supported - public void test0008_field_with_explicit_this_access() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " Object o;\n" + - " void foo() {\n" + - " o = null;\n" + - " this.o.toString();\n" + - " }\n" + - "}\n"}, - "" -// "----------\n" + -// "1. WARNING in X.java (at line 5)\n" + -// " this.o.toString();\n" + -// " ^^^^^^\n" + -// "The field o is likely null; it was either set to null or checked for null when last used\n" + -// "----------\n" - ); - } +// null analysis -- fields +// check that fields that are protected against concurrent access +// behave as locals when no call to further methods can affect them +public void test0019_field_synchronized() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " Object o;\n" + + " public synchronized void foo() {\n" + + " o = null;\n" + + " o.toString();\n" + + " }\n" + + " void bar() {/* */}\n" + + "}\n"}, + "" +// "----------\n" + +// "1. ERROR in X.java (at line 5)\n" + +// " o.toString();\n" + +// " ^\n" + +// "The field o is likely null; it was either set to null or checked for null when last used\n" + +// "----------\n" + ); +} - // null analysis -- field - // TODO (maxime) reset diagnostic once supported - public void test0009_field_with_explicit_this_access() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " Object o;\n" + - " void foo() {\n" + - " this.o = null;\n" + - " o.toString();\n" + - " }\n" + - "}\n"}, - "" -// "----------\n" + -// "1. WARNING in X.java (at line 5)\n" + -// " o.toString();\n" + -// " ^\n" + -// "The field o is likely null; it was either set to null or checked for null when last used\n" + -// "----------\n" - ); - } +// null analysis -- field +// check that final fields behave as locals despite calls to further +// methods +public void test0020_final_field() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " final Object o = null;\n" + + " public synchronized void foo() {\n" + + " bar();\n" + + " o.toString();\n" + + " }\n" + + " void bar() {/* */}\n" + + "}\n"}, + "" +// "----------\n" + +// "1. ERROR in X.java (at line 5)\n" + +// " o.toString();\n" + +// " ^\n" + +// "The field o is likely null; it was either set to null or checked for null when last used\n" + +// "----------\n" + ); +} - // null analysis -- field - public void test0010_field_of_another_object() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " Object o;\n" + - " void foo() {\n" + - " X other = new X();\n" + - " other.o = null;\n" + - " other.o.toString();\n" + - " }\n" + - "}\n"}, - "" - ); - } - - // null analysis -- field - public void test0011_field_of_another_object() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " Object o;\n" + - " void foo() {\n" + - " X other = this;\n" + - " o = null;\n" + - " other.o.toString();\n" + - " }\n" + - "}\n"}, - "" - ); - } +// null analysis -- field +public void test0021_final_field() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " final Object o = null;\n" + + " X () {\n" + + " bar();\n" + + " o.toString();\n" + + " }\n" + + " void bar() {/* */}\n" + + "}\n"}, + "" +// "----------\n" + +// "1. ERROR in X.java (at line 5)\n" + +// " o.toString();\n" + +// " ^\n" + +// "The field o is likely null; it was either set to null or checked for null when last used\n" + +// "----------\n" + ); +} - // null analysis -- field - // TODO (maxime) reset diagnostic once supported - public void test0012_field_of_enclosing_object() { - this.runNegativeTest( +// null analysis -- field +public void test0022_final_field() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " final Object o = new Object();\n" + + " X () {\n" + + " bar();\n" + + " if (o == null) { /* empty */ }\n" + + " }\n" + + " void bar() {/* */}\n" + + "}\n"}, + "" +// "----------\n" + +// "1. ERROR in X.java (at line 5)\n" + +// " if (o == null) { /* empty */ }\n" + +// " ^\n" + +// "The field o is likely non null; it was either set to a non-null value or assumed to be non-null when last used\n" + +// "----------\n" + ); +} + +// null analysis -- field +public void test0023_field_assignment() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " Object m;\n" + + " void foo(X x) {\n" + + " Object o = x.m;\n" + + " if (o == null) { /* */ };\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- field +public void test0024_field_cast_assignment() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " Object m;\n" + + " void foo(Object x) {\n" + + " Object o = ((X) x).m;\n" + + " if (o == null) { /* */ };\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- parameter +public void test0025_parameter() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o) {\n" + + " o.toString();\n" + // quiet: parameters have unknown value + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- suppress warnings +public void test0026_suppress_warnings() { + if (COMPLIANCE_1_5.equals(this.complianceLevel)) { + Map compilerOptions = getCompilerOptions(); + compilerOptions.put(CompilerOptions.OPTION_ReportNullReference, CompilerOptions.WARNING); + this.runConformTest( new String[] { "X.java", - "public class X {\n" + - " Object o;\n" + - " public class Y {\n" + - " void foo() {\n" + - " X.this.o = null;\n" + - " X.this.o.toString();\n" + // complain - " }\n" + + "@SuppressWarnings(\"null\")\n" + + "public class X {\n" + + " void foo() {\n" + + " Object o = null;\n" + + " o.toString();\n" + " }\n" + "}\n"}, - "" -// "----------\n" + -// "1. WARNING in X.java (at line 6)\n" + -// " X.this.o.toString();\n" + -// " ^^^^^^^^\n" + -// "The field o is likely null; it was either set to null or checked for null when last used\n" + -// "----------\n" - ); - } - - // null analysis -- fields - // check that fields that are protected against concurrent access - // behave as locals when no call to further methods can affect them - // TODO (maxime) reset diagnostic once supported - public void test0013_field_synchronized() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " Object o;\n" + - " public synchronized void foo() {\n" + - " o = null;\n" + - " o.toString();\n" + - " }\n" + - " void bar() {/* */}\n" + - "}\n"}, - "" -// "----------\n" + -// "1. WARNING in X.java (at line 5)\n" + -// " o.toString();\n" + -// " ^\n" + -// "The field o is likely null; it was either set to null or checked for null when last used\n" + -// "----------\n" - ); + "", null, true, null, compilerOptions, null); } +} - // null analysis -- field - // check that final fields behave as locals despite calls to further - // methods - // TODO (maxime) reset diagnostic once supported - public void test0014_final_field() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " final Object o = null;\n" + - " public synchronized void foo() {\n" + - " bar();\n" + - " o.toString();\n" + - " }\n" + - " void bar() {/* */}\n" + - "}\n"}, - "" -// "----------\n" + -// "1. WARNING in X.java (at line 5)\n" + -// " o.toString();\n" + -// " ^\n" + -// "The field o is likely null; it was either set to null or checked for null when last used\n" + -// "----------\n" - ); - } +// null analysis -- embedded comparison +public void test0027_embedded_comparison() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o) {\n" + + " boolean b = o != null;\n" + // shades doubts upon o + " if (b) { /* */ }\n" + + " o.toString();\n" + // complain + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n"); +} - // null analysis -- field - // TODO (maxime) reset diagnostic once supported - public void test0015_final_field() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " final Object o = null;\n" + - " X () {\n" + - " bar();\n" + - " o.toString();\n" + - " }\n" + - " void bar() {/* */}\n" + - "}\n"}, - "" -// "----------\n" + -// "1. WARNING in X.java (at line 5)\n" + -// " o.toString();\n" + -// " ^\n" + -// "The field o is likely null; it was either set to null or checked for null when last used\n" + -// "----------\n" - ); - } +// null analysis -- field +public void test0028_field_as_initializer() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " X f;\n" + + " void foo() {\n" + + " X x = f;\n" + + " if (x == null) { /* */ }\n" + + " }\n" + + "}\n"}, + ""); +} - // null analysis -- field - // TODO (maxime) reset diagnostic once supported - public void test0016_final_field() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " final Object o = new Object();\n" + - " X () {\n" + - " bar();\n" + - " if (o == null) { /* empty */ }\n" + - " }\n" + - " void bar() {/* */}\n" + - "}\n"}, - "" -// "----------\n" + -// "1. WARNING in X.java (at line 5)\n" + -// " if (o == null) { /* empty */ }\n" + -// " ^\n" + -// "The field o is likely non null; it was either set to a non-null value or assumed to be non-null when last used\n" + -// "----------\n" - ); - } +// null analysis -- field +public void test0029_field_assignment() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " Object m;\n" + + " void foo() {\n" + + " X x = null;\n" + + " x.m = new Object();\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " x.m = new Object();\n" + + " ^^^\n" + + "The variable x can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} - // null analysis -- parameter - public void test0017_parameter() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo(Object o) {\n" + - " o.toString();\n" + // quiet: parameters have unknown value - " }\n" + - "}\n"}, - "" - ); - } +// null analysis -- conditional expression +public void test0030_conditional_expression() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o = true ? null : null;\n" + + " o.toString();\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- conditional expression +public void test0031_conditional_expression() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o = true ? null : new Object();\n" + + " o.toString();\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- conditional expression +public void test0032_conditional_expression() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o = false ? null : new Object();\n" + + " o.toString();\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- conditional expression +public void test0033_conditional_expression() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o = (1 == 1) ? null : new Object();\n" + + " o.toString();\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} - // null analysis -- conditional expression - // TODO (maxime) fix - public void _test0020_conditional_expression() { +// null analysis -- conditional expression +// TODO (maxime) fix - may consider simultaneous computation of expression null status +// this case is one of those which raise the need for the simultaneous calculation of +// the null status of an expression and the code analysis of the said expression; this +// case is simplistic: we need a value (here, potentially null), that is *not* carried +// by the current embodiment of the flow info; other cases are less trivial in which +// side effects on variables could introduce errors into after the facts evaluations; +// one possible trick would be to add a slot for this +// other path: use a tainted unknown expression status; does not seem to cope well +// with o = (o == null ? new Object() : o) +public void _test0034_conditional_expression() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean b;\n" + + " void foo() {\n" + + " Object o = b ? null : new Object();\n" + + " o.toString();\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n"); +} + +// null analysis -- conditional expression +public void test0035_conditional_expression() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean b;\n" + + " void foo() {\n" + + " Object o = b ? null : new Object();\n" + + " if (o == null) { /* */ }\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- conditional expression +public void test0036_conditional_expression() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean b;\n" + + " void foo() {\n" + + " Object o = b ? null : null;\n" + + " if (o == null) { /* */ }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " if (o == null) { /* */ }\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- autoboxing +public void test0040_autoboxing_compound_assignment() { + if (COMPLIANCE_1_5.equals(this.complianceLevel)) { this.runNegativeTest( new String[] { "X.java", "public class X {\n" + - " void foo() {\n" + - " Object o = true ? null : null;\n" + - " o.toString();\n" + - " }\n" + + " void foo() {\n" + + " Integer i = null;\n" + + " i += 1;\n" + + " }\n" + "}\n"}, "----------\n" + - "1. WARNING in X.java (at line 4)\n" + - " o.toString();\n" + + "1. ERROR in X.java (at line 4)\n" + + " i += 1;\n" + " ^\n" + - "The variable o can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); + "The variable i can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); } +} - // null analysis -- conditional expression - // TODO (maxime) fix - public void _test0021_conditional_expression() { +// null analysis -- autoboxing +public void test0041_autoboxing_increment_operator() { + if (COMPLIANCE_1_5.equals(this.complianceLevel)) { this.runNegativeTest( new String[] { "X.java", "public class X {\n" + - " void foo() {\n" + - " Object o = true ? null : new Object();\n" + - " o.toString();\n" + - " }\n" + + " void foo() {\n" + + " Integer i = null;\n" + + " i++;\n" + // complain: this is null + " ++i;\n" + // quiet (because previous step guards it) + " }\n" + "}\n"}, "----------\n" + - "1. WARNING in X.java (at line 4)\n" + - " o.toString();\n" + + "1. ERROR in X.java (at line 4)\n" + + " i++;\n" + " ^\n" + - "The variable o can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); + "The variable i can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); } +} - // null analysis -- conditional expression - public void test0022_conditional_expression() { +// null analysis -- autoboxing +public void test0042_autoboxing_literal() { + if (COMPLIANCE_1_5.equals(this.complianceLevel)) { this.runNegativeTest( new String[] { "X.java", "public class X {\n" + - " void foo() {\n" + - " Object o = false ? null : new Object();\n" + - " o.toString();\n" + - " }\n" + + " void foo() {\n" + + " Integer i = 0;\n" + + " if (i == null) {};\n" + // complain: this is non null + " }\n" + "}\n"}, - "" - ); + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " if (i == null) {};\n" + + " ^\n" + + "The variable i cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); } +} - // null analysis -- conditional expression - // TODO (maxime) fix - public void _test0023_conditional_expression() { +// null analysis -- autoboxing +public void test0043_autoboxing_literal() { + if (COMPLIANCE_1_5.equals(this.complianceLevel)) { this.runNegativeTest( new String[] { "X.java", "public class X {\n" + - " void foo() {\n" + - " Object o = (1 == 1) ? null : new Object();\n" + - " o.toString();\n" + - " }\n" + + " void foo() {\n" + + " Integer i = null;\n" + + " System.out.println(i + 4);\n" + // complain: this is null + " }\n" + "}\n"}, "----------\n" + - "1. WARNING in X.java (at line 4)\n" + - " o.toString();\n" + - " ^\n" + - "The variable o can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); + "1. ERROR in X.java (at line 4)\n" + + " System.out.println(i + 4);\n" + + " ^\n" + + "The variable i can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); } - //TODO (maxime) - add case with non constant condition in conditional expression cond() ? ... : ... - - // null analysis -- autoboxing - // TODO (maxime) fix - public void _test0030_autoboxing_compound_assignment() { - if (COMPLIANCE_1_5.equals(this.complianceLevel)) { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Integer i = null;\n" + - " i += 1;\n" + - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 4)\n" + - " i += 1;\n" + - " ^\n" + - "The variable i can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); - } - } - - // null analysis -- autoboxing - // TODO (maxime) fix - public void _test0031_autoboxing_increment_operator() { - if (COMPLIANCE_1_5.equals(this.complianceLevel)) { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Integer i = null;\n" + - " i++;\n" + // complain: this is null - " ++i;\n" + // quiet (because previous step guards it) - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 4)\n" + - " i++;\n" + - " ^\n" + - "The variable i can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); - } - } - - // null analysis -- autoboxing - public void test0032_autoboxing_literal() { - if (COMPLIANCE_1_5.equals(this.complianceLevel)) { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Integer i = 0;\n" + - " if (i == null) {};\n" + // complain: this is non null - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 4)\n" + - " if (i == null) {};\n" + - " ^\n" + - "The variable i cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + - "----------\n" - ); - } - } - - // null analysis -- autoboxing - // TODO (maxime) fix - public void _test0033_autoboxing_literal() { - if (COMPLIANCE_1_5.equals(this.complianceLevel)) { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Integer i = null;\n" + - " System.out.println(i + 4);\n" + // complain: this is null - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 4)\n" + - " System.out.println(i + 4);\n" + - " ^\n" + - "The variable i can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); - } - } - - // null analysis -- autoboxing - // origin: AssignmentTest#test020 - public void test034_autoboxing() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " int i = 0;\n" + - " boolean b = i < 10;\n" + - " }\n" + - "}\n", - }, +} + +// null analysis -- autoboxing +// origin: AssignmentTest#test020 +public void test0044_autoboxing() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " int i = 0;\n" + + " boolean b = i < 10;\n" + + " }\n" + + "}\n"}, ""); +} + +// null analysis -- strings concatenation +// JLS 15.18.1: if one of the operands is null, it is replaced by "null" +// Note: having the diagnostic could come handing when the initialization path +// is non trivial; to get the diagnostic, simply put in place an +// extraneous call to toString() -- and remove it before releasing. +public void test0045_strings_concatenation() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " String foo(String s1, String s2) {\n" + + " if (s1 == null) { /* */ };\n" + + " return s1 + s2;\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- strings concatenation +public void test0046_strings_concatenation() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " String foo(String s1, String s2) {\n" + + " if (s1 == null) { /* */ };\n" + + " s1 += s2;\n" + + " return s1;\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- strings concatenation +public void test0047_strings_concatenation() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " String foo(String s1) {\n" + + " if (s1 == null) { /* */ };\n" + + " return s1.toString();\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " return s1.toString();\n" + + " ^^\n" + + "The variable s1 may be null\n" + + "----------\n"); +} + +// null analysis -- array +public void test0050_array() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " public static void main(String args[]) {\n" + + " args = new String[] {\"zero\"};\n" + + " args[0] = null;\n" + + " if (args[0] == null) {};\n" + + // quiet: we don't keep track of all array elements + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- array +public void test0051_array() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " public static void main(String args[]) {\n" + + " args = null;\n" + + " args[0].toString();\n" + // complain: args is null + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " args[0].toString();\n" + + " ^^^^\n" + + "The variable args can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- array +public void test0052_array() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " public void foo(String args[]) {\n" + + " String s = args[0];\n" + + " if (s == null) {};\n" + + // quiet: we don't keep track of all array elements + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- array +public void test0053_array() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " public void foo(String args[]) {\n" + + " for (int i = 0; i < args.length; i++) { /* */}\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- method call +public void test0061_method_call_guard() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o) {\n" + + " o.toString();\n" + // guards o from being null + " if (o == null) {};\n" + // complain + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " if (o == null) {};\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis - method call +public void test0062_method_call_isolation() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o) {\n" + + " if (bar(o = null)) {\n" + + " if (o == null) {/* empty */}\n" + // complain + " }\n" + + " }\n" + + " boolean bar(Object o) {\n" + + " return true;\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " if (o == null) {/* empty */}\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis - method call +public void test0063_method_call_isolation() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o) {\n" + + " if (bar(o == null ? new Object() : o)) {\n" + + " if (o == null) {/* empty */}\n" + // quiet + " }\n" + + " }\n" + + " boolean bar(Object o) {\n" + + " return true;\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis - method call +public void test0064_method_call_isolation() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o) {\n" + + " if (bar(o = new Object())) {\n" + + " if (o == null) {/* empty */}\n" + // complain + " }\n" + + " }\n" + + " boolean bar(Object o) {\n" + + " return true;\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " if (o == null) {/* empty */}\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis - method call +public void test0065_method_call_invocation_target() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o = null;\n" + + " (o = new Object()).toString();\n" + // quiet + " }\n" + + "}\n"}, + ""); +} + +// null analysis - method call +public void test0066_method_call_invocation_target() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o = new Object();\n" + + " (o = null).toString();\n" + // complain + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " (o = null).toString();\n" + + " ^^^^^^^^^^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis - method call +public void test0067_method_call_invocation_target() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o) {\n" + + " (o = new Object()).toString();\n" + // quiet + " if (o == null) { /* */ }\n" + // complain + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " if (o == null) { /* */ }\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis - method call +public void test0068_method_call_assignment() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " X bar() {\n" + + " return null;\n" + + " }\n" + + " void foo(X x) {\n" + + " x = x.bar();\n" + + " if (x == null) { /* */ }\n" + // quiet + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- type reference +public void test0070_type_reference() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " public static void main(String args[]) {\n" + + " Class c = java.lang.Object.class;\n" + + " if (c == null) {};\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " if (c == null) {};\n" + + " ^\n" + + "The variable c cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +public void test0080_shortcut_boolean_expressions() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o1, Object o2) {\n" + + " if (o1 != null && (o2 = o1) != null) { /* */ }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 3)\n" + + " if (o1 != null && (o2 = o1) != null) { /* */ }\n" + + " ^^^^^^^^^\n" + + "The variable o2 cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n" + ); +} + +public void test0081_shortcut_boolean_expressions() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o1, Object o2) {\n" + + " while (o1 != null && (o2 = o1) != null) { /* */ }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 3)\n" + + " while (o1 != null && (o2 = o1) != null) { /* */ }\n" + + " ^^^^^^^^^\n" + + "The variable o2 cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n" + ); +} + +// null analysis - shortcut boolean expression +public void test0082_shortcut_boolean_expression() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o) {\n" + + " if (o == null || o == null) {\n" + + " o = new Object();\n" + + " }\n" + + " if (o == null) { /* */ }\n" + + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 3)\n" + + " if (o == null || o == null) {\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n" + + "2. ERROR in X.java (at line 6)\n" + + " if (o == null) { /* */ }\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis - shortcut boolean expression +public void test0083_shortcut_boolean_expression() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o) {\n" + + " if (o == null && o == null) {\n" + + " o = new Object();\n" + + " }\n" + + " if (o == null) { /* */ }\n" + + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 3)\n" + + " if (o == null && o == null) {\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n" + + "2. ERROR in X.java (at line 6)\n" + + " if (o == null) { /* */ }\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis -- instanceof +// JLS: instanceof returns false if o turns out to be null +public void test0090_instanceof() { + this.runConformTest( + new String[] { + "X.java", + "class X {\n" + + " boolean dummy;\n" + + " void foo (Object o) {\n" + + " if (dummy) {\n" + + " o = null;\n" + + " }\n" + + " if (o instanceof X) { /* */ }\n" + + " }\n" + + "}"}, + ""); +} + +// null analysis -- instanceof +public void test0091_instanceof() { + this.runConformTest( + new String[] { + "X.java", + "class X {\n" + + " boolean dummy;\n" + + " void foo (Object o) {\n" + + " if (dummy) {\n" + + " o = null;\n" + + " }\n" + + " if (o instanceof X) { /* */ }\n" + + " if (o == null) { /* */ }\n" + + " }\n" + + "}"}, + ""); +} + +// null analysis -- instanceof +// can only be null always yields false +public void test0092_instanceof() { + this.runNegativeTest( + new String[] { + "X.java", + "class X {\n" + + " boolean dummy;\n" + + " void foo () {\n" + + " Object o = null;\n" + + " if (o instanceof X) { /* */ }\n" + + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " if (o instanceof X) { /* */ }\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- instanceof +public void test0093_instanceof() { + this.runNegativeTest( + new String[] { + "X.java", + "class X {\n" + + " void foo(Object x) {\n" + + " if (x instanceof X) {\n" + + " if (x == null) { /* */ }\n" + // cannot happen + " }\n" + + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " if (x == null) { /* */ }\n" + + " ^\n" + + "The variable x cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis -- instanceof +public void test0094_instanceof() { + this.runConformTest( + new String[] { + "X.java", + "class X {\n" + + " void foo(Object x) {\n" + + " if (x instanceof X) {\n" + + " return;\n" + + " }\n" + + " if (x != null) { /* */ }\n" + + // cannot decide: could be null of new Object() for example + " }\n" + + "}"}, + ""); +} + +// null analysis -- if/else +// check that obviously unreachable code does not modify the null +// status of a local +// the said code is not marked as unreachable per JLS 14.21 (the rationale +// being the accommodation for the if (constant_flag_evaluating_to_false) +// {code...} volontary code exclusion pattern) +public void test0300_if_else() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " public void foo() {\n" + + " Object o = null;\n" + + " if (false) {\n" + + " o = new Object();\n" + // skipped + " }\n" + + " if (true) {\n" + + " //\n" + + " }\n" + + " else {\n" + + " o = new Object();\n" + // skipped + " }\n" + + " o.toString();\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 13)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis - if/else +public void test0301_if_else() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o = new Object();\n" + + " if (o != null) {\n" + + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " if (o != null) {\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis - if/else +public void test0302_if_else() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o) throws Exception {\n" + + " if (o == null) {\n" + + " throw new Exception();\n" + + " }\n" + + " if (o != null) {\n" + // only get there if o non null + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " if (o != null) {\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis - if/else +public void test0303_if_else() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o) {\n" + + " if (o == null) {\n" + + " return;\n" + + " }\n" + + " if (o != null) {\n" + + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " if (o != null) {\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis - if/else +public void test0304_if_else() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o) {\n" + + " if (o == null) {\n" + + " o.toString();\n" + + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis - if/else +public void test0305_if_else() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o) {\n" + + " if (o == null) {\n" + + " // do nothing\n" + + " }\n" + + " o.toString();\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n"); +} + +// null analysis - if/else +public void test0306_if_else() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o) {\n" + + " if (o.toString().equals(\"\")) {\n" + + " if (o == null) {\n" + // complain: could not get here + " // do nothing\n" + + " }\n" + + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " if (o == null) {\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis - if/else +public void test0307_if_else() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o) {\n" + + " if (o == null) {\n" + + " System.exit(0);\n" + + " }\n" + + " if (o == null) {\n" + + // quiet + // a direct call to System.exit() can be recognized as such; yet, + // a lot of other methods may have the same property (aka calling + // System.exit() themselves.) + " // do nothing\n" + + " }\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis - if/else +public void test0308_if_else() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean b;\n" + + " void foo(Object o) {\n" + + " if (b) {\n" + + " o = null;\n" + + " }\n" + + " o.toString();\n" + // complain + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n"); +} + +// null analysis - if/else +public void test0309_if_else() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean b1, b2;\n" + + " void foo(Object o) {\n" + + " if (b1) {\n" + + " o = null;\n" + + " }\n" + + " if (b2) {\n" + + " o = new Object();\n" + + " }\n" + + " o.toString();\n" + // complain + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 10)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n"); +} + +// null analysis - if/else +public void test0310_if_else() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean b1, b2;\n" + + " void foo(Object o) {\n" + + " if (b1) {\n" + + " o = null;\n" + + " }\n" + + " if (b2) {\n" + + " o.toString();\n" + // complain + " o.toString();\n" + // silent + " }\n" + + " o.toString();\n" + // complain + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 8)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n" + + "2. ERROR in X.java (at line 11)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n"); +} + +// null analysis - if/else +public void test0311_if_else() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o) {\n" + + " if (o == null)\n" + + " o = new Object();\n" + + " o.toString();\n" + // quiet + " }\n" + + "}" }, + ""); +} + +// null analysis - if/else +// PMT: exactly the case we talked about; what happens is that the first +// if shade doubts upon o; what we could do is to avoid marking in case +// of error? not sure this is appropriate though, because of inner code +// into the if itself; I believe I somewhat did that on purpose: the latest +// wins; completed with o.toString()... +// basically, the choice is about what we should do in case of error: +// neglect the effect of the error, or propagate this effect; the second +// tends to produce less repeated errors (I believe) than the first... +// PREMATURE could refine adding a null-dependent reachable mark... not urgent +public void test0312_if_else() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + "\n" + + " void foo() {\n" + + " Object o = new Object();\n" + + " if (o == null) { /* */ }\n" + // complain + " if (o != null) { /* */ }\n" + // quiet + " o.toString();\n" + // complain + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " if (o == null) { /* */ }\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n" + + "2. ERROR in X.java (at line 7)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n"); +} + +// null analysis - if/else +public void test0313_if_else() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o) {\n" + + " if (o == null) {\n" + // quiet + " o = new Object();\n" + + " }\n" + + " if (o == null) { /* */ }\n" + + // complain: o set to non null iff it was null + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " if (o == null) { /* */ }\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis - if/else +public void test0314_if_else() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o) {\n" + + " if (o != null) {\n" + // quiet + " o = null;\n" + + " }\n" + + " if (o == null) { /* */ }\n" + // complain + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " if (o == null) { /* */ }\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis - if/else +public void test0315_if_else() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o) {\n" + + " if (o != null) {\n" + // quiet + " o = null;\n" + + " }\n" + + " o.toString();\n" + // complain + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis - if/else +public void test0316_if_else() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o, boolean b) {\n" + + " if (o == null || b) { /* */ }\n" + // quiet + " else { /* */ }\n" + + " o.toString();\n" + // complain + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n"); +} + +// null analysis - if/else +public void test0317_if_else_nested() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o, boolean b) {\n" + + " if (o != null) {\n" + // quiet + " if (b) {\n" + // quiet + " o = null;\n" + + " }\n" + + " }\n" + + " if (o == null) { /* */ }\n" + // quiet + " }\n" + + "}"}, + ""); +} + +// null analysis - if/else +public void test0318_if_else_nested() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o, boolean b) {\n" + + " if (o != null) {\n" + // quiet + " if (b) {\n" + // quiet + " o = null;\n" + + " }\n" + + " if (o == null) { /* */ }\n" + // quiet + " }\n" + + " }\n" + + "}"}, + ""); +} + +// null analysis - if/else +// REVIEW we do nothing at this point to diagnose the contents of fake reachable code +public void test0319_if_else_dead_branch() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o, boolean b) {\n" + + " if (false) {\n" + + " o = null;\n" + + " if (o == null) { /* */ }\n" + + " }\n" + + " }\n" + + "}"}, + ""); +} + +// null analysis - if/else +public void test0320_if_else() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o) {\n" + + " o.toString();\n" + + " if (o == null) { /* */ }\n" + // complain + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " if (o == null) { /* */ }\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis - if/else +public void test0321_if_else() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o, boolean b) {\n" + + " Object other = new Object();\n" + + " if (b) {\n" + + " other = o;\n" + + " }\n" + + " if (o != null) { /* */ }\n" + // quiet + " }\n" + + "}"}, + ""); +} + +// null analysis - if/else +public void test0322_if_else() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o, boolean b) {\n" + + " o.toString();\n" + + " if (b) { /* */ }\n" + + " if (o == null) { /* */ }\n" + // complain + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " if (o == null) { /* */ }\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis - if/else +public void test0323_if_else() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o, boolean b) {\n" + + " if (o == null && b) {\n" + + " o = new Object();\n" + + " }\n" + + " if (o == null) { /* */ }\n" + // quiet + " }\n" + + "}"}, + ""); +} + +// null analysis - if/else +public void test0324_if_else_nested() { + this.runConformTest( + new String[] { + "X.java", + "class X {\n" + + " void foo (boolean b) {\n" + + " String s = null;\n" + + " if (b) {\n" + + " if (b) {\n" + + " s = \"1\";\n" + + " } \n" + + " else {\n" + + " s = \"2\";\n" + + " }\n" + + " } \n" + + " else if (b) {\n" + + " s = \"3\"; \n" + + " } \n" + + " else {\n" + + " s = \"4\";\n" + + " }\n" + + " s.toString();\n" + + " }\n" + + "}"}, + ""); +} + +// null analysis - if/else +public void test0325_if_else_nested() { + this.runNegativeTest( + new String[] { + "X.java", + "class X {\n" + + " void foo (boolean b) {\n" + + " String s = null;\n" + + " if (b) {\n" + + " if (b) {\n" + + " s = \"1\";\n" + + " } \n" + + " else {\n" + + " s = \"2\";\n" + + " }\n" + + " } \n" + + " else if (b) {\n" + + " if (b) {\n" + + " s = \"3\"; \n" + + " }\n" + + " } \n" + + " else {\n" + + " s = \"4\";\n" + + " }\n" + + " s.toString();\n" + + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 20)\n" + + " s.toString();\n" + + " ^\n" + + "The variable s may be null\n" + + "----------\n"); +} + +// null analysis - if/else +// REVIEW limit: we cannot sync on external factors, even if this is a pattern +// REVIEW that is quite used +public void test0326_if_else() { + this.runNegativeTest( + new String[] { + "X.java", + "class X {\n" + + " void foo (boolean b) {\n" + + " String s1 = null;\n" + + " if (b) {\n" + + " s1 = \"1\";\n" + + " }\n" + + " s1.toString();\n" + // complain: can't guess if b means anything for s1 init + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " s1.toString();\n" + + " ^^\n" + + "The variable s1 may be null\n" + + "----------\n"); +} + +// null analysis - if/else +// REVIEW limit: we cannot sync on external factors, even if this is a pattern +// REVIEW that is quite used +public void test0327_if_else() { + this.runNegativeTest( + new String[] { + "X.java", + "class X {\n" + + " void foo (String s1) {\n" + + " String s2 = null;\n" + + " if (s1 == null) {\n" + + " s1 = \"1\";\n" + + " s2 = \"2\";\n" + + " }\n" + + " s1.toString();\n" + // quiet + " s2.toString();\n" + // complain: can't guess whether s2 depends on s1 for init + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 9)\n" + + " s2.toString();\n" + + " ^^\n" + + "The variable s2 may be null\n" + + "----------\n"); +} + +// null analysis - if/else +public void test0328_if_else() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o, boolean b) {\n" + + " if (o != null || b) {\n" + + " if (b) {\n" + + " o = new Object();\n" + + " }\n" + + " }\n" + // quiet + " else { /* */ }\n" + + " o.toString();\n" + // complain + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 9)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n"); +} + +// null analysis - if/else +// REVIEW may be surprising within more elaborate code +public void test0329_if_else_nested() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o, boolean b) {\n" + + " if (b) {\n" + + " if (o != null) { /* */ }\n" + // shade doubts on o + " }\n" + + " o.toString();\n" + // complain + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n"); +} + +// null analysis - if/else +public void test0330_if_else_nested() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o, boolean b) {\n" + + " if (b) {\n" + + " if (o == null) {\n" + + " o = new Object();\n" + + " }\n" + + " }\n" + + " o.toString();\n" + // quiet + " }\n" + + "}"}, + ""); +} + +// null analysis - if/else +public void test0331_if_else_nested() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o1, Object o2) {\n" + + " Object o3 = o2;\n" + + " if (o1 != null) {\n" + + " o3.toString(); // guards o3\n" + + " }\n" + + " o1 = o3;\n" + + " if (o1 != null) { /* */ }\n" + + " }\n" + + "}"}, + ""); +} + +// null analysis - if/else +public void test0332_if_else() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o, boolean b) {\n" + + " o = new Object();\n" + + " if (b) {\n" + + " o = new Object();\n" + + " }\n" + + " if (o != null) { /* */ }\n" + // complain + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " if (o != null) { /* */ }\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis -- while +public void test0401_while() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o = null;\n" + + " while (o.toString() != null) {/* */}\n" + + // complain: NPE + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " while (o.toString() != null) {/* */}\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- while +public void test0402_while() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o = null;\n" + + " while (o != null) {/* */}\n" + + // complain: get o null first time and forever + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " while (o != null) {/* */}\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- while +public void test0403_while() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o = null;\n" + + " while (o == null) {\n" + + // quiet: first iteration is sure to find o null, + // but other iterations may change it + " o = new Object();\n" + + " }\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- while +public void test0404_while() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o = null;\n" + + " while (o == null) {\n" + + // quiet: first iteration is sure to find o null, + // but other iterations may change it + " if (System.currentTimeMillis() > 10L) {\n" + + " o = new Object();\n" + + " }\n" + + " }\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- while +public void test0405_while() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean bar() {\n" + + " return true;\n" + + " }\n" + + " void foo(Object o) {\n" + + " while (bar() && o == null) {\n" + + " o.toString();\n" + // complain: NPE + " o = new Object();\n" + + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- while +public void test0406_while() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean dummy;\n" + + " void foo(Object o) {\n" + + " o = null;\n" + + " while (dummy || o != null) { /* */ }\n" + // o can only be null + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " while (dummy || o != null) { /* */ }\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- while +public void test0407_while() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean dummy;\n" + + " void foo() {\n" + + " Object o = null;\n" + + " while (dummy) {\n" + + " o.toString();\n" + // complain: NPE on first iteration + " o = new Object();\n" + + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n"); +} + +// null analysis -- while +// this test shows that, as long as we do not explore all possible +// paths, we have to take potential initializations into account +// even in branches that could be pruned in the first passes +// first approximation is to stop pruning code conditioned by +// variables +// second approximation could still rely upon variables that are +// never affected by the looping code (unassigned variables) +// complete solution would call for multiple iterations in the +// null analysis +public void test0408_while() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o = null,\n" + + " u = new Object(),\n" + + " v = new Object();\n" + + " while (o == null) {\n" + + " if (v == null) {\n" + + " o = new Object();\n" + + " };\n" + + " if (u == null) {\n" + + " v = null;\n" + + " };\n" + + " u = null;\n" + + " }\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- while +public void test0409_while() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean dummy;\n" + + " void foo() {\n" + + " Object o = null;\n" + + " while (dummy || (o = new Object()).equals(o)) {\n" + + " o.toString();\n" + + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n"); +} + +// null analysis -- while +public void test0410_while_nested() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean dummy;\n" + + " void foo() {\n" + + " Object o = null;\n" + + " while (dummy) {\n" + + " while (o != null) {\n" + + " o.toString();\n" + + " }\n" + + " if (System.currentTimeMillis() > 10L) {\n" + + " o = new Object();\n" + + " }\n" + + " }\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- while +public void test0411_while_nested() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o = null,\n" + + " u = new Object(),\n" + + " v = new Object();\n" + + " while (o == null) {\n" + + " if (v == null) {\n" + + " o = new Object();\n" + + " };\n" + + " while (o == null) {\n" + + " if (u == null) {\n" + + " v = null;\n" + + " };\n" + + " u = null;\n" + + " }\n" + + " }\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- while +public void test0412_while_if_nested() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean dummy, other;\n" + + " void foo() {\n" + + " Object o = null;\n" + + " while (dummy) {\n" + + " if (other) {\n" + + " o.toString();\n" + + " }\n" + + " o = new Object();\n" + + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n"); +} + +// null analysis -- while +public void test0413_while_unknown_field() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " Object o;\n" + + " void foo(boolean dummy) {\n" + + " while (dummy) {\n" + + " o = null;\n" + + " }\n" + + " o.toString();\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- while +public void test0414_while_unknown_parameter() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean dummy;\n" + + " void foo(Object o) {\n" + + " while (dummy) {\n" + + " o = null;\n" + // quiet: first iteration doesn't know + " }\n" + + " o.toString();\n" + // complain: only get out of the loop with null + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n"); +} + +// null analysis -- while +public void test0415_while_unknown_if_else() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean dummy;\n" + + " void foo() {\n" + + " Object o = null;\n" + + " if (dummy) {\n" + + " o = new Object();\n" + + " }\n" + + " while (dummy) {\n" + + // limit of the analysis: we do not correlate if and while conditions + " if (o == null) {/* */}\n" + + " }\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- while +public void test0416_while() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean dummy;\n" + + " void foo() {\n" + + " Object o = null;\n" + + " while (dummy) {\n" + + " o = new Object();\n" + + " }\n" + + " o.toString();\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 8)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n"); +} + +// null analysis -- while +public void test0417_while() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean dummy;\n" + + " void foo() {\n" + + " Object o = null;\n" + + " while (dummy) { /* */ }\n" + // doesn't affect o + " o.toString();\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- while +// origin AssignmentTest.testO22 +public void test0418_while_try() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean bool() { return true; }\n" + + " void foo() {\n" + + " Object o = null;\n" + + " while (bool()) {\n" + + " try {\n" + + " if (o == null) {\n" + + " o = new Object();\n" + + " }\n" + + " } finally { /* */ }\n" + + " }\n" + + " }\n" + + "}"}, + ""); +} + +// null analysis -- while +public void test0419_while() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean bool;\n" + + " void foo(Object o) {\n" + + " while (bool) {\n" + + " o.toString();" + // complain NPE because of second iteration + " o = null;\n" + + " }\n" + + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " o.toString(); o = null;\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n"); +} + +// null analysis -- while +public void test0420_while() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean bool;\n" + + " void foo(Object compare) {\n" + + " Object o = new Object();\n" + + " while ((o = null) == compare) {\n" + + " if (true) {\n" + + " break;\n" + + " }\n" + + " }\n" + + " if (o == null) { /* */ }\n" + // complain can only be null + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 10)\n" + + " if (o == null) { /* */ }\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- while +public void test0421_while() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean bool;\n" + + " void foo(Object compare) {\n" + + " Object o = null;\n" + + " while (bool) {\n" + + " o = new Object();\n" + + " o.toString();\n" + + " }\n" + + " }\n" + + "}"}, + ""); +} + +// null analysis -- while +public void test0422_while() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean bool;\n" + + " void foo() {\n" + + " Object o;\n" + + " while (bool) {\n" + + " o = new Object();\n" + + " if (o == null) { /* */ }\n" + + " o = null;\n" + + " }\n" + + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " if (o == null) { /* */ }\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis -- while +// REVIEW we get one extraneous message that looks a bit strange +public void test0423_while() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean bool;\n" + + " void foo() {\n" + + " Object o = null;\n" + + " while (bool) {\n" + + " o = new Object();\n" + + " if (o == null) { /* */ }\n" + + " o = null;\n" + + " }\n" + + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " if (o == null) { /* */ }\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n" + + "2. ERROR in X.java (at line 8)\n" + + " o = null;\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- while +public void test0424_while_try() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(boolean b) {\n" + + " Object o = null;\n" + + " while (o == null) {\n" + + // quiet: first iteration is sure to find o null, + // but other iterations may change it + " try { /* */ }\n" + + " finally {\n" + + " if (b) {\n" + + " o = new Object();\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- while +public void test0425_while() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean dummy;\n" + + " void foo(Object u) {\n" + + " Object o = null;\n" + + " while (dummy) {\n" + + " o = u;\n" + + " }\n" + + " o.toString();\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 8)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n"); +} + +// null analysis -- while +public void test0426_while() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean dummy;\n" + + " void foo(Object o) {\n" + + " o.toString();\n" + + " while (dummy) { /* */ }\n" + + " if (o == null) { /* */ }\n" + // complain + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " if (o == null) { /* */ }\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis -- while +public void test0427_while_return() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean dummy;\n" + + " void foo() {\n" + + " Object o = null;\n" + + " while (dummy) {\n" + + " if (o == null) {\n" + + " return;\n" + + " }\n" + + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " if (o == null) {\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis - while +public void test0428_while() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " X bar() {\n" + + " return null;\n" + + " }\n" + + " void foo(X x) {\n" + + " x.bar();\n" + + " while (x != null) {\n" + + " x = x.bar();\n" + + " }\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis - while +public void test0429_while_nested() { + this.runConformTest( + new String[] { + "X.java", + "class X {\n" + + " boolean dummy;\n" + + " void foo (X[] xa) {\n" + + " while (dummy) {\n" + + " xa = null;\n" + + " if (dummy) {\n" + + " xa = new X[5];\n" + + " }\n" + + " if (xa != null) {\n" + + " int i = 0;\n" + + " while (dummy) {\n" + + " X x = xa[i++];\n" + + " x.toString();\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + "}"}, + ""); +} + +// null analysis - while +public void test0430_while_for_nested() { + this.runConformTest( + new String[] { + "X.java", + "class X {\n" + + " boolean dummy;\n" + + " void foo (X[] xa) {\n" + + " while (dummy) {\n" + + " xa = null;\n" + + " if (dummy) {\n" + + " xa = new X[5];\n" + + " }\n" + + " if (xa != null) {\n" + + " for (int i = 0; i < xa.length; i++) {\n" + + " X x = xa[i];\n" + + " x.toString();\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + "}"}, + ""); +} + +// null analysis - while +public void test0431_while() { + this.runConformTest( + new String[] { + "X.java", + "class X {\n" + + " boolean dummy;\n" + + " void foo (X x) {\n" + + " x = null;\n" + + " while (dummy) {\n" + + " x = bar();\n" + + " x.toString();\n" + + " }\n" + + " }\n" + + " X bar() {\n" + + " return null;\n" + + " }\n" + + "}"}, + ""); +} + +// null analysis - while +public void test0432_while() { + this.runConformTest( + new String[] { + "X.java", + "class X {\n" + + " boolean dummy;\n" + + " void foo (X x) {\n" + + " while (dummy) {\n" + + " x = bar();\n" + + " x.toString();\n" + + " }\n" + + " }\n" + + " X bar() {\n" + + " return null;\n" + + " }\n" + + "}"}, + ""); +} + +// null analysis - while +public void test0433_while() { + this.runNegativeTest( + new String[] { + "X.java", + "class X {\n" + + " boolean dummy;\n" + + " void foo (X x) {\n" + + " x = null;\n" + + " while (dummy) {\n" + + " x.toString();\n" + // complain and protect + " x.toString();\n" + // quiet + " }\n" + + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " x.toString();\n" + + " ^\n" + + "The variable x can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis - while +// this one shows that we cannot project definitely unknown onto potentially unknown too soon +public void test0434_while_switch_nested() { + this.runConformTest( + new String[] { + "X.java", + "class X {\n" + + " Object bar() {\n" + + " return new Object();\n" + + " }\n" + + " void foo(boolean b, int selector) {\n" + + " Object o = null;\n" + + " while (b) {\n" + + " switch (selector) {\n" + + " case 0:\n" + + " o = bar();\n" + + " if (o != null) { \n" + + " return;\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + "}"}, + ""); +} + +// null analysis - while +public void test0435_while_init() { + this.runConformTest( + new String[] { + "X.java", + "class X {\n" + + " int f1;\n" + + " X f2;\n" + + " void foo(X x1, boolean b) {\n" + + " X x2;\n" + + " x2 = x1;\n" + + " while (b) {\n" + +// " if (x2.f1 > 0) { /* */ }\n" + + " if (x2.toString().equals(\"\")) { /* */ }\n" + + " x2 = x2.f2;\n" + + " }\n" + + " }\n" + + "}"}, + ""); +} + +// null analysis - while +public void test0436_while_init() { + this.runConformTest( + new String[] { + "X.java", + "class X {\n" + + " int f1;\n" + + " X f2;\n" + + " void foo(X x1, boolean b) {\n" + + " X x2 = x1;\n" + + " while (b) {\n" + + " if (x2.f1 > 0) { /* */ }\n" + + " x2 = x2.f2;\n" + + " }\n" + + " }\n" + + "}"}, + ""); +} + +// null analysis - while +public void test0437_while_exit() { + this.runConformTest( + new String[] { + "X.java", + "class X {\n" + + " void foo(boolean b) {\n" + + " Object o = null;\n" + + " while (b) {\n" + + " if (b) {\n" + + " o = new Object();\n" + + " }\n" + + " if (o != null) {\n" + + " throw new RuntimeException(); \n" + + " }\n" + + " }\n" + + " }\n" + + "}"}, + ""); +} + + +// null analysis - while +public void test0438_while() { + this.runNegativeTest( + new String[] { + "X.java", + "class X {\n" + + " void foo(Object o) {\n" + + " while (o == null) { /* */ }\n" + + " o.toString();\n" + // quiet + " if (o != null) { /* */ }\n" + // complain + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " if (o != null) { /* */ }\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis - while +public void test0439_while() { + this.runNegativeTest( + new String[] { + "X.java", + "class X {\n" + + " void foo(Object o) {\n" + + " while (o == null) {\n" + + " o = new Object();\n" + + " }\n" + + " o.toString();\n" + // quiet + " }\n" + + "}"}, + ""); +} + +// null analysis - while +public void test0440_while() { + this.runNegativeTest( + new String[] { + "X.java", + "class X {\n" + + " void foo(Object o) {\n" + + " while (o == null) {\n" + + " o = new Object();\n" + + " }\n" + + " if (o != null) { /* */ }\n" + // complain + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " if (o != null) { /* */ }\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis - while +public void test0441_while() { + this.runNegativeTest( + new String[] { + "X.java", + "class X {\n" + + " X bar() {\n" + + " return new X();\n" + + " }\n" + + " void foo(Object o) {\n" + + " while (o == null) {\n" + + " o = bar();\n" + + " }\n" + + " if (o != null) { /* */ }\n" + // complain + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 9)\n" + + " if (o != null) { /* */ }\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis - while +public void test0442_while() { + this.runNegativeTest( + new String[] { + "X.java", + "class X {\n" + + " boolean bar() {\n" + + " return true;\n" + + " }\n" + + " void foo(Object o) {\n" + + " while (o == null && bar()) { /* */ }\n" + + " o.toString();\n" + // complain + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n"); +} + +// null analysis - while +public void test0443_while_nested() { + this.runConformTest( + new String[] { + "X.java", + "class X {\n" + + " void foo() {\n" + + " Object o = null;\n" + + " ext: for (int i = 0; i < 5 ; i++) {\n" + + " if (o != null) {\n" + + " break;\n" + + " }\n" + + " o = new Object();\n" + + " int j = 0;\n" + + " while (j++ < 2) {\n" + + " continue ext;\n" + + " }\n" + + " return;\n" + + " }\n" + + " }\n" + + "}"}, + ""); +} + +// null analysis - while +public void test0444_while_deeply_nested() { + this.runConformTest( + new String[] { + "X.java", + "class X {\n" + + " void foo(boolean b) {\n" + + " Object o = null;\n" + + " ext: for (int i = 0; i < 5 ; i++) {\n" + + " if (o != null) {\n" + + " break;\n" + + " }\n" + + " do {\n" + + " o = new Object();\n" + + " int j = 0;\n" + + " while (j++ < 2) {\n" + + " continue ext;\n" + + " }\n" + + " } while (b);\n" + + " return;\n" + + " }\n" + + " }\n" + + "}"}, + ""); +} + +// null analysis - while +public void test0445_while_deeply_nested() { + this.runNegativeTest( + new String[] { + "X.java", + "class X {\n" + + " void foo(boolean b) {\n" + + " Object o = null;\n" + + " ext: for (int i = 0; i < 5 ; i++) {\n" + + " if (o != null) {\n" + + " break;\n" + + " }\n" + + " do {\n" + + " // o = new Object();\n" + + " int j = 0;\n" + + " while (j++ < 2) {\n" + + " continue ext;\n" + + " }\n" + + " } while (b);\n" + + " return;\n" + + " }\n" + + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " if (o != null) {\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis - while +public void test0446_while() { + this.runNegativeTest( + new String[] { + "X.java", + "class X {\n" + + " void foo(Object o, boolean b) {\n" + + " while (o == null || b) {\n" + + " o = new Object();\n" + + " }\n" + + " if (o != null) { /* */ }\n" + // complain + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " if (o != null) { /* */ }\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis - while +public void test0447_while() { + this.runConformTest( + new String[] { + "X.java", + "class X {\n" + + " void foo(Object o, boolean b) {\n" + + " while (o == null & b) {\n" + + " o = new Object();\n" + + " }\n" + + " if (o != null) { /* */ }\n" + + " }\n" + + "}"}, + ""); +} + +// null analysis - while +public void test0448_while() { + this.runNegativeTest( + new String[] { + "X.java", + "class X {\n" + + " void foo(boolean b[]) {\n" + + " Object o = null;\n" + + " ext: for (int i = 0; i < 5 ; i++) {\n" + + " if (o != null) {\n" + + " break;\n" + + " }\n" + + " while (b[1]) {\n" + + " continue ext;\n" + + " }\n" + + " while (b[2]) {\n" + + " continue ext;\n" + + " }\n" + + " while (b[3]) {\n" + + " continue ext;\n" + + " }\n" + + " while (b[4]) {\n" + + " continue ext;\n" + + " }\n" + + " while (b[5]) {\n" + + " continue ext;\n" + + " }\n" + + " while (b[6]) {\n" + + " continue ext;\n" + + " }\n" + + " return;\n" + + " }\n" + + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " if (o != null) {\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis - while +// REVIEW this series (up to 451) shows that the merge of the states +// REVIEW potential non null and potential unknown yields damages in +// REVIEW case of nested loops (unested loops still OK because we can +// REVIEW carry the definite non null property) +public void test0449_while_nested() { + this.runConformTest( + new String[] { + "X.java", + "class X {\n" + + " void foo(Object p, boolean b) {\n" + + " Object o = new Object();\n" + + " while (b) {\n" + + " while (b) {\n" + + " o = p;\n" + // now o is unknown + " }\n" + + " }\n" + + " if (o != null) { /* */ }\n" + + " }\n" + + "}"}, + ""); +} + +// null analysis - while +public void test0450_while() { + this.runNegativeTest( + new String[] { + "X.java", + "class X {\n" + + " void foo(boolean b) {\n" + + " Object o = new Object();\n" + + " while (b) {\n" + + " o = new Object();\n" + // o still non null + " }\n" + + " if (o != null) { /* */ }\n" + + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " if (o != null) { /* */ }\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis - while +public void _test0451_while_nested() { + this.runNegativeTest( + new String[] { + "X.java", + "class X {\n" + + " void foo(boolean b) {\n" + + " Object o = new Object();\n" + + " while (b) {\n" + + " while (b) {\n" + + " o = new Object();\n" + // o still non null + " }\n" + + " }\n" + + " if (o != null) { /* */ }\n" + + " }\n" + + "}"}, + "ERR"); +} + +// null analysis -- try/finally +public void test0500_try_finally() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " Object m;\n" + + " void foo() {\n" + + " Object o = null;\n" + + " try { /* */ }\n" + + " finally {\n" + + " o = m;\n" + + " }\n" + + " o.toString();\n" + + " }\n" + + "}\n"}, + "" // because finally assigns to unknown value + ); +} + +// null analysis -- try/finally +public void test0501_try_finally() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o = new Object();\n" + + " try { /* */ }\n" + + " finally {\n" + + " o = null;\n" + + " }\n" + + " o.toString();\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 8)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n" // because finally assigns to null + ); +} + +// null analysis -- try/finally +public void test0502_try_finally() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o = null;\n" + + " try {\n" + + " System.out.println();\n" + // might throw a runtime exception + " o = new Object();\n" + + " }\n" + + " finally { /* */ }\n" + + " o.toString();\n" + + // still OK because in case of exception this code is + // not reached + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- try/finally +public void test0503_try_finally() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(X x) {\n" + + " x = null;\n" + + " try {\n" + + " x = null;\n" + // complain, already null + " } finally { /* */ }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " x = null;\n" + + " ^\n" + + "The variable x can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- try/finally +public void test0504_try_finally() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(X x) {\n" + + " x = null;\n" + + " try {\n" + + " } finally {\n" + + " if (x != null) { /* */ }\n" + // complain null + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " if (x != null) { /* */ }\n" + + " ^\n" + + "The variable x can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- try/finally +// origin: AssignmentTest#test017 +// The whole issue here is whether or not to detect premature exits. We +// follow JLS's conservative approach, which considers that the try +// block may exit before the assignment is completed. +// Note: conversely, without line 1, we would complain about x not being +// initialized (for sure) on line 2. +public void test0505_try_finally() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(X x) {\n" + + " x = this;\n" + // 1 + " try {\n" + + " x = null;\n" + + " } finally {\n" + + " if (x == null) {/* */}\n" + // 2 + " }\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- try finally +public void test0506_try_finally() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o) {\n" + + " try { /* */ }\n" + + " finally {\n" + + " o = new Object();\n" + + " }\n" + + " if (o == null) { /* */ }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " if (o == null) { /* */ }\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis -- try finally +public void test0507_try_finally() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o, boolean b) {\n" + + " try { /* */ }\n" + + " finally {\n" + + " o.toString();\n" + // protect + " }\n" + + " if (o == null) {\n" + // complain + " o = new Object();\n" + + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " if (o == null) {\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis -- try finally +public void test0508_try_finally() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o) {\n" + + " o = null;" + + " try { /* */ }\n" + + " finally {\n" + + " o.toString();\n" + // complain and protect + " o.toString();\n" + // quiet + " }\n" + + " o.toString();\n" + // quiet + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- try finally +public void test0509_try_finally_embedded() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o1) {\n" + + " Object o2 = null;" + + " while (true) {\n" + + " // o2 = o1;\n" + + " try { /* */ }\n" + + " finally {\n" + + " o2.toString();\n" + // complain and protect + " o2.toString();\n" + // quiet + " }\n" + + " o2.toString();\n" + // quiet + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " o2.toString();\n" + + " ^^\n" + + "The variable o2 can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- try finally +public void test0510_try_finally() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void bar() throws Exception {\n" + + " // empty\n" + + " }\n" + + " void foo(Object o, boolean b) throws Exception {\n" + + " try {\n" + + " bar();\n" + + " if (b) {\n" + + " o.toString();\n" + + " }\n" + + " }\n" + + " finally {\n" + + " if (o != null) {\n" + + " o.toString();\n" + + " }\n" + + " }\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- try finally +public void test0511_try_finally() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o1, boolean b) {\n" + + " Object o2 = null;\n" + + " if (b) {\n" + + " o2 = new Object();\n" + + " }\n" + // 0011 + " try { /* */ }\n" + + " finally {\n" + + " o2 = o1;\n" + // 1011 + " }\n" + + " o2.toString();\n" + // 1011 -- quiet + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- try/finally +public void test0512_try_finally() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(X x) {\n" + + " x = null;\n" + + " try {\n" + + " x = new X();\n" + + " } finally {\n" + + " x.toString();\n" + + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " x.toString();\n" + + " ^\n" + + "The variable x may be null\n" + + "----------\n"); +} + +// null analysis -- try/catch +public void test0550_try_catch() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o = null;\n" + + " try {\n" + + " System.out.println();\n" + // might throw a runtime exception + " o = new Object();\n" + + " }\n" + + " catch (Throwable t) {\n" + // catches everything + " return;\n" + // gets out + " }\n" + + " o.toString();\n" + // non null + " }\n" + + "}\n"}, + ""); +} + +// null analysis - try/catch +public void test0551_try_catch() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean dummy;\n" + + " void foo() {\n" + + " Object o = new Object();\n" + + " try {\n" + + " System.out.println();\n" + + " if (dummy) {\n" + + " o = null;\n" + + " throw new Exception();\n" + + " }\n" + + " }\n" + + " catch (Exception e) {\n" + + " o.toString();\n" + // complain + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 13)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n"); +} + +// null analysis - try/catch +public void test0552_try_catch() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean dummy;\n" + + " void foo() throws Exception {\n" + + " Object o = new Object();\n" + + " try {\n" + + " if (dummy) {\n" + + " o = null;\n" + + " throw new Exception();\n" + + " }\n" + + " }\n" + + " catch (Exception e) {\n" + + " }\n" + + " if (o != null) {\n" + + // quiet: get out of try either through normal flow, leaves a new + // object, or through Exception, leaves a null + " }\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis - try/catch +public void test0553_try_catch() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean dummy, other;\n" + + " void foo() {\n" + + " Object o = new Object();\n" + + " try {\n" + + " if (dummy) {\n" + + " if (other) {\n" + + " throw new LocalException();\n" + // may launch new exception + " }\n" + + " o = null;\n" + + " throw new LocalException();\n" + // must launch new exception + " }\n" + + " }\n" + + " catch (LocalException e) {\n" + + " o.toString();\n" + // complain + " }\n" + + " }\n" + + " class LocalException extends Exception {\n" + + " private static final long serialVersionUID = 1L;\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 15)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n"); +} + +// null analysis - try/catch +public void test0554_try_catch() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o) throws Exception {\n" + + " try {\n" + + " o = null;\n" + + " throwLocalException();\n" + + " throw new Exception();\n" + + " }\n" + + " catch (LocalException e) {\n" + + " }\n" + + " if (o != null) {\n" + + // complain: only way to get out of try and get there is to go + // through throwLocalException, after the assignment + " }\n" + + " }\n" + + " class LocalException extends Exception {\n" + + " private static final long serialVersionUID = 1L;\n" + + " }\n" + + " void throwLocalException() throws LocalException {\n" + + " throw new LocalException();\n" + + " }\n" + + "}\n"}, + "" + // conservative flow analysis suppresses the warning +// "----------\n" + +// "1. ERROR in X.java (at line 10)\n" + +// " if (o != null) {\n" + +// " ^\n" + +// "The variable o can only be null; it was either set to null or checked for null when last used\n" + +// "----------\n" + ); +} + +// null analysis - try/catch +public void test0555_try_catch() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o = new Object();\n" + + " try {\n" + + " o = null;\n" + + " throwException();\n" + + " }\n" + + " catch (Exception e) {\n" + + " o.toString();\n" + // complain NPE + " }\n" + + " }\n" + + " void throwException() throws Exception {\n" + + " throw new Exception();\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 9)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n"); +} + +// null analysis - try/catch +public void test0556_try_catch() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o = new Object();\n" + + " try {\n" + + " o = null;\n" + + " throwException();\n" + + " }\n" + + " catch (Throwable t) {\n" + + " o.toString();\n" + // complain NPE + " }\n" + + " }\n" + + " void throwException() throws Exception {\n" + + " throw new Exception();\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 9)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n"); +} + +// null analysis - try/catch +public void test0557_try_catch() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean dummy;\n" + + " void foo() {\n" + + " Object o = new Object();\n" + + " try {\n" + + " if (dummy) {\n" + + " o = null;\n" + + " throw new Exception();\n" + + " }\n" + + " }\n" + + " catch (Exception e) {\n" + + " o.toString();\n" + // complain NPE + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 12)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n"); +} + +// null analysis - try/catch +public void test0558_try_catch() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean dummy;\n" + + " void foo() {\n" + + " Object o = new Object();\n" + + " try {\n" + + " if (dummy) {\n" + + " System.out.print(0);\n" + // may thow RuntimeException + " o = null;\n" + + " throw new LocalException();\n" + + " }\n" + + " }\n" + + " catch (LocalException e) {\n" + // doesn't catch RuntimeException + " o.toString();\n" + // complain NPE + " }\n" + + " }\n" + + " class LocalException extends Exception {\n" + + " private static final long serialVersionUID = 1L;\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 13)\n" + + " o.toString();\n" + + " ^\n" + +// "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "The variable o may be null\n" + + // conservative flow analysis softens the error + "----------\n"); +} + +// null analysis - try/catch +public void test0559_try_catch() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean dummy;\n" + + " void foo() {\n" + + " Object o = new Object();\n" + + " try {\n" + + " if (dummy) {\n" + + " o = null;\n" + + " throw new SubException();\n" + + " }\n" + + " }\n" + + " catch (LocalException e) {\n" + // must catch SubException + " o.toString();\n" + // complain NPE + " }\n" + + " }\n" + + " class LocalException extends Exception {\n" + + " private static final long serialVersionUID = 1L;\n" + + " }\n" + + " class SubException extends LocalException {\n" + + " private static final long serialVersionUID = 1L;\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 12)\n" + + " o.toString();\n" + + " ^\n" + +// "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "The variable o may be null\n" + + // conservative flow analysis softens the error + "----------\n"); +} + +// null analysis - try/catch +public void test0560_try_catch() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " Class bar(boolean b) throws ClassNotFoundException {\n" + + " if (b) {\n" + + " throw new ClassNotFoundException();\n" + + " }\n" + + " return null;\n" + + " }\n" + + " public Class foo(Class c, boolean b) {\n" + + " if (c != null)\n" + + " return c;\n" + + " if (b) {\n" + + " try {\n" + + " c = bar(b);\n" + + " return c;\n" + + " } catch (ClassNotFoundException e) {\n" + + " // empty\n" + + " }\n" + + " }\n" + + " if (c == null) { // should complain: c can only be null\n" + + " }\n" + + " return c;\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 19)\n" + + " if (c == null) { // should complain: c can only be null\n" + + " ^\n" + + "The variable c can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- do while +public void test0601_do_while() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o = null;\n" + + " do {/* */}\n" + + " while (o.toString() != null);\n" + + // complain: NPE + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " while (o.toString() != null);\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- do while +public void test0602_do_while() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o = null;\n" + + " do {/* */}\n" + + " while (o != null);\n" + + // complain: get o null first time and forever + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " while (o != null);\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- do while +public void test0603_do_while() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o = null;\n" + + " do {\n" + + " o = new Object();\n" + + " }\n" + + " while (o == null);\n" + + // complain: set it to non null before test, for each iteration + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " while (o == null);\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis -- do while +public void test0604_do_while() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o = null;\n" + + " do {\n" + + " if (System.currentTimeMillis() > 10L) {\n" + + " o = new Object();\n" + + " }\n" + + " }\n" + + " while (o == null);\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- do while +public void test0605_do_while() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean dummy;\n" + + " void foo(Object o) {\n" + + " o = null;\n" + + " do {\n" + + " // do nothing\n" + + " }\n" + + " while (dummy || o != null);\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 8)\n" + + " while (dummy || o != null);\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- do while +public void test0606_do_while() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o = null,\n" + + " u = new Object(),\n" + + " v = new Object();\n" + + " do {\n" + + " if (v == null) {\n" + + " o = new Object();\n" + + " };\n" + + " if (u == null) {\n" + + " v = null;\n" + + " };\n" + + " u = null;\n" + + " }\n" + + " while (o == null);\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- do while +public void test0607_do_while() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean dummy;\n" + + " void foo() {\n" + + " Object o = null;\n" + + " do {\n" + + " o.toString();\n" + + // complain: NPE + " o = new Object();\n" + + " }\n" + + " while (dummy);\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n"); +} + +// null analysis -- do while +public void test0608_do_while() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean dummy;\n" + + " void foo() {\n" + + " Object o = null;\n" + + " do {\n" + + " o = new Object();\n" + + " }\n" + + " while (dummy);\n" + + " o.toString();\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- do while +public void test0609_do_while() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean dummy;\n" + + " void foo() {\n" + + " Object o = null;\n" + + " do { /* */ }\n" + + " while (dummy);\n" + + " o.toString();\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis - do while +public void test0610_do_while() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " X bar() {\n" + + " return null;\n" + + " }\n" + + " void foo(X x) {\n" + + " x.bar();\n" + + " do {\n" + + " x = x.bar();\n" + + " } while (x != null);\n" + // quiet + " }\n" + + "}\n"}, + ""); +} + +// null analysis - do while +public void test0611_do_while() { + this.runNegativeTest( + new String[] { + "X.java", + "class X {\n" + + " X bar() {\n" + + " return new X();\n" + + " }\n" + + " void foo(Object o) {\n" + + " do {\n" + + " o = bar();\n" + + " } while (o == null);\n" + + " if (o != null) { /* */ }\n" + // complain + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 9)\n" + + " if (o != null) { /* */ }\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// the problem here is that a single pass cannot know for the return +// embedded into the if; prior approach did use the upstream flow +// info to catch this, but this is inappropriate in many cases (eg +// test0606); may be able to do better if keeping all deep returns +// as we do for labeled continue +// TODO (maxime) https://bugs.eclipse.org/bugs/show_bug.cgi?id=123399 +public void _test0612_do_while() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object doubt) {\n" + + " Object o = null;\n" + + " do {\n" + + " if (o == null) {\n" + + " return;\n" + + " }\n" + + " o = doubt;\n" + + " } while (true);\n" + + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " if (o == null) {\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n" + ); +} + +// null analysis -- for +public void test0701_for() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o = null;\n" + + " for (;o.toString() != null;) {/* */}\n" + + // complain: NPE + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " for (;o.toString() != null;) {/* */}\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- for +public void test0702_for() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o = null;\n" + + " for (;o != null;) {/* */}\n" + + // complain: get o null first time and forever + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " for (;o != null;) {/* */}\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- for +public void test0703_for() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o = null;\n" + + " for (;o == null;) {\n" + + // quiet: first iteration is sure to find it null, + // but other iterations may change it + " o = new Object();\n" + + " }\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- for +public void test0704_for() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o = null;\n" + + " for (;o == null;) {\n" + + // quiet: first iteration is sure to find it null, + // but other iterations may change it + " if (System.currentTimeMillis() > 10L) {\n" + + " o = new Object();\n" + + " }\n" + + " }\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- for +public void test0705_for() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean bar() {\n" + + " return true;\n" + + " }\n" + + " void foo(Object o) {\n" + + " for (;bar() && o == null;) {\n" + + " o.toString();\n" + // complain: NPE because of condition + " o = new Object();\n" + + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- for +public void test0707_for() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o) {\n" + + " for (;o == null; o.toString()) {\n" + + " o = new Object();\n" + + " }\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- for +public void test0708_for() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o) {\n" + + " for (;o == null; o.toString()) {\n" + + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 3)\n" + + " for (;o == null; o.toString()) {\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- for +public void test0709_for() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o) {\n" + + " for (o.toString(); o == null;) { /* */ }\n" + // complain: protected then unchanged + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 3)\n" + + " for (o.toString(); o == null;) { /* */ }\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis -- for +public void test0710_for() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean bar() {\n" + + " return true;\n" + + " }\n" + + " void foo(Object o) {\n" + + " o = null;\n" + + " for (o.toString(); bar();) {\n" + + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " for (o.toString(); bar();) {\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- for +public void test0711_for() { + if (COMPLIANCE_1_5.equals(this.complianceLevel)) { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object t[] = null;\n" + + " for (Object o : t) {/* */}\n" + + // complain: NPE + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " for (Object o : t) {/* */}\n" + + " ^\n" + + "The variable t can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); + } +} + +// null analysis -- for +public void test0712_for() { + if (COMPLIANCE_1_5.equals(this.complianceLevel)) { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Iterable i = null;\n" + + " for (Object o : i) {/* */}\n" + + // complain: NPE + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " for (Object o : i) {/* */}\n" + + " ^\n" + + "The variable i can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); + } +} + +// null analysis -- for +public void test0713_for() { + if (COMPLIANCE_1_5.equals(this.complianceLevel)) { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object t[] = new Object[1];\n" + + " for (Object o : t) {/* */}\n" + + " }\n" + + "}\n"}, + ""); + } +} + +// null analysis -- for +public void test0714_for() { + if (COMPLIANCE_1_5.equals(this.complianceLevel)) { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Iterable i = new java.util.Vector();\n" + + " for (Object o : i) {/* */}\n" + + " }\n" + + "}\n"}, + ""); + } +} + +// null analysis -- for +public void test0715_for() { + if (COMPLIANCE_1_5.equals(this.complianceLevel)) { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Iterable i = new java.util.Vector();\n" + + " Object flag = null;\n" + + " for (Object o : i) {\n" + + " flag = new Object();\n" + + " }\n" + + " flag.toString();\n" + + // complain: cannot know if at least one iteration got executed + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 8)\n" + + " flag.toString();\n" + + " ^^^^\n" + + "The variable flag may be null\n" + + "----------\n"); + } +} + +// null analysis -- for +public void test0716_for() { + if (COMPLIANCE_1_5.equals(this.complianceLevel)) { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Iterable i = new java.util.Vector();\n" + + " Object flag = null;\n" + + " for (Object o : i) { /* */ }\n" + + " flag.toString();\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " flag.toString();\n" + + " ^^^^\n" + + "The variable flag can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); + } +} + +// null analysis -- for +public void test0717_for() { + if (COMPLIANCE_1_5.equals(this.complianceLevel)) { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(boolean dummy) {\n" + + " Object flag = null;\n" + + " for (;dummy;) {\n" + + " flag = new Object();\n" + + " }\n" + + " flag.toString();\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " flag.toString();\n" + + " ^^^^\n" + + "The variable flag may be null\n" + + "----------\n"); } +} + +// null analysis -- for +public void test0718_for() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(boolean dummy) {\n" + + " Object flag = null;\n" + + " for (;dummy;) { /* */ }\n" + + " flag.toString();\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " flag.toString();\n" + + " ^^^^\n" + + "The variable flag can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- for +// origin: AssignmentTest#test019 +public void test0719_for() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " public static final char[] foo(char[] a, char c1, char c2) {\n" + + " char[] r = null;\n" + + " for (int i = 0, length = a.length; i < length; i++) {\n" + + " char c = a[i];\n" + + " if (c == c1) {\n" + + " if (r == null) {\n" + + " r = new char[length];\n" + + " }\n" + + " r[i] = c2;\n" + + " } else if (r != null) {\n" + + " r[i] = c;\n" + + " }\n" + + " }\n" + + " if (r == null) return a;\n" + + " return r;\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- for +public void test0720_for_continue_break() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o = new Object();\n" + + " for (int i = 0; i < 10; i++) {\n" + + " if (o == null) {\n" + // complain: o cannot be null + " continue;\n" + + " }\n" + + " o = null;\n" + + " break;\n" + + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " if (o == null) {\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis -- for +public void test0721_for() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(boolean b) {\n" + + " Object o = null;\n" + + " for (; b ? (o = new Object()).equals(o) : false ;) {\n" + + // contrast this with test0238; here the condition shades doubts + // upon o being null + " /* */\n" + + " }\n" + + " if (o == null) { /* */ }\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- for +public void test0722_for_return() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo (boolean b) {\n" + + " Object o = null;\n" + + " for (int i = 0; i < 25; i++) {\n" + + " if (b) {\n" + + " if (o == null) {\n" + + " o = new Object();\n" + // cleared by return downstream + " }\n" + + " return;\n" + + " }\n" + + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " if (o == null) {\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- for +public void test0723_for() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo () {\n" + + " Object o[] = new Object[1];\n" + + " for (int i = 0; i < 1; i++) {\n" + + " if (i < 1) {\n" + + " o[i].toString();\n" + + " }\n" + + " }\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- for +public void test0724_for_with_initialization() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " X field;\n" + + " void foo(X x1) {\n" + + " // X x2;\n" + + " outer: for (int i = 0; i < 30; i++) {\n" + + " X x2 = x1;\n" + + " do {\n" + + " if (x2.equals(x1)) {\n" + + " continue outer;\n" + + " }\n" + + " x2 = x2.field;\n" + + " } while (x2 != null);\n" + + " }\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- for +public void test0725_for_with_assignment() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " X field;\n" + + " void foo(X x1) {\n" + + " X x2;\n" + + " outer: for (int i = 0; i < 30; i++) {\n" + + " x2 = x1;\n" + + " do {\n" + + " if (x2.equals(x1)) {\n" + + " continue outer;\n" + + " }\n" + + " x2 = x2.field;\n" + + " } while (x2 != null);\n" + + " }\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- for +// contrast this with #311 +public void test0726_for() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(X x1) {\n" + + " X x2 = null;\n" + + " for (int i = 0; i < 5; i++) {\n" + + " if (x2 == null) {\n" + + " x2 = x1;\n" + + " }\n" + + " x2.toString();\n" + + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 8)\n" + + " x2.toString();\n" + + " ^^\n" + + "The variable x2 may be null\n" + + "----------\n"); +} + +// null analysis -- for +public void test0727_for() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " for (; true;) { /* */ }\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- for +public void test0728_for() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(X x) {\n" + + " for (; true; x.toString()) { /* */ }\n" + + " if (x == null) { /* */ }\n" + // complain + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " if (x == null) { /* */ }\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Unreachable code\n" + + "----------\n"); +} + +// null analysis -- for +public void test0729_for_try_catch_finally() { + this.runConformTest( + new String[] { + "X.java", + "import java.io.IOException;\n" + + "class X {\n" + + " X f;\n" + + " void bar() throws IOException {\n" + + " throw new IOException();\n" + + " }\n" + + " void foo(boolean b) {\n" + + " for (int i = 0 ; i < 5 ; i++) {\n" + + " X x = this.f;\n" + + " if (x == null) { \n" + + " continue;\n" + + " }\n" + + " if (b) {\n" + + " try {\n" + + " bar();\n" + + " } \n" + + " catch(IOException e) { /* */ }\n" + + " finally {\n" + + " x.toString();\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + "}"}, + ""); +} + +// null analysis - for +public void test0730_for() { + this.runNegativeTest( + new String[] { + "X.java", + "class X {\n" + + " void foo(Object o) {\n" + + " for ( ; o == null ; ) {\n" + + " o = new Object();\n" + + " }\n" + + " if (o != null) { /* */ }\n" + // complain + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " if (o != null) { /* */ }\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis - for +public void test0731_for() { + this.runNegativeTest( + new String[] { + "X.java", + "class X {\n" + + " X bar() {\n" + + " return new X();\n" + + " }\n" + + " void foo(Object o) {\n" + + " for ( ; o == null ; ) {\n" + + " o = bar();\n" + + " }\n" + + " if (o != null) { /* */ }\n" + // complain + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 9)\n" + + " if (o != null) { /* */ }\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis -- switch +public void test0800_switch() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " int k;\n" + + " void foo() {\n" + + " Object o = null;\n" + + " switch (k) {\n" + + " case 0 :\n" + + " o = new Object();\n" + + " break;\n" + + " case 2 :\n" + + " return;\n" + + " }\n" + + " if(o == null) { /* */ }\n" + // quiet: don't know whether came from 0 or default + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- switch +public void test0801_switch() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " int k;\n" + + " void foo() {\n" + + " Object o = null;\n" + + " switch (k) {\n" + + " case 0 :\n" + + " o = new Object();\n" + + " break;\n" + + " default :\n" + + " return;\n" + + " }\n" + + " if(o == null) { /* */ }\n" + // complain: only get there through 0, o non null + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 12)\n" + + " if(o == null) { /* */ }\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis -- switch +public void test0802_switch() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " int k;\n" + + " void foo() {\n" + + " Object o = null;\n" + + " switch (k) {\n" + + " case 0 :\n" + + " o.toString();\n" + // complain: o can only be null + " break;\n" + + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// null analysis -- switch +public void test0803_switch() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " int k;\n" + + " void foo() {\n" + + " Object o = null;\n" + + " switch (k) {\n" + + " case 0 :\n" + + " o = new Object();\n" + + " case 1 :\n" + + " o.toString();\n" + // complain: may come through 0 or 1 + " break;\n" + + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 9)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n"); +} + +// null analysis -- switch +public void test0804_switch() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo (Object o, int info) {\n" + + " o = null;\n" + + " switch (info) {\n" + + " case 0 :\n" + + " o = new Object();\n" + + " break;\n" + + " case 1 :\n" + + " o = new String();\n" + + " break;\n" + + " default :\n" + + " o = new X();\n" + + " break;\n" + + " }\n" + + " if(o != null) { /* */ }\n" + // complain: all branches allocate a new o + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 15)\n" + + " if(o != null) { /* */ }\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis -- switch +public void test0805_switch() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(X p) {\n" + + " X x = this;\n" + + " for (int i = 0; i < 5; i++) {\n" + + " switch (i) {\n" + + " case 1:\n" + + " x = p;\n" + + " }\n" + + " }\n" + + " if (x != null) { /* */ }\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- non null protection tag +public void test0900_non_null_protection_tag() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o) {\n" + + " boolean b = o != null;\n" + // shades doubts upon o + " o/*NN*/.toString();\n" + // protection => do not complain + " o.toString();\n" + // protected by previous line + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- non null protection tag +public void test0901_non_null_protection_tag() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o, boolean b) {\n" + + " if (b) {\n" + + " o = null;\n" + + " }\n" + + " o/*NN*/.toString();\n" + + " if (b) {\n" + + " o = null;\n" + + " }\n" + + " o/*\n" + + " NN comment */.toString();\n" + + " if (b) {\n" + + " o = null;\n" + + " }\n" + + " o/* NN\n" + + " */.toString();\n" + + " if (b) {\n" + + " o = null;\n" + + " }\n" + + " o // NN \n" + + " .toString();\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- non null protection tag +public void test0902_non_null_protection_tag() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o, boolean b) {\n" + + " if (b) {\n" + + " o = null;\n" + + " }\n" + + " o/*NON-NULL*/.toString();\n" + + " if (b) {\n" + + " o = null;\n" + + " }\n" + + " o/* NON-NULL comment */.toString();\n" + + " if (b) {\n" + + " o = null;\n" + + " }\n" + + " o/* NON-NULL \n" + + " */.toString();\n" + + " if (b) {\n" + + " o = null;\n" + + " }\n" + + " o // NON-NULL \n" + + " .toString();\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- non null protection tag +public void test0903_non_null_protection_tag() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o, boolean b) {\n" + + " if (b) {\n" + + " o = null;\n" + + " }\n" + + " o/*N N*/.toString();\n" + + " if (b) {\n" + + " o = null;\n" + + " }\n" + + " o/*NNa*/.toString();\n" + + " if (b) {\n" + + " o = null;\n" + + " }\n" + + " o/*aNN */.toString();\n" + + " if (b) {\n" + + " o = null;\n" + + " }\n" + + " o/*NON NULL*/.toString();\n" + + " if (b) {\n" + + " o = null;\n" + + " }\n" + + " o/*Non-Null*/.toString();\n" + + " if (b) {\n" + + " o = null;\n" + + " }\n" + + " o/*aNON-NULL */.toString();\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " o/*N N*/.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n" + + "2. ERROR in X.java (at line 10)\n" + + " o/*NNa*/.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n" + + "3. ERROR in X.java (at line 14)\n" + + " o/*aNN */.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n" + + "4. ERROR in X.java (at line 18)\n" + + " o/*NON NULL*/.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n" + + "5. ERROR in X.java (at line 22)\n" + + " o/*Non-Null*/.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n" + + "6. ERROR in X.java (at line 26)\n" + + " o/*aNON-NULL */.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n"); +} + + +// null analysis -- non null protection tag +public void test0905_non_null_protection_tag() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o) {\n" + + " boolean b = o != null;\n" + // shades doubts upon o + " o.toString();/*NN*/\n" + // too late to protect => complain + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " o.toString();/*NN*/\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n"); +} + +// null analysis -- non null protection tag +public void test0906_non_null_protection_tag() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o) {\n" + + " boolean b = o != null;\n" + // shades doubts upon o + " /*NN*/o.toString();\n" + // too soon to protect => complain + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " /*NN*/o.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n"); +} + +// null analysis -- notNull protection tag +public void _test0900_notNull_protection_tag() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(/** @notNull */ Object o) {\n" + + " boolean b = o != null;\n" + + " }\n" + + "}\n"}, + "ERR cannot be null"); +} + +// null analysis -- notNull protection tag +public void _test0901_notNull_protection_tag() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Object o) {\n" + + " /** @notNull */ Object l = o;\n" + + " }\n" + + "}\n"}, + "ERR cannot be null... ou pas ?"); +} + +// null analysis -- notNull protection tag +public void _test0902_notNull_protection_tag() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(/** @nullable */ Object o) {\n" + + " /** @notNull */ Object l = o;\n" + + " }\n" + + "}\n"}, + "ERR cannot be null"); +} + +// null analysis -- notNull protection tag +public void _test0903_notNull_protection_tag() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " Object bar() {\n" + + " return null;\n" + + " }\n" + + " void foo() {\n" + + " /** @notNull */ Object l = bar();\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- notNull protection tag +public void _test0904_notNull_protection_tag() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " /** @notNull */\n" + + " Object bar() {\n" + + " return new Object();\n" + + " }\n" + + " void foo() {\n" + + " Object l = bar();\n" + + " if (l == null) { /* empty */ }\n" + + " }\n" + + "}\n"}, + "ERR cannot be null"); +} + +// null analysis -- notNull protection tag +public void _test0905_notNull_protection_tag() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " /** @notNull */\n" + + " Object bar() {\n" + + " return null;\n" + + " }\n" + + "}\n"}, + "ERR cannot be null"); +} + +// null analysis -- nullable tag +public void _test0950_nullable_tag() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(/** @nullable */ Object o) {\n" + + " o.toString();\n" + + " }\n" + + "}\n"}, + "ERR may be null"); +} + +// null analysis -- nullable tag +public void _test0951_nullable_tag() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(/** @nullable */ Object o) {\n" + + " Object l = o;\n" + + " l.toString();\n" + + " }\n" + + "}\n"}, + "ERR may be null"); +} + +// null analysis -- nullable tag +public void _test0952_nullable_tag() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(boolean b) {\n" + + " /** @nullable */ Object o;\n" + + " if (b) {\n" + + " o = new Object();\n" + + " }\n" + + " o.toString();\n" + + " }\n" + + "}\n"}, + "ERR may be null"); +} + +// moved from AssignmentTest +public void test1004() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " X foo(X x) {\n" + + " x.foo(null); // 0\n" + + " if (x != null) { // 1\n" + + " if (x == null) { // 2\n" + + " x.foo(null); // 3\n" + + " } else if (x instanceof X) { // 4\n" + + " x.foo(null); // 5 \n" + + " } else if (x != null) { // 6\n" + + " x.foo(null); // 7\n" + + " }\n" + + " x.foo(null); // 8\n" + + " }\n" + + " return this;\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " if (x != null) { // 1\n" + + " ^\n" + + "The variable x cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n" + + "2. ERROR in X.java (at line 5)\n" + + " if (x == null) { // 2\n" + + " ^\n" + + "The variable x cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n" + + "3. ERROR in X.java (at line 6)\n" + + " x.foo(null); // 3\n" + + " ^\n" + + "The variable x can only be null; it was either set to null or checked for null when last used\n" + + "----------\n" + + "4. ERROR in X.java (at line 9)\n" + + " } else if (x != null) { // 6\n" + + " ^\n" + + "The variable x cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n" + + "5. ERROR in X.java (at line 12)\n" + + " x.foo(null); // 8\n" + + " ^\n" + + "The variable x may be null\n" + + "----------\n"); +} + +public void test1005() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(Class c) {\n" + + " if (c.isArray() ) {\n" + + " } else if (c == java.lang.String.class ) {\n" + + " }\n" + + " }\n" + + "}\n"}, + ""); +} + +public void test1006() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(X x) {\n" + + " if (x == this)\n" + + " return;\n" + + " x.foo(this);\n" + + " }\n" + + "}\n"}, + ""); +} + +public void test1007() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(X x, X x2) {\n" + + " if (x != null)\n" + + " return;\n" + + " x = x2;\n" + + " if (x == null) {\n" + + " }\n" + + " }\n" + + "}\n"}, + ""); +} + +public void test1008() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(X x, X x2) {\n" + + " if (x != null)\n" + + " return;\n" + + " try {\n" + + " x = x2;\n" + + " } catch(Exception e) {}\n" + + " if (x == null) {\n" + + " }\n" + + " }\n" + + "}\n"}, + ""); +} + +// TODO (philippe) reenable once fixed +public void _test1009() { + this.runNegativeTest( + new String[] { + "X.java", + "import java.io.File;\n" + + "\n" + + "public class X {\n" + + " boolean check(String name) { return true; }\n" + + " Class bar(String name) throws ClassNotFoundException { return null; }\n" + + " File baz(String name) { return null; }\n" + + " \n" + + " public Class foo(String name, boolean resolve) throws ClassNotFoundException {\n" + + " \n" + + " Class c = bar(name);\n" + + " if (c != null)\n" + + " return c;\n" + + " if (check(name)) {\n" + + " try {\n" + + " c= bar(name);\n" + + " return c;\n" + + " } catch (ClassNotFoundException e) {\n" + + " // keep searching\n" + + " // only path to here left c unassigned from try block, means it was assumed to be null\n" + + " }\n" + + " }\n" + + " if (c == null) {// should complain: c can only be null\n" + + " File file= baz(name);\n" + + " if (file == null)\n" + + " throw new ClassNotFoundException();\n" + + " }\n" + + " return c;\n" + + " }\n" + + "\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 22)\n" + + " if (c == null) {// should complain: c can only be null\n" + + " ^\n" + + "The variable c can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// REVIEW maybe leave this one in AssignmentTest? +public void test1010() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + "\n" + + " X itself() { return this; }\n" + + "\n" + + " void bar() {\n" + + " X itself = this.itself();\n" + + " if (this == itself) {\n" + + " System.out.println(itself.toString()); //1\n" + + " } else {\n" + + " System.out.println(itself.toString()); //2\n" + + " }\n" + + " }\n" + + "}\n"}, + ""); +} + +public void test1011() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + "\n" + + " X itself() { return this; }\n" + + "\n" + + " void bar() {\n" + + " X itself = this.itself();\n" + + " if (this == itself) {\n" + + " X other = (X)itself;\n" + + " if (other != null) {\n" + + " }\n" + + " if (other == null) {\n" + + " }\n" + + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 9)\n" + + " if (other != null) {\n" + + " ^^^^^\n" + + "The variable other cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +public void test1012() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " \n" + + " void foo() {\n" + + " Object o = null;\n" + + " do {\n" + + " if (o == null) {\n" + + " return;\n" + + " }\n" + + " // o = bar();\n" + + " } while (true);\n" + + " }\n" + + " X bar() { \n" + + " return null; \n" + + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " if (o == null) {\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n" + ); +} + +// REVIEW here we do not catch the dead branch: x cannot equal this then null with no assignment in between +public void test1013() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(X x) {\n" + + " if (x == this) {\n" + + " if (x == null) {\n" + + " x.foo(this);\n" + + " }\n" + + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " if (x == null) {\n" + + " ^\n" + + "The variable x cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n" + + "2. ERROR in X.java (at line 5)\n" + + " x.foo(this);\n" + + " ^\n" + + "The variable x can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +public void test1014() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(X x) {\n" + + " x = null;\n" + + " try {\n" + + " x = this;\n" + + " } finally {\n" + + " x.foo(null);\n" + + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " x.foo(null);\n" + + " ^\n" + + "The variable x may be null\n" + + "----------\n"); +} + +public void test1015() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o = null;\n" + + " int i = 1;\n" + + " switch (i) {\n" + + " case 1:\n" + + " o = new Object();\n" + + " break;\n" + + " }\n" + + " if (o != null)\n" + + " o.toString();\n" + + " }\n" + + "}\n"}, + ""); +} + +public void test1016() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(X x) {\n" + + " x = null;\n" + + " try {\n" + + " x = null;\n" + + " } finally {\n" + + " if (x != null) {\n" + + " x.foo(null);\n" + + " }\n" + + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " x = null;\n" + + " ^\n" + + "The variable x can only be null; it was either set to null or checked for null when last used\n" + + "----------\n" + + "2. ERROR in X.java (at line 7)\n" + + " if (x != null) {\n" + + " ^\n" + + "The variable x can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} - // null analysis -- array - public void test0041_array() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " public static void main(String args[]) {\n" + - " args[0] = null;\n" + - " if (args[0] == null) {};\n" + - // quiet: we don't keep track of all array elements - " }\n" + - "}\n"}, - "" - ); - } +public void test1017() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(X x) {\n" + + " x = this;\n" + + " try {\n" + + " x = null;\n" + + " } finally {\n" + + " if (x == null) {\n" + + " x.foo(null);\n" + + " }\n" + + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 8)\n" + + " x.foo(null);\n" + + " ^\n" + + "The variable x can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} - // null analysis -- array - public void test0042_array() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " public static void main(String args[]) {\n" + - " args = null;\n" + - " args[0].toString();\n" + // complain: args is null - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 4)\n" + - " args[0].toString();\n" + - " ^^^^\n" + - "The variable args can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); - } +public void test1018() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " \n" + + " void foo() {\n" + + " Object o = null;\n" + + " do {\n" + + " if (o != null) return;\n" + + " o = null;\n" + + " } while (true);\n" + + " }\n" + + " X bar() { \n" + + " return null; \n" + + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 6)\r\n" + + " if (o != null) return;\r\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n" + + "2. ERROR in X.java (at line 7)\r\n" + + " o = null;\r\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} - // null analysis -- type reference - public void test0051_type_reference() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " public static void main(String args[]) {\n" + - " Class c = java.lang.Object.class;\n" + - " if (c == null) {};\n" + - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 4)\n" + - " if (c == null) {};\n" + - " ^\n" + - "The variable c cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + - "----------\n" - ); - } +public void test1019() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " public static final char[] replaceOnCopy(\n" + + " char[] array,\n" + + " char toBeReplaced,\n" + + " char replacementChar) {\n" + + " \n" + + " char[] result = null;\n" + + " for (int i = 0, length = array.length; i < length; i++) {\n" + + " char c = array[i];\n" + + " if (c == toBeReplaced) {\n" + + " if (result == null) {\n" + + " result = new char[length];\n" + + " System.arraycopy(array, 0, result, 0, i);\n" + + " }\n" + + " result[i] = replacementChar;\n" + + " } else if (result != null) {\n" + + " result[i] = c;\n" + + " }\n" + + " }\n" + + " if (result == null) return array;\n" + + " return result;\n" + + " }\n" + + "}\n"}, + ""); +} - // null analysis -- method call - public void test0061_method_call_guard() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo(Object o) {\n" + - " if (o == null) {};\n" + // quiet: we don't know anything - " o.toString();\n" + // guards o from being null - " if (o == null) {};\n" + // complain - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 5)\n" + - " if (o == null) {};\n" + - " ^\n" + - "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + - "----------\n" - ); - } - - // null analysis - method call - public void test0062_method_call_isolation() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo(Object o) {\n" + - " if (bar(o = null)) {\n" + - " if (o == null) {/* empty */}\n" + // complain - " }\n" + - " }\n" + - " boolean bar(Object o) {\n" + - " return true;\n" + - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 4)\n" + - " if (o == null) {/* empty */}\n" + - " ^\n" + - "The variable o can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); - } - - // null analysis - method call - public void test0063_method_call_isolation() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo(Object o) {\n" + - " if (bar(o == null ? new Object() : o)) {\n" + - " if (o == null) {/* empty */}\n" + // quiet - " }\n" + - " }\n" + - " boolean bar(Object o) {\n" + - " return true;\n" + - " }\n" + - "}\n"}, - "" - ); - } - - // null analysis - method call - public void test0064_method_call_isolation() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo(Object o) {\n" + - " if (bar(o = new Object())) {\n" + - " if (o == null) {/* empty */}\n" + // complain - " }\n" + - " }\n" + - " boolean bar(Object o) {\n" + - " return true;\n" + - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 4)\n" + - " if (o == null) {/* empty */}\n" + - " ^\n" + - "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + - "----------\n" - ); - } +public void test1021() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " int kind;\n" + + " X parent;\n" + + " Object[] foo() { return null; }\n" + + " void findTypeParameters(X scope) {\n" + + " Object[] typeParameters = null;\n" + + " while (scope != null) {\n" + + " typeParameters = null;\n" + + " switch (scope.kind) {\n" + + " case 0 :\n" + + " typeParameters = foo();\n" + + " break;\n" + + " case 1 :\n" + + " typeParameters = foo();\n" + + " break;\n" + + " case 2 :\n" + + " return;\n" + + " }\n" + + " if(typeParameters != null) {\n" + + " foo();\n" + + " }\n" + + " scope = scope.parent;\n" + + " }\n" + + " }\n" + + "}\n"}, + ""); +} - // null analysis - method call - public void test0065_method_call_invocation_target() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Object o = null;\n" + - " (o = new Object()).toString();\n" + // quiet - " }\n" + - "}\n"}, - "" - ); - } - - // null analysis - method call - // TODO (maxime) fix - public void _test0066_method_call_invocation_target() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Object o = new Object();\n" + - " (o = null).toString();\n" + // complain - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 4)\n" + - " (o = null).toString();\n" + - " ^^^^^^^^^^\n" + - "The variable o can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); - } - - // null analysis -- if/else - // check that obviously unreachable code does not modify the null - // status of a local - // the said code is not marked as unreachable per JLS 14.21 (the rationale - // being the accommodation for the if (constant_flag_evaluating_to_false) - // {code...} volontary code exclusion pattern) - // TODO (maxime) fix - public void _test0100_if_else() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " public void foo() {\n" + - " Object o = null;\n" + - " if (false) {\n" + - " o = new Object();\n" + // skipped - " }\n" + - " if (true) {\n" + - " //\n" + - " }\n" + - " else {\n" + - " o = new Object();\n" + // skipped - " }\n" + - " o.toString();\n" + - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 13)\n" + - " o.toString();\n" + - " ^\n" + - "The variable o can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); - } - - // TODO (maxime) - what about further diagnostics inside fake reachable code ? if (false) { o = null; o.toString(); } - - // null analysis - if/else - public void test0101_if_else() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Object o = new Object();\n" + - " if (o != null) {\n" + - " }\n" + - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 4)\n" + - " if (o != null) {\n" + - " ^\n" + - "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + - "----------\n" - ); - } +public void test1022() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean bool() { return true; }\n" + + " void doSomething() {}\n" + + " \n" + + " void foo() {\n" + + " Object progressJob = null;\n" + + " while (bool()) {\n" + + " if (bool()) {\n" + + " if (progressJob != null)\n" + + " progressJob = null;\n" + + " doSomething();\n" + + " }\n" + + " try {\n" + + " if (progressJob == null) {\n" + + " progressJob = new Object();\n" + + " }\n" + + " } finally {\n" + + " doSomething();\n" + + " }\n" + + " }\n" + + " }\n" + + "}"}, + ""); +} - // null analysis - if/else - public void test0102_if_else() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo(Object o) throws Exception {\n" + - " if (o == null) {\n" + - " throw new Exception();\n" + - " }\n" + - " if (o != null) {\n" + // only get there if o non null - " }\n" + - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 6)\n" + - " if (o != null) {\n" + - " ^\n" + - "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + - "----------\n" - ); - } +public void test1023() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + "\n" + + " void foo() {\n" + + " Object o = new Object();\n" + + " while (this != null) {\n" + + " try {\n" + + " o = null;\n" + + " break;\n" + + " } finally {\n" + + " o = new Object();\n" + + " }\n" + + " }\n" + + " if (o == null) return;\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 13)\n" + + " if (o == null) return;\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} - // null analysis - if/else - public void test0103_if_else() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo(Object o) {\n" + - " if (o == null) {\n" + - " return;\n" + - " }\n" + - " if (o != null) {\n" + - " }\n" + - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 6)\n" + - " if (o != null) {\n" + - " ^\n" + - "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + - "----------\n" - ); - } +public void test1024() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " \n" + + " boolean bool() { return true; }\n" + + " void doSomething() {}\n" + + " \n" + + " void foo() {\n" + + " Object progressJob = null;\n" + + " while (bool()) {\n" + + " if (progressJob != null)\n" + + " progressJob = null;\n" + + " doSomething();\n" + + " try {\n" + + " if (progressJob == null) {\n" + + " progressJob = new Object();\n" + + " }\n" + + " } finally {\n" + + " doSomething();\n" + + " }\n" + + " }\n" + + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 13)\n" + + " if (progressJob == null) {\n" + + " ^^^^^^^^^^^\n" + + "The variable progressJob can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} - // null analysis - if/else - public void test0104_if_else() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo(Object o) {\n" + - " if (o == null) {\n" + - " o.toString();\n" + - " }\n" + - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 4)\n" + - " o.toString();\n" + - " ^\n" + - "The variable o can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); - } +public void test1025() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " \n" + + " void foo() {\n" + + " Object o;\n" + + " try {\n" + + " o = null;\n" + + " } finally {\n" + + " o = new Object();\n" + + " }\n" + + " if (o == null) return;\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 10)\n" + + " if (o == null) return;\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} - // null analysis - if/else - public void test0105_if_else() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo(Object o) {\n" + - " if (o == null) {\n" + - " // do nothing\n" + - " }\n" + - " o.toString();\n" + - " }\n" + - "}\n"}, - "" - ); - } +// TODO (philippe) reenable once fixed +public void _test1026() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " \n" + + " public static void main(String[] args) {\n" + + " Object o;\n" + + " try {\n" + + " o = null;\n" + + " } finally {\n" + + " if (args == null) o = new Object();\n" + + " }\n" + + " if (o == null) System.out.println(\"SUCCESS\");\n" + + " }\n" + + "}\n"}, + "SUCCESS"); +} - // null analysis - if/else - public void test0106_if_else() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo(Object o) {\n" + - " if (o.toString().equals(\"\")) {\n" + - " if (o == null) {\n" + // complain: could not get here - " // do nothing\n" + - " }\n" + - " }\n" + - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 4)\n" + - " if (o == null) {\n" + - " ^\n" + - "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + - "----------\n" - ); - } +// TODO (philippe) reenable once fixed +public void _test1027() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean b;\n" + + " void foo() {\n" + + " Object o = null;\n" + + " while (b) {\n" + + " try {\n" + + " o = null;\n" + + " } finally {\n" + + " if (o == null) \n" + + " o = new Object();\n" + + " }\n" + + " }\n" + + " if (o == null) return;\n" + + " }\n" + + "}\n"}, + ""); +} + +// TODO (philippe) reenable once fixed +public void _test1028() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean b;\n" + + " void foo() {\n" + + " Object o = null;\n" + + " while (b) {\n" + + " try {\n" + + " o = null;\n" + + " break;\n" + + " } finally {\n" + + " if (o == null) \n" + + " o = new Object();\n" + + " }\n" + + " }\n" + + " if (o == null) return;\n" + + " }\n" + + "}\n"}, + ""); +} + +public void test1029() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " public static void main(String[] args) {\n" + + " Object o = null;\n" + + " int i = 0;\n" + + " while (i++ < 2) {\n" + + " try {\n" + + " if (i == 2) return;\n" + + " o = null;\n" + + " } finally {\n" + + " if (i == 2) System.out.println(o);\n" + + " o = \"SUCCESS\";\n" + + " }\n" + + " }\n" + + " if (o == null) return;\n" + + " }\n" + + "}\n"}, + "SUCCESS"); +} + +public void test1030() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " \n" + + " void foo() {\n" + + " Object a = null;\n" + + " while (true) {\n" + + " a = null;\n" + + " if (a == null) {\n" + + " System.out.println();\n" + + " }\n" + + " a = new Object();\n" + + " break;\n" + + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " if (a == null) {\n" + + " ^\n" + + "The variable a can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// TODO (philippe) reenable once fixed +public void _test1031() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " \n" + + " void foo() {\n" + + " Object a = null;\n" + + " while (true) {\n" + + " a = null;\n" + + " if (a == null) {\n" + + " System.out.println();\n" + + " }\n" + + " a = new Object();\n" + + " break;\n" + + " }\n" + + " if (a == null) {\n" + + " System.out.println();\n" + + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " if (a == null) {\n" + + " ^\n" + + "The variable a can only be null; it was either set to null or checked for null when last used\n" + + "----------\n" + + "2. ERROR in X.java (at line 13)\n" + + " if (a == null) {\n" + + " ^\n" + + "The variable a cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +public void test1032() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo() {\n" + + " Object o1 = this;\n" + + " Object o3;\n" + + " while (o1 != null && (o3 = o1) != null) {\n" + + " o1 = o3;\n" + + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " while (o1 != null && (o3 = o1) != null) {\n" + + " ^^\n" + + "The variable o1 cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n" + + "2. ERROR in X.java (at line 5)\n" + + " while (o1 != null && (o3 = o1) != null) {\n" + + " ^^^^^^^^^\n" + + "The variable o3 cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// (simplified to focus on nulls) +public void test1033() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " \n" + + " void foo() {\n" + + " String a,b;\n" + + " do{\n" + + " a=\"Hello \";\n" + + " }while(a!=null);\n" + + " if(a!=null)\n" + + " { /* */ }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " }while(a!=null);\n" + + " ^\n" + + "The variable a cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n" + + "2. ERROR in X.java (at line 8)\n" + + " if(a!=null)\n" + + " ^\n" + + "The variable a can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// from AssignmentTest#test034, simplified +public void test1034() { + this.runConformTest( + new String[] { + "X.java", + "public final class X \n" + + "{\n" + + " void foo()\n" + + " {\n" + + " String rs = null;\n" + + " try\n" + + " {\n" + + " rs = \"\";\n" + + " return;\n" + + " }\n" + + " catch (Exception e)\n" + + " {\n" + + " }\n" + + " finally\n" + + " {\n" + + " if (rs != null)\n" + + " {\n" + + " try\n" + + " {\n" + + " rs.toString();\n" + + " }\n" + + " catch (Exception e)\n" + + " {\n" + + " }\n" + + " }\n" + + " }\n" + + " return;\n" + + " }\n" + + "}\n", + }, + ""); +} + +public void test1036() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + "\n" + + " void foo() {\n" + + " Object o = new Object();\n" + + " do {\n" + + " o = null;\n" + + " } while (o != null);\n" + + " if (o == null) {\n" + + " // throw new Exception();\n" + + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " } while (o != null);\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n" + + "2. ERROR in X.java (at line 8)\n" + + " if (o == null) {\n" + + " ^\n" + + "The variable o can only be null; it was either set to null or checked for null when last used\n" + + "----------\n"); +} + +// flow info low-level validation +public void test2000_flow_info() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + "\n" + + " void foo() {\n" + + " Object o0 = new Object(), o1 = o0, o2 = o0, o3 = o0, o4 = o0,\n" + + " o5 = o0, o6 = o0, o7 = o0, o8 = o0, o9 = o0,\n" + + " o10 = o0, o11 = o0, o12 = o0, o13 = o0, o14 = o0,\n" + + " o15 = o0, o16 = o0, o17 = o0, o18 = o0, o19 = o0,\n" + + " o20 = o0, o21 = o0, o22 = o0, o23 = o0, o24 = o0,\n" + + " o25 = o0, o26 = o0, o27 = o0, o28 = o0, o29 = o0,\n" + + " o30 = o0, o31 = o0, o32 = o0, o33 = o0, o34 = o0,\n" + + " o35 = o0, o36 = o0, o37 = o0, o38 = o0, o39 = o0,\n" + + " o40 = o0, o41 = o0, o42 = o0, o43 = o0, o44 = o0,\n" + + " o45 = o0, o46 = o0, o47 = o0, o48 = o0, o49 = o0,\n" + + " o50 = o0, o51 = o0, o52 = o0, o53 = o0, o54 = o0,\n" + + " o55 = o0, o56 = o0, o57 = o0, o58 = o0, o59 = o0,\n" + + " o60 = o0, o61 = o0, o62 = o0, o63 = o0, o64 = o0,\n" + + " o65 = o0, o66 = o0, o67 = o0, o68 = o0, o69 = o0;\n" + + " if (o65 == null) { /* */ }\n" + // complain + " if (o65 != null) { /* */ }\n" + // quiet (already reported) + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 18)\n" + + " if (o65 == null) { /* */ }\n" + + " ^^^\n" + + "The variable o65 cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +public void test2001_flow_info() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + "\n" + + " void foo(\n" + + " Object o0, Object o1, Object o2, Object o3, Object o4,\n" + + " Object o5, Object o6, Object o7, Object o8, Object o9,\n" + + " Object o10, Object o11, Object o12, Object o13, Object o14,\n" + + " Object o15, Object o16, Object o17, Object o18, Object o19,\n" + + " Object o20, Object o21, Object o22, Object o23, Object o24,\n" + + " Object o25, Object o26, Object o27, Object o28, Object o29,\n" + + " Object o30, Object o31, Object o32, Object o33, Object o34,\n" + + " Object o35, Object o36, Object o37, Object o38, Object o39,\n" + + " Object o40, Object o41, Object o42, Object o43, Object o44,\n" + + " Object o45, Object o46, Object o47, Object o48, Object o49,\n" + + " Object o50, Object o51, Object o52, Object o53, Object o54,\n" + + " Object o55, Object o56, Object o57, Object o58, Object o59,\n" + + " Object o60, Object o61, Object o62, Object o63, Object o64,\n" + + " Object o65, Object o66, Object o67, Object o68, Object o69) {\n" + + " if (o65 == null) { /* */ }\n" + + " if (o65 != null) { /* */ }\n" + + " }\n" + + "}\n"}, + ""); +} + +public void test2002_flow_info() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " Object m0, m1, m2, m3, m4,\n" + + " m5, m6, m7, m8, m9,\n" + + " m10, m11, m12, m13, m14,\n" + + " m15, m16, m17, m18, m19,\n" + + " m20, m21, m22, m23, m24,\n" + + " m25, m26, m27, m28, m29,\n" + + " m30, m31, m32, m33, m34,\n" + + " m35, m36, m37, m38, m39,\n" + + " m40, m41, m42, m43, m44,\n" + + " m45, m46, m47, m48, m49,\n" + + " m50, m51, m52, m53, m54,\n" + + " m55, m56, m57, m58, m59,\n" + + " m60, m61, m62, m63;\n" + + " void foo(Object o) {\n" + + " if (o == null) { /* */ }\n" + + " if (o != null) { /* */ }\n" + + " }\n" + + "}\n"}, + ""); +} + +public void test2003_flow_info() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " Object m0, m1, m2, m3, m4,\n" + + " m5, m6, m7, m8, m9,\n" + + " m10, m11, m12, m13, m14,\n" + + " m15, m16, m17, m18, m19,\n" + + " m20, m21, m22, m23, m24,\n" + + " m25, m26, m27, m28, m29,\n" + + " m30, m31, m32, m33, m34,\n" + + " m35, m36, m37, m38, m39,\n" + + " m40, m41, m42, m43, m44,\n" + + " m45, m46, m47, m48, m49,\n" + + " m50, m51, m52, m53, m54,\n" + + " m55, m56, m57, m58, m59,\n" + + " m60, m61, m62, m63;\n" + + " void foo(Object o) {\n" + + " o.toString();\n" + + " }\n" + + "}\n"}, + ""); +} + +public void test2004_flow_info() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " Object m0, m1, m2, m3, m4,\n" + + " m5, m6, m7, m8, m9,\n" + + " m10, m11, m12, m13, m14,\n" + + " m15, m16, m17, m18, m19,\n" + + " m20, m21, m22, m23, m24,\n" + + " m25, m26, m27, m28, m29,\n" + + " m30, m31, m32, m33, m34,\n" + + " m35, m36, m37, m38, m39,\n" + + " m40, m41, m42, m43, m44,\n" + + " m45, m46, m47, m48, m49,\n" + + " m50, m51, m52, m53, m54,\n" + + " m55, m56, m57, m58, m59,\n" + + " m60, m61, m62, m63;\n" + + " void foo() {\n" + + " Object o;\n" + + " if (o == null) { /* */ }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 17)\n" + + " if (o == null) { /* */ }\n" + + " ^\n" + + "The local variable o may not have been initialized\n" + + "----------\n"); +} + +public void test2005_flow_info() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " Object m0, m1, m2, m3, m4,\n" + + " m5, m6, m7, m8, m9,\n" + + " m10, m11, m12, m13, m14,\n" + + " m15, m16, m17, m18, m19,\n" + + " m20, m21, m22, m23, m24,\n" + + " m25, m26, m27, m28, m29,\n" + + " m30, m31, m32, m33, m34,\n" + + " m35, m36, m37, m38, m39,\n" + + " m40, m41, m42, m43, m44,\n" + + " m45, m46, m47, m48, m49,\n" + + " m50, m51, m52, m53, m54,\n" + + " m55, m56, m57, m58, m59,\n" + + " m60, m61, m62, m63;\n" + + " void foo(Object o) {\n" + + " o = null;\n" + + " }\n" + + "}\n"}, + ""); +} + +public void test2006_flow_info() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " Object m0, m1, m2, m3, m4,\n" + + " m5, m6, m7, m8, m9,\n" + + " m10, m11, m12, m13, m14,\n" + + " m15, m16, m17, m18, m19,\n" + + " m20, m21, m22, m23, m24,\n" + + " m25, m26, m27, m28, m29,\n" + + " m30, m31, m32, m33, m34,\n" + + " m35, m36, m37, m38, m39,\n" + + " m40, m41, m42, m43, m44,\n" + + " m45, m46, m47, m48, m49,\n" + + " m50, m51, m52, m53, m54,\n" + + " m55, m56, m57, m58, m59,\n" + + " m60, m61, m62, m63;\n" + + " void foo() {\n" + + " Object o = null;\n" + + " }\n" + + "}\n"}, + ""); +} + +public void test2007_flow_info() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " Object m0, m1, m2, m3, m4,\n" + + " m5, m6, m7, m8, m9,\n" + + " m10, m11, m12, m13, m14,\n" + + " m15, m16, m17, m18, m19,\n" + + " m20, m21, m22, m23, m24,\n" + + " m25, m26, m27, m28, m29,\n" + + " m30, m31, m32, m33, m34,\n" + + " m35, m36, m37, m38, m39,\n" + + " m40, m41, m42, m43, m44,\n" + + " m45, m46, m47, m48, m49,\n" + + " m50, m51, m52, m53, m54,\n" + + " m55, m56, m57, m58, m59,\n" + + " m60, m61, m62, m63;\n" + + " void foo() {\n" + + " Object o[] = null;\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- flow info +public void test2008_flow_info() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " Object m0, m1, m2, m3, m4,\n" + + " m5, m6, m7, m8, m9,\n" + + " m10, m11, m12, m13, m14,\n" + + " m15, m16, m17, m18, m19,\n" + + " m20, m21, m22, m23, m24,\n" + + " m25, m26, m27, m28, m29,\n" + + " m30, m31, m32, m33, m34,\n" + + " m35, m36, m37, m38, m39,\n" + + " m40, m41, m42, m43, m44,\n" + + " m45, m46, m47, m48, m49,\n" + + " m50, m51, m52, m53, m54,\n" + + " m55, m56, m57, m58, m59,\n" + + " m60, m61, m62, m63;\n" + + " void foo(boolean b) {\n" + + " Object o = null;\n" + + " while (o == null) {\n" + + // quiet: first iteration is sure to find o null, + // but other iterations may change it + " try { /* */ }\n" + + " finally {\n" + + " if (b) {\n" + + " o = new Object();\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis -- flow info +public void test2009_flow_info() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " Object m0, m1, m2, m3, m4,\n" + + " m5, m6, m7, m8, m9,\n" + + " m10, m11, m12, m13, m14,\n" + + " m15, m16, m17, m18, m19,\n" + + " m20, m21, m22, m23, m24,\n" + + " m25, m26, m27, m28, m29,\n" + + " m30, m31, m32, m33, m34,\n" + + " m35, m36, m37, m38, m39,\n" + + " m40, m41, m42, m43, m44,\n" + + " m45, m46, m47, m48, m49,\n" + + " m50, m51, m52, m53, m54,\n" + + " m55, m56, m57, m58, m59,\n" + + " m60, m61, m62, m63;\n" + + " void foo(Object o) {\n" + + " try { /* */ }\n" + + " finally {\n" + + " o = new Object();\n" + + " }\n" + + " if (o == null) { /* */ }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 20)\n" + + " if (o == null) { /* */ }\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis -- flow info +public void test2010_flow_info() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " Object m00, m01, m02, m03, m04,\n" + + " m05, m06, m07, m08, m09,\n" + + " m10, m11, m12, m13, m14,\n" + + " m15, m16, m17, m18, m19,\n" + + " m20, m21, m22, m23, m24,\n" + + " m25, m26, m27, m28, m29,\n" + + " m30, m31, m32, m33, m34,\n" + + " m35, m36, m37, m38, m39,\n" + + " m40, m41, m42, m43, m44,\n" + + " m45, m46, m47, m48, m49,\n" + + " m50, m51, m52, m53, m54,\n" + + " m55, m56, m57, m58, m59,\n" + + " m60, m61, m62, m63;\n" + + " void foo() {\n" + + " Object o;\n" + + " try { /* */ }\n" + + " finally {\n" + + " o = new Object();\n" + + " }\n" + + " if (o == null) { /* */ }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 21)\n" + + " if (o == null) { /* */ }\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis -- flow info +public void test2011_flow_info() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " Object m000, m001, m002, m003, m004, m005, m006, m007, m008, m009,\n" + + " m010, m011, m012, m013, m014, m015, m016, m017, m018, m019,\n" + + " m020, m021, m022, m023, m024, m025, m026, m027, m028, m029,\n" + + " m030, m031, m032, m033, m034, m035, m036, m037, m038, m039,\n" + + " m040, m041, m042, m043, m044, m045, m046, m047, m048, m049,\n" + + " m050, m051, m052, m053, m054, m055, m056, m057, m058, m059,\n" + + " m060, m061, m062, m063;\n" + + " void foo() {\n" + + " Object o000, o001, o002, o003, o004, o005, o006, o007, o008, o009,\n" + + " o010, o011, o012, o013, o014, o015, o016, o017, o018, o019,\n" + + " o020, o021, o022, o023, o024, o025, o026, o027, o028, o029,\n" + + " o030, o031, o032, o033, o034, o035, o036, o037, o038, o039,\n" + + " o040, o041, o042, o043, o044, o045, o046, o047, o048, o049,\n" + + " o050, o051, o052, o053, o054, o055, o056, o057, o058, o059,\n" + + " o060, o061, o062, o063;\n" + + " Object o;\n" + + " try {\n" + + " o000 = new Object();\n" + + " }\n" + + " finally {\n" + + " o = new Object();\n" + + " }\n" + + " if (o == null) { /* */ }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 24)\n" + + " if (o == null) { /* */ }\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis -- flow info +public void test2012_flow_info() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " Object m000, m001, m002, m003, m004, m005, m006, m007, m008, m009,\n" + + " m010, m011, m012, m013, m014, m015, m016, m017, m018, m019,\n" + + " m020, m021, m022, m023, m024, m025, m026, m027, m028, m029,\n" + + " m030, m031, m032, m033, m034, m035, m036, m037, m038, m039,\n" + + " m040, m041, m042, m043, m044, m045, m046, m047, m048, m049,\n" + + " m050, m051, m052, m053, m054, m055, m056, m057, m058, m059,\n" + + " m060, m061, m062, m063;\n" + + " void foo() {\n" + + " Object o000, o001, o002, o003, o004, o005, o006, o007, o008, o009,\n" + + " o010, o011, o012, o013, o014, o015, o016, o017, o018, o019,\n" + + " o020, o021, o022, o023, o024, o025, o026, o027, o028, o029,\n" + + " o030, o031, o032, o033, o034, o035, o036, o037, o038, o039,\n" + + " o040, o041, o042, o043, o044, o045, o046, o047, o048, o049,\n" + + " o050, o051, o052, o053, o054, o055, o056, o057, o058, o059,\n" + + " o060, o061, o062, o063;\n" + + " Object o;\n" + + " try {\n" + + " o = new Object();\n" + + " }\n" + + " finally {\n" + + " o000 = new Object();\n" + + " }\n" + + " if (o == null) { /* */ }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 24)\n" + + " if (o == null) { /* */ }\n" + + " ^\n" + + "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + + "----------\n"); +} + +// null analysis -- flow info +public void test2013_flow_info() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean dummy;\n" + + " Object m000, m001, m002, m003, m004, m005, m006, m007, m008, m009,\n" + + " m010, m011, m012, m013, m014, m015, m016, m017, m018, m019,\n" + + " m020, m021, m022, m023, m024, m025, m026, m027, m028, m029,\n" + + " m030, m031, m032, m033, m034, m035, m036, m037, m038, m039,\n" + + " m040, m041, m042, m043, m044, m045, m046, m047, m048, m049,\n" + + " m050, m051, m052, m053, m054, m055, m056, m057, m058, m059,\n" + + " m060, m061, m062, m063;\n" + + " void foo(Object u) {\n" + + " Object o = null;\n" + + " while (dummy) {\n" + + " o = u;\n" + + " }\n" + + " o.toString();\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 15)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n"); +} + +// null analysis -- flow info +public void test2014_flow_info() { + this.runNegativeTest( + new String[] { + "X.java", + "class X {\n" + + " int m000, m001, m002, m003, m004, m005, m006, m007, m008, m009,\n" + + " m010, m011, m012, m013, m014, m015, m016, m017, m018, m019,\n" + + " m020, m021, m022, m023, m024, m025, m026, m027, m028, m029,\n" + + " m030, m031, m032, m033, m034, m035, m036, m037, m038, m039,\n" + + " m040, m041, m042, m043, m044, m045, m046, m047, m048, m049,\n" + + " m050, m051, m052, m053, m054, m055, m056, m057, m058, m059,\n" + + " m060, m061, m062, m063;\n" + + " final int m064;\n" + + " X() {\n" + + " m064 = 10;\n" + + " class Inner extends X {\n" + + " int m100, m101, m102, m103, m104, m105, m106, m107, m108, m109,\n" + + " m110, m111, m112, m113, m114, m115, m116, m117, m118, m119,\n" + + " m120, m121, m122, m123, m124, m125, m126, m127, m128, m129,\n" + + " m130, m131, m132, m133, m134, m135, m136, m137, m138, m139,\n" + + " m140, m141, m142, m143, m144, m145, m146, m147, m148, m149,\n" + + " m150, m151, m152, m153, m154, m155, m156, m157, m158, m159,\n" + + " m160, m161, m162, m163;\n" + + " final int m164;\n" + + " int bar() {\n" + + " return m100 + m101 + m102 + m103 + m104 +\n" + + " m105 + m106 + m107 + m108 + m109 +\n" + + " m110 + m111 + m112 + m113 + m114 +\n" + + " m115 + m116 + m117 + m118 + m119 +\n" + + " m120 + m121 + m122 + m123 + m124 +\n" + + " m125 + m126 + m127 + m128 + m129 +\n" + + " m130 + m131 + m132 + m133 + m134 +\n" + + " m135 + m136 + m137 + m138 + m139 +\n" + + " m140 + m141 + m142 + m143 + m144 +\n" + + " m145 + m146 + m147 + m148 + m149 +\n" + + " m150 + m151 + m152 + m153 + m154 +\n" + + " m155 + m156 + m157 + m158 + m159 +\n" + + " m160 + m161 + m162 + m163 + m164;\n" + + " }\n" + + " };\n" + + " System.out.println((new Inner()).bar());\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 12)\n" + + " class Inner extends X {\n" + + " ^^^^^\n" + + "The blank final field m164 may not have been initialized\n" + + "----------\n"); +} + +// null analysis -- flow info +public void test2015_flow_info() { + this.runNegativeTest( + new String[] { + "X.java", + "class X {\n" + + " int m000, m001, m002, m003, m004, m005, m006, m007, m008, m009,\n" + + " m010, m011, m012, m013, m014, m015, m016, m017, m018, m019,\n" + + " m020, m021, m022, m023, m024, m025, m026, m027, m028, m029,\n" + + " m030, m031, m032, m033, m034, m035, m036, m037, m038, m039,\n" + + " m040, m041, m042, m043, m044, m045, m046, m047, m048, m049,\n" + + " m050, m051, m052, m053, m054, m055, m056, m057, m058, m059,\n" + + " m060, m061, m062, m063;\n" + + " final int m200;\n" + + " int m201, m202, m203, m204, m205, m206, m207, m208, m209,\n" + + " m210, m211, m212, m213, m214, m215, m216, m217, m218, m219,\n" + + " m220, m221, m222, m223, m224, m225, m226, m227, m228, m229,\n" + + " m230, m231, m232, m233, m234, m235, m236, m237, m238, m239,\n" + + " m240, m241, m242, m243, m244, m245, m246, m247, m248, m249,\n" + + " m250, m251, m252, m253, m254, m255, m256, m257, m258, m259,\n" + + " m260, m261, m262, m263;\n" + + " int m301, m302, m303, m304, m305, m306, m307, m308, m309,\n" + + " m310, m311, m312, m313, m314, m315, m316, m317, m318, m319,\n" + + " m320, m321, m322, m323, m324, m325, m326, m327, m328, m329,\n" + + " m330, m331, m332, m333, m334, m335, m336, m337, m338, m339,\n" + + " m340, m341, m342, m343, m344, m345, m346, m347, m348, m349,\n" + + " m350, m351, m352, m353, m354, m355, m356, m357, m358, m359,\n" + + " m360, m361, m362, m363;\n" + + " X() {\n" + + " m200 = 10;\n" + + " class Inner extends X {\n" + + " int m100, m101, m102, m103, m104, m105, m106, m107, m108, m109,\n" + + " m110, m111, m112, m113, m114, m115, m116, m117, m118, m119,\n" + + " m120, m121, m122, m123, m124, m125, m126, m127, m128, m129,\n" + + " m130, m131, m132, m133, m134, m135, m136, m137, m138, m139,\n" + + " m140, m141, m142, m143, m144, m145, m146, m147, m148, m149,\n" + + " m150, m151, m152, m153, m154, m155, m156, m157, m158, m159,\n" + + " m160, m161, m162, m163;\n" + + " final int m164;\n" + + " int bar() {\n" + + " return m100 + m101 + m102 + m103 + m104 +\n" + + " m105 + m106 + m107 + m108 + m109 +\n" + + " m110 + m111 + m112 + m113 + m114 +\n" + + " m115 + m116 + m117 + m118 + m119 +\n" + + " m120 + m121 + m122 + m123 + m124 +\n" + + " m125 + m126 + m127 + m128 + m129 +\n" + + " m130 + m131 + m132 + m133 + m134 +\n" + + " m135 + m136 + m137 + m138 + m139 +\n" + + " m140 + m141 + m142 + m143 + m144 +\n" + + " m145 + m146 + m147 + m148 + m149 +\n" + + " m150 + m151 + m152 + m153 + m154 +\n" + + " m155 + m156 + m157 + m158 + m159 +\n" + + " m160 + m161 + m162 + m163 + m164;\n" + + " }\n" + + " };\n" + + " System.out.println((new Inner()).bar());\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 26)\n" + + " class Inner extends X {\n" + + " ^^^^^\n" + + "The blank final field m164 may not have been initialized\n" + + "----------\n"); +} + +// null analysis -- flow info +public void test2016_flow_info() { + this.runNegativeTest( + new String[] { + "X.java", + "class X {\n" + + " int m000, m001, m002, m003, m004, m005, m006, m007, m008, m009,\n" + + " m010, m011, m012, m013, m014, m015, m016, m017, m018, m019,\n" + + " m020, m021, m022, m023, m024, m025, m026, m027, m028, m029,\n" + + " m030, m031, m032, m033, m034, m035, m036, m037, m038, m039,\n" + + " m040, m041, m042, m043, m044, m045, m046, m047, m048, m049,\n" + + " m050, m051, m052, m053, m054, m055, m056, m057, m058, m059,\n" + + " m060, m061;\n" + + " final int m062;\n" + + " {\n" + + " int l063, m201 = 0, m202, m203, m204, m205, m206, m207, m208, m209,\n" + + " m210, m211, m212, m213, m214, m215, m216, m217, m218, m219,\n" + + " m220, m221, m222, m223, m224, m225, m226, m227, m228, m229,\n" + + " m230, m231, m232, m233, m234, m235, m236, m237, m238, m239,\n" + + " m240, m241, m242, m243, m244, m245, m246, m247, m248, m249,\n" + + " m250, m251, m252, m253, m254, m255, m256, m257, m258, m259,\n" + + " m260, m261, m262, m263;\n" + + " int m301, m302, m303, m304, m305, m306, m307, m308, m309,\n" + + " m310, m311, m312, m313, m314, m315, m316, m317, m318, m319,\n" + + " m320, m321, m322, m323, m324, m325, m326, m327, m328, m329,\n" + + " m330, m331, m332, m333, m334, m335, m336, m337, m338, m339,\n" + + " m340, m341, m342, m343, m344, m345, m346, m347, m348, m349,\n" + + " m350, m351, m352, m353, m354, m355, m356, m357, m358, m359,\n" + + " m360 = 0, m361 = 0, m362 = 0, m363 = 0;\n" + + " m062 = m360;\n" + + " }\n" + + " X() {\n" + + " int l0, l1;\n" + + " m000 = l1;\n" + + " class Inner extends X {\n" + + " int bar() {\n" + + " return 0;\n" + + " }\n" + + " };\n" + + " System.out.println((new Inner()).bar());\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 29)\n" + + " m000 = l1;\n" + + " ^^\n" + + "The local variable l1 may not have been initialized\n" + + "----------\n"); +} - // null analysis - if/else - public void test0107_if_else() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo(Object o) {\n" + - " if (o == null) {\n" + - " System.exit(0);\n" + - " }\n" + - " if (o == null) {\n" + - // quiet - // a direct call to System.exit() can be recognized as such; yet, - // a lot of other methods may have the same property (aka calling - // System.exit() themselves.) - " // do nothing\n" + - " }\n" + - " }\n" + - "}\n"}, - "" - ); - } - - // null analysis -- while - // TODO (maxime) fix - public void _test0111_while() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Object o = null;\n" + - " while (o.toString() != null) {/* */}\n" + - // complain: NPE - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 4)\n" + - " while (o.toString() != null) {/* */}\n" + - " ^\n" + - "The variable o can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); - } -/* TODO (maxime) - Object o = new Object(); - while (b) { - o.toString(); // should signal NPE risk - if (b2) o = null; - } - */ - -/* TODO (maxime) - Object o = new Object(); - if (b2) o = null; - o.toString(); // should barf - */ - // null analysis -- while - // TODO (maxime) fix - public void _test0112_while() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Object o = null;\n" + - " while (o != null) {/* */}\n" + - // complain: get o null first time and forever - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 4)\n" + - " while (o != null) {/* */}\n" + - " ^\n" + - "The variable o can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); - } +public void test2017_flow_info() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean dummy;\n" + + " Object m000, m001, m002, m003, m004, m005, m006, m007, m008, m009,\n" + + " m010, m011, m012, m013, m014, m015, m016, m017, m018, m019,\n" + + " m020, m021, m022, m023, m024, m025, m026, m027, m028, m029,\n" + + " m030, m031, m032, m033, m034, m035, m036, m037, m038, m039,\n" + + " m040, m041, m042, m043, m044, m045, m046, m047, m048, m049,\n" + + " m050, m051, m052, m053, m054, m055, m056, m057, m058, m059,\n" + + " m060, m061, m062, m063;\n" + + " void foo(Object u) {\n" + + " Object o = null;\n" + + " while (dummy) {\n" + + " if (dummy) {\n" + // uncorrelated + " o = u;\n" + + " continue;\n" + + " }\n" + + " }\n" + + " if (o != null) { /* */ }\n" + + " }\n" + + "}\n"}, + ""); +} - // null analysis -- while - public void test0113_while() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Object o = null;\n" + - " while (o == null) {\n" + - // quiet: first iteration is sure to find o null, - // but other iterations may change it - " o = new Object();\n" + - " }\n" + - " }\n" + - "}\n"}, - "" - ); - } +public void test2018_flow_info() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean dummy;\n" + + " Object m000, m001, m002, m003, m004, m005, m006, m007, m008, m009,\n" + + " m010, m011, m012, m013, m014, m015, m016, m017, m018, m019,\n" + + " m020, m021, m022, m023, m024, m025, m026, m027, m028, m029,\n" + + " m030, m031, m032, m033, m034, m035, m036, m037, m038, m039,\n" + + " m040, m041, m042, m043, m044, m045, m046, m047, m048, m049,\n" + + " m050, m051, m052, m053, m054, m055, m056, m057, m058, m059,\n" + + " m060, m061, m062, m063;\n" + + " void foo() {\n" + + " Object o;\n" + + " while (dummy) {\n" + + " if (dummy) {\n" + // uncorrelated + " o = null;\n" + + " continue;\n" + + " }\n" + + " }\n" + + " o.toString();\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 18)\n" + + " o.toString();\n" + + " ^\n" + + "The local variable o may not have been initialized\n" + + "----------\n" + + "2. ERROR in X.java (at line 18)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n"); +} - // null analysis -- while - public void test0114_while() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Object o = null;\n" + - " while (o == null) {\n" + - // quiet: first iteration is sure to find o null, - // but other iterations may change it - " if (System.currentTimeMillis() > 10L) {\n" + - " o = new Object();\n" + - " }\n" + - " }\n" + - " }\n" + - "}\n"}, - "" - ); - } +public void test2019_flow_info() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean dummy;\n" + + " Object m000, m001, m002, m003, m004, m005, m006, m007, m008, m009,\n" + + " m010, m011, m012, m013, m014, m015, m016, m017, m018, m019,\n" + + " m020, m021, m022, m023, m024, m025, m026, m027, m028, m029,\n" + + " m030, m031, m032, m033, m034, m035, m036, m037, m038, m039,\n" + + " m040, m041, m042, m043, m044, m045, m046, m047, m048, m049,\n" + + " m050, m051, m052, m053, m054, m055, m056, m057, m058, m059,\n" + + " m060, m061, m062, m063;\n" + + " void foo() {\n" + + " Object o;\n" + + " while (dummy) {\n" + + " if (dummy) {\n" + // uncorrelated + " continue;\n" + + " }\n" + + " o = null;\n" + + " }\n" + + " o.toString();\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 18)\n" + + " o.toString();\n" + + " ^\n" + + "The local variable o may not have been initialized\n" + + "----------\n" + + "2. ERROR in X.java (at line 18)\n" + + " o.toString();\n" + + " ^\n" + + "The variable o may be null\n" + + "----------\n"); +} - // null analysis -- while - // TODO (maxime) fix - public void _test0115_while() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " boolean bar() {\n" + - " return true;\n" + - " }\n" + - " void foo(Object o) {\n" + - " while (bar() && o == null) {\n" + - " o.toString();\n" + // complain: NPE - " o = new Object();\n" + - " }\n" + - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 7)\n" + - " o.toString();\n" + - " ^\n" + - "The variable o can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); - } +public void test2020_flow_info() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean dummy;\n" + + " Object m000, m001, m002, m003, m004, m005, m006, m007, m008, m009,\n" + + " m010, m011, m012, m013, m014, m015, m016, m017, m018, m019,\n" + + " m020, m021, m022, m023, m024, m025, m026, m027, m028, m029,\n" + + " m030, m031, m032, m033, m034, m035, m036, m037, m038, m039,\n" + + " m040, m041, m042, m043, m044, m045, m046, m047, m048, m049,\n" + + " m050, m051, m052, m053, m054, m055, m056, m057, m058, m059,\n" + + " m060, m061, m062, m063;\n" + + " int m200, m201, m202, m203, m204, m205, m206, m207, m208, m209,\n" + + " m210, m211, m212, m213, m214, m215, m216, m217, m218, m219,\n" + + " m220, m221, m222, m223, m224, m225, m226, m227, m228, m229,\n" + + " m230, m231, m232, m233, m234, m235, m236, m237, m238, m239,\n" + + " m240, m241, m242, m243, m244, m245, m246, m247, m248, m249,\n" + + " m250, m251, m252, m253, m254, m255, m256, m257, m258, m259,\n" + + " m260, m261;\n" + + " void foo() {\n" + + " Object o0, o1;\n" + + " while (dummy) {\n" + + " o0 = new Object();\n" + + " if (dummy) {\n" + // uncorrelated + " o1 = null;\n" + + " continue;\n" + + " }\n" + + " }\n" + + " o1.toString();\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 26)\n" + + " o1.toString();\n" + + " ^^\n" + + "The local variable o1 may not have been initialized\n" + + "----------\n" + + "2. ERROR in X.java (at line 26)\n" + + " o1.toString();\n" + + " ^^\n" + + "The variable o1 may be null\n" + + "----------\n"); +} - // null analysis -- while - // TODO (maxime) fix - public void _test0116_while() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " boolean dummy;\n" + - " void foo(Object o) {\n" + - " o = null;\n" + - " while (dummy || o != null) { /* */ }\n" + // o can only be null - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 5)\n" + - " while (dummy || o != null) {\n" + - " ^\n" + - "The variable o can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); +// Technical tests -- only available with patched sources +static final boolean + printTablesAsNames = false, + printTablesAsCodes = false, + printTruthMaps = false; +static final int + combinationTestsloopsNb = 1; // define to 10000s to measure performances + + +public void test2050_markAsComparedEqualToNonNull() { + long [][][] testData = { + {{0,0,0,0},{1,1,0,0}}, + {{0,0,0,1},{1,1,0,1}}, + {{0,0,1,0},{1,1,0,0}}, + {{0,0,1,1},{1,1,0,1}}, + {{0,1,0,0},{1,1,0,0}}, + {{0,1,0,1},{1,1,0,1}}, + {{0,1,1,0},{1,1,0,0}}, + {{0,1,1,1},{1,1,0,1}}, + {{1,0,0,0},{1,1,0,0}}, + {{1,0,0,1},{1,0,0,1}}, + {{1,0,1,0},{1,1,0,0}}, + {{1,0,1,1},{1,1,0,1}}, + {{1,1,0,0},{1,1,0,0}}, + {{1,1,0,1},{1,1,0,1}}, + {{1,1,1,0},{1,1,0,0}}, + {{1,1,1,1},{1,1,0,1}}, + }; + int failures = 0; + LocalVariableBinding local = new TestLocalVariableBinding(0); + for (int i = 0; i < testData.length; i++) { + UnconditionalFlowInfoTestHarness + result = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][0]); + result.markAsComparedEqualToNonNull(local); + if (!(result.testEquals(UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][1])))) { + if (failures == 0) { + System.out.println("markAsComparedEqualToNonNull failures: "); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + result.testString() + + "}, // instead of: " + testStringValueOf(testData[i][1])); + } } - - // null analysis -- while - // TODO (maxime) fix - public void _test0117_while() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " boolean dummy;\n" + - " void foo() {\n" + - " Object o = null;\n" + - " while (dummy) {\n" + - " o.toString();\n" + // complain: NPE on first iteration - " o = new Object();\n" + - " }\n" + - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 6)\n" + - " o.toString();\n" + - " ^\n" + - "The variable o can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); + local = new TestLocalVariableBinding(64); + for (int i = 0; i < testData.length; i++) { + UnconditionalFlowInfoTestHarness + result = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][0], 64), + expected = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][1], 64); + result.markAsComparedEqualToNonNull(local); + if (!(result.testEquals(expected))) { + if (failures == 0) { + System.out.println("markAsComparedEqualToNonNull failures: "); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + result.testString() + + "}, // (64) instead of: " + testStringValueOf(testData[i][1])); + } + if (testData[i][0][0] == 0 && + testData[i][0][1] == 0 && + testData[i][0][2] == 0 && + testData[i][0][3] == 0) { + result = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][1]); + result.markAsComparedEqualToNonNull(local); + if (!result.testEquals(expected, 64)) { + if (failures == 0) { + System.out.println("markAsComparedEqualToNonNull failures: "); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + result.testString() + + "}, // (zero 64) instead of: " + testStringValueOf(testData[i][1])); + } + } } - - // TODO (maxime) should nuance error message: The variable o may be null... - - // null analysis -- while - // this test shows that, as long as we do not explore all possible - // paths, we have to take potential initializations into account - // even in branches that could be pruned in the first passes - // first approximation is to stop pruning code conditioned by - // variables - // second approximation could still rely upon variables that are - // never affected by the looping code (unassigned variables) - // complete solution would call for multiple iterations in the - // null analysis - // TODO (maxime) fix - public void _test0118_while() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Object o = null,\n" + - " u = new Object(),\n" + - " v = new Object();\n" + - " while (o == null) {\n" + - " if (v == null) {\n" + - " o = new Object();\n" + - " };\n" + - " if (u == null) {\n" + - " v = null;\n" + - " };\n" + - " u = null;\n" + - " }\n" + - " }\n" + - "}\n"}, - "" - ); + local = new TestLocalVariableBinding(128); + for (int i = 0; i < testData.length; i++) { + if (testData[i][0][0] == 0 && + testData[i][0][1] == 0 && + testData[i][0][2] == 0 && + testData[i][0][3] == 0) { + UnconditionalFlowInfoTestHarness + result = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][1], 64), + expected = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][1], 128); + result.markAsComparedEqualToNonNull(local); + if (!result.testEquals(expected, 128)) { + if (failures == 0) { + System.out.println("markAsComparedEqualToNonNull failures: "); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + result.testString() + + "}, // (zero 128) instead of: " + testStringValueOf(testData[i][1])); + } + } } - - // null analysis -- while - public void test0119_while() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " boolean dummy;\n" + - " void foo() {\n" + - " Object o = null;\n" + - " while (dummy || (o = new Object()).equals(o)) {\n" + - " o.toString();\n" + - " }\n" + - " }\n" + - "}\n"}, - "" - ); + if (printTablesAsNames) { + System.out.println("RECAP TABLE FOR MARK COMPARED NON NULL"); + for (int i = 0; i < testData.length; i++) { + System.out.println(testSymbolicValueOf(testData[i][0]) + " -> " + + testSymbolicValueOf(testData[i][1])); + } + } + if (printTablesAsCodes) { + System.out.println("RECAP TABLE FOR MARK COMPARED NON NULL"); + for (int i = 0; i < testData.length; i++) { + System.out.println(testCodedValueOf(testData[i][0]) + " " + + testCodedValueOf(testData[i][1])); + } } + assertTrue("nb of failures: " + failures, failures == 0); +} - // null analysis -- while - public void test0120_while_nested() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " boolean dummy;\n" + - " void foo() {\n" + - " Object o = null;\n" + - " while (dummy) {\n" + - " while (o != null) {\n" + - " o.toString();\n" + - " }\n" + - " if (System.currentTimeMillis() > 10L) {\n" + - " o = new Object();\n" + - " }\n" + - " }\n" + - " }\n" + - "}\n"}, - "" - ); +public void test2051_markAsComparedEqualToNull() { + long [][][] testData = { + {{0,0,0,0},{0,1,0,0}}, + {{0,0,0,1},{0,1,0,0}}, + {{0,0,1,0},{0,1,1,0}}, + {{0,0,1,1},{0,1,1,0}}, + {{0,1,0,0},{0,1,0,0}}, + {{0,1,0,1},{0,1,0,0}}, + {{0,1,1,0},{0,1,1,0}}, + {{0,1,1,1},{0,1,1,0}}, + {{1,0,0,0},{0,1,0,0}}, + {{1,0,0,1},{0,1,0,0}}, + {{1,0,1,0},{1,0,1,0}}, + {{1,0,1,1},{0,1,0,0}}, + {{1,1,0,0},{0,1,0,0}}, + {{1,1,0,1},{0,1,0,0}}, + {{1,1,1,0},{0,1,1,0}}, + {{1,1,1,1},{0,1,1,0}}, + }; + int failures = 0; + LocalVariableBinding local = new TestLocalVariableBinding(0); + for (int i = 0; i < testData.length; i++) { + UnconditionalFlowInfoTestHarness + result = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][0]); + result.markAsComparedEqualToNull(local); + if (!(result.testEquals(UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][1])))) { + if (failures == 0) { + System.out.println("markAsComparedEqualToNull failures: "); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + result.testString() + + "}, // instead of: " + testStringValueOf(testData[i][1])); + } } - - // null analysis -- while - // TODO (maxime) fix - public void _test0121_while_nested() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Object o = null,\n" + - " u = new Object(),\n" + - " v = new Object();\n" + - " while (o == null) {\n" + - " if (v == null) {\n" + - " o = new Object();\n" + - " };\n" + - " while (o == null) {\n" + - " if (u == null) {\n" + - " v = null;\n" + - " };\n" + - " u = null;\n" + - " }\n" + - " }\n" + - " }\n" + - "}\n"}, - "" - ); + local = new TestLocalVariableBinding(64); + for (int i = 0; i < testData.length; i++) { + UnconditionalFlowInfoTestHarness + result = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][0], 64), + expected = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][1], 64); + result.markAsComparedEqualToNull(local); + if (!(result.testEquals(expected))) { + if (failures == 0) { + System.out.println("markAsComparedEqualToNull failures: "); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + result.testString() + + "}, // (64) instead of: " + testStringValueOf(testData[i][1])); + } + if (testData[i][0][0] == 0 && + testData[i][0][1] == 0 && + testData[i][0][2] == 0 && + testData[i][0][3] == 0) { + result = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][1]); + result.markAsComparedEqualToNull(local); + if (!result.testEquals(expected, 64)) { + if (failures == 0) { + System.out.println("markAsComparedEqualToNull failures: "); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + result.testString() + + "}, // (zero 64) instead of: " + testStringValueOf(testData[i][1])); + } + } + } + local = new TestLocalVariableBinding(128); + for (int i = 0; i < testData.length; i++) { + if (testData[i][0][0] == 0 && + testData[i][0][1] == 0 && + testData[i][0][2] == 0 && + testData[i][0][3] == 0) { + UnconditionalFlowInfoTestHarness + result = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][1], 64), + expected = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][1], 128); + result.markAsComparedEqualToNull(local); + if (!result.testEquals(expected, 128)) { + if (failures == 0) { + System.out.println("markAsComparedEqualToNull failures: "); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + result.testString() + + "}, // (zero 128) instead of: " + testStringValueOf(testData[i][1])); + } + } + } + if (printTablesAsNames) { + System.out.println("RECAP TABLE FOR MARK COMPARED NULL"); + for (int i = 0; i < testData.length; i++) { + System.out.println(testSymbolicValueOf(testData[i][0]) + " -> " + + testSymbolicValueOf(testData[i][1])); + } + } + if (printTablesAsCodes) { + System.out.println("RECAP TABLE FOR MARK COMPARED NULL"); + for (int i = 0; i < testData.length; i++) { + System.out.println(testCodedValueOf(testData[i][0]) + " " + + testCodedValueOf(testData[i][1])); + } } + assertTrue("nb of failures: " + failures, failures == 0); +} - // null analysis -- while - public void test0122_while_if_nested() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " boolean dummy, other;\n" + - " void foo() {\n" + - " Object o = null;\n" + - " while (dummy) {\n" + - " if (other) {\n" + - " o.toString();\n" + - " }\n" + - " o = new Object();\n" + - " }\n" + - " }\n" + - "}\n"}, - "" - ); +public void test2052_markAsDefinitelyNonNull() { + long [][][] testData = { + {{0,0,0,0},{1,0,0,1}}, + {{0,0,0,1},{1,0,0,1}}, + {{0,0,1,0},{1,0,0,1}}, + {{0,0,1,1},{1,0,0,1}}, + {{0,1,0,0},{1,0,0,1}}, + {{0,1,0,1},{1,0,0,1}}, + {{0,1,1,0},{1,0,0,1}}, + {{0,1,1,1},{1,0,0,1}}, + {{1,0,0,0},{1,0,0,1}}, + {{1,0,0,1},{1,0,0,1}}, + {{1,0,1,0},{1,0,0,1}}, + {{1,0,1,1},{1,0,0,1}}, + {{1,1,0,0},{1,0,0,1}}, + {{1,1,0,1},{1,0,0,1}}, + {{1,1,1,0},{1,0,0,1}}, + {{1,1,1,1},{1,0,0,1}}, + }; + int failures = 0; + LocalVariableBinding local = new TestLocalVariableBinding(0); + for (int i = 0; i < testData.length; i++) { + UnconditionalFlowInfoTestHarness + result = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][0]); + result.markAsDefinitelyNonNull(local); + if (!(result.testEquals(UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][1])))) { + if (failures == 0) { + System.out.println("markAsDefinitelyNonNull failures: "); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + result.testString() + + "}, // instead of: " + testStringValueOf(testData[i][1])); + } } - - // null analysis -- while - public void test0123_while_unknown_field() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " Object o;\n" + - " void foo(boolean dummy) {\n" + - " while (dummy) {\n" + - " o = null;\n" + - " }\n" + - " o.toString();\n" + - " }\n" + - "}\n"}, - "" - ); + local = new TestLocalVariableBinding(64); + for (int i = 0; i < testData.length; i++) { + UnconditionalFlowInfoTestHarness + result = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][0], 64); + result.markAsDefinitelyNonNull(local); + if (!(result.testEquals(UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][1], 64)))) { + if (failures == 0) { + System.out.println("markAsDefinitelyNonNull failures: "); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + result.testString() + + "}, // (64) instead of: " + testStringValueOf(testData[i][1])); + } } - - // null analysis -- while - public void test0124_while_unknown_parameter() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " boolean dummy;\n" + - " void foo(Object o) {\n" + - " while (dummy) {\n" + - " o = null;\n" + - " }\n" + - " o.toString();\n" + - " }\n" + - "}\n"}, - "" - ); + if (printTablesAsNames) { + System.out.println("RECAP TABLE FOR MARK DEFINITELY NON NULL"); + for (int i = 0; i < testData.length; i++) { + System.out.println(testSymbolicValueOf(testData[i][0]) + " -> " + + testSymbolicValueOf(testData[i][1])); + } + } + if (printTablesAsCodes) { + System.out.println("RECAP TABLE FOR MARK DEFINITELY NON NULL"); + for (int i = 0; i < testData.length; i++) { + System.out.println(testCodedValueOf(testData[i][0]) + " " + + testCodedValueOf(testData[i][1])); + } } + assertTrue("nb of failures: " + failures, failures == 0); +} - // null analysis -- while - public void test0125_while_unknown_if_else() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " boolean dummy;\n" + - " void foo() {\n" + - " Object o = null;\n" + - " if (dummy) {\n" + - " o = new Object();\n" + - " }\n" + - " while (dummy) {\n" + - // limit of the analysis: we do not correlate if and while conditions - " if (o == null) {/* */}\n" + - " }\n" + - " }\n" + - "}\n"}, - "" - ); +public void test2053_markAsDefinitelyNull() { + long [][][] testData = { + {{0,0,0,0},{1,0,1,0}}, + {{0,0,0,1},{1,0,1,0}}, + {{0,0,1,0},{1,0,1,0}}, + {{0,0,1,1},{1,0,1,0}}, + {{0,1,0,0},{1,0,1,0}}, + {{0,1,0,1},{1,0,1,0}}, + {{0,1,1,0},{1,0,1,0}}, + {{0,1,1,1},{1,0,1,0}}, + {{1,0,0,0},{1,0,1,0}}, + {{1,0,0,1},{1,0,1,0}}, + {{1,0,1,0},{1,0,1,0}}, + {{1,0,1,1},{1,0,1,0}}, + {{1,1,0,0},{1,0,1,0}}, + {{1,1,0,1},{1,0,1,0}}, + {{1,1,1,0},{1,0,1,0}}, + {{1,1,1,1},{1,0,1,0}}, + }; + int failures = 0; + LocalVariableBinding local = new TestLocalVariableBinding(0); + for (int i = 0; i < testData.length; i++) { + UnconditionalFlowInfoTestHarness + result = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][0]); + result.markAsDefinitelyNull(local); + if (!(result.testEquals(UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][1])))) { + if (failures == 0) { + System.out.println("markAsDefinitelyNull failures: "); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + result.testString() + + "}, // instead of: " + testStringValueOf(testData[i][1])); + } + } + local = new TestLocalVariableBinding(64); + for (int i = 0; i < testData.length; i++) { + UnconditionalFlowInfoTestHarness + result = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][0], 64); + result.markAsDefinitelyNull(local); + if (!(result.testEquals(UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][1], 64)))) { + if (failures == 0) { + System.out.println("markAsDefinitelyNull failures: "); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + result.testString() + + "}, // (64) instead of: " + testStringValueOf(testData[i][1])); + } + } + if (printTablesAsNames) { + System.out.println("RECAP TABLE FOR MARK DEFINITELY NULL"); + for (int i = 0; i < testData.length; i++) { + System.out.println(testSymbolicValueOf(testData[i][0]) + " -> " + + testSymbolicValueOf(testData[i][1])); + } + } + if (printTablesAsCodes) { + System.out.println("RECAP TABLE FOR MARK DEFINITELY NULL"); + for (int i = 0; i < testData.length; i++) { + System.out.println(testCodedValueOf(testData[i][0]) + " " + + testCodedValueOf(testData[i][1])); + } } + assertTrue("nb of failures: " + failures, failures == 0); +} - // null analysis -- while - public void test0126_while() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " boolean dummy;\n" + - " void foo() {\n" + - " Object o = null;\n" + - " while (dummy) {\n" + - " o = new Object();\n" + - " }\n" + - " o.toString();\n" + - " }\n" + - "}\n"}, - "" - ); +public void test2054_markAsDefinitelyUnknown() { + long [][][] testData = { + {{0,0,0,0},{1,0,1,1}}, + {{0,0,0,1},{1,0,1,1}}, + {{0,0,1,0},{1,0,1,1}}, + {{0,0,1,1},{1,0,1,1}}, + {{0,1,0,0},{1,0,1,1}}, + {{0,1,0,1},{1,0,1,1}}, + {{0,1,1,0},{1,0,1,1}}, + {{0,1,1,1},{1,0,1,1}}, + {{1,0,0,0},{1,0,1,1}}, + {{1,0,0,1},{1,0,1,1}}, + {{1,0,1,0},{1,0,1,1}}, + {{1,0,1,1},{1,0,1,1}}, + {{1,1,0,0},{1,0,1,1}}, + {{1,1,0,1},{1,0,1,1}}, + {{1,1,1,0},{1,0,1,1}}, + {{1,1,1,1},{1,0,1,1}}, + }; + int failures = 0; + LocalVariableBinding local = new TestLocalVariableBinding(0); + for (int i = 0; i < testData.length; i++) { + UnconditionalFlowInfoTestHarness + result = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][0]); + result.markAsDefinitelyUnknown(local); + if (!(result.testEquals(UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][1])))) { + if (failures == 0) { + System.out.println("markAsDefinitelyUnknown failures: "); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + result.testString() + + "}, // instead of: " + testStringValueOf(testData[i][1])); + } } - - // null analysis -- while - // TODO (maxime) fix - public void _test0127_while() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " boolean dummy;\n" + - " void foo() {\n" + - " Object o = null;\n" + - " while (dummy) { /* */ }\n" + // doesn't affect o - " o.toString();\n" + - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 6)\n" + - " o.toString();\n" + - " ^\n" + - "The variable o can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); + local = new TestLocalVariableBinding(64); + for (int i = 0; i < testData.length; i++) { + UnconditionalFlowInfoTestHarness + result = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][0], 64); + result.markAsDefinitelyUnknown(local); + if (!(result.testEquals(UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][1], 64)))) { + if (failures == 0) { + System.out.println("markAsDefinitelyUnknown failures: "); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + result.testString() + + "}, // (64) instead of: " + testStringValueOf(testData[i][1])); + } } - - // null analysis -- while - // origin AssignmentTest.testO22 - public void test0128_while_try() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " boolean bool() { return true; }\n" + - " void foo() {\n" + - " Object o = null;\n" + - " while (bool()) {\n" + - " try {\n" + - " if (o == null) {\n" + - " o = new Object();\n" + - " }\n" + - " } finally { /* */ }\n" + - " }\n" + - " }\n" + - "}", - }, - ""); + if (printTablesAsNames) { + System.out.println("RECAP TABLE FOR MARK DEFINITELY UNKNOWN"); + for (int i = 0; i < testData.length; i++) { + System.out.println(testSymbolicValueOf(testData[i][0]) + " -> " + + testSymbolicValueOf(testData[i][1])); + } + } + if (printTablesAsCodes) { + System.out.println("RECAP TABLE FOR MARK DEFINITELY UNKNOWN"); + for (int i = 0; i < testData.length; i++) { + System.out.println(testCodedValueOf(testData[i][0]) + " " + + testCodedValueOf(testData[i][1])); + } } + assertTrue("nb of failures: " + failures, failures == 0); +} - // null analysis -- try/finally - // TODO (maxime) fix - public void _test0150_try_finally() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " Object m;\n" + - " void foo() {\n" + - " Object o = null;\n" + - " try { /* */ }\n" + - " finally {\n" + - " o = m;\n" + - " }\n" + - " o.toString();\n" + - " }\n" + - "}\n"}, - "" // because finally assigns to unknown value - ); +public void test2055_addInitializationsFrom() { + long [][][] testData = { + {{0,0,0,0},{0,0,0,0},{0,0,0,0}}, + {{0,0,0,0},{0,0,0,1},{0,0,0,1}}, + {{0,0,0,0},{0,0,1,0},{0,0,1,0}}, + {{0,0,0,0},{0,0,1,1},{0,0,1,1}}, + {{0,0,0,0},{0,1,0,0},{0,1,0,0}}, + {{0,0,0,0},{0,1,1,0},{0,1,1,0}}, + {{0,0,0,0},{1,0,0,1},{1,0,0,1}}, + {{0,0,0,0},{1,0,1,0},{1,0,1,0}}, + {{0,0,0,0},{1,0,1,1},{1,0,1,1}}, + {{0,0,0,0},{1,1,0,0},{1,1,0,0}}, + {{0,0,0,0},{1,1,0,1},{1,1,0,1}}, + {{0,0,0,1},{0,0,0,0},{0,0,0,1}}, + {{0,0,0,1},{0,0,0,1},{0,0,0,1}}, + {{0,0,0,1},{0,0,1,0},{0,0,1,1}}, + {{0,0,0,1},{0,0,1,1},{0,0,1,1}}, + {{0,0,0,1},{0,1,0,0},{0,1,0,0}}, + {{0,0,0,1},{0,1,1,0},{0,1,0,0}}, + {{0,0,0,1},{1,0,0,1},{1,0,0,1}}, + {{0,0,0,1},{1,0,1,0},{1,0,1,0}}, + {{0,0,0,1},{1,0,1,1},{1,0,1,1}}, + {{0,0,0,1},{1,1,0,0},{1,1,0,1}}, + {{0,0,0,1},{1,1,0,1},{1,1,0,1}}, + {{0,0,1,0},{0,0,0,0},{0,0,1,0}}, + {{0,0,1,0},{0,0,0,1},{0,0,1,1}}, + {{0,0,1,0},{0,0,1,0},{0,0,1,0}}, + {{0,0,1,0},{0,0,1,1},{0,0,1,1}}, + {{0,0,1,0},{0,1,0,0},{0,1,1,0}}, + {{0,0,1,0},{0,1,1,0},{0,1,1,0}}, + {{0,0,1,0},{1,0,0,1},{1,0,0,1}}, + {{0,0,1,0},{1,0,1,0},{1,0,1,0}}, + {{0,0,1,0},{1,0,1,1},{1,0,1,1}}, + {{0,0,1,0},{1,1,0,0},{1,1,0,0}}, + {{0,0,1,0},{1,1,0,1},{1,1,0,1}}, + {{0,0,1,1},{0,0,0,0},{0,0,1,1}}, + {{0,0,1,1},{0,0,0,1},{0,0,1,1}}, + {{0,0,1,1},{0,0,1,0},{0,0,1,1}}, + {{0,0,1,1},{0,0,1,1},{0,0,1,1}}, + {{0,0,1,1},{0,1,0,0},{0,1,1,0}}, + {{0,0,1,1},{0,1,1,0},{0,1,1,0}}, + {{0,0,1,1},{1,0,0,1},{1,0,0,1}}, + {{0,0,1,1},{1,0,1,0},{1,0,1,0}}, + {{0,0,1,1},{1,0,1,1},{1,0,1,1}}, + {{0,0,1,1},{1,1,0,0},{1,1,0,1}}, + {{0,0,1,1},{1,1,0,1},{1,1,0,1}}, + {{0,1,0,0},{0,0,0,0},{0,1,0,0}}, + {{0,1,0,0},{0,0,0,1},{0,0,0,1}}, + {{0,1,0,0},{0,0,1,0},{0,1,1,0}}, + {{0,1,0,0},{0,0,1,1},{0,0,1,1}}, + {{0,1,0,0},{0,1,0,0},{0,1,0,0}}, + {{0,1,0,0},{0,1,1,0},{0,1,1,0}}, + {{0,1,0,0},{1,0,0,1},{1,0,0,1}}, + {{0,1,0,0},{1,0,1,0},{1,0,1,0}}, + {{0,1,0,0},{1,0,1,1},{1,0,1,1}}, + {{0,1,0,0},{1,1,0,0},{1,1,0,0}}, + {{0,1,0,0},{1,1,0,1},{1,1,0,1}}, + {{0,1,1,0},{0,0,0,0},{0,1,1,0}}, + {{0,1,1,0},{0,0,0,1},{0,0,1,1}}, + {{0,1,1,0},{0,0,1,0},{0,1,1,0}}, + {{0,1,1,0},{0,0,1,1},{0,0,1,1}}, + {{0,1,1,0},{0,1,0,0},{0,1,1,0}}, + {{0,1,1,0},{0,1,1,0},{0,1,1,0}}, + {{0,1,1,0},{1,0,0,1},{1,0,0,1}}, + {{0,1,1,0},{1,0,1,0},{1,0,1,0}}, + {{0,1,1,0},{1,0,1,1},{1,0,1,1}}, + {{0,1,1,0},{1,1,0,0},{1,1,0,0}}, + {{0,1,1,0},{1,1,0,1},{1,1,0,1}}, + {{1,0,0,1},{0,0,0,0},{1,0,0,1}}, + {{1,0,0,1},{0,0,0,1},{1,0,0,1}}, + {{1,0,0,1},{0,0,1,0},{0,0,1,1}}, + {{1,0,0,1},{0,0,1,1},{0,0,1,1}}, + {{1,0,0,1},{0,1,0,0},{0,1,0,0}}, + {{1,0,0,1},{0,1,1,0},{0,1,1,0}}, + {{1,0,0,1},{1,0,0,1},{1,0,0,1}}, + {{1,0,0,1},{1,0,1,0},{1,0,1,0}}, + {{1,0,0,1},{1,0,1,1},{1,0,1,1}}, + {{1,0,0,1},{1,1,0,0},{1,1,0,1}}, + {{1,0,0,1},{1,1,0,1},{1,1,0,1}}, + {{1,0,1,0},{0,0,0,0},{1,0,1,0}}, + {{1,0,1,0},{0,0,0,1},{0,0,1,1}}, + {{1,0,1,0},{0,0,1,0},{1,0,1,0}}, + {{1,0,1,0},{0,0,1,1},{0,0,1,1}}, + {{1,0,1,0},{0,1,0,0},{1,0,1,0}}, + {{1,0,1,0},{0,1,1,0},{1,0,1,0}}, + {{1,0,1,0},{1,0,0,1},{1,0,0,1}}, + {{1,0,1,0},{1,0,1,0},{1,0,1,0}}, + {{1,0,1,0},{1,0,1,1},{1,0,1,1}}, + {{1,0,1,0},{1,1,0,0},{1,1,0,0}}, + {{1,0,1,0},{1,1,0,1},{1,1,0,1}}, + {{1,0,1,1},{0,0,0,0},{1,0,1,1}}, + {{1,0,1,1},{0,0,0,1},{1,0,1,1}}, + {{1,0,1,1},{0,0,1,0},{0,0,1,1}}, + {{1,0,1,1},{0,0,1,1},{0,0,1,1}}, + {{1,0,1,1},{0,1,0,0},{0,1,0,0}}, + {{1,0,1,1},{0,1,1,0},{0,1,1,0}}, + {{1,0,1,1},{1,0,0,1},{1,0,0,1}}, + {{1,0,1,1},{1,0,1,0},{1,0,1,0}}, + {{1,0,1,1},{1,0,1,1},{1,0,1,1}}, + {{1,0,1,1},{1,1,0,0},{1,1,0,1}}, + {{1,0,1,1},{1,1,0,1},{1,1,0,1}}, + {{1,1,0,0},{0,0,0,0},{1,1,0,0}}, + {{1,1,0,0},{0,0,0,1},{1,1,0,1}}, + {{1,1,0,0},{0,0,1,0},{0,0,1,0}}, + {{1,1,0,0},{0,0,1,1},{0,0,1,1}}, + {{1,1,0,0},{0,1,0,0},{0,1,0,0}}, + {{1,1,0,0},{0,1,1,0},{0,1,1,0}}, + {{1,1,0,0},{1,0,0,1},{1,0,0,1}}, + {{1,1,0,0},{1,0,1,0},{1,0,1,0}}, + {{1,1,0,0},{1,0,1,1},{1,0,1,1}}, + {{1,1,0,0},{1,1,0,0},{1,1,0,0}}, + {{1,1,0,0},{1,1,0,1},{1,1,0,1}}, + {{1,1,0,1},{0,0,0,0},{1,1,0,1}}, + {{1,1,0,1},{0,0,0,1},{0,0,0,1}}, + {{1,1,0,1},{0,0,1,0},{0,0,1,1}}, + {{1,1,0,1},{0,0,1,1},{0,0,1,1}}, + {{1,1,0,1},{0,1,0,0},{0,1,0,0}}, + {{1,1,0,1},{0,1,1,0},{0,1,1,0}}, + {{1,1,0,1},{1,0,0,1},{1,0,0,1}}, + {{1,1,0,1},{1,0,1,0},{1,0,1,0}}, + {{1,1,0,1},{1,0,1,1},{1,0,1,1}}, + {{1,1,0,1},{1,1,0,0},{1,1,0,1}}, + {{1,1,0,1},{1,1,0,1},{1,1,0,1}}, + }; + int failures = 0; + long start; + if (combinationTestsloopsNb > 1) { + start = System.currentTimeMillis(); + } + String header = "addInitializationsFrom failures: "; //$NON-NLS-1$ + for (int l = 0; l < combinationTestsloopsNb ; l++) { + for (int i = 0; i < testData.length; i++) { + UnconditionalFlowInfoTestHarness result; + if (!(result = (UnconditionalFlowInfoTestHarness)( + UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][0])). + addInitializationsFrom( + UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][1]))). + testEquals(UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][2]))) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString() + + "}, // instead of: " + testStringValueOf(testData[i][2])); + } + } } - - // null analysis -- try/finally - public void test0151_try_finally() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Object o = new Object();\n" + - " try { /* */ }\n" + - " finally {\n" + - " o = null;\n" + - " }\n" + - " o.toString();\n" + - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 8)\n" + - " o.toString();\n" + - " ^\n" + - "The variable o can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" // because finally assigns to null - ); + if (combinationTestsloopsNb > 1) { + System.out.println("addInitial...\t\t" + combinationTestsloopsNb + "\t" + + (System.currentTimeMillis() - start)); + } + // PREMATURE optimize test (extraneous allocations and copies) + // PREMATURE optimize test (extraneous iterations - undup) + UnconditionalFlowInfoTestHarness + zero = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(new long[] {0,0,0,0}), + left0, left1, right1, left2, right2, + expected0, expected1, expected2, result; + for (int i = 0; i < testData.length; i++) { + left0 = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][0]); + left1 = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][0], 64); + left2 = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][0], 128); + right1 = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][1], 64); + right2 = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][1], 128); + expected0 = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][2]); + expected1 = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][2], 64); + expected2 = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][2], 128); + if (!(result = (UnconditionalFlowInfoTestHarness) + left1.copy().addInitializationsFrom(right1)). + testEquals(expected1)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString() + + "}, // (64, 64) - instead of: " + testStringValueOf(testData[i][2])); + } + if ((testData[i][0][0] | testData[i][0][1] | + testData[i][0][2] | testData[i][0][3]) == 0) { + if (!(result = (UnconditionalFlowInfoTestHarness) + zero.copy().addInitializationsFrom(right1)). + testEquals(expected1)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString() + + "}, // (zero, 64) - instead of: " + testStringValueOf(testData[i][2])); + } + if (!(result = (UnconditionalFlowInfoTestHarness) right2.copy(). + addInitializationsFrom(right1)). + testEquals(expected1, 64)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString() + + "}, // (zero 128, 64) - instead of: " + testStringValueOf(testData[i][2])); + } + if (!(result = (UnconditionalFlowInfoTestHarness) + zero.copy().addInitializationsFrom(right2)). + testEquals(expected2, 128)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString(128) + + "}, // (zero, 128) - instead of: " + testStringValueOf(testData[i][2])); + } + if (!(result = (UnconditionalFlowInfoTestHarness) + right1.copy().addInitializationsFrom(right2)). + testEquals(expected2, 128)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString(128) + + "}, // (zero 64, 128) - instead of: " + testStringValueOf(testData[i][2])); + } + } + if ((testData[i][1][0] | testData[i][1][1] | + testData[i][1][2] | testData[i][1][3]) == 0) { + if (!(result = (UnconditionalFlowInfoTestHarness) + left0.copy().addInitializationsFrom(left2)). + testEquals(expected0, 0)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString() + + "}, // (1, zero 128) - instead of: " + testStringValueOf(testData[i][2])); + } + if (!(result = (UnconditionalFlowInfoTestHarness) + left1.copy().addInitializationsFrom(zero)). + testEquals(expected1)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString() + + "}, // (64, zero) - instead of: " + testStringValueOf(testData[i][2])); + } + if (!(result = (UnconditionalFlowInfoTestHarness) + left1.copy().addInitializationsFrom(left2)). + testEquals(expected1, 64)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString() + + "}, // (64, zero 128) - instead of: " + testStringValueOf(testData[i][2])); + } + if (!(result = (UnconditionalFlowInfoTestHarness) + left2.copy().addInitializationsFrom(zero)). + testEquals(expected2, 128)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString() + + "}, // (128, zero) - instead of: " + testStringValueOf(testData[i][2])); + } + if (!(result = (UnconditionalFlowInfoTestHarness) + left2.addInitializationsFrom(left1)). + testEquals(expected2, 128)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString() + + "}, // (128, zero 64) - instead of: " + testStringValueOf(testData[i][2])); + } + } } - - // null analysis -- try/finally - public void test0152_try_finally() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Object o = null;\n" + - " try {\n" + - " System.out.println();\n" + // might throw a runtime exception - " o = new Object();\n" + - " }\n" + - " finally { /* */ }\n" + - " o.toString();\n" + - // still OK because in case of exception this code is - // not reached - " }\n" + - "}\n"}, - "" - ); + if (printTablesAsNames) { + System.out.println("RECAP TABLE FOR ADD"); + for (int i = 0; i < testData.length; i++) { + System.out.println(testSymbolicValueOf(testData[i][0]) + " + " + + testSymbolicValueOf(testData[i][1]) + " -> " + + testSymbolicValueOf(testData[i][2])); + } + } + if (printTablesAsCodes) { + System.out.println("RECAP TABLE FOR ADD"); + for (int i = 0; i < testData.length; i++) { + System.out.println(testCodedValueOf(testData[i][0]) + " " + + testCodedValueOf(testData[i][1]) + " " + + testCodedValueOf(testData[i][2])); + } } + if (printTruthMaps) { + for (int i = 0; i < 4; i++) { + System.out.println("======================================================"); + System.out.println("Truth map for addInitializationsFrom null bit " + (i + 1)); + System.out.println(); + printTruthMap(testData, i); + } + } + assertTrue("nb of failures: " + failures, failures == 0); +} - // null analysis -- try/finally - public void test0153_try_finally() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo(X x) {\n" + - " x = null;\n" + - " try {\n" + - " x = null;\n" + // complain, already null - " } finally { /* */ }\n" + - " }\n" + - "}\n", - }, - "----------\n" + - "1. WARNING in X.java (at line 5)\n" + - " x = null;\n" + - " ^\n" + - "The variable x can only be null; it was either set to null or checked for null when last used\n" + - "----------\n"); +public void test2056_addPotentialInitializationsFrom() { + long [][][] testData = { + {{0,0,0,0},{0,0,0,0},{0,0,0,0}}, + {{0,0,0,0},{0,0,0,1},{0,0,0,1}}, + {{0,0,0,0},{0,0,1,0},{0,0,1,0}}, + {{0,0,0,0},{0,0,1,1},{0,0,1,1}}, + {{0,0,0,0},{0,1,0,0},{0,0,0,0}}, + {{0,0,0,0},{0,1,1,0},{0,0,1,0}}, + {{0,0,0,0},{1,0,0,1},{0,0,0,1}}, + {{0,0,0,0},{1,0,1,0},{0,0,1,0}}, + {{0,0,0,0},{1,0,1,1},{0,0,0,1}}, + {{0,0,0,0},{1,1,0,0},{0,0,0,0}}, + {{0,0,0,0},{1,1,0,1},{0,0,0,1}}, + {{0,0,0,1},{0,0,0,0},{0,0,0,1}}, + {{0,0,0,1},{0,0,0,1},{0,0,0,1}}, + {{0,0,0,1},{0,0,1,0},{0,0,1,1}}, + {{0,0,0,1},{0,0,1,1},{0,0,1,1}}, + {{0,0,0,1},{0,1,0,0},{0,0,0,1}}, + {{0,0,0,1},{0,1,1,0},{0,0,1,1}}, + {{0,0,0,1},{1,0,0,1},{0,0,0,1}}, + {{0,0,0,1},{1,0,1,0},{0,0,1,1}}, + {{0,0,0,1},{1,0,1,1},{0,0,0,1}}, + {{0,0,0,1},{1,1,0,0},{0,0,0,1}}, + {{0,0,0,1},{1,1,0,1},{0,0,0,1}}, + {{0,0,1,0},{0,0,0,0},{0,0,1,0}}, + {{0,0,1,0},{0,0,0,1},{0,0,1,1}}, + {{0,0,1,0},{0,0,1,0},{0,0,1,0}}, + {{0,0,1,0},{0,0,1,1},{0,0,1,1}}, + {{0,0,1,0},{0,1,0,0},{0,0,1,0}}, + {{0,0,1,0},{0,1,1,0},{0,0,1,0}}, + {{0,0,1,0},{1,0,0,1},{0,0,1,1}}, + {{0,0,1,0},{1,0,1,0},{0,0,1,0}}, + {{0,0,1,0},{1,0,1,1},{0,0,1,1}}, + {{0,0,1,0},{1,1,0,0},{0,0,1,0}}, + {{0,0,1,0},{1,1,0,1},{0,0,1,1}}, + {{0,0,1,1},{0,0,0,0},{0,0,1,1}}, + {{0,0,1,1},{0,0,0,1},{0,0,1,1}}, + {{0,0,1,1},{0,0,1,0},{0,0,1,1}}, + {{0,0,1,1},{0,0,1,1},{0,0,1,1}}, + {{0,0,1,1},{0,1,0,0},{0,0,1,1}}, + {{0,0,1,1},{0,1,1,0},{0,0,1,1}}, + {{0,0,1,1},{1,0,0,1},{0,0,1,1}}, + {{0,0,1,1},{1,0,1,0},{0,0,1,1}}, + {{0,0,1,1},{1,0,1,1},{0,0,1,1}}, + {{0,0,1,1},{1,1,0,0},{0,0,1,1}}, + {{0,0,1,1},{1,1,0,1},{0,0,1,1}}, + {{0,1,0,0},{0,0,0,0},{0,1,0,0}}, + {{0,1,0,0},{0,0,0,1},{0,0,0,1}}, + {{0,1,0,0},{0,0,1,0},{0,1,1,0}}, + {{0,1,0,0},{0,0,1,1},{0,0,1,1}}, + {{0,1,0,0},{0,1,0,0},{0,1,0,0}}, + {{0,1,0,0},{0,1,1,0},{0,1,1,0}}, + {{0,1,0,0},{1,0,0,1},{0,0,0,1}}, + {{0,1,0,0},{1,0,1,0},{0,1,1,0}}, + {{0,1,0,0},{1,0,1,1},{0,0,0,1}}, + {{0,1,0,0},{1,1,0,0},{0,1,0,0}}, + {{0,1,0,0},{1,1,0,1},{0,0,0,1}}, + {{0,1,1,0},{0,0,0,0},{0,1,1,0}}, + {{0,1,1,0},{0,0,0,1},{0,0,1,1}}, + {{0,1,1,0},{0,0,1,0},{0,1,1,0}}, + {{0,1,1,0},{0,0,1,1},{0,0,1,1}}, + {{0,1,1,0},{0,1,0,0},{0,1,1,0}}, + {{0,1,1,0},{0,1,1,0},{0,1,1,0}}, + {{0,1,1,0},{1,0,0,1},{0,0,1,1}}, + {{0,1,1,0},{1,0,1,0},{0,1,1,0}}, + {{0,1,1,0},{1,0,1,1},{0,0,1,1}}, + {{0,1,1,0},{1,1,0,0},{0,1,1,0}}, + {{0,1,1,0},{1,1,0,1},{0,0,1,1}}, + {{1,0,0,1},{0,0,0,0},{1,0,0,1}}, + {{1,0,0,1},{0,0,0,1},{1,0,1,1}}, + {{1,0,0,1},{0,0,1,0},{0,0,1,1}}, + {{1,0,0,1},{0,0,1,1},{0,0,1,1}}, + {{1,0,0,1},{0,1,0,0},{1,0,0,1}}, + {{1,0,0,1},{0,1,1,0},{0,0,1,1}}, + {{1,0,0,1},{1,0,0,1},{1,0,0,1}}, + {{1,0,0,1},{1,0,1,0},{0,0,1,1}}, + {{1,0,0,1},{1,0,1,1},{1,0,1,1}}, + {{1,0,0,1},{1,1,0,0},{1,0,0,1}}, + {{1,0,0,1},{1,1,0,1},{1,0,0,1}}, + {{1,0,1,0},{0,0,0,0},{1,0,1,0}}, + {{1,0,1,0},{0,0,0,1},{0,0,1,1}}, + {{1,0,1,0},{0,0,1,0},{1,0,1,0}}, + {{1,0,1,0},{0,0,1,1},{0,0,1,1}}, + {{1,0,1,0},{0,1,0,0},{1,0,1,0}}, + {{1,0,1,0},{0,1,1,0},{1,0,1,0}}, + {{1,0,1,0},{1,0,0,1},{0,0,1,1}}, + {{1,0,1,0},{1,0,1,0},{1,0,1,0}}, + {{1,0,1,0},{1,0,1,1},{0,0,1,1}}, + {{1,0,1,0},{1,1,0,0},{1,0,1,0}}, + {{1,0,1,0},{1,1,0,1},{0,0,1,1}}, + {{1,0,1,1},{0,0,0,0},{1,0,1,1}}, + {{1,0,1,1},{0,0,0,1},{1,0,1,1}}, + {{1,0,1,1},{0,0,1,0},{0,0,1,1}}, + {{1,0,1,1},{0,0,1,1},{0,0,1,1}}, + {{1,0,1,1},{0,1,0,0},{1,0,1,1}}, + {{1,0,1,1},{0,1,1,0},{0,0,1,1}}, + {{1,0,1,1},{1,0,0,1},{1,0,1,1}}, + {{1,0,1,1},{1,0,1,0},{0,0,1,1}}, + {{1,0,1,1},{1,0,1,1},{1,0,1,1}}, + {{1,0,1,1},{1,1,0,0},{1,0,1,1}}, + {{1,0,1,1},{1,1,0,1},{1,0,1,1}}, + {{1,1,0,0},{0,0,0,0},{1,1,0,0}}, + {{1,1,0,0},{0,0,0,1},{0,0,0,1}}, + {{1,1,0,0},{0,0,1,0},{0,0,1,0}}, + {{1,1,0,0},{0,0,1,1},{0,0,1,1}}, + {{1,1,0,0},{0,1,0,0},{1,1,0,0}}, + {{1,1,0,0},{0,1,1,0},{0,0,1,0}}, + {{1,1,0,0},{1,0,0,1},{0,0,0,1}}, + {{1,1,0,0},{1,0,1,0},{0,0,1,0}}, + {{1,1,0,0},{1,0,1,1},{0,0,0,1}}, + {{1,1,0,0},{1,1,0,0},{1,1,0,0}}, + {{1,1,0,0},{1,1,0,1},{1,1,0,1}}, + {{1,1,0,1},{0,0,0,0},{1,1,0,1}}, + {{1,1,0,1},{0,0,0,1},{0,0,0,1}}, + {{1,1,0,1},{0,0,1,0},{0,0,1,1}}, + {{1,1,0,1},{0,0,1,1},{0,0,1,1}}, + {{1,1,0,1},{0,1,0,0},{1,1,0,1}}, + {{1,1,0,1},{0,1,1,0},{0,0,1,1}}, + {{1,1,0,1},{1,0,0,1},{0,0,0,1}}, + {{1,1,0,1},{1,0,1,0},{0,0,1,1}}, + {{1,1,0,1},{1,0,1,1},{0,0,0,1}}, + {{1,1,0,1},{1,1,0,0},{1,1,0,1}}, + {{1,1,0,1},{1,1,0,1},{1,1,0,1}}, + }; + int failures = 0; + long start; + if (combinationTestsloopsNb > 1) { + start = System.currentTimeMillis(); + } + String header = "addPotentialInitializationsFrom failures: "; //$NON-NLS-1$ + for (int l = 0; l < combinationTestsloopsNb ; l++) { + for (int i = 0; i < testData.length; i++) { + UnconditionalFlowInfoTestHarness result; + if (!(result = (UnconditionalFlowInfoTestHarness)( + UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][0])). + addPotentialInitializationsFrom( + UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][1]))). + testEquals(UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][2]))) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString() + + "}, // instead of: " + testStringValueOf(testData[i][2])); + } + if (combinationTestsloopsNb < 2 && + !(result = (UnconditionalFlowInfoTestHarness)( + UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][0])). + addPotentialNullInfoFrom( + UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][1], 128))). + testEquals(UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[14][0], 65), 64)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString(64) + + "}, // bit 64 only, instead of: {0,0,0,0}"); + } + } } - - // null analysis -- try/finally - public void test0154_try_finally() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo(X x) {\n" + - " x = null;\n" + - " try {\n" + - " x = null;\n" + - " } finally {\n" + - " if (x != null) { /* */ }\n" + // complain, null in both paths - " }\n" + - " }\n" + - "}\n", - }, - "----------\n" + - "1. WARNING in X.java (at line 5)\n" + - " x = null;\n" + - " ^\n" + - "The variable x can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" + - "2. WARNING in X.java (at line 7)\n" + - " if (x != null) { /* */ }\n" + - " ^\n" + - "The variable x can only be null; it was either set to null or checked for null when last used\n" + - "----------\n"); + if (combinationTestsloopsNb > 1) { + System.out.println("addPotential...\t" + combinationTestsloopsNb + "\t" + + (System.currentTimeMillis() - start)); + } + UnconditionalFlowInfoTestHarness + zero = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(new long[] {0,0,0,0}), + left0, left1, right1, left2, right2, + expected0, expected1, expected2, result; + for (int i = 0; i < testData.length; i++) { + left0 = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][0]); + left1 = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][0], 64); + left2 = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][0], 128); + right1 = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][1], 64); + right2 = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][1], 128); + expected0 = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][2]); + expected1 = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][2], 64); + expected2 = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][2], 128); + if (!(result = (UnconditionalFlowInfoTestHarness) + left1.copy().addPotentialInitializationsFrom(right1)). + testEquals(expected1)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString() + + "}, // (64, 64) - instead of: " + testStringValueOf(testData[i][2])); + } + if (testData[i][0][0] + testData[i][0][1] + + testData[i][0][2] + testData[i][0][3] == 0) { + if (!(result = (UnconditionalFlowInfoTestHarness) + zero.copy().addPotentialInitializationsFrom(right1)). + testEquals(expected1)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString() + + "}, // (zero, 64) - instead of: " + testStringValueOf(testData[i][2])); + } + if (!(result = (UnconditionalFlowInfoTestHarness) + right2.copy().addPotentialInitializationsFrom(right1)). + testEquals(expected1, 64)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString() + + "}, // (zero 128, 64) - instead of: " + testStringValueOf(testData[i][2])); + } + if (!(result = (UnconditionalFlowInfoTestHarness) + (UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(new long[] {0,0,0,0}, 64)). + // make just in time to get the needed structure + addPotentialNullInfoFrom(right2)). + testEquals(expected2, 128)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString() + + "}, // (zero extra, 128) null only - instead of: " + testStringValueOf(testData[i][2])); + } + if (!(result = (UnconditionalFlowInfoTestHarness) + ((UnconditionalFlowInfo)right2.copy()). + addPotentialNullInfoFrom(right1)). + testEquals(expected1, 64)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString() + + "}, // (zero 128, 64) null only - instead of: " + testStringValueOf(testData[i][2])); + } + if (!(result = (UnconditionalFlowInfoTestHarness) + ((UnconditionalFlowInfo)zero.copy()). + addPotentialNullInfoFrom(right2)). + testEquals(expected2, 128)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString(128) + + "}, // (zero, 128) null only - instead of: " + testStringValueOf(testData[i][2])); + } + if (!(result = (UnconditionalFlowInfoTestHarness) + ((UnconditionalFlowInfo)right1.copy()). + addPotentialNullInfoFrom(right2)). + testEquals(expected2, 128)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString(128) + + "}, // (zero 64, 128) null only - instead of: " + testStringValueOf(testData[i][2])); + } } + if (testData[i][1][0] + testData[i][1][1] + + testData[i][1][2] + testData[i][1][3] == 0) { + if (!(result = (UnconditionalFlowInfoTestHarness) + ((UnconditionalFlowInfoTestHarness)left0.copy()). + addPotentialNullInfoFrom(left2)). + testEquals(expected0, 1)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString() + + "}, // (1, zero 128) null only - instead of: " + testStringValueOf(testData[i][2])); + } + if (!(result = (UnconditionalFlowInfoTestHarness) + left1.copy().addPotentialInitializationsFrom(zero)). + testEquals(expected1)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString() + + "}, // (64, zero) - instead of: " + testStringValueOf(testData[i][2])); + } + if (!(result = (UnconditionalFlowInfoTestHarness) + left1.copy().addPotentialInitializationsFrom(left2)). + testEquals(expected1, 64)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString() + + "}, // (64, zero 128) - instead of: " + testStringValueOf(testData[i][2])); + } + if (!(result = (UnconditionalFlowInfoTestHarness) + ((UnconditionalFlowInfo)left1.copy()). + addPotentialNullInfoFrom(left2)). + testEquals(expected1, 64)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString() + + "}, // (64, zero 128) null only - instead of: " + testStringValueOf(testData[i][2])); + } + if (!(result = (UnconditionalFlowInfoTestHarness) + ((UnconditionalFlowInfo)left2.copy()). + addPotentialNullInfoFrom(zero)). + testEquals(expected2, 128)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString() + + "}, // (128, zero) null only - instead of: " + testStringValueOf(testData[i][2])); + } + if (!(result = (UnconditionalFlowInfoTestHarness) + left2.addPotentialNullInfoFrom(left1)). + testEquals(expected2, 128)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString() + + "}, // (128, zero 64) null only - instead of: " + testStringValueOf(testData[i][2])); + } + } } - - // null analysis -- try/finally - // origin: AssignmentTest#test017 - // REVIEW design choice - // See also test0174. The whole issue here is whether or not to detect - // premature exits. We follow JLS's conservative approach, which considers - // that the try block may exit before the assignment is completed. - // TODO (maxime) fix - public void _test0155_try_finally() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo(X x) {\n" + - " x = this;\n" + - " try {\n" + - " x = null;\n" + - " } finally {\n" + - " if (x == null) {/* */}\n" + - " }\n" + - " }\n" + - "}\n", - }, - "" - ); + if (printTablesAsNames) { + System.out.println("RECAP TABLE FOR ADD POTENTIAL"); + for (int i = 0; i < testData.length; i++) { + System.out.println(testSymbolicValueOf(testData[i][0]) + " + " + + testSymbolicValueOf(testData[i][1]) + " -> " + + testSymbolicValueOf(testData[i][2])); + } } - - // null analysis -- try/catch - public void test0170_try_catch() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Object o = null;\n" + - " try {\n" + - " System.out.println();\n" + // might throw a runtime exception - " o = new Object();\n" + - " }\n" + - " catch (Throwable t) {\n" + // catches everything - " return;\n" + // gets out - " }\n" + - " o.toString();\n" + // can't tell if o is null or not - " }\n" + - "}\n"}, - "" - ); + if (printTablesAsCodes) { + System.out.println("RECAP TABLE FOR ADD POTENTIAL"); + for (int i = 0; i < testData.length; i++) { + System.out.println(testCodedValueOf(testData[i][0]) + " " + + testCodedValueOf(testData[i][1]) + " " + + testCodedValueOf(testData[i][2])); + } } + if (printTruthMaps) { + for (int i = 0; i < 4; i++) { + System.out.println("======================================================"); + System.out.println("Truth map for addPotentialInitializationsFrom null bit " + (i + 1)); + System.out.println(); + } + } + assertTrue("nb of failures: " + failures, failures == 0); +} - // null analysis - try/catch - public void test0171_try_catch() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " boolean dummy;\n" + - " void foo() {\n" + - " Object o = new Object();\n" + - " try {\n" + - " System.out.println();\n" + - " if (dummy) {\n" + - " o = null;\n" + - " throw new Exception();\n" + - " }\n" + - " }\n" + - " catch (Exception e) {\n" + - " o.toString();\n" + - // quiet: println may throw a RuntimeException - " }\n" + - " }\n" + - "}\n"}, - "" - ); +public void test2057_mergedWith() { + long [][][] testData = { + {{0,0,0,0},{0,0,0,0},{0,0,0,0}}, + {{0,0,0,0},{0,0,0,1},{0,0,0,1}}, + {{0,0,0,0},{0,0,1,0},{0,0,1,0}}, + {{0,0,0,0},{0,0,1,1},{0,0,1,1}}, + {{0,0,0,0},{0,1,0,0},{0,0,1,0}}, + {{0,0,0,0},{0,1,1,0},{0,0,1,0}}, + {{0,0,0,0},{1,0,0,1},{0,0,0,1}}, + {{0,0,0,0},{1,0,1,0},{0,0,1,0}}, + {{0,0,0,0},{1,0,1,1},{0,0,0,1}}, + {{0,0,0,0},{1,1,0,0},{0,0,0,0}}, + {{0,0,0,0},{1,1,0,1},{0,0,0,1}}, + {{0,0,0,1},{0,0,0,0},{0,0,0,1}}, + {{0,0,0,1},{0,0,0,1},{0,0,0,1}}, + {{0,0,0,1},{0,0,1,0},{0,0,1,1}}, + {{0,0,0,1},{0,0,1,1},{0,0,1,1}}, + {{0,0,0,1},{0,1,0,0},{0,0,1,1}}, + {{0,0,0,1},{0,1,1,0},{0,0,1,1}}, + {{0,0,0,1},{1,0,0,1},{0,0,0,1}}, + {{0,0,0,1},{1,0,1,0},{0,0,1,1}}, + {{0,0,0,1},{1,0,1,1},{0,0,0,1}}, + {{0,0,0,1},{1,1,0,0},{0,0,0,1}}, + {{0,0,0,1},{1,1,0,1},{0,0,0,1}}, + {{0,0,1,0},{0,0,0,0},{0,0,1,0}}, + {{0,0,1,0},{0,0,0,1},{0,0,1,1}}, + {{0,0,1,0},{0,0,1,0},{0,0,1,0}}, + {{0,0,1,0},{0,0,1,1},{0,0,1,1}}, + {{0,0,1,0},{0,1,0,0},{0,0,1,0}}, + {{0,0,1,0},{0,1,1,0},{0,0,1,0}}, + {{0,0,1,0},{1,0,0,1},{0,0,1,1}}, + {{0,0,1,0},{1,0,1,0},{0,0,1,0}}, + {{0,0,1,0},{1,0,1,1},{0,0,1,1}}, + {{0,0,1,0},{1,1,0,0},{0,0,1,0}}, + {{0,0,1,0},{1,1,0,1},{0,0,1,1}}, + {{0,0,1,1},{0,0,0,0},{0,0,1,1}}, + {{0,0,1,1},{0,0,0,1},{0,0,1,1}}, + {{0,0,1,1},{0,0,1,0},{0,0,1,1}}, + {{0,0,1,1},{0,0,1,1},{0,0,1,1}}, + {{0,0,1,1},{0,1,0,0},{0,0,1,1}}, + {{0,0,1,1},{0,1,1,0},{0,0,1,1}}, + {{0,0,1,1},{1,0,0,1},{0,0,1,1}}, + {{0,0,1,1},{1,0,1,0},{0,0,1,1}}, + {{0,0,1,1},{1,0,1,1},{0,0,1,1}}, + {{0,0,1,1},{1,1,0,0},{0,0,1,1}}, + {{0,0,1,1},{1,1,0,1},{0,0,1,1}}, + {{0,1,0,0},{0,0,0,0},{0,0,1,0}}, + {{0,1,0,0},{0,0,0,1},{0,0,1,1}}, + {{0,1,0,0},{0,0,1,0},{0,0,1,0}}, + {{0,1,0,0},{0,0,1,1},{0,0,1,1}}, + {{0,1,0,0},{0,1,0,0},{0,1,0,0}}, + {{0,1,0,0},{0,1,1,0},{0,1,1,0}}, + {{0,1,0,0},{1,0,0,1},{0,0,1,1}}, + {{0,1,0,0},{1,0,1,0},{0,1,1,0}}, + {{0,1,0,0},{1,0,1,1},{0,0,1,1}}, + {{0,1,0,0},{1,1,0,0},{0,0,1,0}}, + {{0,1,0,0},{1,1,0,1},{0,0,1,1}}, + {{0,1,1,0},{0,0,0,0},{0,0,1,0}}, + {{0,1,1,0},{0,0,0,1},{0,0,1,1}}, + {{0,1,1,0},{0,0,1,0},{0,0,1,0}}, + {{0,1,1,0},{0,0,1,1},{0,0,1,1}}, + {{0,1,1,0},{0,1,0,0},{0,1,1,0}}, + {{0,1,1,0},{0,1,1,0},{0,1,1,0}}, + {{0,1,1,0},{1,0,0,1},{0,0,1,1}}, + {{0,1,1,0},{1,0,1,0},{0,1,1,0}}, + {{0,1,1,0},{1,0,1,1},{0,0,1,1}}, + {{0,1,1,0},{1,1,0,0},{0,0,1,0}}, + {{0,1,1,0},{1,1,0,1},{0,0,1,1}}, + {{1,0,0,1},{0,0,0,0},{0,0,0,1}}, + {{1,0,0,1},{0,0,0,1},{0,0,0,1}}, + {{1,0,0,1},{0,0,1,0},{0,0,1,1}}, + {{1,0,0,1},{0,0,1,1},{0,0,1,1}}, + {{1,0,0,1},{0,1,0,0},{0,0,1,1}}, + {{1,0,0,1},{0,1,1,0},{0,0,1,1}}, + {{1,0,0,1},{1,0,0,1},{1,0,0,1}}, + {{1,0,0,1},{1,0,1,0},{0,0,1,1}}, + {{1,0,0,1},{1,0,1,1},{1,0,1,1}}, + {{1,0,0,1},{1,1,0,0},{1,1,0,1}}, + {{1,0,0,1},{1,1,0,1},{1,1,0,1}}, + {{1,0,1,0},{0,0,0,0},{0,0,1,0}}, + {{1,0,1,0},{0,0,0,1},{0,0,1,1}}, + {{1,0,1,0},{0,0,1,0},{0,0,1,0}}, + {{1,0,1,0},{0,0,1,1},{0,0,1,1}}, + {{1,0,1,0},{0,1,0,0},{0,1,1,0}}, + {{1,0,1,0},{0,1,1,0},{0,1,1,0}}, + {{1,0,1,0},{1,0,0,1},{0,0,1,1}}, + {{1,0,1,0},{1,0,1,0},{1,0,1,0}}, + {{1,0,1,0},{1,0,1,1},{0,0,1,1}}, + {{1,0,1,0},{1,1,0,0},{0,0,1,0}}, + {{1,0,1,0},{1,1,0,1},{0,0,1,1}}, + {{1,0,1,1},{0,0,0,0},{0,0,0,1}}, + {{1,0,1,1},{0,0,0,1},{0,0,0,1}}, + {{1,0,1,1},{0,0,1,0},{0,0,1,1}}, + {{1,0,1,1},{0,0,1,1},{0,0,1,1}}, + {{1,0,1,1},{0,1,0,0},{0,0,1,1}}, + {{1,0,1,1},{0,1,1,0},{0,0,1,1}}, + {{1,0,1,1},{1,0,0,1},{1,0,1,1}}, + {{1,0,1,1},{1,0,1,0},{0,0,1,1}}, + {{1,0,1,1},{1,0,1,1},{1,0,1,1}}, + {{1,0,1,1},{1,1,0,0},{0,0,0,1}}, + {{1,0,1,1},{1,1,0,1},{0,0,0,1}}, + {{1,1,0,0},{0,0,0,0},{0,0,0,0}}, + {{1,1,0,0},{0,0,0,1},{0,0,0,1}}, + {{1,1,0,0},{0,0,1,0},{0,0,1,0}}, + {{1,1,0,0},{0,0,1,1},{0,0,1,1}}, + {{1,1,0,0},{0,1,0,0},{0,0,1,0}}, + {{1,1,0,0},{0,1,1,0},{0,0,1,0}}, + {{1,1,0,0},{1,0,0,1},{1,1,0,1}}, + {{1,1,0,0},{1,0,1,0},{0,0,1,0}}, + {{1,1,0,0},{1,0,1,1},{0,0,0,1}}, + {{1,1,0,0},{1,1,0,0},{1,1,0,0}}, + {{1,1,0,0},{1,1,0,1},{1,1,0,1}}, + {{1,1,0,1},{0,0,0,0},{0,0,0,1}}, + {{1,1,0,1},{0,0,0,1},{0,0,0,1}}, + {{1,1,0,1},{0,0,1,0},{0,0,1,1}}, + {{1,1,0,1},{0,0,1,1},{0,0,1,1}}, + {{1,1,0,1},{0,1,0,0},{0,0,1,1}}, + {{1,1,0,1},{0,1,1,0},{0,0,1,1}}, + {{1,1,0,1},{1,0,0,1},{1,1,0,1}}, + {{1,1,0,1},{1,0,1,0},{0,0,1,1}}, + {{1,1,0,1},{1,0,1,1},{0,0,0,1}}, + {{1,1,0,1},{1,1,0,0},{1,1,0,1}}, + {{1,1,0,1},{1,1,0,1},{1,1,0,1}} + }; + int failures = 0; + long start; + if (combinationTestsloopsNb > 1) { + start = System.currentTimeMillis(); + } + String header = "mergedWith failures: "; + for (int l = 0; l < combinationTestsloopsNb ; l++) { + for (int i = 0; i < testData.length; i++) { + UnconditionalFlowInfoTestHarness result; + if (!(result = (UnconditionalFlowInfoTestHarness) + UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][0]). + mergedWith( + UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][1]))). + testEquals(UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][2]))) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString() + + "}, // instead of: " + testStringValueOf(testData[i][2])); + } + } } - - // null analysis - try/catch - public void test0172_try_catch() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " boolean dummy;\n" + - " void foo() throws Exception {\n" + - " Object o = new Object();\n" + - " try {\n" + - " if (dummy) {\n" + - " o = null;\n" + - " throw new Exception();\n" + - " }\n" + - " }\n" + - " catch (Exception e) {\n" + - " }\n" + - " if (o != null) {\n" + - // quiet: get out of try either through normal flow, leaves a new - // object, or through Exception, leaves a null - " }\n" + - " }\n" + - "}\n"}, - "" - ); + if (combinationTestsloopsNb > 1) { + System.out.println("mergedWith\t\t\t" + combinationTestsloopsNb + "\t" + + (System.currentTimeMillis() - start)); + } + UnconditionalFlowInfoTestHarness + zero = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(new long[] {0,0,0,0}), + left1, right1, left2, right2, + expected1, expected2, result; + for (int i = 0; i < testData.length; i++) { + left1 = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][0], 64); + left2 = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][0], 128); + right1 = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][1], 64); + right2 = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][1], 128); + expected1 = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][2], 64); + expected2 = UnconditionalFlowInfoTestHarness. + testUnconditionalFlowInfo(testData[i][2], 128); + if (!(result = (UnconditionalFlowInfoTestHarness) + left1.copy().mergedWith(right1)).testEquals(expected1)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString() + + "}, // (64, 64) - instead of: " + testStringValueOf(testData[i][2])); + } + if (testData[i][0][0] + testData[i][0][1] + + testData[i][0][2] + testData[i][0][3] == 0) { + if (!(result = (UnconditionalFlowInfoTestHarness) + zero.copy().mergedWith(right1)).testEquals(expected1)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString() + + "}, // (zero, 64) - instead of: " + testStringValueOf(testData[i][2])); + } + if (!(result = (UnconditionalFlowInfoTestHarness) + right2.copy().mergedWith(right1)). + testEquals(expected1, 64)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString() + + "}, // (zero 128, 64) - instead of: " + testStringValueOf(testData[i][2])); + } + if (!(result = (UnconditionalFlowInfoTestHarness) + zero.copy().mergedWith(right2)). + testEquals(expected2, 128)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString() + + "}, // (zero, 128) - instead of: " + testStringValueOf(testData[i][2])); + } + if (!(result = (UnconditionalFlowInfoTestHarness) + right1.copy().mergedWith(right2)). + testEquals(expected2, 128)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString() + + "}, // (zero 64, 128) - instead of: " + testStringValueOf(testData[i][2])); + } + } + if (testData[i][1][0] + testData[i][1][1] + + testData[i][1][2] + testData[i][1][3] == 0) { + if (!(result = (UnconditionalFlowInfoTestHarness) + left1.copy().mergedWith(zero)).testEquals(expected1)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString() + + "}, // (64, zero) - instead of: " + testStringValueOf(testData[i][2])); + } + if (!(result = (UnconditionalFlowInfoTestHarness) + left1.mergedWith(left2)). + testEquals(expected1, 64)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString() + + "}, // (64, zero 128) - instead of: " + testStringValueOf(testData[i][2])); + } + if (!(result = (UnconditionalFlowInfoTestHarness) + left2.copy().mergedWith(zero)). + testEquals(expected2, 128)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString() + + "}, // (128, zero) - instead of: " + testStringValueOf(testData[i][2])); + } + if (!(result = (UnconditionalFlowInfoTestHarness) + left2.mergedWith(left1)). + testEquals(expected2, 128)) { + if (failures == 0) { + System.out.println(header); + } + failures++; + System.out.println("\t\t{" + testStringValueOf(testData[i][0]) + + ',' + testStringValueOf(testData[i][1]) + + ',' + result.testString() + + "}, // (128, zero 64) - instead of: " + testStringValueOf(testData[i][2])); + } + } + } + if (printTablesAsNames) { + System.out.println("RECAP TABLE FOR MERGE"); + for (int i = 0; i < testData.length; i++) { + System.out.println(testSymbolicValueOf(testData[i][0]) + " + " + + testSymbolicValueOf(testData[i][1]) + " -> " + + testSymbolicValueOf(testData[i][2])); + } + } - - // null analysis - try/catch - public void test0173_try_catch() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " boolean dummy, other;\n" + - " void foo() {\n" + - " Object o = new Object();\n" + - " try {\n" + - " if (dummy) {\n" + - " if (other) {\n" + - " throw new LocalException();\n" + // may launch new exception - " }\n" + - " o = null;\n" + - " throw new LocalException();\n" + // must launch new exception - " }\n" + - " }\n" + - " catch (LocalException e) {\n" + - " o.toString();\n" + - // quiet: don't know the exact state when exception is launched - " }\n" + - " }\n" + - " class LocalException extends Exception {\n" + - " private static final long serialVersionUID = 1L;\n" + - " }\n" + - "}\n"}, - "" - ); + if (printTablesAsCodes) { + System.out.println("RECAP TABLE FOR MERGE"); + for (int i = 0; i < testData.length; i++) { + System.out.println(testCodedValueOf(testData[i][0]) + " " + + testCodedValueOf(testData[i][1]) + " " + + testCodedValueOf(testData[i][2])); + } } - - // null analysis - try/catch - // REVIEW the following series of try catch tests all relate to the finer - // analysis of possible exception paths; such analysis - // calls for a supplementary context for each condition - // (so as to sort out certain paths from hypothetical - // ones), which is due to be expensive. - // TODO (maxime) fix - public void _test0174_try_catch() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo(Object o) throws Exception {\n" + - " try {\n" + - " o = null;\n" + - " throwLocalException();\n" + - " throw new Exception();\n" + - " }\n" + - " catch (LocalException e) {\n" + - " }\n" + - " if (o != null) {\n" + - // complain: only way to get out of try and get there is to go - // through throwLocalException, after the assignment - " }\n" + - " }\n" + - " class LocalException extends Exception {\n" + - " private static final long serialVersionUID = 1L;\n" + - " }\n" + - " void throwLocalException() throws LocalException {\n" + - " throw new LocalException();\n" + - " }\n" + - "}\n"}, - "WARN" - ); + if (printTruthMaps) { + for (int i = 0; i < 4; i++) { + System.out.println("======================================================"); + System.out.println("Truth map for mergedWith null bit " + (i + 1)); + System.out.println(); + } } + assertTrue("nb of failures: " + failures, failures == 0); +} - // null analysis - try/catch - // TODO (maxime) fix - public void _test0175_try_catch() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Object o = new Object();\n" + - " try {\n" + - " o = null;\n" + - " throwException();\n" + - " }\n" + - " catch (Exception e) {\n" + - " o.toString();\n" + - // complain: know o is null despite the lack of a definite assignment - " }\n" + - " }\n" + - " void throwException() throws Exception {\n" + - " throw new Exception();\n" + - " }\n" + - "}\n"}, - "WARN" - ); +// Use for coverage tests only. Needs specific instrumentation of code, +// that is controled by UnconditionalFlowInfo#coverageTestFlag. +// Note: coverage tests tend to fill the console with messages, and the +// instrumented code is slower, so never release code with active +// coverage tests. +private static int coveragePointsNb = 46; + +// Coverage by state transition tables methods. +public void test2998_coverage() { + if (UnconditionalFlowInfo.coverageTestFlag) { + // sanity check: need to be sure that the tests execute properly when not + // trying to check coverage + UnconditionalFlowInfo.coverageTestId = 0; + test0053_array(); + test0070_type_reference(); + test2050_markAsComparedEqualToNonNull(); + test2051_markAsComparedEqualToNull(); + test2052_markAsDefinitelyNonNull(); + test2053_markAsDefinitelyNull(); + test2054_markAsDefinitelyUnknown(); + test2055_addInitializationsFrom(); + test2056_addPotentialInitializationsFrom(); + test2057_mergedWith(); + // coverage check + int failuresNb = 0; + for (int i = 1; i <= coveragePointsNb; i++) { + try { + UnconditionalFlowInfo.coverageTestId = i; + test0053_array(); + test0070_type_reference(); + test2050_markAsComparedEqualToNonNull(); + test2051_markAsComparedEqualToNull(); + test2052_markAsDefinitelyNonNull(); + test2053_markAsDefinitelyNull(); + test2054_markAsDefinitelyUnknown(); + test2055_addInitializationsFrom(); + test2056_addPotentialInitializationsFrom(); + test2057_mergedWith(); + } + catch (AssertionFailedError e) { + continue; + } + catch (AssertionFailedException e) { + continue; + } + failuresNb++; + System.out.println("Missing coverage point: " + i); + } + UnconditionalFlowInfo.coverageTestId = 0; // reset for other tests + assertEquals(failuresNb + " missing coverage point(s)", failuresNb, 0); } +} - // null analysis - try/catch - // TODO (maxime) fix - public void _test0176_try_catch() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Object o = new Object();\n" + - " try {\n" + - " o = null;\n" + - " throwException();\n" + - " }\n" + - " catch (Throwable t) {\n" + - " o.toString();\n" + - // complain: know o is null despite the lack of a definite assignment - " }\n" + - " }\n" + - " void throwException() throws Exception {\n" + - " throw new Exception();\n" + - " }\n" + - "}\n"}, - "WARN" - ); +// Coverage by code samples. +public void test2999_coverage() { + if (UnconditionalFlowInfo.coverageTestFlag) { + // sanity check: need to be sure that the tests execute properly when not + // trying to check coverage + UnconditionalFlowInfo.coverageTestId = 0; + test0001_simple_local(); + test0053_array(); + test0070_type_reference(); + test0327_if_else(); + test0401_while(); + test0420_while(); + test0509_try_finally_embedded(); + test2000_flow_info(); + test2004_flow_info(); + test2008_flow_info(); + test2011_flow_info(); + test2013_flow_info(); + test2018_flow_info(); + test2019_flow_info(); + test2020_flow_info(); + // coverage check + int failuresNb = 0; + for (int i = 1; i <= coveragePointsNb; i++) { + if (i > 4 && i < 15 || + i > 15 && i < 19 || + i == 22 || + i == 23 || + i == 27 || + i == 28 || + i == 30 || + i == 33 || + i == 34 || + i == 38 || + i >= 43 + ) { // TODO (maxime) complete coverage tests + continue; + } + try { + UnconditionalFlowInfo.coverageTestId = i; + test0001_simple_local(); + test0053_array(); + test0070_type_reference(); + test0327_if_else(); + test0401_while(); + test0420_while(); + test0509_try_finally_embedded(); + test2000_flow_info(); + test2004_flow_info(); + test2008_flow_info(); + test2011_flow_info(); + test2013_flow_info(); + test2018_flow_info(); + test2019_flow_info(); + test2020_flow_info(); + } + catch (AssertionFailedError e) { + continue; + } + catch (AssertionFailedException e) { + continue; + } + failuresNb++; + System.out.println("Missing coverage point: " + i); + } + UnconditionalFlowInfo.coverageTestId = 0; // reset for other tests + assertEquals(failuresNb + " missing coverage point(s)", failuresNb, 0); } +} - // null analysis - try/catch - // TODO (maxime) fix - public void _test0177_try_catch() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " boolean dummy;\n" + - " void foo() {\n" + - " Object o = new Object();\n" + - " try {\n" + - " if (dummy) {\n" + - " o = null;\n" + - " throw new Exception();\n" + - " }\n" + - " }\n" + - " catch (Exception e) {\n" + - " o.toString();\n" + - // complain: know o is null despite the lack of definite assignment - " }\n" + - " }\n" + - "}\n"}, - "WARN" - ); +// only works for info coded on bit 0 - least significant +String testCodedValueOf(long[] data) { + StringBuffer result = new StringBuffer(4); + for (int i = 0; i < data.length; i++) { + result.append(data[i] == 0 ? '0' : '1'); } + return result.toString(); +} - // null analysis - try/catch - // TODO (maxime) fix - public void _test0178_try_catch() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " boolean dummy;\n" + - " void foo() {\n" + - " Object o = new Object();\n" + - " try {\n" + - " if (dummy) {\n" + - " System.out.print(0);\n" + // may thow RuntimeException - " o = null;\n" + - " throw new LocalException();\n" + - " }\n" + - " }\n" + - " catch (LocalException e) {\n" + // doesn't catch RuntimeException - " o.toString();\n" + - // complain: know o is null despite the lack of definite assignment - " }\n" + - " }\n" + - " class LocalException extends Exception {\n" + - " private static final long serialVersionUID = 1L;\n" + - " }\n" + - "}\n"}, - "WARN" - ); +String testStringValueOf(long[] data) { + StringBuffer result = new StringBuffer(9); + result.append('{'); + for (int j = 0; j < 4; j++) { + if (j > 0) { + result.append(','); + } + result.append(data[j]); } + result.append('}'); + return result.toString(); +} - // null analysis - try/catch - // TODO (maxime) fix - public void _test0179_try_catch() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " boolean dummy;\n" + - " void foo() {\n" + - " Object o = new Object();\n" + - " try {\n" + - " if (dummy) {\n" + - " o = null;\n" + - " throw new SubException();\n" + - " }\n" + - " }\n" + - " catch (LocalException e) {\n" + // must catch SubException - " o.toString();\n" + - // complain: know o is null despite the lack of definite assignment - " }\n" + - " }\n" + - " class LocalException extends Exception {\n" + - " private static final long serialVersionUID = 1L;\n" + - " }\n" + - " class SubException extends LocalException {\n" + - " private static final long serialVersionUID = 1L;\n" + - " }\n" + - "}\n"}, - "WARN" - ); - } - - // null analysis -- do while - // TODO (maxime) fix - public void _test0201_do_while() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Object o = null;\n" + - " do {/* */}\n" + - " while (o.toString() != null);\n" + - // complain: NPE - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 5)\n" + - " while (o.toString() != null);\n" + - " ^\n" + - "The variable o can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); +String testStringValueOf(long[][] data) { + StringBuffer result = new StringBuffer(25); + result.append('{'); + for (int i = 0; i < 3; i++) { + if (i > 0) { + result.append(','); + } + result.append('{'); + for (int j = 0; j < 4; j++) { + if (j > 0) { + result.append(','); + } + result.append(data[i][j]); + } + result.append('}'); } + result.append('}'); + return result.toString(); +} - // null analysis -- do while - // TODO (maxime) fix - public void _test0202_do_while() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Object o = null;\n" + - " do {/* */}\n" + - " while (o != null);\n" + - // complain: get o null first time and forever - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 5)\n" + - " while (o != null);\n" + - " ^\n" + - "The variable o can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); +// only works for info coded on bit 0 - least significant +String testSymbolicValueOf(long[] data) { + if (data[0] == 0) { + if (data[1] == 0) { + if (data[2] == 0) { + if (data[3] == 0) { + return "start "; //$NON-NLS1$ + } + else { + return "pot. nn/unknown "; //$NON-NLS1$ + } + } + else { + if (data[3] == 0) { + return "pot. null "; //$NON-NLS1$ + } + else { + return "pot. n/nn/unkn. "; //$NON-NLS1$ + } + } + } + else { + if (data[2] == 0) { + if (data[3] == 0) { + return "prot. null "; //$NON-NLS1$ + } + else { + return "0101 "; //$NON-NLS1$ + } + } + else { + if (data[3] == 0) { + return "prot. null + pot. null"; //$NON-NLS1$ + } + else { + return "0111 "; //$NON-NLS1$ + } + } + } } - - // null analysis -- do while - // TODO (maxime) fix - public void _test0203_do_while() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Object o = null;\n" + - " do {\n" + - " o = new Object();\n" + - " }\n" + - " while (o == null);\n" + - // complain: set it to non null before test, for each iteration - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 7)\n" + - " while (o == null);\n" + - " ^\n" + - "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + - "----------\n" - ); + else { + if (data[1] == 0) { + if (data[2] == 0) { + if (data[3] == 0) { + return "1000 "; //$NON-NLS1$ + } + else { + return "assigned non null "; //$NON-NLS1$ + } + } + else { + if (data[3] == 0) { + return "assigned null "; //$NON-NLS1$ + } + else { + return "assigned unknown "; //$NON-NLS1$ + } + } + } + else { + if (data[2] == 0) { + if (data[3] == 0) { + return "protected non null "; //$NON-NLS1$ + } + else { + return "prot. nn + pot. nn/unknown"; //$NON-NLS1$ + } + } + else { + if (data[3] == 0) { + return "1110 "; //$NON-NLS1$ + } + else { + return "1111 "; //$NON-NLS1$ + } + } + } } +} - // null analysis -- do while - public void test0204_do_while() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Object o = null;\n" + - " do {\n" + - " if (System.currentTimeMillis() > 10L) {\n" + - " o = new Object();\n" + - " }\n" + - " }\n" + - " while (o == null);\n" + - " }\n" + - "}\n"}, - "" - ); +private void printTruthMap(long data[][][], int bit) { + final int dimension = 16; + printTruthMapHeader(); + char truthValues[][] = new char[dimension][dimension]; + int row, column; + for (row = 0; row < dimension; row++) { + for (column = 0; column < dimension; column++) { + truthValues[row][column] = '.'; + } } - - // null analysis -- do while - // TODO (maxime) fix - public void _test0205_do_while() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " boolean dummy;\n" + - " void foo(Object o) {\n" + - " o = null;\n" + - " do {\n" + - " // do nothing\n" + - " }\n" + - " while (dummy || o != null);\n" + - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 8)\n" + - " while (dummy || o != null);\n" + - " ^\n" + - "The variable o can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); + String rows[] = { + "0000", + "0001", + "0011", + "0111", + "1111", + "1110", + "1100", + "1000", + "1010", + "1011", + "1001", + "1101", + "0101", + "0100", + "0110", + "0010" + }; + if (false) { // checking row names + for (row = 0; row < dimension; row++) { + long [] state = new long [4]; + for (int i = 0; i < 4; i++) { + state[i] = rows[row].charAt(i) - '0'; + } + System.out.println(row + " " + rows[row] + " " + rankForState(state)); + } } - - // null analysis -- do while - // TODO (maxime) fix - public void _test0206_do_while() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Object o = null,\n" + - " u = new Object(),\n" + - " v = new Object();\n" + - " do {\n" + - " if (v == null) {\n" + - " o = new Object();\n" + - " };\n" + - " if (u == null) {\n" + - " v = null;\n" + - " };\n" + - " u = null;\n" + - " }\n" + - " while (o == null);\n" + - " }\n" + - "}\n"}, - "" - ); + for (int i = 0; i < data.length; i++) { + truthValues[rankForState(data[i][0])][rankForState(data[i][1])] = + (char) ('0' + data[i][2][bit]); + } + for (row = 0; row < dimension; row++) { + StringBuffer line = new StringBuffer(120); + line.append(rows[row]); + line.append(" | "); + for (column = 0; column < dimension; column++) { + line.append(truthValues[row][column]); + line.append(' '); + } + System.out.println(line); } +} - // null analysis -- do while - // TODO (maxime) fix - public void _test0207_do_while() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " boolean dummy;\n" + - " void foo() {\n" + - " Object o = null;\n" + - " do {\n" + - " o.toString();\n" + - // complain: NPE on first iteration - " o = new Object();\n" + - " }\n" + - " while (dummy);\n" + - " }\n" + - "}\n"}, - "WARN" - ); - } +private void printTruthMapHeader() { + System.out.println(" 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0"); + System.out.println(" 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0"); + System.out.println(" 0 0 1 1 1 1 0 0 1 1 0 0 0 0 1 1"); + System.out.println(" 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0"); + System.out.println(" --------------------------------"); +} - // null analysis -- do while - // TODO (maxime) fix - public void _test0208_do_while() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " boolean dummy;\n" + - " void foo() {\n" + - " Object o = null;\n" + - " do {\n" + - " o = new Object();\n" + - " }\n" + - " while (dummy);\n" + - " o.toString();\n" + - " }\n" + - "}\n"}, - "" - ); +private int rankForState(long [] state) { + if (state[0] == 0) { + if (state[1] == 0) { + if (state[2] == 0) { + if (state[3] == 0) { + return 0; // 0000 + } + else { + return 1; // 0001 + } + } + else { + if (state[3] == 0) { + return 15; // 0010 + } + else { + return 2; // 0011 + } + } + } + else { + if (state[2] == 0) { + if (state[3] == 0) { + return 13; // 0100 + } + else { + return 12; // 0101 + } + } + else { + if (state[3] == 0) { + return 14; // 0110 + } + else { + return 3; // 0111 + } + } + } } - - // null analysis -- do while - public void test0209_do_while() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " boolean dummy;\n" + - " void foo() {\n" + - " Object o = null;\n" + - " do { /* */ }\n" + - " while (dummy);\n" + - " o.toString();\n" + - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 7)\n" + - " o.toString();\n" + - " ^\n" + - "The variable o can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); + else { + if (state[1] == 0) { + if (state[2] == 0) { + if (state[3] == 0) { + return 7; // 1000 + } + else { + return 10; // 1001 + } + } + else { + if (state[3] == 0) { + return 8; // 1010 + } + else { + return 9; // 1011 + } + } + } + else { + if (state[2] == 0) { + if (state[3] == 0) { + return 6; // 1100 + } + else { + return 11; // 1101 + } + } + else { + if (state[3] == 0) { + return 5; // 1110 + } + else { + return 4; // 1111 + } + } + } } +} +} - // null analysis -- for - // TODO (maxime) fix - public void _test0221_for() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Object o = null;\n" + - " for (;o.toString() != null;) {/* */}\n" + - // complain: NPE - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 4)\n" + - " for (;o.toString() != null;) {/* */}\n" + - " ^\n" + - "The variable o can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); +class TestLocalVariableBinding extends LocalVariableBinding { + final static char [] testName = {'t', 'e', 's', 't'}; + TestLocalVariableBinding(int id) { + super(testName, null, 0, false); + this.id = id; } +} - // null analysis -- for - // TODO (maxime) fix - public void _test0222_for() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Object o = null;\n" + - " for (;o != null;) {/* */}\n" + - // complain: get o null first time and forever - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 4)\n" + - " for (;o != null;) {/* */}\n" + - " ^\n" + - "The variable o can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); - } +/** + * A class meant to augment + * @link{org.eclipse.jdt.internal.compiler.flow.UnconditionalFlowInfo} with + * capabilities in the test domain. It especially provides factories to build + * fake flow info instances for use in state transition tables validation. + */ +class UnconditionalFlowInfoTestHarness extends UnconditionalFlowInfo { + private int testPosition; + +/** + * Return a fake unconditional flow info which bit fields represent the given + * null bits for a local variable of id 0 within a class that would have no + * field. + * @param nullBits the bits that must be set, given in the same order as the + * nullAssignment* fields in UnconditionalFlowInfo definition; use 0 + * for a bit that is not set, 1 else + * @return a fake unconditional flow info which bit fields represent the + * null bits given in parameter + */ +public static UnconditionalFlowInfoTestHarness testUnconditionalFlowInfo( + long [] nullBits) { + return testUnconditionalFlowInfo(nullBits, 0); +} - // null analysis -- for - public void test0223_for() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Object o = null;\n" + - " for (;o == null;) {\n" + - // quiet: first iteration is sure to find it null, - // but other iterations may change it - " o = new Object();\n" + - " }\n" + - " }\n" + - "}\n"}, - "" - ); +public FlowInfo copy() { + UnconditionalFlowInfoTestHarness copy = + new UnconditionalFlowInfoTestHarness(); + copy.testPosition = this.testPosition; + copy.definiteInits = this.definiteInits; + copy.potentialInits = this.potentialInits; + boolean hasNullInfo = (this.tagBits & NULL_FLAG_MASK) != 0; + if (hasNullInfo) { + copy.nullAssignmentStatusBit1 = this.nullAssignmentStatusBit1; + copy.nullAssignmentStatusBit2 = this.nullAssignmentStatusBit2; + copy.nullAssignmentValueBit1 = this.nullAssignmentValueBit1; + copy.nullAssignmentValueBit2 = this.nullAssignmentValueBit2; + } + copy.tagBits = this.tagBits; + copy.maxFieldCount = this.maxFieldCount; + if (this.extra != null) { + int length; + copy.extra = new long[extraLength][]; + System.arraycopy(this.extra[0], 0, + (copy.extra[0] = new long[length = extra[0].length]), 0, length); + System.arraycopy(this.extra[1], 0, + (copy.extra[1] = new long[length]), 0, length); + if (hasNullInfo) { + for (int j = 0; j < extraLength; j++) { + System.arraycopy(this.extra[j], 0, + (copy.extra[j] = new long[length]), 0, length); + } + } + else { + for (int j = 0; j < extraLength; j++) { + copy.extra[j] = new long[length]; + } + } } + return copy; +} - // null analysis -- for - public void test0224_for() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Object o = null;\n" + - " for (;o == null;) {\n" + - // quiet: first iteration is sure to find it null, - // but other iterations may change it - " if (System.currentTimeMillis() > 10L) {\n" + - " o = new Object();\n" + - " }\n" + - " }\n" + - " }\n" + - "}\n"}, - "" - ); +/** + * Return a fake unconditional flow info which bit fields represent the given + * null bits for a local variable of id position within a class that would have + * no field. + * @param nullBits the bits that must be set, given in the same order as the + * nullAssignment* fields in UnconditionalFlowInfo definition; use 0 + * for a bit that is not set, 1 else + * @param position the position of the variable within the bit fields; use + * various values to test different parts of the bit fields, within + * or beyond BitCacheSize + * @return a fake unconditional flow info which bit fields represent the + * null bits given in parameter + */ +public static UnconditionalFlowInfoTestHarness testUnconditionalFlowInfo( + long [] nullBits, int position) { + UnconditionalFlowInfoTestHarness result = + new UnconditionalFlowInfoTestHarness(); + result.testPosition = position; + if (position < BitCacheSize) { + result.nullAssignmentStatusBit1 = nullBits[0] << position; + result.nullAssignmentStatusBit2 = nullBits[1] << position; + result.nullAssignmentValueBit1 = nullBits[2] << position; + result.nullAssignmentValueBit2 = nullBits[3] << position; + } + else { + int vectorIndex = (position / BitCacheSize) - 1, + length = vectorIndex + 1; + position %= BitCacheSize; + result.extra = new long[extraLength][]; + result.extra[0] = new long[length]; + result.extra[1] = new long[length]; + for (int j = 2; j < extraLength; j++) { + result.extra[j] = new long[length]; + result.extra[j][vectorIndex] = nullBits[j - 2] << + position; + } } - - // null analysis -- for - // TODO (maxime) fix - public void _test0225_for() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " boolean bar() {\n" + - " return true;\n" + - " }\n" + - " void foo(Object o) {\n" + - " for (;bar() && o == null;) {\n" + - " o.toString();\n" + // complain: NPE because of condition - " o = new Object();\n" + - " }\n" + - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 7)\n" + - " o.toString();\n" + - " ^\n" + - "The variable o can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); + if ((nullBits[0] | nullBits[1] | nullBits[2] | nullBits[3]) != 0) { + result.tagBits |= NULL_FLAG_MASK; } + result.maxFieldCount = 0; + return result; +} - // null analysis -- for - public void test0227_for() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo(Object o) {\n" + - " for (;o == null; o.toString()) {\n" + - " o = new Object();\n" + - " }\n" + - " }\n" + - "}\n"}, - "" - ); +/** + * Return a fake unconditional flow info which bit fields represent the given + * null bits for a pair of local variables of id position and position + + * extra * BitCacheSize within a class that would have no field. + * @param nullBits the bits that must be set, given in the same order as the + * nullAssignment* fields in UnconditionalFlowInfo definition; use 0 + * for a bit that is not set, 1 else + * @param position the position of the variable within the bit fields; use + * various values to test different parts of the bit fields, within + * or beyond BitCacheSize + * @param extra the length of the allocated extra bit fields, if position is + * beyond BitCacheSize; unused otherwise; make sure it is big enough to + * match position (that is, extra > position - BitCacheSize) + * @return a fake unconditional flow info which bit fields represent the + * null bits given in parameter + */ +public static UnconditionalFlowInfoTestHarness testUnconditionalFlowInfo( + long [] nullBits, int position, int extra) { + UnconditionalFlowInfoTestHarness result = + new UnconditionalFlowInfoTestHarness(); + result.testPosition = position; + if (position < BitCacheSize) { + result.nullAssignmentStatusBit1 = nullBits[0] << position; + result.nullAssignmentStatusBit2 = nullBits[1] << position; + result.nullAssignmentValueBit1 = nullBits[2] << position; + result.nullAssignmentValueBit2 = nullBits[3] << position; + } + else { + int vectorIndex = (position / BitCacheSize) - 1, + length = extra / BitCacheSize; + position %= BitCacheSize; + result.extra = new long[extraLength][]; + result.extra[0] = new long[length]; + result.extra[1] = new long[length]; + for (int j = 2; j < extraLength; j++) { + result.extra[j] = new long[length]; + result.extra[j] [vectorIndex]= nullBits[j - 2] << position; + } + } + if (nullBits[1] != 0 || nullBits[3] != 0 || nullBits[0] != 0 || nullBits[2] != 0 ) { + // cascade better than nullBits[0] | nullBits[1] | nullBits[2] | nullBits[3] + // by 10%+ + // TODO (maxime) run stats to determine which is the better order + result.tagBits |= NULL_FLAG_MASK; } + result.maxFieldCount = 0; + return result; +} - // null analysis -- for - // TODO (maxime) fix - public void _test0228_for() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo(Object o) {\n" + - " for (;o == null; o.toString()) {\n" + - " }\n" + - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 3)\n" + - " for (;o == null; o.toString()) {\n" + - " ^\n" + - "The variable o can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); - } - - // null analysis -- for - // TODO (maxime) fix - public void _test0229_for() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo(Object o) {\n" + - " for (o.toString(); o == null;) { /* */ }\n" + // complain: protected then unchanged - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 3)\n" + - " for (o.toString(); o == null;) { /* */ }\n" + - " ^\n" + - "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + - "----------\n" - ); - } - - // null analysis -- for - public void test0230_for() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " boolean bar() {\n" + - " return true;\n" + - " }\n" + - " void foo(Object o) {\n" + - " o = null;\n" + - " for (o.toString(); bar();) {\n" + - " }\n" + - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 7)\n" + - " for (o.toString(); bar();) {\n" + - " ^\n" + - "The variable o can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); +/** + * Return true iff this flow info can be considered as equal to the one passed + * in parameter. + * @param other the flow info to compare to + * @return true iff this flow info compares equal to other + */ +public boolean testEquals(UnconditionalFlowInfo other) { + if (this.tagBits != other.tagBits) { + return false; + } + if (this.nullAssignmentStatusBit1 != other.nullAssignmentStatusBit1 || + this.nullAssignmentStatusBit2 != other.nullAssignmentStatusBit2 || + this.nullAssignmentValueBit1 != other.nullAssignmentValueBit1 || + this.nullAssignmentValueBit2 != other.nullAssignmentValueBit2) { + return false; + } + int left = this.extra == null ? 0 : this.extra[0].length, + right = other.extra == null ? 0 : other.extra[0].length, + both = 0, i; + if (left > right) { + both = right; + } + else { + both = left; + } + for (i = 0; i < both ; i++) { + if (this.extra[2][i] != + other.extra[2][i] || + this.extra[3][i] != + other.extra[3][i] || + this.extra[4][i] != + other.extra[4][i] || + this.extra[5][i] != + other.extra[5][i]) { + return false; + } } - - // null analysis -- for - // TODO (maxime) fix - public void _test0231_for() { - if (COMPLIANCE_1_5.equals(this.complianceLevel)) { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Object t[] = null;\n" + - " for (Object o : t) {/* */}\n" + - // complain: NPE - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 4)\n" + - " for (Object o : t) {/* */}\n" + - " ^\n" + - "The variable t can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); - } - } - - // null analysis -- for - // TODO (maxime) fix - public void _test0232_for() { - if (COMPLIANCE_1_5.equals(this.complianceLevel)) { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Iterable i = null;\n" + - " for (Object o : i) {/* */}\n" + - // complain: NPE - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 4)\n" + - " for (Object o : i) {/* */}\n" + - " ^\n" + - "The variable i can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); - } - } - - // null analysis -- for - public void test0233_for() { - if (COMPLIANCE_1_5.equals(this.complianceLevel)) { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Object t[] = new Object[1];\n" + - " for (Object o : t) {/* */}\n" + - " }\n" + - "}\n"}, - "" - ); - } - } - - // null analysis -- for - public void test0234_for() { - if (COMPLIANCE_1_5.equals(this.complianceLevel)) { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Iterable i = new java.util.Vector();\n" + - " for (Object o : i) {/* */}\n" + - " }\n" + - "}\n"}, - "" - ); - } - } - - // null analysis -- for - public void test0235_for() { - if (COMPLIANCE_1_5.equals(this.complianceLevel)) { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Iterable i = new java.util.Vector();\n" + - " Object flag = null;\n" + - " for (Object o : i) {\n" + - " flag = new Object();\n" + - " }\n" + - " flag.toString();\n" + - " }\n" + - "}\n"}, - "" - ); + for (; i < left; i++) { + if (this.extra[2][i] != 0 || + this.extra[3][i] != 0 || + this.extra[4][i] != 0 || + this.extra[5][i] != 0) { + return false; } } - - // null analysis -- for - // TODO (maxime) fix - public void _test0236_for() { - if (COMPLIANCE_1_5.equals(this.complianceLevel)) { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Iterable i = new java.util.Vector();\n" + - " Object flag = null;\n" + - " for (Object o : i) { /* */ }\n" + - " flag.toString();\n" + - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 6)\n" + - " flag.toString();\n" + - " ^^^^\n" + - "The variable flag can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); + for (; i < right; i++) { + if (other.extra[2][i] != 0 || + other.extra[3][i] != 0 || + other.extra[4][i] != 0 || + other.extra[5][i] != 0) { + return false; } } + return true; +} - // null analysis -- for - public void test0237_for() { - if (COMPLIANCE_1_5.equals(this.complianceLevel)) { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo(boolean dummy) {\n" + - " Object flag = null;\n" + - " for (;dummy;) {\n" + - " flag = new Object();\n" + - " }\n" + - " flag.toString();\n" + - " }\n" + - "}\n"}, - "" - ); +/** + * Return true iff this flow info can be considered as equal to the one passed + * in parameter in respect with a single local variable which id would be + * position in a class with no field. + * @param other the flow info to compare to + * @param position the position of the local to consider + * @return true iff this flow info compares equal to other for a given local + */ +public boolean testEquals(UnconditionalFlowInfo other, int position) { + int vectorIndex = position / BitCacheSize - 1; + if ((this.tagBits & other.tagBits & NULL_FLAG_MASK) == 0) { + return true; + } + long mask; + if (vectorIndex < 0) { + return ((this.nullAssignmentStatusBit1 & (mask = (1L << position))) ^ + (other.nullAssignmentStatusBit1 & mask)) == 0 && + ((this.nullAssignmentStatusBit2 & mask) ^ + (other.nullAssignmentStatusBit2 & mask)) == 0 && + ((this.nullAssignmentValueBit1 & mask) ^ + (other.nullAssignmentValueBit1 & mask)) == 0 && + ((this.nullAssignmentValueBit2 & mask) ^ + (other.nullAssignmentValueBit2 & mask)) == 0; + } + else { + int left = this.extra == null ? + 0 : + this.extra[0].length; + int right = other.extra == null ? + 0 : + other.extra[0].length; + int both = left < right ? left : right; + if (vectorIndex < both) { + return ((this.extra[2][vectorIndex] & + (mask = (1L << (position % BitCacheSize)))) ^ + (other.extra[2][vectorIndex] & mask)) == 0 && + ((this.extra[3][vectorIndex] & mask) ^ + (other.extra[3][vectorIndex] & mask)) == 0 && + ((this.extra[4][vectorIndex] & mask) ^ + (other.extra[4][vectorIndex] & mask)) == 0 && + ((this.extra[5][vectorIndex] & mask) ^ + (other.extra[5][vectorIndex] & mask)) == 0; } - } - - // null analysis -- for - // TODO (maxime) fix - public void _test0238_for() { - if (COMPLIANCE_1_5.equals(this.complianceLevel)) { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo(boolean dummy) {\n" + - " Object flag = null;\n" + - " for (;dummy;) { /* */ }\n" + - " flag.toString();\n" + - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 5)\n" + - " flag.toString();\n" + - " ^^^^\n" + - "The variable flag can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); + if (vectorIndex < left) { + return ((this.extra[2][vectorIndex] | + this.extra[3][vectorIndex] | + this.extra[4][vectorIndex] | + this.extra[5][vectorIndex]) & + (1L << (position % BitCacheSize))) == 0; } + return ((other.extra[2][vectorIndex] | + other.extra[3][vectorIndex] | + other.extra[4][vectorIndex] | + other.extra[5][vectorIndex]) & + (1L << (position % BitCacheSize))) == 0; } - - // null analysis -- for - // origin: AssignmentTest#test019 - public void test0239_for() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " public static final char[] foo(char[] a, char c1, char c2) {\n" + - " char[] r = null;\n" + - " for (int i = 0, length = a.length; i < length; i++) {\n" + - " char c = a[i];\n" + - " if (c == c1) {\n" + - " if (r == null) {\n" + - " r = new char[length];\n" + - " }\n" + - " r[i] = c2;\n" + - " } else if (r != null) {\n" + - " r[i] = c;\n" + - " }\n" + - " }\n" + - " if (r == null) return a;\n" + - " return r;\n" + - " }\n" + - "}\n", - }, - ""); - } +} - // null analysis -- for - public void test0240_for_continue_break() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " void foo() {\n" + - " Object o = new Object();\n" + - " for (int i = 0; i < 10; i++) {\n" + - " if (o == null) {\n" + // complain: o cannot be null - " continue;\n" + - " }\n" + - " o = null;\n" + - " break;\n" + - " }\n" + - " }\n" + - "}\n", - }, - "----------\n" + - "1. WARNING in X.java (at line 5)\n" + - " if (o == null) {\n" + - " ^\n" + - "The variable o can only be null; it was either set to null or checked for null when last used\n" + - "----------\n"); - } - - // null analysis -- switch - public void test0300_switch() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " int k;\n" + - " void foo() {\n" + - " Object o = null;\n" + - " switch (k) {\n" + - " case 0 :\n" + - " o = new Object();\n" + - " break;\n" + - " case 2 :\n" + - " return;\n" + - " }\n" + - " if(o == null) { /* */ }\n" + // quiet: don't know whether came from 0 or default - " }\n" + - "}\n"}, - "" - ); +/** + * Return a string suitable for use as a representation of this flow info + * within test series. + * @return a string suitable for use as a representation of this flow info + */ +public String testString() { + if (this == DEAD_END) { + return "FlowInfo.DEAD_END"; //$NON-NLS-1$ } + return testString(this.testPosition); +} - // null analysis -- switch - public void test0301_switch() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " int k;\n" + - " void foo() {\n" + - " Object o = null;\n" + - " switch (k) {\n" + - " case 0 :\n" + - " o = new Object();\n" + - " break;\n" + - " default :\n" + - " return;\n" + - " }\n" + - " if(o == null) { /* */ }\n" + // complain: only get there through 0, o non null - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 12)\n" + - " if(o == null) { /* */ }\n" + - " ^\n" + - "The variable o cannot be null; it was either set to a non-null value or assumed to be non-null when last used\n" + - "----------\n" - ); - } - - // null analysis -- switch - public void test0302_switch() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " int k;\n" + - " void foo() {\n" + - " Object o = null;\n" + - " switch (k) {\n" + - " case 0 :\n" + - " o.toString();\n" + // complain: o can only be null - " break;\n" + - " }\n" + - " }\n" + - "}\n"}, - "----------\n" + - "1. WARNING in X.java (at line 7)\n" + - " o.toString();\n" + - " ^\n" + - "The variable o can only be null; it was either set to null or checked for null when last used\n" + - "----------\n" - ); - } - - // null analysis -- switch - public void test0303_switch() { - this.runNegativeTest( - new String[] { - "X.java", - "public class X {\n" + - " int k;\n" + - " void foo() {\n" + - " Object o = null;\n" + - " switch (k) {\n" + - " case 0 :\n" + - " o = new Object();\n" + - " case 1 :\n" + - " o.toString();\n" + // quiet: may come through 0 or 1 - " break;\n" + - " }\n" + - " }\n" + - "}\n", - }, - ""); +/** + * Return a string suitable for use as a representation of this flow info + * within test series. + * @param position a position to consider instead of this flow info default + * test position + * @return a string suitable for use as a representation of this flow info + */ +public String testString(int position) { + if (this == DEAD_END) { + return "FlowInfo.DEAD_END"; //$NON-NLS-1$ + } + if (position < BitCacheSize) { + return "{" + (this.nullAssignmentStatusBit1 >> position) //$NON-NLS-1$ + + "," + (this.nullAssignmentStatusBit2 >> position) //$NON-NLS-1$ + + "," + (this.nullAssignmentValueBit1 >> position) //$NON-NLS-1$ + + "," + (this.nullAssignmentValueBit2 >> position) //$NON-NLS-1$ + + "}"; //$NON-NLS-1$ + } + else { + int vectorIndex = position / BitCacheSize - 1, + shift = position % BitCacheSize; + return "{" + (this.extra[2][vectorIndex] //$NON-NLS-1$ + >> shift) + + "," + (this.extra[3][vectorIndex] //$NON-NLS-1$ + >> shift) + + "," + (this.extra[4][vectorIndex] //$NON-NLS-1$ + >> shift) + + "," + (this.extra[5][vectorIndex] //$NON-NLS-1$ + >> shift) + + "}"; //$NON-NLS-1$ } - - // flow info low-level validation - // TODO (maxime) try to cover with source level tests instead of intrusive code +} } Index: src/org/eclipse/jdt/core/tests/compiler/regression/TestAll.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TestAll.java,v retrieving revision 1.53 diff -u -r1.53 TestAll.java --- src/org/eclipse/jdt/core/tests/compiler/regression/TestAll.java 19 Jan 2006 17:10:20 -0000 1.53 +++ src/org/eclipse/jdt/core/tests/compiler/regression/TestAll.java 24 Jan 2006 09:57:13 -0000 @@ -54,6 +54,7 @@ standardTests.add(CharOperationTest.class); standardTests.add(RuntimeTests.class); standardTests.add(DebugAttributeTest.class); + standardTests.add(NullReferenceTest.class); // add all javadoc tests for (int i=0, l=JavadocTest.ALL_CLASSES.size(); i