Download
Getting Started
Members
Projects
Community
Marketplace
Events
Planet Eclipse
Newsletter
Videos
Participate
Report a Bug
Forums
Mailing Lists
Wiki
IRC
How to Contribute
Working Groups
Automotive
Internet of Things
LocationTech
Long-Term Support
PolarSys
Science
OpenMDM
More
Community
Marketplace
Events
Planet Eclipse
Newsletter
Videos
Participate
Report a Bug
Forums
Mailing Lists
Wiki
IRC
How to Contribute
Working Groups
Automotive
Internet of Things
LocationTech
Long-Term Support
PolarSys
Science
OpenMDM
Toggle navigation
Bugzilla – Attachment 33507 Details for
Bug 110030
[plan][compiler] Provide support for null reference analysis
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
Log In
[x]
|
Terms of Use
|
Copyright Agent
[patch]
Experimental version for checks upon locals
org.eclipse.jdt.core_whole_139_HEAD.txt (text/plain), 621.29 KB, created by
Maxime Daniel
on 2006-01-24 06:04:29 EST
(
hide
)
Description:
Experimental version for checks upon locals
Filename:
MIME Type:
Creator:
Maxime Daniel
Created:
2006-01-24 06:04:29 EST
Size:
621.29 KB
patch
obsolete
>### 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: <ul> >- * <li> intersection of definitely assigned variables, >- * <li> union of potentially assigned variables. >- * </ul> >- */ >- 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<true: " + initsWhenTrue.toString() + ", false: " + initsWhenFalse.toString() + ">"; //$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: <ul> >- * <li> intersection of definitely assigned variables, >- * <li> union of potentially assigned variables. >- * </ul> >- */ >- 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.<br> >+ * 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:<ul> >+ * <li>the receiver updated in the following way:<ul> >+ * <li>intersection of definitely assigned variables, >+ * <li>union of potentially assigned variables, >+ * <li>similar operations for null,</ul> >+ * <li>or the receiver or otherInits if the other one is non >+ * reachable.</ul> >+ * 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: <defN1:0,defNoN1:1> + <defN2:1,defNoN2:0> --> <defN:0,defNon:0> >- >- // 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: <ul> >- * <li> intersection of definitely assigned variables, >- * <li> union of potentially assigned variables. >- * </ul> >- */ >- 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<def: "+ this.definiteInits //$NON-NLS-1$ >- +", pot: " + this.potentialInits //$NON-NLS-1$ >- + ", reachable:" + ((this.reachMode & UNREACHABLE) == 0) //$NON-NLS-1$ >- +", defNull: " + this.definiteNulls //$NON-NLS-1$ >- +", defNonNull: " + this.definiteNonNulls //$NON-NLS-1$ >- +">"; //$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<def: " + this.definiteInits //$NON-NLS-1$ >+ +", pot: " + this.potentialInits //$NON-NLS-1$ >+ + ", reachable:" + ((this.tagBits & UNREACHABLE) == 0) //$NON-NLS-1$ >+ +", nullS1: " + this.nullAssignmentStatusBit1 //$NON-NLS-1$ >+ +", nullS2: " + this.nullAssignmentStatusBit2 //$NON-NLS-1$ >+ +", nullV1: " + this.nullAssignmentValueBit1 //$NON-NLS-1$ >+ +", nullV2: " + this.nullAssignmentValueBit2 //$NON-NLS-1$ >+ +">"; //$NON-NLS-1$ >+ } >+ else { >+ String def = "FlowInfo<def:[" + this.definiteInits, //$NON-NLS-1$ >+ pot = "], pot:[" + this.potentialInits, //$NON-NLS-1$ >+ nullS1 = ", nullS1:[" + this.nullAssignmentStatusBit1, //$NON-NLS-1$ >+ nullS2 = "], nullS2:[" + this.nullAssignmentStatusBit2, //$NON-NLS-1$ >+ nullV1 = "], nullV1:[" + this.nullAssignmentValueBit1, //$NON-NLS-1$ >+ nullV2 = "], nullV2:[" + this.nullAssignmentValueBit2; //$NON-NLS-1$ >+ int i, ceil; >+ for (i = 0, ceil = this.extra[0].length > 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<def: " + this.definiteInits //$NON-NLS-1$ >+ +", pot: " + this.potentialInits //$NON-NLS-1$ >+ + ", reachable:" + ((this.tagBits & UNREACHABLE) == 0) //$NON-NLS-1$ >+ +", no null info>"; //$NON-NLS-1$ >+ } >+ else { >+ String def = "FlowInfo<def:[" + this.definiteInits, //$NON-NLS-1$ >+ pot = "], pot:[" + this.potentialInits; //$NON-NLS-1$ >+ int i, ceil; >+ for (i = 0, ceil = this.extra[0].length > 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 = "SUCC<A should be initialized>ESS"; >@@ -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<number>" will be run for each number of this array >-// TESTS_NUMBERS = new int[] { 183 }; >- // Range numbers of tests to run: all tests between "test<first>" and "test<last>" 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<Object>();\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<Object>();\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<Object>();\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<Object>();\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<Object>();\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<Object>();\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<l; i++) {
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 110030
:
30700
| 33507 |
33699