### 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.223 diff -u -r1.223 IProblem.java --- compiler/org/eclipse/jdt/core/compiler/IProblem.java 17 Dec 2010 09:38:57 -0000 1.223 +++ compiler/org/eclipse/jdt/core/compiler/IProblem.java 22 Dec 2010 18:19:10 -0000 @@ -359,7 +359,7 @@ int DuplicateBlankFinalFieldInitialization = FieldRelated + 82; /** @since 3.6 */ int UnresolvedVariable = FieldRelated + 83; - + // variable hiding /** @since 3.0 */ int LocalVariableHidingLocalVariable = Internal + 90; @@ -1245,6 +1245,26 @@ /** @since 3.4 */ int UnusedTypeArgumentsForConstructorInvocation = MethodRelated + 660; + /** + * Null analysis for fields + */ + /** @since 3.7 */ + int NullFieldReference = Internal + FieldRelated + 670; + /** @since 3.7 */ + int PotentialNullFieldReference = Internal + FieldRelated + 671; + /** @since 3.7 */ + int RedundantNullCheckOnNullField = Internal + FieldRelated + 672; + /** @since 3.7 */ + int NullFieldComparisonYieldsFalse = Internal + FieldRelated + 673; + /** @since 3.7 */ + int RedundantNullCheckOnNonNullField = Internal + FieldRelated + 674; + /** @since 3.7 */ + int NonNullFieldComparisonYieldsFalse = Internal + FieldRelated + 675; + /** @since 3.7 */ + int RedundantFieldNullAssignment = Internal + FieldRelated + 676; + /** @since 3.7 */ + int NullFieldInstanceofYieldsFalse = Internal + FieldRelated + 677; + /** * Corrupted binaries */ 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.92 diff -u -r1.92 Assignment.java --- compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java 9 Sep 2010 17:36:20 -0000 1.92 +++ compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java 22 Dec 2010 18:19:10 -0000 @@ -38,7 +38,7 @@ // 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(); + VariableBinding local = this.lhs.variableBinding(); if ((this.expression.implicitConversion & TypeIds.UNBOXING) != 0) { this.expression.checkNPE(currentScope, flowContext, flowInfo); } @@ -56,6 +56,12 @@ flowInfo.markNullStatus(local, nullStatus); if (flowContext.initsOnFinally != null) flowContext.initsOnFinally.markNullStatus(local, nullStatus); + if (local instanceof FieldBinding && local.isFinal() && ((FieldBinding) local).isStatic()) { + // static final field being assigned. Record its null status for future reference + // since the flowInfo from a constructor or static block wont be available in a method + FieldBinding fieldBinding = (FieldBinding) local; + fieldBinding.setNullStatusForStaticFinalField(nullStatus); + } } return flowInfo; } @@ -205,4 +211,7 @@ public LocalVariableBinding localVariableBinding() { return this.lhs.localVariableBinding(); } +public VariableBinding variableBinding() { + return this.lhs.variableBinding(); +} } Index: compiler/org/eclipse/jdt/internal/compiler/ast/Block.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Block.java,v retrieving revision 1.43 diff -u -r1.43 Block.java --- compiler/org/eclipse/jdt/internal/compiler/ast/Block.java 14 Oct 2009 18:08:37 -0000 1.43 +++ compiler/org/eclipse/jdt/internal/compiler/ast/Block.java 22 Dec 2010 18:19:10 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2009 IBM Corporation and others. + * Copyright (c) 2000, 2010 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -36,6 +36,8 @@ flowInfo = stat.analyseCode(this.scope, flowContext, flowInfo); } } + // don't let the flow info collected for fields from this block persist. + flowInfo.resetNullInfoForFields(); return flowInfo; } /** Index: compiler/org/eclipse/jdt/internal/compiler/ast/CastExpression.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CastExpression.java,v retrieving revision 1.137 diff -u -r1.137 CastExpression.java --- compiler/org/eclipse/jdt/internal/compiler/ast/CastExpression.java 17 Dec 2010 06:40:12 -0000 1.137 +++ compiler/org/eclipse/jdt/internal/compiler/ast/CastExpression.java 22 Dec 2010 18:19:10 -0000 @@ -32,6 +32,7 @@ import org.eclipse.jdt.internal.compiler.lookup.TagBits; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeIds; +import org.eclipse.jdt.internal.compiler.lookup.VariableBinding; import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities; public class CastExpression extends Expression { @@ -444,6 +445,13 @@ return this.expression.localVariableBinding(); } +/** + * @see org.eclipse.jdt.internal.compiler.ast.Expression#variableBinding() + */ +public VariableBinding variableBinding() { + return this.expression.variableBinding(); +} + public int nullStatus(FlowInfo flowInfo) { return this.expression.nullStatus(flowInfo); } 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.81 diff -u -r1.81 EqualExpression.java --- compiler/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java 15 Sep 2010 16:10:50 -0000 1.81 +++ compiler/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java 22 Dec 2010 18:19:10 -0000 @@ -24,23 +24,23 @@ } private void checkNullComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse) { - LocalVariableBinding local = this.left.localVariableBinding(); + VariableBinding local = this.left.variableBinding(); if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) { checkVariableComparison(scope, flowContext, flowInfo, initsWhenTrue, initsWhenFalse, local, this.right.nullStatus(flowInfo), this.left); } - local = this.right.localVariableBinding(); + local = this.right.variableBinding(); if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) { checkVariableComparison(scope, flowContext, flowInfo, initsWhenTrue, initsWhenFalse, local, this.left.nullStatus(flowInfo), this.right); } } - private void checkVariableComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse, LocalVariableBinding local, int nullStatus, Expression reference) { + private void checkVariableComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse, VariableBinding local, int nullStatus, Expression reference) { switch (nullStatus) { case FlowInfo.NULL : if (((this.bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) { flowContext.recordUsingNullReference(scope, local, reference, FlowContext.CAN_ONLY_NULL_NON_NULL | FlowContext.IN_COMPARISON_NULL, flowInfo); initsWhenTrue.markAsComparedEqualToNull(local); // from thereon it is set - initsWhenFalse.markAsComparedEqualToNonNull(local); // from thereon it is set + initsWhenFalse.markAsComparedEqualToNonNull(local ); // from thereon it is set } else { flowContext.recordUsingNullReference(scope, local, reference, FlowContext.CAN_ONLY_NULL_NON_NULL | FlowContext.IN_COMPARISON_NON_NULL, flowInfo); 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.132 diff -u -r1.132 Expression.java --- compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java 17 Dec 2010 06:40:12 -0000 1.132 +++ compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java 22 Dec 2010 18:19:12 -0000 @@ -35,6 +35,7 @@ import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeIds; import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding; +import org.eclipse.jdt.internal.compiler.lookup.VariableBinding; import org.eclipse.jdt.internal.compiler.lookup.WildcardBinding; import org.eclipse.jdt.internal.compiler.problem.ShouldNotImplement; import org.eclipse.jdt.internal.compiler.util.Messages; @@ -503,14 +504,14 @@ * @param flowInfo the upstream flow info; caveat: may get modified */ public void checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo) { - LocalVariableBinding local = localVariableBinding(); + VariableBinding local = variableBinding(); if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) { if ((this.bits & ASTNode.IsNonNull) == 0) { flowContext.recordUsingNullReference(scope, local, this, FlowContext.MAY_NULL, flowInfo); } - flowInfo.markAsComparedEqualToNonNull(local); + flowInfo.markAsComparedEqualToNonNull(local ); // from thereon it is set if ((flowContext.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) != 0) { flowInfo.markedAsNullOrNonNullInAssertExpression(local); @@ -843,7 +844,7 @@ this.constant != null && this.constant != Constant.NotAConstant) return FlowInfo.NON_NULL; // constant expression cannot be null - LocalVariableBinding local = localVariableBinding(); + VariableBinding local = variableBinding(); if (local != null) return flowInfo.nullStatus(local); return FlowInfo.NON_NULL; @@ -1049,4 +1050,12 @@ public void traverse(ASTVisitor visitor, ClassScope scope) { // nothing to do } + +/** + * Returns the field or local variable referenced by this node. Can be a direct reference (SingleNameReference) + * or thru a cast expression etc... + */ +public VariableBinding variableBinding() { + return null; +} } Index: compiler/org/eclipse/jdt/internal/compiler/ast/FieldDeclaration.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldDeclaration.java,v retrieving revision 1.101 diff -u -r1.101 FieldDeclaration.java --- compiler/org/eclipse/jdt/internal/compiler/ast/FieldDeclaration.java 1 Jul 2010 04:39:20 -0000 1.101 +++ compiler/org/eclipse/jdt/internal/compiler/ast/FieldDeclaration.java 22 Dec 2010 18:19:12 -0000 @@ -74,6 +74,13 @@ .analyseCode(initializationScope, flowContext, flowInfo) .unconditionalInits(); flowInfo.markAsDefinitelyAssigned(this.binding); + if (this.binding.isFinal() && this.binding.isStatic()) { + int nullStatus = this.initialization.nullStatus(flowInfo); + // static final field being initialized. Record its null status for future reference + // since the flowInfo from an initialization wont be available in a method + + this.binding.setNullStatusForStaticFinalField(nullStatus); + } } return flowInfo; } 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.131 diff -u -r1.131 FieldReference.java --- compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java 17 Dec 2010 09:38:55 -0000 1.131 +++ compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java 22 Dec 2010 18:19:12 -0000 @@ -35,6 +35,7 @@ import org.eclipse.jdt.internal.compiler.lookup.TagBits; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeIds; +import org.eclipse.jdt.internal.compiler.lookup.VariableBinding; public class FieldReference extends Reference implements InvocationSite { @@ -668,4 +669,11 @@ } visitor.endVisit(this, scope); } + +public VariableBinding variableBinding() { + if (this.receiver.isThis() || this.binding.isStatic()) + return this.binding; + return super.variableBinding(); +} + } 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.62 diff -u -r1.62 InstanceOfExpression.java --- compiler/org/eclipse/jdt/internal/compiler/ast/InstanceOfExpression.java 15 Sep 2010 16:10:51 -0000 1.62 +++ compiler/org/eclipse/jdt/internal/compiler/ast/InstanceOfExpression.java 22 Dec 2010 18:19:12 -0000 @@ -31,16 +31,16 @@ } public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { - LocalVariableBinding local = this.expression.localVariableBinding(); - if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) { + VariableBinding variable = this.expression.variableBinding(); + if (variable != null && (variable.type.tagBits & TagBits.IsBaseType) == 0) { flowInfo = this.expression.analyseCode(currentScope, flowContext, flowInfo). unconditionalInits(); FlowInfo initsWhenTrue = flowInfo.copy(); - initsWhenTrue.markAsComparedEqualToNonNull(local); + initsWhenTrue.markAsComparedEqualToNonNull(variable ); if ((flowContext.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) != 0) { - initsWhenTrue.markedAsNullOrNonNullInAssertExpression(local); + initsWhenTrue.markedAsNullOrNonNullInAssertExpression(variable); } - flowContext.recordUsingNullReference(currentScope, local, + flowContext.recordUsingNullReference(currentScope, variable, this.expression, FlowContext.CAN_ONLY_NULL | FlowContext.IN_INSTANCEOF, flowInfo); // no impact upon enclosing try context return FlowInfo.conditional(initsWhenTrue, flowInfo.copy()); 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.75 diff -u -r1.75 LocalDeclaration.java --- compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java 17 Dec 2010 09:38:55 -0000 1.75 +++ compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java 22 Dec 2010 18:19:12 -0000 @@ -81,7 +81,7 @@ } flowInfo.markAsDefinitelyAssigned(this.binding); if ((this.binding.type.tagBits & TagBits.IsBaseType) == 0) { - flowInfo.markNullStatus(this.binding, nullStatus); + flowInfo.markNullStatus(this.binding, nullStatus ); // no need to inform enclosing try block since its locals won't get // known by the finally block } 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.149 diff -u -r1.149 MessageSend.java --- compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java 17 Dec 2010 09:38:55 -0000 1.149 +++ compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java 22 Dec 2010 18:19:12 -0000 @@ -95,6 +95,9 @@ // NullReferenceTest#test0510 } manageSyntheticAccessIfNecessary(currentScope, flowInfo); + // a method call can result in changed values for fields, + // so wipe out null info for fields collected till now. + flowInfo.resetNullInfoForFields(); return flowInfo; } /** 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.129 diff -u -r1.129 SingleNameReference.java --- compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java 17 Dec 2010 09:38:55 -0000 1.129 +++ compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java 22 Dec 2010 18:19:13 -0000 @@ -807,6 +807,15 @@ return null; } +public VariableBinding variableBinding() { + switch (this.bits & ASTNode.RestrictiveFlagMASK) { + case Binding.FIELD : + // reading a field + case Binding.LOCAL : // reading a local variable + return (VariableBinding) this.binding; + } + return null; +} public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) { //If inlinable field, forget the access emulation, the code gen will directly target it if (((this.bits & ASTNode.DepthMASK) == 0) || (this.constant != Constant.NotAConstant)) { @@ -859,11 +868,10 @@ } switch (this.bits & ASTNode.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) - return flowInfo.nullStatus(local); + VariableBinding variable = (VariableBinding) this.binding; + if (variable != null) + return flowInfo.nullStatus(variable); } return FlowInfo.NON_NULL; // never get there } 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.164 diff -u -r1.164 TypeDeclaration.java --- compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java 21 Jun 2010 09:47:48 -0000 1.164 +++ compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java 22 Dec 2010 18:19:13 -0000 @@ -1086,6 +1086,21 @@ if (this.maxFieldCount < localMaxFieldCount) { this.maxFieldCount = localMaxFieldCount; } + + // field count from supertypes should be included in maxFieldCount, + // so that a field from supertype doesn't end up with same id as a local variable + // in a method being analyzed. + ReferenceBinding superClassBinding = this.binding.superclass; + while (superClassBinding != null) { + FieldBinding[] unResolvedFields = superClassBinding.unResolvedFields(); + if (unResolvedFields != null) { + this.maxFieldCount += unResolvedFields.length; + } + superClassBinding = superClassBinding.superclass(); + } + ReferenceBinding[] superInterfacesBinding = this.binding.superInterfaces; + this.maxFieldCount += findFieldCountFromSuperInterfaces(superInterfacesBinding); + if (needSerialVersion) { //check that the current type doesn't extend javax.rmi.CORBA.Stub TypeBinding javaxRmiCorbaStub = this.scope.getType(TypeConstants.JAVAX_RMI_CORBA_STUB, 4); @@ -1172,6 +1187,17 @@ } } +private int findFieldCountFromSuperInterfaces(ReferenceBinding[] superinterfaces) { + int numOfFields = 0; + if (superinterfaces == null) + return numOfFields ; + for (int i = 0; i < superinterfaces.length; i++) { + numOfFields += superinterfaces[i].fieldCount(); + numOfFields += findFieldCountFromSuperInterfaces(superinterfaces[i].superInterfaces()); + } + return numOfFields; +} + /** * Resolve a local type declaration */ 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.33 diff -u -r1.33 ConditionalFlowInfo.java --- compiler/org/eclipse/jdt/internal/compiler/flow/ConditionalFlowInfo.java 16 Dec 2010 13:02:30 -0000 1.33 +++ compiler/org/eclipse/jdt/internal/compiler/flow/ConditionalFlowInfo.java 22 Dec 2010 18:19:13 -0000 @@ -13,6 +13,7 @@ import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; +import org.eclipse.jdt.internal.compiler.lookup.VariableBinding; /** * Record conditional initialization status during definite assignment analysis @@ -85,17 +86,17 @@ && this.initsWhenFalse.isDefinitelyAssigned(local); } -public boolean isDefinitelyNonNull(LocalVariableBinding local) { +public boolean isDefinitelyNonNull(VariableBinding local) { return this.initsWhenTrue.isDefinitelyNonNull(local) && this.initsWhenFalse.isDefinitelyNonNull(local); } -public boolean isDefinitelyNull(LocalVariableBinding local) { +public boolean isDefinitelyNull(VariableBinding local) { return this.initsWhenTrue.isDefinitelyNull(local) && this.initsWhenFalse.isDefinitelyNull(local); } -public boolean isDefinitelyUnknown(LocalVariableBinding local) { +public boolean isDefinitelyUnknown(VariableBinding local) { return this.initsWhenTrue.isDefinitelyUnknown(local) && this.initsWhenFalse.isDefinitelyUnknown(local); } @@ -110,37 +111,37 @@ || this.initsWhenFalse.isPotentiallyAssigned(local); } -public boolean isPotentiallyNonNull(LocalVariableBinding local) { +public boolean isPotentiallyNonNull(VariableBinding local) { return this.initsWhenTrue.isPotentiallyNonNull(local) || this.initsWhenFalse.isPotentiallyNonNull(local); } -public boolean isPotentiallyNull(LocalVariableBinding local) { +public boolean isPotentiallyNull(VariableBinding local) { return this.initsWhenTrue.isPotentiallyNull(local) || this.initsWhenFalse.isPotentiallyNull(local); } -public boolean isPotentiallyUnknown(LocalVariableBinding local) { +public boolean isPotentiallyUnknown(VariableBinding local) { return this.initsWhenTrue.isPotentiallyUnknown(local) || this.initsWhenFalse.isPotentiallyUnknown(local); } -public boolean isProtectedNonNull(LocalVariableBinding local) { +public boolean isProtectedNonNull(VariableBinding local) { return this.initsWhenTrue.isProtectedNonNull(local) && this.initsWhenFalse.isProtectedNonNull(local); } -public boolean isProtectedNull(LocalVariableBinding local) { +public boolean isProtectedNull(VariableBinding local) { return this.initsWhenTrue.isProtectedNull(local) && this.initsWhenFalse.isProtectedNull(local); } -public void markAsComparedEqualToNonNull(LocalVariableBinding local) { +public void markAsComparedEqualToNonNull(VariableBinding local) { this.initsWhenTrue.markAsComparedEqualToNonNull(local); this.initsWhenFalse.markAsComparedEqualToNonNull(local); } -public void markAsComparedEqualToNull(LocalVariableBinding local) { +public void markAsComparedEqualToNull(VariableBinding local) { this.initsWhenTrue.markAsComparedEqualToNull(local); this.initsWhenFalse.markAsComparedEqualToNull(local); } @@ -155,37 +156,42 @@ this.initsWhenFalse.markAsDefinitelyAssigned(local); } -public void markAsDefinitelyNonNull(LocalVariableBinding local) { +public void markAsDefinitelyNonNull(VariableBinding local) { this.initsWhenTrue.markAsDefinitelyNonNull(local); this.initsWhenFalse.markAsDefinitelyNonNull(local); } -public void markAsDefinitelyNull(LocalVariableBinding local) { +public void markAsDefinitelyNull(VariableBinding local) { this.initsWhenTrue.markAsDefinitelyNull(local); this.initsWhenFalse.markAsDefinitelyNull(local); } -public void resetNullInfo(LocalVariableBinding local) { +public void resetNullInfo(VariableBinding local) { this.initsWhenTrue.resetNullInfo(local); this.initsWhenFalse.resetNullInfo(local); } -public void markPotentiallyNullBit(LocalVariableBinding local) { +public void resetNullInfoForFields() { + this.initsWhenTrue.resetNullInfoForFields(); + this.initsWhenFalse.resetNullInfoForFields(); +} + +public void markPotentiallyNullBit(VariableBinding local) { this.initsWhenTrue.markPotentiallyNullBit(local); this.initsWhenFalse.markPotentiallyNullBit(local); } -public void markPotentiallyNonNullBit(LocalVariableBinding local) { +public void markPotentiallyNonNullBit(VariableBinding local) { this.initsWhenTrue.markPotentiallyNonNullBit(local); this.initsWhenFalse.markPotentiallyNonNullBit(local); } -public void markAsDefinitelyUnknown(LocalVariableBinding local) { +public void markAsDefinitelyUnknown(VariableBinding local) { this.initsWhenTrue.markAsDefinitelyUnknown(local); this.initsWhenFalse.markAsDefinitelyUnknown(local); } -public void markPotentiallyUnknownBit(LocalVariableBinding local) { +public void markPotentiallyUnknownBit(VariableBinding local) { this.initsWhenTrue.markPotentiallyUnknownBit(local); this.initsWhenFalse.markPotentiallyUnknownBit(local); } @@ -243,12 +249,12 @@ mergedWith(this.initsWhenFalse.unconditionalInits()); } -public void markedAsNullOrNonNullInAssertExpression(LocalVariableBinding local) { +public void markedAsNullOrNonNullInAssertExpression(VariableBinding local) { this.initsWhenTrue.markedAsNullOrNonNullInAssertExpression(local); this.initsWhenFalse.markedAsNullOrNonNullInAssertExpression(local); } -public boolean isMarkedAsNullOrNonNullInAssertExpression(LocalVariableBinding local) { +public boolean isMarkedAsNullOrNonNullInAssertExpression(VariableBinding local) { return (this.initsWhenTrue.isMarkedAsNullOrNonNullInAssertExpression(local) || this.initsWhenFalse.isMarkedAsNullOrNonNullInAssertExpression(local)); } 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.35 diff -u -r1.35 FinallyFlowContext.java --- compiler/org/eclipse/jdt/internal/compiler/flow/FinallyFlowContext.java 25 Feb 2010 15:27:00 -0000 1.35 +++ compiler/org/eclipse/jdt/internal/compiler/flow/FinallyFlowContext.java 22 Dec 2010 18:19:13 -0000 @@ -30,7 +30,7 @@ VariableBinding[] finalVariables; int assignCount; - LocalVariableBinding[] nullLocals; + VariableBinding[] nullVariables; Expression[] nullReferences; int[] nullCheckTypes; int nullCount; @@ -83,7 +83,7 @@ // check inconsistent null checks if ((this.tagBits & FlowContext.DEFER_NULL_DIAGNOSTIC) != 0) { // within an enclosing loop, be conservative for (int i = 0; i < this.nullCount; i++) { - this.parent.recordUsingNullReference(scope, this.nullLocals[i], + this.parent.recordUsingNullReference(scope, this.nullVariables[i], this.nullReferences[i], this.nullCheckTypes[i], flowInfo); } } @@ -91,18 +91,18 @@ for (int i = 0; i < this.nullCount; i++) { Expression expression = this.nullReferences[i]; // final local variable - LocalVariableBinding local = this.nullLocals[i]; + VariableBinding local = this.nullVariables[i]; switch (this.nullCheckTypes[i]) { case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL: case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL: if (flowInfo.isDefinitelyNonNull(local)) { if (this.nullCheckTypes[i] == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableRedundantCheckOnNonNull(local, expression); + scope.problemReporter().variableRedundantCheckOnNonNull(local, expression); } } else { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableNonNullComparedToNull(local, expression); + scope.problemReporter().variableNonNullComparedToNull(local, expression); } } continue; @@ -116,27 +116,27 @@ switch(this.nullCheckTypes[i] & CONTEXT_MASK) { case FlowContext.IN_COMPARISON_NULL: if (((this.nullCheckTypes[i] & CHECK_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning - scope.problemReporter().localVariableNullReference(local, expression); + scope.problemReporter().variableNullReference(local, expression); continue; } if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableRedundantCheckOnNull(local, expression); + scope.problemReporter().variableRedundantCheckOnNull(local, expression); } continue; case FlowContext.IN_COMPARISON_NON_NULL: if (((this.nullCheckTypes[i] & CHECK_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning - scope.problemReporter().localVariableNullReference(local, expression); + scope.problemReporter().variableNullReference(local, expression); continue; } if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableNullComparedToNonNull(local, expression); + scope.problemReporter().variableNullComparedToNonNull(local, expression); } continue; case FlowContext.IN_ASSIGNMENT: - scope.problemReporter().localVariableRedundantNullAssignment(local, expression); + scope.problemReporter().variableRedundantNullAssignment(local, expression); continue; case FlowContext.IN_INSTANCEOF: - scope.problemReporter().localVariableNullInstanceof(local, expression); + scope.problemReporter().variableNullInstanceof(local, expression); continue; } } else if (flowInfo.isPotentiallyNull(local)) { @@ -144,14 +144,14 @@ case FlowContext.IN_COMPARISON_NULL: this.nullReferences[i] = null; if (((this.nullCheckTypes[i] & CHECK_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning - scope.problemReporter().localVariablePotentialNullReference(local, expression); + scope.problemReporter().variablePotentialNullReference(local, expression); continue; } break; case FlowContext.IN_COMPARISON_NON_NULL: this.nullReferences[i] = null; if (((this.nullCheckTypes[i] & CHECK_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning - scope.problemReporter().localVariablePotentialNullReference(local, expression); + scope.problemReporter().variablePotentialNullReference(local, expression); continue; } break; @@ -160,11 +160,11 @@ break; case MAY_NULL: if (flowInfo.isDefinitelyNull(local)) { - scope.problemReporter().localVariableNullReference(local, expression); + scope.problemReporter().variableNullReference(local, expression); continue; } if (flowInfo.isPotentiallyNull(local)) { - scope.problemReporter().localVariablePotentialNullReference(local, expression); + scope.problemReporter().variablePotentialNullReference(local, expression); } break; default: @@ -212,7 +212,7 @@ return true; } - public void recordUsingNullReference(Scope scope, LocalVariableBinding local, + public void recordUsingNullReference(Scope scope, VariableBinding local, Expression reference, int checkType, FlowInfo flowInfo) { if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0 && !flowInfo.isDefinitelyUnknown(local)) { if ((this.tagBits & FlowContext.DEFER_NULL_DIAGNOSTIC) != 0) { // within an enclosing loop, be conservative @@ -226,14 +226,14 @@ if (flowInfo.cannotBeNull(local)) { if (checkType == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableRedundantCheckOnNonNull(local, reference); + scope.problemReporter().variableRedundantCheckOnNonNull(local, reference); } if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) { flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE); } } else if (checkType == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL)) { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableNonNullComparedToNull(local, reference); + scope.problemReporter().variableNonNullComparedToNull(local, reference); } if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) { flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE); @@ -245,11 +245,11 @@ switch(checkType & CONTEXT_MASK) { case FlowContext.IN_COMPARISON_NULL: if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning - scope.problemReporter().localVariableNullReference(local, reference); + scope.problemReporter().variableNullReference(local, reference); return; } if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableRedundantCheckOnNull(local, reference); + scope.problemReporter().variableRedundantCheckOnNull(local, reference); } if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) { flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE); @@ -257,34 +257,34 @@ return; case FlowContext.IN_COMPARISON_NON_NULL: if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning - scope.problemReporter().localVariableNullReference(local, reference); + scope.problemReporter().variableNullReference(local, reference); return; } if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableNullComparedToNonNull(local, reference); + scope.problemReporter().variableNullComparedToNonNull(local, reference); } if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) { flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE); } return; case FlowContext.IN_ASSIGNMENT: - scope.problemReporter().localVariableRedundantNullAssignment(local, reference); + scope.problemReporter().variableRedundantNullAssignment(local, reference); return; case FlowContext.IN_INSTANCEOF: - scope.problemReporter().localVariableNullInstanceof(local, reference); + scope.problemReporter().variableNullInstanceof(local, reference); return; } } else if (flowInfo.isPotentiallyNull(local)) { switch(checkType & CONTEXT_MASK) { case FlowContext.IN_COMPARISON_NULL: if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning - scope.problemReporter().localVariablePotentialNullReference(local, reference); + scope.problemReporter().variablePotentialNullReference(local, reference); return; } break; case FlowContext.IN_COMPARISON_NON_NULL: if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning - scope.problemReporter().localVariablePotentialNullReference(local, reference); + scope.problemReporter().variablePotentialNullReference(local, reference); return; } break; @@ -296,7 +296,7 @@ return; } if (flowInfo.canOnlyBeNull(local)) { - scope.problemReporter().localVariableNullReference(local, reference); + scope.problemReporter().variableNullReference(local, reference); return; } break; @@ -311,14 +311,14 @@ if (flowInfo.isDefinitelyNonNull(local)) { if (checkType == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableRedundantCheckOnNonNull(local, reference); + scope.problemReporter().variableRedundantCheckOnNonNull(local, reference); } if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) { flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE); } } else { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableNonNullComparedToNull(local, reference); + scope.problemReporter().variableNonNullComparedToNull(local, reference); } if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) { flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE); @@ -335,11 +335,11 @@ switch(checkType & CONTEXT_MASK) { case FlowContext.IN_COMPARISON_NULL: if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning - scope.problemReporter().localVariableNullReference(local, reference); + scope.problemReporter().variableNullReference(local, reference); return; } if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableRedundantCheckOnNull(local, reference); + scope.problemReporter().variableRedundantCheckOnNull(local, reference); } if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) { flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE); @@ -347,34 +347,34 @@ return; case FlowContext.IN_COMPARISON_NON_NULL: if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning - scope.problemReporter().localVariableNullReference(local, reference); + scope.problemReporter().variableNullReference(local, reference); return; } if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableNullComparedToNonNull(local, reference); + scope.problemReporter().variableNullComparedToNonNull(local, reference); } if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) { flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE); } return; case FlowContext.IN_ASSIGNMENT: - scope.problemReporter().localVariableRedundantNullAssignment(local, reference); + scope.problemReporter().variableRedundantNullAssignment(local, reference); return; case FlowContext.IN_INSTANCEOF: - scope.problemReporter().localVariableNullInstanceof(local, reference); + scope.problemReporter().variableNullInstanceof(local, reference); return; } } else if (flowInfo.isPotentiallyNull(local)) { switch(checkType & CONTEXT_MASK) { case FlowContext.IN_COMPARISON_NULL: if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning - scope.problemReporter().localVariablePotentialNullReference(local, reference); + scope.problemReporter().variablePotentialNullReference(local, reference); return; } break; case FlowContext.IN_COMPARISON_NON_NULL: if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning - scope.problemReporter().localVariablePotentialNullReference(local, reference); + scope.problemReporter().variablePotentialNullReference(local, reference); return; } break; @@ -383,11 +383,11 @@ break; case MAY_NULL : if (flowInfo.isDefinitelyNull(local)) { - scope.problemReporter().localVariableNullReference(local, reference); + scope.problemReporter().variableNullReference(local, reference); return; } if (flowInfo.isPotentiallyNull(local)) { - scope.problemReporter().localVariablePotentialNullReference(local, reference); + scope.problemReporter().variablePotentialNullReference(local, reference); return; } if (flowInfo.isDefinitelyNonNull(local)) { @@ -419,17 +419,17 @@ } } -protected void recordNullReference(LocalVariableBinding local, +protected void recordNullReference(VariableBinding local, Expression expression, int status) { if (this.nullCount == 0) { - this.nullLocals = new LocalVariableBinding[5]; + this.nullVariables = new VariableBinding[5]; this.nullReferences = new Expression[5]; this.nullCheckTypes = new int[5]; } - else if (this.nullCount == this.nullLocals.length) { + else if (this.nullCount == this.nullVariables.length) { int newLength = this.nullCount * 2; - System.arraycopy(this.nullLocals, 0, - this.nullLocals = new LocalVariableBinding[newLength], 0, + System.arraycopy(this.nullVariables, 0, + this.nullVariables = new VariableBinding[newLength], 0, this.nullCount); System.arraycopy(this.nullReferences, 0, this.nullReferences = new Expression[newLength], 0, @@ -438,7 +438,7 @@ this.nullCheckTypes = new int[newLength], 0, this.nullCount); } - this.nullLocals[this.nullCount] = local; + this.nullVariables[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.68 diff -u -r1.68 FlowContext.java --- compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java 1 Sep 2010 15:49:57 -0000 1.68 +++ compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java 22 Dec 2010 18:19:13 -0000 @@ -23,7 +23,6 @@ import org.eclipse.jdt.internal.compiler.codegen.BranchLabel; import org.eclipse.jdt.internal.compiler.lookup.Binding; import org.eclipse.jdt.internal.compiler.lookup.BlockScope; -import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; import org.eclipse.jdt.internal.compiler.lookup.Scope; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; @@ -520,7 +519,7 @@ * combined with a context indicator (one of {@link #IN_COMPARISON_NULL}, * {@link #IN_COMPARISON_NON_NULL}, {@link #IN_ASSIGNMENT} or {@link #IN_INSTANCEOF}) */ -protected void recordNullReference(LocalVariableBinding local, +protected void recordNullReference(VariableBinding local, Expression expression, int status) { // default implementation: do nothing } @@ -563,7 +562,7 @@ * 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, +public void recordUsingNullReference(Scope scope, VariableBinding local, Expression reference, int checkType, FlowInfo flowInfo) { if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0 || flowInfo.isDefinitelyUnknown(local)) { @@ -575,14 +574,14 @@ if (flowInfo.isDefinitelyNonNull(local)) { if (checkType == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableRedundantCheckOnNonNull(local, reference); + scope.problemReporter().variableRedundantCheckOnNonNull(local, reference); } if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) { flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE); } } else { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableNonNullComparedToNull(local, reference); + scope.problemReporter().variableNonNullComparedToNull(local, reference); } if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) { flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE); @@ -602,11 +601,11 @@ switch(checkType & CONTEXT_MASK) { case FlowContext.IN_COMPARISON_NULL: if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning - scope.problemReporter().localVariableNullReference(local, reference); + scope.problemReporter().variableNullReference(local, reference); return; } if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableRedundantCheckOnNull(local, reference); + scope.problemReporter().variableRedundantCheckOnNull(local, reference); } if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) { flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE); @@ -614,34 +613,34 @@ return; case FlowContext.IN_COMPARISON_NON_NULL: if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning - scope.problemReporter().localVariableNullReference(local, reference); + scope.problemReporter().variableNullReference(local, reference); return; } if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableNullComparedToNonNull(local, reference); + scope.problemReporter().variableNullComparedToNonNull(local, reference); } if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) { flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE); } return; case FlowContext.IN_ASSIGNMENT: - scope.problemReporter().localVariableRedundantNullAssignment(local, reference); + scope.problemReporter().variableRedundantNullAssignment(local, reference); return; case FlowContext.IN_INSTANCEOF: - scope.problemReporter().localVariableNullInstanceof(local, reference); + scope.problemReporter().variableNullInstanceof(local, reference); return; } } else if (flowInfo.isPotentiallyNull(local)) { switch(checkType & CONTEXT_MASK) { case FlowContext.IN_COMPARISON_NULL: if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning - scope.problemReporter().localVariablePotentialNullReference(local, reference); + scope.problemReporter().variablePotentialNullReference(local, reference); return; } break; case FlowContext.IN_COMPARISON_NON_NULL: if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning - scope.problemReporter().localVariablePotentialNullReference(local, reference); + scope.problemReporter().variablePotentialNullReference(local, reference); return; } break; @@ -652,11 +651,11 @@ break; case MAY_NULL : if (flowInfo.isDefinitelyNull(local)) { - scope.problemReporter().localVariableNullReference(local, reference); + scope.problemReporter().variableNullReference(local, reference); return; } if (flowInfo.isPotentiallyNull(local)) { - scope.problemReporter().localVariablePotentialNullReference(local, reference); + scope.problemReporter().variablePotentialNullReference(local, reference); return; } break; 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.45 diff -u -r1.45 FlowInfo.java --- compiler/org/eclipse/jdt/internal/compiler/flow/FlowInfo.java 16 Dec 2010 13:02:30 -0000 1.45 +++ compiler/org/eclipse/jdt/internal/compiler/flow/FlowInfo.java 22 Dec 2010 18:19:13 -0000 @@ -17,6 +17,7 @@ import org.eclipse.jdt.internal.compiler.ast.IfStatement; import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; +import org.eclipse.jdt.internal.compiler.lookup.VariableBinding; public abstract class FlowInfo { @@ -79,39 +80,42 @@ } /** - * Check whether a given local variable is known to be unable to gain a definite + * Check whether a given field or local variable is known to be unable to gain a definite * non null or definite null status by the use of an enclosing flow info. The * semantics are that if the current flow info marks the variable as potentially * unknown or else as being both potentially null and potentially non null, * then it won't ever be promoted as definitely null or definitely non null. (It * could still get promoted to definite unknown). - * @param local the variable to check - * @return true iff this flow info prevents local from being promoted to + * @param binding the field or local variable to check + * @return true iff this flow info prevents field or local from being promoted to * definite non null or definite null against an enclosing flow info */ -public boolean cannotBeDefinitelyNullOrNonNull(LocalVariableBinding local) { - return isPotentiallyUnknown(local) || - isPotentiallyNonNull(local) && isPotentiallyNull(local); +public boolean cannotBeDefinitelyNullOrNonNull(VariableBinding binding) { + return isPotentiallyUnknown(binding) || + isPotentiallyNonNull(binding) && isPotentiallyNull(binding); } /** - * Check whether a given local variable is known to be non null, either because + * Check whether a given field or local variable is known to be non null, either because * it is definitely non null, or because is has been tested against non null. - * @param local the variable to ckeck - * @return true iff local cannot be null for this flow info + * @param binding the field or local to check + * @return true iff field or local cannot be null for this flow info */ -public boolean cannotBeNull(LocalVariableBinding local) { - return isDefinitelyNonNull(local) || isProtectedNonNull(local); +public boolean cannotBeNull(VariableBinding binding) { + return isDefinitelyNonNull(binding) || isProtectedNonNull(binding); } /** - * Check whether a given local variable is known to be null, either because it - * is definitely null, or because is has been tested against null. - * @param local the variable to ckeck - * @return true iff local can only be null for this flow info + * Check whether a given field or local variable is known to be null, either because it + * is definitely null, or because is has been tested against null. Note that for fields, + * this method only takes compile time analysis into account and there's no + * guarantee of the field being definitely null during runtime + * since it can be modified in some other thread. + * @param binding the field or local to check + * @return true iff field or local can only be null for this flow info */ -public boolean canOnlyBeNull(LocalVariableBinding local) { - return isDefinitelyNull(local) || isProtectedNull(local); +public boolean canOnlyBeNull(VariableBinding binding) { + return isDefinitelyNull(binding) || isProtectedNull(binding); } /** @@ -159,25 +163,29 @@ public abstract boolean isDefinitelyAssigned(LocalVariableBinding 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 + * Check status of definite non-null value for a given field or local variable. Note that for fields, this method only + * takes compile time analysis into account and there's no guarantee of the field being definitely non null during runtime + * since it can be modified in some other thread. + * @param binding the field or local to check + * @return true iff field or local is definitely non null for this flow info */ - public abstract boolean isDefinitelyNonNull(LocalVariableBinding local); + public abstract boolean isDefinitelyNonNull(VariableBinding binding); /** - * 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 + * Check status of definite null value for a given field or local variable. Note that for fields, this method only + * takes compile time analysis into account and there's no guarantee of the field being definitely null during runtime + * since it can be modified in some other thread. + * @param binding the field or local to check + * @return true iff field or local is definitely null for this flow info */ -public abstract boolean isDefinitelyNull(LocalVariableBinding local); +public abstract boolean isDefinitelyNull(VariableBinding binding); /** - * 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 + * Check status of definite unknown value for a given field or local variable. + * @param binding the field or local to check + * @return true iff field or local is definitely unknown for this flow info */ -public abstract boolean isDefinitelyUnknown(LocalVariableBinding local); +public abstract boolean isDefinitelyUnknown(VariableBinding binding); /** * Check status of potential assignment for a field. @@ -191,59 +199,59 @@ abstract public boolean isPotentiallyAssigned(LocalVariableBinding field); /** - * Check status of potential null assignment for a local. Return true if there + * Check status of potential null assignment for a field or local. Return true if there * is a reasonable expectation that the variable be non null at this point. - * @param local LocalVariableBinding - the binding for the checked local - * @return true if there is a reasonable expectation that local be non null at + * @param binding VariableBinding - the binding for the checked field or local + * @return true if there is a reasonable expectation that the field or local be non null at * this point */ -public abstract boolean isPotentiallyNonNull(LocalVariableBinding local); +public abstract boolean isPotentiallyNonNull(VariableBinding binding); /** - * Check status of potential null assignment for a local. Return true if there + * Check status of potential null assignment for a field or local. Return true if there * is a reasonable expectation that the variable be null at this point. This * includes the protected null case, so as to augment diagnostics, but does not * really check that someone deliberately assigned to null on any specific * path - * @param local LocalVariableBinding - the binding for the checked local - * @return true if there is a reasonable expectation that local be null at + * @param binding VariableBinding - the binding for the checked field or local + * @return true if there is a reasonable expectation that the field or local be null at * this point */ -public abstract boolean isPotentiallyNull(LocalVariableBinding local); +public abstract boolean isPotentiallyNull(VariableBinding binding); /** - * 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 + * Return true if the given field or local may have been assigned to an unknown value. + * @param binding the field or local to check + * @return true if the given field or local may have been assigned to an unknown value */ -public abstract boolean isPotentiallyUnknown(LocalVariableBinding local); +public abstract boolean isPotentiallyUnknown(VariableBinding binding); /** - * Return true if the given local is protected by a test against a non null + * Return true if the given field or 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 + * @param binding the field or local to check + * @return true if the given field or local is protected by a test against a non null */ -public abstract boolean isProtectedNonNull(LocalVariableBinding local); +public abstract boolean isProtectedNonNull(VariableBinding binding); /** - * 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 + * Return true if the given field or local is protected by a test against null. + * @param binding the field or local to check + * @return true if the given field or local is protected by a test against null */ -public abstract boolean isProtectedNull(LocalVariableBinding local); +public abstract boolean isProtectedNull(VariableBinding binding); /** - * Record that a local variable got checked to be non null. - * @param local the checked local variable + * Record that a field or local variable got checked to be non null. + * @param binding the checked field or local variable */ -abstract public void markAsComparedEqualToNonNull(LocalVariableBinding local); +abstract public void markAsComparedEqualToNonNull(VariableBinding binding); /** - * Record that a local variable got checked to be null. - * @param local the checked local variable + * Record that a field or local variable got checked to be null. + * @param binding the checked field or local variable */ -abstract public void markAsComparedEqualToNull(LocalVariableBinding local); +abstract public void markAsComparedEqualToNull(VariableBinding binding); /** * Record a field got definitely assigned. @@ -251,34 +259,38 @@ abstract public void markAsDefinitelyAssigned(FieldBinding field); /** - * Record a local got definitely assigned to a non-null value. + * Record a field or local got definitely assigned to a non-null value. */ - abstract public void markAsDefinitelyNonNull(LocalVariableBinding local); + abstract public void markAsDefinitelyNonNull(VariableBinding binding); /** - * Record a local got definitely assigned to null. + * Record a field or local got definitely assigned to null. */ - abstract public void markAsDefinitelyNull(LocalVariableBinding local); + abstract public void markAsDefinitelyNull(VariableBinding binding); /** - * Reset all null-information about a given local. + * Reset all null-information about a given field or local. */ - abstract public void resetNullInfo(LocalVariableBinding local); + abstract public void resetNullInfo(VariableBinding binding); /** - * Record a local may have got assigned to unknown (set the bit on existing info). + * variant of {@link #resetNullInfo(VariableBinding)} for resetting null info for all fields */ - abstract public void markPotentiallyUnknownBit(LocalVariableBinding local); + abstract public void resetNullInfoForFields(); + /** + * Record a field or local may have got assigned to unknown (set the bit on existing info). + */ + abstract public void markPotentiallyUnknownBit(VariableBinding binding); /** - * Record a local may have got assigned to null (set the bit on existing info). + * Record a field or local may have got assigned to null (set the bit on existing info). */ - abstract public void markPotentiallyNullBit(LocalVariableBinding local); + abstract public void markPotentiallyNullBit(VariableBinding binding); /** - * Record a local may have got assigned to non-null (set the bit on existing info). + * Record a field or local may have got assigned to non-null (set the bit on existing info). */ - abstract public void markPotentiallyNonNullBit(LocalVariableBinding local); + abstract public void markPotentiallyNonNullBit(VariableBinding binding); /** * Record a local got definitely assigned. @@ -286,59 +298,59 @@ abstract public void markAsDefinitelyAssigned(LocalVariableBinding local); /** - * Record a local got definitely assigned to an unknown value. + * Record a field or local got definitely assigned to an unknown value. */ -abstract public void markAsDefinitelyUnknown(LocalVariableBinding local); +abstract public void markAsDefinitelyUnknown(VariableBinding binding); /** - * Mark the null status of the given local according to the given status - * @param local + * Mark the null status of the given field or local according to the given status + * @param binding * @param nullStatus bitset of FLowInfo.UNKNOWN ... FlowInfo.POTENTIALLY_NON_NULL */ -public void markNullStatus(LocalVariableBinding local, int nullStatus) { +public void markNullStatus(VariableBinding binding, int nullStatus) { switch(nullStatus) { // definite status? case FlowInfo.UNKNOWN : - markAsDefinitelyUnknown(local); + markAsDefinitelyUnknown(binding); break; case FlowInfo.NULL : - markAsDefinitelyNull(local); + markAsDefinitelyNull(binding); break; case FlowInfo.NON_NULL : - markAsDefinitelyNonNull(local); + markAsDefinitelyNonNull(binding); break; default: // collect potential status: - resetNullInfo(local); + resetNullInfo(binding); if ((nullStatus & FlowInfo.POTENTIALLY_UNKNOWN) != 0) - markPotentiallyUnknownBit(local); + markPotentiallyUnknownBit(binding); if ((nullStatus & FlowInfo.POTENTIALLY_NULL) != 0) - markPotentiallyNullBit(local); + markPotentiallyNullBit(binding); if ((nullStatus & FlowInfo.POTENTIALLY_NON_NULL) != 0) - markPotentiallyNonNullBit(local); + markPotentiallyNonNullBit(binding); if ((nullStatus & (FlowInfo.POTENTIALLY_NULL|FlowInfo.POTENTIALLY_NON_NULL|FlowInfo.POTENTIALLY_UNKNOWN)) == 0) - markAsDefinitelyUnknown(local); + markAsDefinitelyUnknown(binding); } } /** - * Answer the null status of the given local - * @param local + * Answer the null status of the given field or local + * @param binding * @return bitset of FlowInfo.UNKNOWN ... FlowInfo.POTENTIALLY_NON_NULL */ -public int nullStatus(LocalVariableBinding local) { - if (isDefinitelyUnknown(local)) +public int nullStatus(VariableBinding binding) { + if (isDefinitelyUnknown(binding)) return FlowInfo.UNKNOWN; - if (isDefinitelyNull(local)) + if (isDefinitelyNull(binding)) return FlowInfo.NULL; - if (isDefinitelyNonNull(local)) + if (isDefinitelyNonNull(binding)) return FlowInfo.NON_NULL; int status = 0; - if (isPotentiallyUnknown(local)) + if (isPotentiallyUnknown(binding)) status |= FlowInfo.POTENTIALLY_UNKNOWN; - if (isPotentiallyNull(local)) + if (isPotentiallyNull(binding)) status |= FlowInfo.POTENTIALLY_NULL; - if (isPotentiallyNonNull(local)) + if (isPotentiallyNonNull(binding)) status |= FlowInfo.POTENTIALLY_NON_NULL; if (status > 0) return status; @@ -555,12 +567,12 @@ * where this variable is being checked against null */ // https://bugs.eclipse.org/bugs/show_bug.cgi?id=303448 -abstract public void markedAsNullOrNonNullInAssertExpression(LocalVariableBinding local); +abstract public void markedAsNullOrNonNullInAssertExpression(VariableBinding binding); /** * Returns true if the local variable being checked for was marked as null or not null * inside an assert expression due to comparison against null. */ //https://bugs.eclipse.org/bugs/show_bug.cgi?id=303448 -abstract public boolean isMarkedAsNullOrNonNullInAssertExpression(LocalVariableBinding local); +abstract public boolean isMarkedAsNullOrNonNullInAssertExpression(VariableBinding binding); } 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.51 diff -u -r1.51 LoopingFlowContext.java --- compiler/org/eclipse/jdt/internal/compiler/flow/LoopingFlowContext.java 23 Aug 2010 08:41:25 -0000 1.51 +++ compiler/org/eclipse/jdt/internal/compiler/flow/LoopingFlowContext.java 22 Dec 2010 18:19:14 -0000 @@ -42,7 +42,7 @@ VariableBinding finalVariables[]; int assignCount = 0; - LocalVariableBinding[] nullLocals; + VariableBinding[] nullVariables; Expression[] nullReferences; int[] nullCheckTypes; int nullCount; @@ -138,7 +138,7 @@ if ((this.tagBits & FlowContext.DEFER_NULL_DIAGNOSTIC) != 0) { // check only immutable null checks on innermost looping context for (int i = 0; i < this.nullCount; i++) { - LocalVariableBinding local = this.nullLocals[i]; + VariableBinding local = this.nullVariables[i]; Expression expression = this.nullReferences[i]; // final local variable switch (this.nullCheckTypes[i]) { @@ -148,11 +148,11 @@ this.nullReferences[i] = null; if (this.nullCheckTypes[i] == (CAN_ONLY_NON_NULL | IN_COMPARISON_NON_NULL)) { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableRedundantCheckOnNonNull(local, expression); + scope.problemReporter().variableRedundantCheckOnNonNull(local, expression); } } else { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableNonNullComparedToNull(local, expression); + scope.problemReporter().variableNonNullComparedToNull(local, expression); } } continue; @@ -164,11 +164,11 @@ this.nullReferences[i] = null; if (this.nullCheckTypes[i] == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableRedundantCheckOnNonNull(local, expression); + scope.problemReporter().variableRedundantCheckOnNonNull(local, expression); } } else { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableNonNullComparedToNull(local, expression); + scope.problemReporter().variableNonNullComparedToNull(local, expression); } } continue; @@ -177,11 +177,11 @@ this.nullReferences[i] = null; if (this.nullCheckTypes[i] == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL)) { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableRedundantCheckOnNull(local, expression); + scope.problemReporter().variableRedundantCheckOnNull(local, expression); } } else { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableNullComparedToNonNull(local, expression); + scope.problemReporter().variableNullComparedToNonNull(local, expression); } } continue; @@ -196,27 +196,27 @@ switch(this.nullCheckTypes[i] & CONTEXT_MASK) { case FlowContext.IN_COMPARISON_NULL: if (((this.nullCheckTypes[i] & CHECK_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning - scope.problemReporter().localVariableNullReference(local, expression); + scope.problemReporter().variableNullReference(local, expression); continue; } if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableRedundantCheckOnNull(local, expression); + scope.problemReporter().variableRedundantCheckOnNull(local, expression); } continue; case FlowContext.IN_COMPARISON_NON_NULL: if (((this.nullCheckTypes[i] & CHECK_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning - scope.problemReporter().localVariableNullReference(local, expression); + scope.problemReporter().variableNullReference(local, expression); continue; } if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableNullComparedToNonNull(local, expression); + scope.problemReporter().variableNullComparedToNonNull(local, expression); } continue; case FlowContext.IN_ASSIGNMENT: - scope.problemReporter().localVariableRedundantNullAssignment(local, expression); + scope.problemReporter().variableRedundantNullAssignment(local, expression); continue; case FlowContext.IN_INSTANCEOF: - scope.problemReporter().localVariableNullInstanceof(local, expression); + scope.problemReporter().variableNullInstanceof(local, expression); continue; } } else if (flowInfo.isPotentiallyNull(local)) { @@ -224,14 +224,14 @@ case FlowContext.IN_COMPARISON_NULL: this.nullReferences[i] = null; if (((this.nullCheckTypes[i] & CHECK_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning - scope.problemReporter().localVariablePotentialNullReference(local, expression); + scope.problemReporter().variablePotentialNullReference(local, expression); continue; } break; case FlowContext.IN_COMPARISON_NON_NULL: this.nullReferences[i] = null; if (((this.nullCheckTypes[i] & CHECK_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning - scope.problemReporter().localVariablePotentialNullReference(local, expression); + scope.problemReporter().variablePotentialNullReference(local, expression); continue; } break; @@ -241,7 +241,7 @@ case MAY_NULL: if (flowInfo.isDefinitelyNull(local)) { this.nullReferences[i] = null; - scope.problemReporter().localVariableNullReference(local, expression); + scope.problemReporter().variableNullReference(local, expression); continue; } break; @@ -257,7 +257,7 @@ for (int i = 0; i < this.nullCount; i++) { Expression expression = this.nullReferences[i]; // final local variable - LocalVariableBinding local = this.nullLocals[i]; + VariableBinding local = this.nullVariables[i]; switch (this.nullCheckTypes[i]) { case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL: case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL: @@ -265,11 +265,11 @@ this.nullReferences[i] = null; if (this.nullCheckTypes[i] == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableRedundantCheckOnNonNull(local, expression); + scope.problemReporter().variableRedundantCheckOnNonNull(local, expression); } } else { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableNonNullComparedToNull(local, expression); + scope.problemReporter().variableNonNullComparedToNull(local, expression); } } continue; @@ -284,27 +284,27 @@ switch(this.nullCheckTypes[i] & CONTEXT_MASK) { case FlowContext.IN_COMPARISON_NULL: if (((this.nullCheckTypes[i] & CHECK_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning - scope.problemReporter().localVariableNullReference(local, expression); + scope.problemReporter().variableNullReference(local, expression); continue; } if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableRedundantCheckOnNull(local, expression); + scope.problemReporter().variableRedundantCheckOnNull(local, expression); } continue; case FlowContext.IN_COMPARISON_NON_NULL: if (((this.nullCheckTypes[i] & CHECK_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning - scope.problemReporter().localVariableNullReference(local, expression); + scope.problemReporter().variableNullReference(local, expression); continue; } if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableNullComparedToNonNull(local, expression); + scope.problemReporter().variableNullComparedToNonNull(local, expression); } continue; case FlowContext.IN_ASSIGNMENT: - scope.problemReporter().localVariableRedundantNullAssignment(local, expression); + scope.problemReporter().variableRedundantNullAssignment(local, expression); continue; case FlowContext.IN_INSTANCEOF: - scope.problemReporter().localVariableNullInstanceof(local, expression); + scope.problemReporter().variableNullInstanceof(local, expression); continue; } } else if (flowInfo.isPotentiallyNull(local)) { @@ -312,14 +312,14 @@ case FlowContext.IN_COMPARISON_NULL: this.nullReferences[i] = null; if (((this.nullCheckTypes[i] & CHECK_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning - scope.problemReporter().localVariablePotentialNullReference(local, expression); + scope.problemReporter().variablePotentialNullReference(local, expression); continue; } break; case FlowContext.IN_COMPARISON_NON_NULL: this.nullReferences[i] = null; if (((this.nullCheckTypes[i] & CHECK_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning - scope.problemReporter().localVariablePotentialNullReference(local, expression); + scope.problemReporter().variablePotentialNullReference(local, expression); continue; } break; @@ -329,12 +329,12 @@ case MAY_NULL: if (flowInfo.isDefinitelyNull(local)) { this.nullReferences[i] = null; - scope.problemReporter().localVariableNullReference(local, expression); + scope.problemReporter().variableNullReference(local, expression); continue; } if (flowInfo.isPotentiallyNull(local)) { this.nullReferences[i] = null; - scope.problemReporter().localVariablePotentialNullReference(local, expression); + scope.problemReporter().variablePotentialNullReference(local, expression); continue; } break; @@ -458,27 +458,27 @@ return true; } -protected void recordNullReference(LocalVariableBinding local, +protected void recordNullReference(VariableBinding local, Expression expression, int status) { if (this.nullCount == 0) { - this.nullLocals = new LocalVariableBinding[5]; + this.nullVariables = new VariableBinding[5]; this.nullReferences = new Expression[5]; this.nullCheckTypes = new int[5]; } - else if (this.nullCount == this.nullLocals.length) { - System.arraycopy(this.nullLocals, 0, - this.nullLocals = new LocalVariableBinding[this.nullCount * 2], 0, this.nullCount); + else if (this.nullCount == this.nullVariables.length) { + System.arraycopy(this.nullVariables, 0, + this.nullVariables = new VariableBinding[this.nullCount * 2], 0, this.nullCount); System.arraycopy(this.nullReferences, 0, this.nullReferences = new Expression[this.nullCount * 2], 0, this.nullCount); System.arraycopy(this.nullCheckTypes, 0, this.nullCheckTypes = new int[this.nullCount * 2], 0, this.nullCount); } - this.nullLocals[this.nullCount] = local; + this.nullVariables[this.nullCount] = local; this.nullReferences[this.nullCount] = expression; this.nullCheckTypes[this.nullCount++] = status; } -public void recordUsingNullReference(Scope scope, LocalVariableBinding local, +public void recordUsingNullReference(Scope scope, VariableBinding local, Expression reference, int checkType, FlowInfo flowInfo) { if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0 || flowInfo.isDefinitelyUnknown(local)) { @@ -490,14 +490,14 @@ if (flowInfo.isDefinitelyNonNull(local)) { if (checkType == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableRedundantCheckOnNonNull(local, reference); + scope.problemReporter().variableRedundantCheckOnNonNull(local, reference); } if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) { flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE); } } else { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableNonNullComparedToNull(local, reference); + scope.problemReporter().variableNonNullComparedToNull(local, reference); } if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) { flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE); @@ -506,21 +506,21 @@ } else if (flowInfo.isDefinitelyNull(local)) { if (checkType == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL)) { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableRedundantCheckOnNull(local, reference); + scope.problemReporter().variableRedundantCheckOnNull(local, reference); } if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) { flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE); } } else { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableNullComparedToNonNull(local, reference); + scope.problemReporter().variableNullComparedToNonNull(local, reference); } if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) { flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE); } } } else if (this.upstreamNullFlowInfo.isDefinitelyNonNull(local) && !flowInfo.isPotentiallyNull(local)) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=291418 - flowInfo.markAsDefinitelyNonNull(local); + flowInfo.markAsDefinitelyNonNull(local ); if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { recordNullReference(local, reference, checkType); } @@ -546,11 +546,11 @@ switch(checkType & CONTEXT_MASK) { case FlowContext.IN_COMPARISON_NULL: if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning - scope.problemReporter().localVariableNullReference(local, reference); + scope.problemReporter().variableNullReference(local, reference); return; } if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableRedundantCheckOnNull(local, reference); + scope.problemReporter().variableRedundantCheckOnNull(local, reference); } if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) { flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE); @@ -558,34 +558,34 @@ return; case FlowContext.IN_COMPARISON_NON_NULL: if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning - scope.problemReporter().localVariableNullReference(local, reference); + scope.problemReporter().variableNullReference(local, reference); return; } if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { - scope.problemReporter().localVariableNullComparedToNonNull(local, reference); + scope.problemReporter().variableNullComparedToNonNull(local, reference); } if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) { flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE); } return; case FlowContext.IN_ASSIGNMENT: - scope.problemReporter().localVariableRedundantNullAssignment(local, reference); + scope.problemReporter().variableRedundantNullAssignment(local, reference); return; case FlowContext.IN_INSTANCEOF: - scope.problemReporter().localVariableNullInstanceof(local, reference); + scope.problemReporter().variableNullInstanceof(local, reference); return; } } else if (flowInfo.isPotentiallyNull(local)) { switch(checkType & CONTEXT_MASK) { case FlowContext.IN_COMPARISON_NULL: if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning - scope.problemReporter().localVariablePotentialNullReference(local, reference); + scope.problemReporter().variablePotentialNullReference(local, reference); return; } break; case FlowContext.IN_COMPARISON_NON_NULL: if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning - scope.problemReporter().localVariablePotentialNullReference(local, reference); + scope.problemReporter().variablePotentialNullReference(local, reference); return; } break; @@ -604,11 +604,11 @@ return; } if (flowInfo.isDefinitelyNull(local)) { - scope.problemReporter().localVariableNullReference(local, reference); + scope.problemReporter().variableNullReference(local, reference); return; } if (flowInfo.isPotentiallyNull(local)) { - scope.problemReporter().localVariablePotentialNullReference(local, reference); + scope.problemReporter().variablePotentialNullReference(local, reference); return; } recordNullReference(local, reference, checkType); Index: compiler/org/eclipse/jdt/internal/compiler/flow/NullInfoRegistry.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/NullInfoRegistry.java,v retrieving revision 1.11 diff -u -r1.11 NullInfoRegistry.java --- compiler/org/eclipse/jdt/internal/compiler/flow/NullInfoRegistry.java 19 Aug 2010 10:38:46 -0000 1.11 +++ compiler/org/eclipse/jdt/internal/compiler/flow/NullInfoRegistry.java 22 Dec 2010 18:19:14 -0000 @@ -11,7 +11,8 @@ *******************************************************************************/ package org.eclipse.jdt.internal.compiler.flow; -import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; +import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; +import org.eclipse.jdt.internal.compiler.lookup.VariableBinding; /** * A degenerate form of UnconditionalFlowInfo explicitly meant to capture @@ -116,13 +117,19 @@ return this; } -public void markAsComparedEqualToNonNull(LocalVariableBinding local) { +public void markAsComparedEqualToNonNull(VariableBinding local) { // protected from non-object locals in calling methods if (this != DEAD_END) { this.tagBits |= NULL_FLAG_MASK; int position; + if (local instanceof FieldBinding) { + this.markNullStatus(local, FlowInfo.POTENTIALLY_NON_NULL); + return; + } else { + position = local.id + this.maxFieldCount; + } // position is zero-based - if ((position = local.id + this.maxFieldCount) < BitCacheSize) { // use bits + if (position < BitCacheSize) { // use bits // set protected non null this.nullBit1 |= (1L << position); if (COVERAGE_TEST_FLAG) { @@ -161,13 +168,19 @@ } } -public void markAsDefinitelyNonNull(LocalVariableBinding local) { +public void markAsDefinitelyNonNull(VariableBinding local) { // protected from non-object locals in calling methods if (this != DEAD_END) { this.tagBits |= NULL_FLAG_MASK; int position; // position is zero-based - if ((position = local.id + this.maxFieldCount) < BitCacheSize) { // use bits + if (local instanceof FieldBinding) { + this.markNullStatus(local, FlowInfo.POTENTIALLY_NON_NULL); + return; + } else { + position = local.id + this.maxFieldCount; + } + if (position < BitCacheSize) { // use bits // set assigned non null this.nullBit3 |= (1L << position); if (COVERAGE_TEST_FLAG) { @@ -207,13 +220,19 @@ } // PREMATURE consider ignoring extra 0 to 2 included - means a1 should not be used either // PREMATURE project protected non null onto something else -public void markAsDefinitelyNull(LocalVariableBinding local) { +public void markAsDefinitelyNull(VariableBinding local) { // protected from non-object locals in calling methods if (this != DEAD_END) { this.tagBits |= NULL_FLAG_MASK; int position; + if (local instanceof FieldBinding) { + this.markNullStatus(local, FlowInfo.POTENTIALLY_NULL); + return; + } else { + position = local.id + this.maxFieldCount; + } // position is zero-based - if ((position = local.id + this.maxFieldCount) < BitCacheSize) { // use bits + if (position < BitCacheSize) { // use bits // set assigned null this.nullBit2 |= (1L << position); if (COVERAGE_TEST_FLAG) { @@ -252,13 +271,18 @@ } } -public void markAsDefinitelyUnknown(LocalVariableBinding local) { +public void markAsDefinitelyUnknown(VariableBinding local) { // protected from non-object locals in calling methods if (this != DEAD_END) { this.tagBits |= NULL_FLAG_MASK; int position; + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } // position is zero-based - if ((position = local.id + this.maxFieldCount) < BitCacheSize) { // use bits + if (position < BitCacheSize) { // use bits // set assigned unknown this.nullBit4 |= (1L << position); if (COVERAGE_TEST_FLAG) { @@ -379,6 +403,133 @@ return source; } +public void markPotentiallyNullBit(VariableBinding local) { + if (this != DEAD_END) { + this.tagBits |= NULL_FLAG_MASK; + int position; + long mask; + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } + if (position < BitCacheSize) { + // use bits + mask = 1L << position; + isTrue((this.nullBit1 & mask) == 0, "Adding 'potentially null' mark in unexpected state"); //$NON-NLS-1$ + this.nullBit2 |= 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 = 2; j < extraLength; j++) { + this.extra[j] = new long[length]; + } + } + else { + int oldLength; // might need to grow the arrays + if (vectorIndex >= (oldLength = this.extra[2].length)) { + for (int j = 2; j < extraLength; j++) { + System.arraycopy(this.extra[j], 0, + (this.extra[j] = new long[vectorIndex + 1]), 0, + oldLength); + } + } + } + mask = 1L << (position % BitCacheSize); + this.extra[3][vectorIndex] |= mask; + isTrue((this.extra[2][vectorIndex] & mask) == 0, "Adding 'potentially null' mark in unexpected state"); //$NON-NLS-1$ + } + } +} + +public void markPotentiallyNonNullBit(VariableBinding local) { + if (this != DEAD_END) { + this.tagBits |= NULL_FLAG_MASK; + int position; + long mask; + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } + if (position < BitCacheSize) { + // use bits + mask = 1L << position; + isTrue((this.nullBit1 & mask) == 0, "Adding 'potentially non-null' mark in unexpected state"); //$NON-NLS-1$ + this.nullBit3 |= 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 = 2; j < extraLength; j++) { + this.extra[j] = new long[length]; + } + } + else { + int oldLength; // might need to grow the arrays + if (vectorIndex >= (oldLength = this.extra[2].length)) { + for (int j = 2; j < extraLength; j++) { + System.arraycopy(this.extra[j], 0, + (this.extra[j] = new long[vectorIndex + 1]), 0, + oldLength); + } + } + } + mask = 1L << (position % BitCacheSize); + isTrue((this.extra[2][vectorIndex] & mask) == 0, "Adding 'potentially non-null' mark in unexpected state"); //$NON-NLS-1$ + this.extra[4][vectorIndex] |= mask; + } + } +} + +public void markPotentiallyUnknownBit(VariableBinding local) { + // protected from non-object locals in calling methods + if (this != DEAD_END) { + this.tagBits |= NULL_FLAG_MASK; + int position; + long mask; + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } + if (position < BitCacheSize) { + // use bits + mask = 1L << position; + isTrue((this.nullBit1 & mask) == 0, "Adding 'unknown' mark in unexpected state"); //$NON-NLS-1$ + this.nullBit4 |= 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 = 2; j < extraLength; j++) { + this.extra[j] = new long[length]; + } + } + else { + int oldLength; // might need to grow the arrays + if (vectorIndex >= (oldLength = this.extra[2].length)) { + for (int j = 2; j < extraLength; j++) { + System.arraycopy(this.extra[j], 0, + (this.extra[j] = new long[vectorIndex + 1]), 0, + oldLength); + } + } + } + mask = 1L << (position % BitCacheSize); + isTrue((this.extra[2][vectorIndex] & mask) == 0, "Adding 'unknown' mark in unexpected state"); //$NON-NLS-1$ + this.extra[5][vectorIndex] |= mask; + } + } +} + public String toString(){ if (this.extra == null) { return "NullInfoRegistry<" + this.nullBit1 //$NON-NLS-1$ 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.71 diff -u -r1.71 UnconditionalFlowInfo.java --- compiler/org/eclipse/jdt/internal/compiler/flow/UnconditionalFlowInfo.java 16 Dec 2010 13:02:30 -0000 1.71 +++ compiler/org/eclipse/jdt/internal/compiler/flow/UnconditionalFlowInfo.java 22 Dec 2010 18:19:14 -0000 @@ -18,6 +18,7 @@ import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; import org.eclipse.jdt.internal.compiler.lookup.TagBits; +import org.eclipse.jdt.internal.compiler.lookup.VariableBinding; /** * Record initialization status during definite assignment analysis @@ -82,7 +83,6 @@ // arrays which have the same size public int maxFieldCount; // limit between fields and locals - // Constants public static final int BitCacheSize = 64; // 64 bits in a long. public int[] nullStatusChangedInAssert; // https://bugs.eclipse.org/bugs/show_bug.cgi?id=303448 @@ -515,13 +515,18 @@ return this; } -final public boolean cannotBeDefinitelyNullOrNonNull(LocalVariableBinding local) { +final public boolean cannotBeDefinitelyNullOrNonNull(VariableBinding 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) { + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } + if (position < BitCacheSize) { // use bits return ( (~this.nullBit1 @@ -546,13 +551,18 @@ & (1L << (position % BitCacheSize))) != 0; } -final public boolean cannotBeNull(LocalVariableBinding local) { +final public boolean cannotBeNull(VariableBinding 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) { + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } + if (position < BitCacheSize) { // use bits return (this.nullBit1 & this.nullBit3 & ((this.nullBit2 & this.nullBit4) | ~this.nullBit2) @@ -573,13 +583,18 @@ & (1L << (position % BitCacheSize))) != 0; } -final public boolean canOnlyBeNull(LocalVariableBinding local) { +final public boolean canOnlyBeNull(VariableBinding 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) { + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } + if (position < BitCacheSize) { // use bits return (this.nullBit1 & this.nullBit2 & (~this.nullBit3 | ~this.nullBit4) @@ -745,7 +760,13 @@ return isDefinitelyAssigned(local.id + this.maxFieldCount); } -final public boolean isDefinitelyNonNull(LocalVariableBinding local) { +final public boolean isDefinitelyNonNull(VariableBinding local) { + if (local instanceof FieldBinding && (this.tagBits & NULL_FLAG_MASK) == 0) { + // no local yet in scope. Came here because of a field being queried for non null + // will only happen for final fields, since they are assigned in a constructor or static block + // and we may currently be in some other method + this.tagBits |= NULL_FLAG_MASK; + } // do not want to complain in unreachable code if ((this.tagBits & UNREACHABLE) != 0 || (this.tagBits & NULL_FLAG_MASK) == 0) { @@ -755,7 +776,16 @@ local.constant() != Constant.NotAConstant) { // String instances return true; } - int position = local.id + this.maxFieldCount; + int position; + if (local instanceof FieldBinding) { + if (local.isFinal() && ((FieldBinding)local).isStatic()) { + // static final field's null status may not be in the flow info + return (((FieldBinding) local).getNullStatusForStaticFinalField() == FlowInfo.NON_NULL); + } + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } if (position < BitCacheSize) { // use bits return ((this.nullBit1 & this.nullBit3 & (~this.nullBit2 | this.nullBit4)) & (1L << position)) != 0; @@ -774,14 +804,29 @@ & (1L << (position % BitCacheSize))) != 0; } -final public boolean isDefinitelyNull(LocalVariableBinding local) { +final public boolean isDefinitelyNull(VariableBinding local) { + if (local instanceof FieldBinding && (this.tagBits & NULL_FLAG_MASK) == 0) { + // no local yet in scope. Came here because of a field being queried for non null + // will only happen for final fields, since they are assigned in a constructor or static block + // and we may currently be in some other method + this.tagBits |= NULL_FLAG_MASK; + } // 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; + int position; + if (local instanceof FieldBinding) { + if (local.isFinal() && ((FieldBinding)local).isStatic()) { + // static final field's null status may not be in the flow info + return (((FieldBinding) local).getNullStatusForStaticFinalField() == FlowInfo.NULL); + } + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } if (position < BitCacheSize) { // use bits return ((this.nullBit1 & this.nullBit2 & (~this.nullBit3 | ~this.nullBit4)) @@ -801,13 +846,18 @@ & (1L << (position % BitCacheSize))) != 0; } -final public boolean isDefinitelyUnknown(LocalVariableBinding local) { +final public boolean isDefinitelyUnknown(VariableBinding 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; + int position; + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } if (position < BitCacheSize) { // use bits return ((this.nullBit1 & this.nullBit4 & ~this.nullBit2 & ~this.nullBit3) & (1L << position)) != 0; @@ -860,13 +910,18 @@ return isPotentiallyAssigned(local.id + this.maxFieldCount); } -final public boolean isPotentiallyNonNull(LocalVariableBinding local) { +final public boolean isPotentiallyNonNull(VariableBinding 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) { + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } + if (position < BitCacheSize) { // use bits return ((this.nullBit3 & (~this.nullBit1 | ~this.nullBit2)) & (1L << position)) != 0; @@ -885,13 +940,22 @@ & (1L << (position % BitCacheSize))) != 0; } -final public boolean isPotentiallyNull(LocalVariableBinding local) { +final public boolean isPotentiallyNull(VariableBinding 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) { + if (local instanceof FieldBinding) { + if (local.isFinal() && ((FieldBinding)local).isStatic()) { + // static final field's null status may not be in the flow info + return (((FieldBinding) local).getNullStatusForStaticFinalField() == FlowInfo.POTENTIALLY_NULL); + } + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } + if (position < BitCacheSize) { // use bits return ((this.nullBit2 & (~this.nullBit1 | ~this.nullBit3)) & (1L << position)) != 0; @@ -910,13 +974,18 @@ & (1L << (position % BitCacheSize))) != 0; } -final public boolean isPotentiallyUnknown(LocalVariableBinding local) { +final public boolean isPotentiallyUnknown(VariableBinding 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; + int position; + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } if (position < BitCacheSize) { // use bits return (this.nullBit4 & (~this.nullBit1 | ~this.nullBit2 & ~this.nullBit3) @@ -937,13 +1006,18 @@ & (1L << (position % BitCacheSize))) != 0; } -final public boolean isProtectedNonNull(LocalVariableBinding local) { +final public boolean isProtectedNonNull(VariableBinding 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) { + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } + if (position < BitCacheSize) { // use bits return (this.nullBit1 & this.nullBit3 & this.nullBit4 & (1L << position)) != 0; } @@ -962,13 +1036,18 @@ & (1L << (position % BitCacheSize))) != 0; } -final public boolean isProtectedNull(LocalVariableBinding local) { +final public boolean isProtectedNull(VariableBinding 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) { + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } + if (position < BitCacheSize) { // use bits return (this.nullBit1 & this.nullBit2 & (this.nullBit3 ^ this.nullBit4) @@ -996,20 +1075,26 @@ * @return true if the check passes (does not return * if the check fails) */ -private static boolean isTrue(boolean expression, String message) { +protected static boolean isTrue(boolean expression, String message) { if (!expression) throw new AssertionFailedException("assertion failed: " + message); //$NON-NLS-1$ return expression; } -public void markAsComparedEqualToNonNull(LocalVariableBinding local) { +public void markAsComparedEqualToNonNull(VariableBinding local) { // protected from non-object locals in calling methods if (this != DEAD_END) { this.tagBits |= NULL_FLAG_MASK; int position; + if (local instanceof FieldBinding) { + this.markNullStatus(local, FlowInfo.POTENTIALLY_NON_NULL); + return; + } else { + position = local.id + this.maxFieldCount; + } long mask; long a1, a2, a3, a4, na2; // position is zero-based - if ((position = local.id + this.maxFieldCount) < BitCacheSize) { + if (position < BitCacheSize) { // use bits if (((mask = 1L << position) & (a1 = this.nullBit1) @@ -1098,14 +1183,20 @@ } } -public void markAsComparedEqualToNull(LocalVariableBinding local) { +public void markAsComparedEqualToNull(VariableBinding 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) { + if (local instanceof FieldBinding) { + this.markNullStatus(local, FlowInfo.POTENTIALLY_NULL); + return; + } else { + position = local.id + this.maxFieldCount; + } + if (position < BitCacheSize) { // use bits if (((mask = 1L << position) & this.nullBit1) != 0) { if ((mask @@ -1236,14 +1327,20 @@ markAsDefinitelyAssigned(local.id + this.maxFieldCount); } -public void markAsDefinitelyNonNull(LocalVariableBinding local) { +public void markAsDefinitelyNonNull(VariableBinding local) { // protected from non-object locals in calling methods if (this != DEAD_END) { this.tagBits |= NULL_FLAG_MASK; long mask; int position; + if (local instanceof FieldBinding) { + this.markNullStatus(local, FlowInfo.POTENTIALLY_NON_NULL); + return; + } else { + position = local.id + this.maxFieldCount; + } // position is zero-based - if ((position = local.id + this.maxFieldCount) < BitCacheSize) { // use bits + if (position < BitCacheSize) { // use bits // set assigned non null this.nullBit1 |= (mask = 1L << position); this.nullBit3 |= mask; @@ -1258,8 +1355,25 @@ } else { // use extra vector - int vectorIndex ; - this.extra[2][vectorIndex = (position / BitCacheSize) - 1] + 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); + } + } + } + this.extra[2][vectorIndex] |= (mask = 1L << (position % BitCacheSize)); this.extra[4][vectorIndex] |= mask; this.extra[3][vectorIndex] &= (mask = ~mask); @@ -1273,14 +1387,20 @@ } } -public void markAsDefinitelyNull(LocalVariableBinding local) { +public void markAsDefinitelyNull(VariableBinding local) { // protected from non-object locals in calling methods if (this != DEAD_END) { this.tagBits |= NULL_FLAG_MASK; long mask; int position; + if (local instanceof FieldBinding) { + this.markNullStatus(local, FlowInfo.POTENTIALLY_NULL); + return; + } else { + position = local.id + this.maxFieldCount; + } // position is zero-based - if ((position = local.id + this.maxFieldCount) < BitCacheSize) { // use bits + if (position < BitCacheSize) { // use bits // mark assigned null this.nullBit1 |= (mask = 1L << position); this.nullBit2 |= mask; @@ -1295,8 +1415,25 @@ } else { // use extra vector - int vectorIndex ; - this.extra[2][vectorIndex = (position / BitCacheSize) - 1] + 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); + } + } + } + this.extra[2][vectorIndex] |= (mask = 1L << (position % BitCacheSize)); this.extra[3][vectorIndex] |= mask; this.extra[4][vectorIndex] &= (mask = ~mask); @@ -1316,14 +1453,19 @@ */ // PREMATURE may try to get closer to markAsDefinitelyAssigned, but not // obvious -public void markAsDefinitelyUnknown(LocalVariableBinding local) { +public void markAsDefinitelyUnknown(VariableBinding local) { // protected from non-object locals in calling methods if (this != DEAD_END) { this.tagBits |= NULL_FLAG_MASK; long mask; int position; + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } // position is zero-based - if ((position = local.id + this.maxFieldCount) < BitCacheSize) { + if (position < BitCacheSize) { // use bits // mark assigned null this.nullBit1 |= (mask = 1L << position); @@ -1339,8 +1481,25 @@ } else { // use extra vector - int vectorIndex ; - this.extra[2][vectorIndex = (position / BitCacheSize) - 1] + 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); + } + } + } + this.extra[2][vectorIndex] |= (mask = 1L << (position % BitCacheSize)); this.extra[5][vectorIndex] |= mask; this.extra[3][vectorIndex] &= (mask = ~mask); @@ -1354,12 +1513,17 @@ } } -public void resetNullInfo(LocalVariableBinding local) { +public void resetNullInfo(VariableBinding local) { if (this != DEAD_END) { this.tagBits |= NULL_FLAG_MASK; int position; long mask; - if ((position = local.id + this.maxFieldCount) < BitCacheSize) { + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } + if (position < BitCacheSize) { // use bits this.nullBit1 &= (mask = ~(1L << position)); this.nullBit2 &= mask; @@ -1367,8 +1531,12 @@ this.nullBit4 &= mask; } else { // use extra vector - int vectorIndex ; - this.extra[2][vectorIndex = (position / BitCacheSize) - 1] + int vectorIndex = (position / BitCacheSize) - 1; + if (this.extra == null || vectorIndex >= this.extra[2].length) { + // Some field which hasnt yet been encountered in null analysis is getting its null info reset + return; + } + this.extra[2][vectorIndex] &= (mask = ~(1L << (position % BitCacheSize))); this.extra[3][vectorIndex] &= mask; this.extra[4][vectorIndex] &= mask; @@ -1377,17 +1545,54 @@ } } +public void resetNullInfoForFields() { + if (this != DEAD_END) { + long mask; + if (this.maxFieldCount < BitCacheSize) { + // use bits + this.nullBit1 &= (mask = -1L << this.maxFieldCount); + this.nullBit2 &= mask; + this.nullBit3 &= mask; + this.nullBit4 &= mask; + } + else { + this.nullBit1 &= (mask = 0L); + this.nullBit2 &= mask; + this.nullBit3 &= mask; + this.nullBit4 &= mask; + if (this.extra != null){ + for (int position = BitCacheSize; position < this.maxFieldCount; position++) { + // use extra vector + int vectorIndex = (position / BitCacheSize) - 1; + if (vectorIndex >= this.extra[2].length) + break; // No null info about fields beyond this point in the extra vector + this.extra[2][vectorIndex] + &= (mask = ~(1L << (position % BitCacheSize))); + this.extra[3][vectorIndex] &= mask; + this.extra[4][vectorIndex] &= mask; + this.extra[5][vectorIndex] &= mask; + } + } + } + } +} + /** * Mark a local as potentially having been assigned to an unknown value. * @param local the local to mark */ -public void markPotentiallyUnknownBit(LocalVariableBinding local) { +public void markPotentiallyUnknownBit(VariableBinding local) { // protected from non-object locals in calling methods if (this != DEAD_END) { this.tagBits |= NULL_FLAG_MASK; int position; long mask; - if ((position = local.id + this.maxFieldCount) < BitCacheSize) { + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } + if (position < BitCacheSize) { // use bits mask = 1L << position; isTrue((this.nullBit1 & mask) == 0, "Adding 'unknown' mark in unexpected state"); //$NON-NLS-1$ @@ -1399,7 +1604,24 @@ } } else { // use extra vector - int vectorIndex = (position / BitCacheSize) - 1; + 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); + } + } + } mask = 1L << (position % BitCacheSize); isTrue((this.extra[2][vectorIndex] & mask) == 0, "Adding 'unknown' mark in unexpected state"); //$NON-NLS-1$ this.extra[5][vectorIndex] |= mask; @@ -1412,12 +1634,17 @@ } } -public void markPotentiallyNullBit(LocalVariableBinding local) { +public void markPotentiallyNullBit(VariableBinding local) { if (this != DEAD_END) { this.tagBits |= NULL_FLAG_MASK; int position; long mask; - if ((position = local.id + this.maxFieldCount) < BitCacheSize) { + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } + if (position < BitCacheSize) { // use bits mask = 1L << position; isTrue((this.nullBit1 & mask) == 0, "Adding 'potentially null' mark in unexpected state"); //$NON-NLS-1$ @@ -1429,7 +1656,24 @@ } } else { // use extra vector - int vectorIndex = (position / BitCacheSize) - 1; + 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); + } + } + } mask = 1L << (position % BitCacheSize); this.extra[3][vectorIndex] |= mask; isTrue((this.extra[2][vectorIndex] & mask) == 0, "Adding 'potentially null' mark in unexpected state"); //$NON-NLS-1$ @@ -1442,12 +1686,17 @@ } } -public void markPotentiallyNonNullBit(LocalVariableBinding local) { +public void markPotentiallyNonNullBit(VariableBinding local) { if (this != DEAD_END) { this.tagBits |= NULL_FLAG_MASK; int position; long mask; - if ((position = local.id + this.maxFieldCount) < BitCacheSize) { + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } + if (position < BitCacheSize) { // use bits mask = 1L << position; isTrue((this.nullBit1 & mask) == 0, "Adding 'potentially non-null' mark in unexpected state"); //$NON-NLS-1$ @@ -1459,7 +1708,24 @@ } } else { // use extra vector - int vectorIndex = (position / BitCacheSize) - 1; + 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); + } + } + } mask = 1L << (position % BitCacheSize); isTrue((this.extra[2][vectorIndex] & mask) == 0, "Adding 'potentially non-null' mark in unexpected state"); //$NON-NLS-1$ this.extra[4][vectorIndex] |= mask; @@ -1951,8 +2217,13 @@ return this; } -public void markedAsNullOrNonNullInAssertExpression(LocalVariableBinding local) { - int position = local.id + this.maxFieldCount; +public void markedAsNullOrNonNullInAssertExpression(VariableBinding binding) { + int position; + if (binding instanceof FieldBinding) { + position = binding.id; + } else { + position = binding.id + this.maxFieldCount; + } int oldLength; if (this.nullStatusChangedInAssert == null) { this.nullStatusChangedInAssert = new int[position + 1]; @@ -1965,8 +2236,13 @@ this.nullStatusChangedInAssert[position] = 1; } -public boolean isMarkedAsNullOrNonNullInAssertExpression(LocalVariableBinding local) { - int position = local.id + this.maxFieldCount; +public boolean isMarkedAsNullOrNonNullInAssertExpression(VariableBinding binding) { + int position; + if (binding instanceof FieldBinding) { + position = binding.id; + } else { + position = binding.id + this.maxFieldCount; + } if(this.nullStatusChangedInAssert == null || position >= this.nullStatusChangedInAssert.length) { return false; } Index: compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java,v retrieving revision 1.131 diff -u -r1.131 BinaryTypeBinding.java --- compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java 19 Nov 2010 14:22:00 -0000 1.131 +++ compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java 22 Dec 2010 18:19:14 -0000 @@ -1220,4 +1220,8 @@ MethodBinding[] unResolvedMethods() { // for the MethodVerifier so it doesn't resolve types return this.methods; } + +public FieldBinding[] unResolvedFields() { + return this.fields; +} } Index: compiler/org/eclipse/jdt/internal/compiler/lookup/FieldBinding.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/FieldBinding.java,v retrieving revision 1.60 diff -u -r1.60 FieldBinding.java --- compiler/org/eclipse/jdt/internal/compiler/lookup/FieldBinding.java 22 Oct 2010 22:42:56 -0000 1.60 +++ compiler/org/eclipse/jdt/internal/compiler/lookup/FieldBinding.java 22 Dec 2010 18:19:14 -0000 @@ -16,11 +16,13 @@ import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; +import org.eclipse.jdt.internal.compiler.flow.FlowInfo; import org.eclipse.jdt.internal.compiler.impl.Constant; public class FieldBinding extends VariableBinding { public ReferenceBinding declaringClass; public int compoundUseFlag = 0; // number or accesses via postIncrement or compoundAssignment + private int nullStatus; protected FieldBinding() { super(null, null, 0, null); @@ -29,12 +31,14 @@ public FieldBinding(char[] name, TypeBinding type, int modifiers, ReferenceBinding declaringClass, Constant constant) { super(name, type, modifiers, constant); this.declaringClass = declaringClass; + this.nullStatus = FlowInfo.UNKNOWN; } // special API used to change field declaring class for runtime visibility check public FieldBinding(FieldBinding initialFieldBinding, ReferenceBinding declaringClass) { super(initialFieldBinding.name, initialFieldBinding.type, initialFieldBinding.modifiers, initialFieldBinding.constant()); this.declaringClass = declaringClass; this.id = initialFieldBinding.id; + this.nullStatus = FlowInfo.UNKNOWN; setAnnotations(initialFieldBinding.getAnnotations()); } /* API @@ -386,4 +390,12 @@ } return null; } + +public int getNullStatusForStaticFinalField() { + return this.nullStatus; +} + +public void setNullStatusForStaticFinalField(int nullStatusToMark) { + this.nullStatus = nullStatusToMark; +} } Index: compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java,v retrieving revision 1.119 diff -u -r1.119 ParameterizedTypeBinding.java --- compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java 2 Nov 2010 16:10:54 -0000 1.119 +++ compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java 22 Dec 2010 18:19:15 -0000 @@ -1115,4 +1115,8 @@ } return Binding.NO_TYPE_VARIABLES; } + + public FieldBinding[] unResolvedFields() { + return this.fields; + } } Index: compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java,v retrieving revision 1.139 diff -u -r1.139 ReferenceBinding.java --- compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java 3 Feb 2010 06:34:21 -0000 1.139 +++ compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java 22 Dec 2010 18:19:15 -0000 @@ -1376,4 +1376,8 @@ MethodBinding[] unResolvedMethods() { // for the MethodVerifier so it doesn't resolve types return methods(); } + +public FieldBinding[] unResolvedFields() { + return Binding.NO_FIELDS; +} } 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.428 diff -u -r1.428 ProblemReporter.java --- compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java 17 Dec 2010 09:38:53 -0000 1.428 +++ compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java 22 Dec 2010 18:19:16 -0000 @@ -103,6 +103,7 @@ import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.eclipse.jdt.internal.compiler.lookup.TypeIds; import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding; +import org.eclipse.jdt.internal.compiler.lookup.VariableBinding; import org.eclipse.jdt.internal.compiler.lookup.WildcardBinding; import org.eclipse.jdt.internal.compiler.parser.JavadocTagConstants; import org.eclipse.jdt.internal.compiler.parser.Parser; @@ -280,17 +281,25 @@ return CompilerOptions.VarargsArgumentNeedCast; case IProblem.NullLocalVariableReference: + case IProblem.NullFieldReference: return CompilerOptions.NullReference; case IProblem.PotentialNullLocalVariableReference: + case IProblem.PotentialNullFieldReference: return CompilerOptions.PotentialNullReference; case IProblem.RedundantLocalVariableNullAssignment: + case IProblem.RedundantFieldNullAssignment: case IProblem.RedundantNullCheckOnNonNullLocalVariable: case IProblem.RedundantNullCheckOnNullLocalVariable: case IProblem.NonNullLocalVariableComparisonYieldsFalse: case IProblem.NullLocalVariableComparisonYieldsFalse: case IProblem.NullLocalVariableInstanceofYieldsFalse: + case IProblem.NullFieldInstanceofYieldsFalse: + case IProblem.RedundantNullCheckOnNonNullField: + case IProblem.RedundantNullCheckOnNullField: + case IProblem.NonNullFieldComparisonYieldsFalse: + case IProblem.NullFieldComparisonYieldsFalse: return CompilerOptions.RedundantNullCheck; case IProblem.BoxingConversion : @@ -4866,108 +4875,156 @@ } } -public void localVariableNonNullComparedToNull(LocalVariableBinding local, ASTNode location) { - int severity = computeSeverity(IProblem.NonNullLocalVariableComparisonYieldsFalse); +public void variableNonNullComparedToNull(VariableBinding variable, ASTNode location) { + int problem; + if (variable instanceof FieldBinding) { + problem = IProblem.NonNullFieldComparisonYieldsFalse; + } else { + problem = IProblem.NonNullLocalVariableComparisonYieldsFalse; + } + int severity = computeSeverity(problem); if (severity == ProblemSeverities.Ignore) return; - String[] arguments = new String[] {new String(local.name) }; + String[] arguments = new String[] {new String(variable.name) }; this.handle( - IProblem.NonNullLocalVariableComparisonYieldsFalse, + problem, arguments, arguments, severity, - nodeSourceStart(local, location), - nodeSourceEnd(local, location)); + nodeSourceStart(variable, location), + nodeSourceEnd(variable, location)); } -public void localVariableNullComparedToNonNull(LocalVariableBinding local, ASTNode location) { - int severity = computeSeverity(IProblem.NullLocalVariableComparisonYieldsFalse); +public void variableNullComparedToNonNull(VariableBinding variable, ASTNode location) { + int problem; + if (variable instanceof FieldBinding) { + problem = IProblem.NullFieldComparisonYieldsFalse; + } else { + problem = IProblem.NullLocalVariableComparisonYieldsFalse; + } + int severity = computeSeverity(problem); if (severity == ProblemSeverities.Ignore) return; - String[] arguments = new String[] {new String(local.name) }; + String[] arguments = new String[] {new String(variable.name) }; this.handle( - IProblem.NullLocalVariableComparisonYieldsFalse, + problem, arguments, arguments, severity, - nodeSourceStart(local, location), - nodeSourceEnd(local, location)); + nodeSourceStart(variable, location), + nodeSourceEnd(variable, location)); } -public void localVariableNullInstanceof(LocalVariableBinding local, ASTNode location) { - int severity = computeSeverity(IProblem.NullLocalVariableInstanceofYieldsFalse); +public void variableNullInstanceof(VariableBinding variable, ASTNode location) { + int problem; + if (variable instanceof FieldBinding) { + problem = IProblem.NullFieldInstanceofYieldsFalse; + } else { + problem = IProblem.NullLocalVariableInstanceofYieldsFalse; + } + int severity = computeSeverity(problem); if (severity == ProblemSeverities.Ignore) return; - String[] arguments = new String[] {new String(local.name) }; + String[] arguments = new String[] {new String(variable.name) }; this.handle( - IProblem.NullLocalVariableInstanceofYieldsFalse, + problem, arguments, arguments, severity, - nodeSourceStart(local, location), - nodeSourceEnd(local, location)); + nodeSourceStart(variable, location), + nodeSourceEnd(variable, location)); } -public void localVariableNullReference(LocalVariableBinding local, ASTNode location) { - int severity = computeSeverity(IProblem.NullLocalVariableReference); +public void variableNullReference(VariableBinding variable, ASTNode location) { + int problem; + if (variable instanceof FieldBinding) { + problem = IProblem.NullFieldReference; + } else { + problem = IProblem.NullLocalVariableReference; + } + int severity = computeSeverity(problem); if (severity == ProblemSeverities.Ignore) return; - String[] arguments = new String[] {new String(local.name) }; + String[] arguments = new String[] {new String(variable.name) }; this.handle( - IProblem.NullLocalVariableReference, + problem, arguments, arguments, severity, - nodeSourceStart(local, location), - nodeSourceEnd(local, location)); + nodeSourceStart(variable, location), + nodeSourceEnd(variable, location)); } -public void localVariablePotentialNullReference(LocalVariableBinding local, ASTNode location) { - int severity = computeSeverity(IProblem.PotentialNullLocalVariableReference); +public void variablePotentialNullReference(VariableBinding variable, ASTNode location) { + int problem; + if (variable instanceof FieldBinding) { + problem = IProblem.PotentialNullFieldReference; + } else { + problem = IProblem.PotentialNullLocalVariableReference; + } + int severity = computeSeverity(problem); if (severity == ProblemSeverities.Ignore) return; - String[] arguments = new String[] {new String(local.name)}; + String[] arguments = new String[] {new String(variable.name) }; this.handle( - IProblem.PotentialNullLocalVariableReference, + problem, arguments, arguments, severity, - nodeSourceStart(local, location), - nodeSourceEnd(local, location)); + nodeSourceStart(variable, location), + nodeSourceEnd(variable, location)); } -public void localVariableRedundantCheckOnNonNull(LocalVariableBinding local, ASTNode location) { - int severity = computeSeverity(IProblem.RedundantNullCheckOnNonNullLocalVariable); +public void variableRedundantCheckOnNonNull(VariableBinding variable, ASTNode location) { + int problem; + if (variable instanceof FieldBinding) { + problem = IProblem.RedundantNullCheckOnNonNullField; + } else { + problem = IProblem.RedundantNullCheckOnNonNullLocalVariable; + } + int severity = computeSeverity(problem); if (severity == ProblemSeverities.Ignore) return; - String[] arguments = new String[] {new String(local.name) }; + String[] arguments = new String[] {new String(variable.name) }; this.handle( - IProblem.RedundantNullCheckOnNonNullLocalVariable, + problem, arguments, arguments, severity, - nodeSourceStart(local, location), - nodeSourceEnd(local, location)); + nodeSourceStart(variable, location), + nodeSourceEnd(variable, location)); } -public void localVariableRedundantCheckOnNull(LocalVariableBinding local, ASTNode location) { - int severity = computeSeverity(IProblem.RedundantNullCheckOnNullLocalVariable); +public void variableRedundantCheckOnNull (VariableBinding variable, ASTNode location) { + int problem; + if (variable instanceof FieldBinding) { + problem = IProblem.RedundantNullCheckOnNullField; + } else { + problem = IProblem.RedundantNullCheckOnNullLocalVariable; + } + int severity = computeSeverity(problem); if (severity == ProblemSeverities.Ignore) return; - String[] arguments = new String[] {new String(local.name) }; + String[] arguments = new String[] {new String(variable.name) }; this.handle( - IProblem.RedundantNullCheckOnNullLocalVariable, + problem, arguments, arguments, severity, - nodeSourceStart(local, location), - nodeSourceEnd(local, location)); + nodeSourceStart(variable, location), + nodeSourceEnd(variable, location)); } -public void localVariableRedundantNullAssignment(LocalVariableBinding local, ASTNode location) { - int severity = computeSeverity(IProblem.RedundantLocalVariableNullAssignment); +public void variableRedundantNullAssignment (VariableBinding variable, ASTNode location) { + int problem; + if (variable instanceof FieldBinding) { + problem = IProblem.RedundantFieldNullAssignment; + } else { + problem = IProblem.RedundantLocalVariableNullAssignment; + } + int severity = computeSeverity(problem); if (severity == ProblemSeverities.Ignore) return; - String[] arguments = new String[] {new String(local.name) }; + String[] arguments = new String[] {new String(variable.name) }; this.handle( - IProblem.RedundantLocalVariableNullAssignment, + problem, arguments, arguments, severity, - nodeSourceStart(local, location), - nodeSourceEnd(local, location)); + nodeSourceStart(variable, location), + nodeSourceEnd(variable, location)); } public void methodMustOverride(AbstractMethodDeclaration method, long complianceLevel) { 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.260 diff -u -r1.260 messages.properties --- compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties 17 Dec 2010 09:38:53 -0000 1.260 +++ compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties 22 Dec 2010 18:19:16 -0000 @@ -572,6 +572,16 @@ ### MORE GENERICS 660 = Unused type arguments for the non generic constructor {0}({1}) of type {2}; it should not be parameterized with arguments <{3}> +### NULL ANALYSIS FOR FIELDS +670 = Null pointer access: The field {0} can only be null at this location +671 = Potential null pointer access: The field {0} may be null at this location +672 = Redundant null check: The field {0} can only be null at this location +673 = Null comparison always yields false: The field {0} can only be null at this location +674 = Redundant null check: The field {0} cannot be null at this location +675 = Null comparison always yields false: The field {0} cannot be null at this location +676 = Redundant assignment: The field {0} can only be null at this location +677 = instanceof always yields false: The field {0} can only be null at this location + ### CORRUPTED BINARIES 700 = The class file {0} contains a signature ''{1}'' ill-formed at position {2} #P org.eclipse.jdt.core.tests.compiler Index: src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java,v retrieving revision 1.38 diff -u -r1.38 CompilerInvocationTests.java --- src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java 17 Dec 2010 09:39:05 -0000 1.38 +++ src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java 22 Dec 2010 18:19:26 -0000 @@ -696,6 +696,7 @@ expectedProblemAttributes.put("NonGenericConstructor", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); expectedProblemAttributes.put("NonGenericMethod", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); expectedProblemAttributes.put("NonGenericType", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); + expectedProblemAttributes.put("NonNullFieldComparisonYieldsFalse", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); expectedProblemAttributes.put("NonNullLocalVariableComparisonYieldsFalse", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); expectedProblemAttributes.put("NonStaticAccessToStaticField", new ProblemAttributes(CategorizedProblem.CAT_CODE_STYLE)); expectedProblemAttributes.put("NonStaticAccessToStaticMethod", new ProblemAttributes(CategorizedProblem.CAT_CODE_STYLE)); @@ -708,6 +709,9 @@ expectedProblemAttributes.put("NotVisibleField", new ProblemAttributes(CategorizedProblem.CAT_MEMBER)); expectedProblemAttributes.put("NotVisibleMethod", new ProblemAttributes(CategorizedProblem.CAT_MEMBER)); expectedProblemAttributes.put("NotVisibleType", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); + expectedProblemAttributes.put("NullFieldComparisonYieldsFalse", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); + expectedProblemAttributes.put("NullFieldInstanceofYieldsFalse", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); + expectedProblemAttributes.put("NullFieldReference", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); expectedProblemAttributes.put("NullLocalVariableComparisonYieldsFalse", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); expectedProblemAttributes.put("NullLocalVariableInstanceofYieldsFalse", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); expectedProblemAttributes.put("NullLocalVariableReference", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); @@ -745,6 +749,7 @@ expectedProblemAttributes.put("ParsingErrorReplaceTokens", new ProblemAttributes(CategorizedProblem.CAT_SYNTAX)); expectedProblemAttributes.put("ParsingErrorUnexpectedEOF", new ProblemAttributes(CategorizedProblem.CAT_SYNTAX)); expectedProblemAttributes.put("PossibleAccidentalBooleanAssignment", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); + expectedProblemAttributes.put("PotentialNullFieldReference", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); expectedProblemAttributes.put("PotentialNullLocalVariableReference", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); expectedProblemAttributes.put("PublicClassMustMatchFileName", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); expectedProblemAttributes.put("RawMemberTypeCannotBeParameterized", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); @@ -752,8 +757,11 @@ expectedProblemAttributes.put("RecursiveConstructorInvocation", new ProblemAttributes(CategorizedProblem.CAT_MEMBER)); expectedProblemAttributes.put("RedefinedArgument", new ProblemAttributes(CategorizedProblem.CAT_INTERNAL)); expectedProblemAttributes.put("RedefinedLocal", new ProblemAttributes(CategorizedProblem.CAT_INTERNAL)); + expectedProblemAttributes.put("RedundantFieldNullAssignment", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); expectedProblemAttributes.put("RedundantLocalVariableNullAssignment", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); + expectedProblemAttributes.put("RedundantNullCheckOnNonNullField", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); expectedProblemAttributes.put("RedundantNullCheckOnNonNullLocalVariable", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); + expectedProblemAttributes.put("RedundantNullCheckOnNullField", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); expectedProblemAttributes.put("RedundantNullCheckOnNullLocalVariable", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); expectedProblemAttributes.put("RedundantSuperinterface", new ProblemAttributes(CategorizedProblem.CAT_UNNECESSARY_CODE)); expectedProblemAttributes.put("ReferenceToForwardField", new ProblemAttributes(CategorizedProblem.CAT_MEMBER)); @@ -1333,6 +1341,7 @@ expectedProblemAttributes.put("NonGenericConstructor", SKIP); expectedProblemAttributes.put("NonGenericMethod", SKIP); expectedProblemAttributes.put("NonGenericType", SKIP); + expectedProblemAttributes.put("NonNullFieldComparisonYieldsFalse", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK)); expectedProblemAttributes.put("NonNullLocalVariableComparisonYieldsFalse", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK)); expectedProblemAttributes.put("NonStaticAccessToStaticField", new ProblemAttributes(JavaCore.COMPILER_PB_STATIC_ACCESS_RECEIVER)); expectedProblemAttributes.put("NonStaticAccessToStaticMethod", new ProblemAttributes(JavaCore.COMPILER_PB_STATIC_ACCESS_RECEIVER)); @@ -1345,6 +1354,9 @@ expectedProblemAttributes.put("NotVisibleField", SKIP); expectedProblemAttributes.put("NotVisibleMethod", SKIP); expectedProblemAttributes.put("NotVisibleType", SKIP); + expectedProblemAttributes.put("NullFieldComparisonYieldsFalse", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK)); + expectedProblemAttributes.put("NullFieldInstanceofYieldsFalse", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK)); + expectedProblemAttributes.put("NullFieldReference", new ProblemAttributes(JavaCore.COMPILER_PB_NULL_REFERENCE)); expectedProblemAttributes.put("NullLocalVariableComparisonYieldsFalse", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK)); expectedProblemAttributes.put("NullLocalVariableInstanceofYieldsFalse", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK)); expectedProblemAttributes.put("NullLocalVariableReference", new ProblemAttributes(JavaCore.COMPILER_PB_NULL_REFERENCE)); @@ -1382,6 +1394,7 @@ expectedProblemAttributes.put("ParsingErrorReplaceTokens", SKIP); expectedProblemAttributes.put("ParsingErrorUnexpectedEOF", SKIP); expectedProblemAttributes.put("PossibleAccidentalBooleanAssignment", new ProblemAttributes(JavaCore.COMPILER_PB_POSSIBLE_ACCIDENTAL_BOOLEAN_ASSIGNMENT)); + expectedProblemAttributes.put("PotentialNullFieldReference", new ProblemAttributes(JavaCore.COMPILER_PB_POTENTIAL_NULL_REFERENCE)); expectedProblemAttributes.put("PotentialNullLocalVariableReference", new ProblemAttributes(JavaCore.COMPILER_PB_POTENTIAL_NULL_REFERENCE)); expectedProblemAttributes.put("PublicClassMustMatchFileName", SKIP); expectedProblemAttributes.put("RawMemberTypeCannotBeParameterized", SKIP); @@ -1389,8 +1402,11 @@ expectedProblemAttributes.put("RecursiveConstructorInvocation", SKIP); expectedProblemAttributes.put("RedefinedArgument", SKIP); expectedProblemAttributes.put("RedefinedLocal", SKIP); + expectedProblemAttributes.put("RedundantFieldNullAssignment", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK)); expectedProblemAttributes.put("RedundantLocalVariableNullAssignment", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK)); + expectedProblemAttributes.put("RedundantNullCheckOnNonNullField", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK)); expectedProblemAttributes.put("RedundantNullCheckOnNonNullLocalVariable", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK)); + expectedProblemAttributes.put("RedundantNullCheckOnNullField", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK)); expectedProblemAttributes.put("RedundantNullCheckOnNullLocalVariable", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK)); expectedProblemAttributes.put("RedundantSuperinterface", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_SUPERINTERFACE)); expectedProblemAttributes.put("ReferenceToForwardField", SKIP); Index: src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceImplTests.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceImplTests.java,v retrieving revision 1.12 diff -u -r1.12 NullReferenceImplTests.java --- src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceImplTests.java 9 Sep 2010 17:36:17 -0000 1.12 +++ src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceImplTests.java 22 Dec 2010 18:19:26 -0000 @@ -34,9 +34,11 @@ import org.eclipse.jdt.internal.compiler.flow.UnconditionalFlowInfo; import org.eclipse.jdt.internal.compiler.flow.UnconditionalFlowInfo.AssertionFailedException; 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.PackageBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.VariableBinding; /** * A tests series especially meant to validate the internals of our null @@ -1083,18 +1085,36 @@ return copy; } -public void markAsDefinitelyNonNull(LocalVariableBinding local) { - grow(local.id + this.maxFieldCount); +public void markAsDefinitelyNonNull(VariableBinding local) { + int position; + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } + grow(position); super.markAsDefinitelyNonNull(local); } -public void markAsDefinitelyNull(LocalVariableBinding local) { - grow(local.id + this.maxFieldCount); +public void markAsDefinitelyNull(VariableBinding local) { + int position; + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } + grow(position); super.markAsDefinitelyNull(local); } -public void markAsDefinitelyUnknown(LocalVariableBinding local) { - grow(local.id + this.maxFieldCount); +public void markAsDefinitelyUnknown(VariableBinding local) { + int position; + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } + grow(position); super.markAsDefinitelyUnknown(local); } 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.108 diff -u -r1.108 NullReferenceTest.java --- src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceTest.java 18 Dec 2010 22:09:48 -0000 1.108 +++ src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceTest.java 22 Dec 2010 18:19:28 -0000 @@ -36,7 +36,7 @@ // 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[] { "testBug325229" }; +// TESTS_NAMES = new String[] { "testBug247564f" }; // TESTS_NUMBERS = new int[] { 561 }; // TESTS_RANGE = new int[] { 1, 2049 }; } @@ -96,13 +96,12 @@ " 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" + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " o.toString();\n" + + " ^\n" + + "Potential null pointer access: The field o may be null at this location\n" + + "----------\n" ); } @@ -333,13 +332,12 @@ " this.o.toString();\n" + " }\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" + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " this.o.toString();\n" + + " ^\n" + + "Potential null pointer access: The field o may be null at this location\n" + + "----------\n" ); } @@ -355,13 +353,12 @@ " 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" + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " o.toString();\n" + + " ^\n" + + "Potential null pointer access: The field o may be null at this location\n" + + "----------\n" ); } @@ -411,13 +408,12 @@ " }\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" + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " X.this.o.toString();\n" + + " ^\n" + + "Potential null pointer access: The field o may be null at this location\n" + + "----------\n" ); } @@ -436,13 +432,12 @@ " }\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" + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " o.toString();\n" + + " ^\n" + + "Potential null pointer access: The field o may be null at this location\n" + + "----------\n" ); } @@ -13673,6 +13668,426 @@ "}"}, "null220"); } + +// null analysis -- simple case for field +public void testBug247564a() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " Object o;\n" + + " void foo() {\n" + + " if (o == null && o.toString() == \"\"){}\n" + + " else {}\n" + + " o.toString();\n" + // toString() call above defuses null info, so no warning here + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " if (o == null && o.toString() == \"\"){}\n" + + " ^\n" + + "Potential null pointer access: The field o may be null at this location\n" + + "----------\n" + ); +} + +// null analysis -- simple case for field +// no redundant null check warnings should be obtained since value of field +// may be changed in another thread. +public void testBug247564a_1() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " Object o;\n" + + " void foo() {\n" + + " o = null;" + + " if (o == null){}\n" + + " if (o != null){}\n" + + " o.toString();\n" + // warn here + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " o.toString();\n" + + " ^\n" + + "Potential null pointer access: The field o may be null at this location\n" + + "----------\n" + ); +} + +// null analysis -- simple case for field +public void testBug247564a_2() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " Object o;\n" + + " void foo() {\n" + + " if (o == null){\n" + // o is null inside the if block + " o.toString();\n" + + " }\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " o.toString();\n" + + " ^\n" + + "Potential null pointer access: The field o may be null at this location\n" + + "----------\n" + ); +} + +// null analysis -- simple case for field +// null info from one method should not be present in the other (for instance fields) +public void testBug247564a_3() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " Object o;\n" + + " void foo() {\n" + + " }\n" + + " void foo1() {\n" + + " o.toString();\n" + + " }\n" + + "}\n"}, + "" + ); +} + +// null analysis -- simple case for static final field +public void testBug247564b() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " static final Object o = null;\n" + + " static final Object o1 = new Object();\n" + + " void foo() {\n" + + " if (o.toString() == \"\") {}\n" + + " if (o == null) {}\n" + + " if (o != null) {}\n" + + " if (o1 == null) {}\n" + + " if (o1 != null) {}\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " if (o.toString() == \"\") {}\n" + + " ^\n" + + "Null pointer access: The field o can only be null at this location\n" + + "----------\n" + + "2. ERROR in X.java (at line 6)\n" + + " if (o == null) {}\n" + + " ^\n" + + "Redundant null check: The field o can only be null at this location\n" + + "----------\n" + + "3. ERROR in X.java (at line 7)\n" + + " if (o != null) {}\n" + + " ^\n" + + "Null comparison always yields false: The field o can only be null at this location\n" + + "----------\n" + + "4. WARNING in X.java (at line 7)\n" + + " if (o != null) {}\n" + + " ^^\n" + + "Dead code\n" + + "----------\n" + + "5. ERROR in X.java (at line 8)\n" + + " if (o1 == null) {}\n" + + " ^^\n" + + "Null comparison always yields false: The field o1 cannot be null at this location\n" + + "----------\n" + + "6. WARNING in X.java (at line 8)\n" + + " if (o1 == null) {}\n" + + " ^^\n" + + "Dead code\n" + + "----------\n" + + "7. ERROR in X.java (at line 9)\n" + + " if (o1 != null) {}\n" + + " ^^\n" + + "Redundant null check: The field o1 cannot be null at this location\n" + + "----------\n" + ); +} + +// null analysis -- simple case for static final field +public void testBug247564b_1() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " static final Object o;\n" + + " static final Object o1;\n" + + " static {\n" + + " o = null;\n" + + " o1 = new Object();\n" + + " }\n" + + " void foo() {\n" + + " if (o.toString() == \"\") {}\n" + + " if (o == null) {}\n" + + " if (o != null) {}\n" + + " if (o1 == null) {}\n" + + " if (o1 != null) {}\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 9)\n" + + " if (o.toString() == \"\") {}\n" + + " ^\n" + + "Null pointer access: The field o can only be null at this location\n" + + "----------\n" + + "2. ERROR in X.java (at line 10)\n" + + " if (o == null) {}\n" + + " ^\n" + + "Redundant null check: The field o can only be null at this location\n" + + "----------\n" + + "3. ERROR in X.java (at line 11)\n" + + " if (o != null) {}\n" + + " ^\n" + + "Null comparison always yields false: The field o can only be null at this location\n" + + "----------\n" + + "4. WARNING in X.java (at line 11)\n" + + " if (o != null) {}\n" + + " ^^\n" + + "Dead code\n" + + "----------\n" + + "5. ERROR in X.java (at line 12)\n" + + " if (o1 == null) {}\n" + + " ^^\n" + + "Null comparison always yields false: The field o1 cannot be null at this location\n" + + "----------\n" + + "6. WARNING in X.java (at line 12)\n" + + " if (o1 == null) {}\n" + + " ^^\n" + + "Dead code\n" + + "----------\n" + + "7. ERROR in X.java (at line 13)\n" + + " if (o1 != null) {}\n" + + " ^^\n" + + "Redundant null check: The field o1 cannot be null at this location\n" + + "----------\n" + ); +} + +// null analysis -- fields in synchronized methods +// check that null analysis for fields in synchronized methods +// behave as it does in ordinary methods. +public void testBug247564c() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " Object o;\n" + + " Object o1;\n" + + " static final Object o2 = null;\n" + + " static final Object o3 = new Object();\n" + + " synchronized void foo() {\n" + + " o = null;\n" + + " if (o == null) {\n" + + " o.toString();\n" + + " }\n" + + " o1 = new Object();\n" + + " if (o1 == null) {\n" + + " o1.toString();\n" + + " }\n" + + " if (o2 != null) {\n" + + " }\n" + + " else {\n" + + " o2.toString();\n" + + " }\n" + + " if (o3 == null) {\n" + + " }\n" + + " else {\n" + + " o3.toString();\n" + + " }\n" + + " }\n" + + " void foo1() {\n" + + " o.toString();\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 9)\n" + + " o.toString();\n" + + " ^\n" + + "Potential null pointer access: The field o may be null at this location\n" + + "----------\n" + + "2. ERROR in X.java (at line 13)\n" + + " o1.toString();\n" + + " ^^\n" + + "Potential null pointer access: The field o1 may be null at this location\n" + + "----------\n" + + "3. ERROR in X.java (at line 15)\n" + + " if (o2 != null) {\n" + + " ^^\n" + + "Null comparison always yields false: The field o2 can only be null at this location\n" + + "----------\n" + + "4. WARNING in X.java (at line 15)\n" + + " if (o2 != null) {\n" + + " }\n" + + " ^^^^^\n" + + "Dead code\n" + + "----------\n" + + "5. ERROR in X.java (at line 18)\n" + + " o2.toString();\n" + + " ^^\n" + + "Null pointer access: The field o2 can only be null at this location\n" + + "----------\n" + + "6. ERROR in X.java (at line 20)\n" + + " if (o3 == null) {\n" + + " ^^\n" + + "Null comparison always yields false: The field o3 cannot be null at this location\n" + + "----------\n" + + "7. WARNING in X.java (at line 20)\n" + + " if (o3 == null) {\n" + + " }\n" + + " ^^^^^\n" + + "Dead code\n" + + "----------\n" + ); +} + +// null analysis -- test redundant instanceof warning for static final field +public void testBug247564d() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " static final Object o = null;\n" + + " static final Object o1 = new Object();\n" + + " void foo() {\n" + + " if (o instanceof String) {}\n" + + " if (o1 instanceof String) {}\n" + + " }\n" + + "}\n"}, + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " if (o instanceof String) {}\n" + + " ^\n" + + "instanceof always yields false: The field o can only be null at this location\n" + + "----------\n" + ); +} + +// null analysis -- test redundant instanceof warning for static final fields +public void testBug247564e_1() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " static final Object o = null;\n" + + " void foo() {\n" + + " if (o instanceof X) return;\n" + + " }\n" + + "}"}, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " if (o instanceof X) return;\n" + + " ^\n" + + "instanceof always yields false: The field o can only be null at this location\n" + + "----------\n", + JavacTestOptions.Excuse.EclipseWarningConfiguredAsError); +} + +// null analysis -- test potential null ptr access warning because of static field access through qualified name reference +public void testBug247564f() { + Map compilerOptions = getCompilerOptions(); + compilerOptions.put(CompilerOptions.OPTION_ReportNonStaticAccessToStatic, CompilerOptions.IGNORE); + this.runNegativeTest( + false, + new String[] { + "X.java", + "public class X {\n" + + " static Object o;\n" + + " static Object o1;\n" + + " Object o2;\n" + + " X getX() { return new X();\n}\n" + + " void foo() {\n" + + " if (getX().o == null && this.o.hashCode() == 0) return;\n" + + " if (getX().o2 == null && this.o2.hashCode() == 0) return;\n" + + " }\n" + + "}"}, + null, + compilerOptions, + "----------\n" + + "1. ERROR in X.java (at line 8)\n" + + " if (getX().o == null && this.o.hashCode() == 0) return;\n" + + " ^\n" + + "Potential null pointer access: The field o may be null at this location\n" + + "----------\n", + JavacTestOptions.Excuse.EclipseWarningConfiguredAsError); +} + +// null analysis -- test field analysis in case of more than 64 fields +public void testBug247564g() { + Map compilerOptions = getCompilerOptions(); + compilerOptions.put(CompilerOptions.OPTION_ReportNonStaticAccessToStatic, CompilerOptions.IGNORE); + this.runNegativeTest( + false, + new String[] { + "X.java", + "public class X {\n" + + "Object field0, \n" + + "field1, field2, field3, field4, \n" + + "field5, field6, field7, field8, \n" + + "field9, field10, field11, field12, \n" + + "field13, field14, field15, field16, \n" + + "field17, field18, field19, field20, \n" + + "field21, field22, field23, field24, \n" + + "field25, field26, field27, field28, \n" + + "field29, field30, field31, field32, \n" + + "field33, field34, field35, field36, \n" + + "field37, field38, field39, field40, \n" + + "field41, field42, field43, field44, \n" + + "field45, field46, field47, field48, \n" + + "field49, field50, field51, field52, \n" + + "field53, field54, field55, field56, \n" + + "field57, field58, field59, field60, \n" + + "field61, field62, field63, field64, \n" + + "field65, field66, field67, field68, \n" + + "field69, field70, field71, field72, \n" + + "field73, field74, field75, field76, \n" + + "field77, field78, field79, field80, \n" + + "field81, field82, field83, field84, \n" + + "field85, field86, field87, field88, \n" + + "field89, field90, field91, field92, \n" + + "field93, field94, field95, field96, \n" + + "field97, field98, field99;\n" + + "static final Object field100 = null;\n" + + " void foo() {\n" + + " int i = 0;" + + " while (i<10){\n" + + " i++;\n" + + " if (this.field99 == null && this.field99.hashCode() == 0){}\n" + + " this.field98 = null;\n" + + " }\n" + + " if (this.field98.hashCode() == 0) {}\n" + // should not complain + " this.field97 = null;\n" + + " if (this.field97.hashCode() == 0) {}\n" + + " if (this.field100.hashCode() == 0) {}\n" + + " }\n" + + "}"}, + null, + compilerOptions, + "----------\n" + + "1. ERROR in X.java (at line 32)\n" + + " if (this.field99 == null && this.field99.hashCode() == 0){}\n" + + " ^^^^^^^\n" + + "Potential null pointer access: The field field99 may be null at this location\n" + + "----------\n" + + "2. ERROR in X.java (at line 37)\n" + + " if (this.field97.hashCode() == 0) {}\n" + + " ^^^^^^^\n" + + "Potential null pointer access: The field field97 may be null at this location\n" + + "----------\n" + + "3. ERROR in X.java (at line 38)\n" + + " if (this.field100.hashCode() == 0) {}\n" + + " ^^^^^^^^\n" + + "Null pointer access: The field field100 can only be null at this location\n" + + "----------\n", + JavacTestOptions.Excuse.EclipseWarningConfiguredAsError); +} + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=332637 // Dead Code detection removing code that isn't dead public void testBug332637() {