### 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.225.2.24 diff -u -r1.225.2.24 IProblem.java --- compiler/org/eclipse/jdt/core/compiler/IProblem.java 13 Jul 2011 08:27:06 -0000 1.225.2.24 +++ compiler/org/eclipse/jdt/core/compiler/IProblem.java 18 Jul 2011 08:34:40 -0000 @@ -1396,6 +1396,17 @@ int DiamondNotBelow17 = TypeRelated + 883; /** @since 3.7 */ int RedundantSpecificationOfTypeArguments = TypeRelated + 884; + /** @since 3.7 */ + int PotentiallyUnclosedCloseable = Internal + 885; + /** @since 3.7 */ + int PotentiallyUnclosedCloseableAtExit = Internal + 886; + /** @since 3.7 */ + int UnclosedCloseable = Internal + 887; + /** @since 3.7 */ + int UnclosedCloseableAtExit = Internal + 888; + /** @since 3.7 */ + int ExplicitlyClosedAutoCloseable = Internal + 889; + /** * External problems -- These are problems defined by other plugins */ Index: compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java,v retrieving revision 1.84.2.13 diff -u -r1.84.2.13 AllocationExpression.java --- compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java 13 Jul 2011 08:27:07 -0000 1.84.2.13 +++ compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java 18 Jul 2011 08:34:40 -0000 @@ -46,6 +46,13 @@ // process arguments if (this.arguments != null) { for (int i = 0, count = this.arguments.length; i < count; i++) { + FakedTrackingVariable trackVar = FakedTrackingVariable.getCloseTrackingVariable(this.arguments[i]); + if (trackVar != null) { + // insert info that the tracked resource *may* be closed (by the target method, i.e.) + FlowInfo infoResourceIsClosed = flowInfo.copy(); + infoResourceIsClosed.markAsDefinitelyNonNull(trackVar.binding); + flowInfo = FlowInfo.conditional(flowInfo, infoResourceIsClosed); + } flowInfo = this.arguments[i] .analyseCode(currentScope, flowContext, flowInfo) 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.94.2.3 diff -u -r1.94.2.3 Assignment.java --- compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java 16 Mar 2011 10:04:29 -0000 1.94.2.3 +++ compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java 18 Jul 2011 08:34:40 -0000 @@ -51,6 +51,33 @@ flowInfo = ((Reference) this.lhs) .analyseAssignment(currentScope, flowContext, flowInfo, this, false) .unconditionalInits(); + if (local != null) { + LocalVariableBinding trackerBinding = null; + if (local.closeTracker != null) { + // Assigning to a variable already holding an AutoCloseable, has it been closed before? + trackerBinding = local.closeTracker.binding; + if (!flowInfo.isDefinitelyNull(local)) { // only if previous value may be non-null + if (flowInfo.isDefinitelyNull(trackerBinding)) + currentScope.problemReporter().unclosedCloseable(local.closeTracker, this); + else if (flowInfo.isPotentiallyNull(trackerBinding)) + currentScope.problemReporter().potentiallyUnclosedCloseable(local.closeTracker, this); + } + } + if (FakedTrackingVariable.isAutoCloseable(this.expression.resolvedType)) { + if (local.closeTracker != null && local.closeTracker.isInsideTryWithResources) { + // re-assigning resource of try-with-resources is a different error + } else { + // new value is AutoCloseable, start tracking, possibly re-using existing tracker var: + if (trackerBinding == null) { + local.closeTracker = new FakedTrackingVariable(local, this); + trackerBinding = local.closeTracker.binding; + } + flowInfo.markAsDefinitelyNull(trackerBinding); +// if (flowContext.initsOnFinally != null) +// flowContext.initsOnFinally.markAsDefinitelyNonNull(trackerBinding); + } + } + } int nullStatus = this.expression.nullStatus(flowInfo); if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) { if (nullStatus == FlowInfo.NULL) { 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 18 Jul 2011 08:34:40 -0000 @@ -36,6 +36,8 @@ flowInfo = stat.analyseCode(this.scope, flowContext, flowInfo); } } + if (this.explicitDeclarations > 0) // if block has its own scope analyze tracking vars now: + this.scope.checkUnclosedCloseables(flowInfo, flowContext, null); return flowInfo; } /** Index: compiler/org/eclipse/jdt/internal/compiler/ast/FakedTrackingVariable.java =================================================================== RCS file: compiler/org/eclipse/jdt/internal/compiler/ast/FakedTrackingVariable.java diff -N compiler/org/eclipse/jdt/internal/compiler/ast/FakedTrackingVariable.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ compiler/org/eclipse/jdt/internal/compiler/ast/FakedTrackingVariable.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2011 GK Software AG and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * GK Software AG - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.compiler.ast; + +import org.eclipse.jdt.internal.compiler.codegen.CodeStream; +import org.eclipse.jdt.internal.compiler.impl.Constant; +import org.eclipse.jdt.internal.compiler.lookup.BlockScope; +import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; +import org.eclipse.jdt.internal.compiler.lookup.MethodScope; +import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; +import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; +import org.eclipse.jdt.internal.compiler.lookup.TypeIds; + +/** + * A faked local variable declaration used for keeping track of data flows of a + * special variable. Certain events will be recorded by changing the null info + * for this variable. + * + * @author Stephan Herrmann + */ +public class FakedTrackingVariable extends LocalDeclaration { + + /** If resource is already managed by try-with-resources don't complain. */ + public boolean isInsideTryWithResources; + + /** + * If close() is invoked from a nested method (inside a local type) + * report remaining problems only as potential. + */ + public boolean closedInNestedMethod; + + /** If a problem has already been reported don't complain again. */ + public boolean hasReportedProblem; + + MethodScope methodScope; // designates the method declaring this variable + + public LocalVariableBinding originalBinding; // the real local being tracked + + public FakedTrackingVariable(LocalVariableBinding original, Statement location) { + super(original.name, location.sourceStart, location.sourceEnd); + this.type = new SingleTypeReference( + TypeConstants.OBJECT, + ((long)this.sourceStart <<32)+this.sourceEnd); + this.methodScope = original.declaringScope.methodScope(); + this.originalBinding = original; + resolve(original.declaringScope); + } + + public void generateCode(BlockScope currentScope, CodeStream codeStream) + { /* NOP - this variable is completely dummy, ie. for analysis only. */ } + + public void resolve (BlockScope scope) { + // only need the binding, which is used as reference in FlowInfo methods. + this.binding = new LocalVariableBinding( + this.name, + scope.getJavaLangObject(), // dummy, just needs to be a reference type + 0, + false); + this.binding.setConstant(Constant.NotAConstant); + this.binding.useFlag = LocalVariableBinding.USED; + // use a free slot without assigning it: + this.binding.id = scope.registerTrackingVariable(this); + } + + /** + * If expression resolves to a local variable binding of type AutoCloseable, + * answer the variable that tracks closing of that local, creating it if needed. + * @param expression + * @return a new {@link FakedTrackingVariable} or null. + */ + public static FakedTrackingVariable getCloseTrackingVariable(Expression expression) { + if (expression instanceof SingleNameReference) { + SingleNameReference name = (SingleNameReference) expression; + if (name.binding instanceof LocalVariableBinding) { + LocalVariableBinding local = (LocalVariableBinding)name.binding; + if (local.closeTracker != null) + return local.closeTracker; + if (local.isParameter() || !isAutoCloseable(expression.resolvedType)) + return null; + // tracking var doesn't yet exist. This happens in finally block + // which is analyzed before the corresponding try block + Statement location = local.declaration; + return local.closeTracker = new FakedTrackingVariable(local, location); + } + } + return null; + } + + /** Answer wither the given type binding is a subtype of java.lang.AutoCloseable. */ + public static boolean isAutoCloseable(TypeBinding typeBinding) { + return typeBinding instanceof ReferenceBinding + && ((ReferenceBinding)typeBinding).hasTypeBit(TypeIds.BitAutoCloseable); + } +} Index: compiler/org/eclipse/jdt/internal/compiler/ast/IfStatement.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/IfStatement.java,v retrieving revision 1.69.2.2 diff -u -r1.69.2.2 IfStatement.java --- compiler/org/eclipse/jdt/internal/compiler/ast/IfStatement.java 5 Mar 2011 18:12:56 -0000 1.69.2.2 +++ compiler/org/eclipse/jdt/internal/compiler/ast/IfStatement.java 18 Jul 2011 08:34:42 -0000 @@ -124,6 +124,8 @@ } elseFlowInfo = this.elseStatement.analyseCode(currentScope, flowContext, elseFlowInfo); } + // process AutoCloseable resources closed in only one branch: + currentScope.correlateTrackingVarsIfElse(thenFlowInfo, elseFlowInfo); // merge THEN & ELSE initializations FlowInfo mergedInfo = FlowInfo.mergedOptimizedBranchesIfElse( thenFlowInfo, 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.77.2.3 diff -u -r1.77.2.3 LocalDeclaration.java --- compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java 8 Mar 2011 17:20:09 -0000 1.77.2.3 +++ compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java 18 Jul 2011 08:34:42 -0000 @@ -76,6 +76,12 @@ this.initialization .analyseCode(currentScope, flowContext, flowInfo) .unconditionalInits(); + if (FakedTrackingVariable.isAutoCloseable(this.initialization.resolvedType)) { + this.binding.closeTracker = new FakedTrackingVariable(this.binding, this); + flowInfo.markAsDefinitelyNull(this.binding.closeTracker.binding); +// if (flowContext.initsOnFinally != null) +// flowContext.initsOnFinally.markAsDefinitelyNonNull(this.binding.closeTracker.binding); + } int nullStatus = this.initialization.nullStatus(flowInfo); if (!flowInfo.isDefinitelyAssigned(this.binding)){// for local variable debug attributes this.bits |= FirstAssignmentToLocal; 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.151.2.8 diff -u -r1.151.2.8 MessageSend.java --- compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java 28 Jun 2011 14:49:04 -0000 1.151.2.8 +++ compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java 18 Jul 2011 08:34:42 -0000 @@ -42,6 +42,7 @@ import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.TagBits; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.eclipse.jdt.internal.compiler.lookup.TypeIds; import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities; @@ -64,6 +65,20 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { boolean nonStatic = !this.binding.isStatic(); flowInfo = this.receiver.analyseCode(currentScope, flowContext, flowInfo, nonStatic).unconditionalInits(); + // recording the closing of AutoCloseable resources: + if (CharOperation.equals(TypeConstants.CLOSE, this.selector)) + { + FakedTrackingVariable trackingVariable = FakedTrackingVariable.getCloseTrackingVariable(this.receiver); + if (trackingVariable != null) { // null happens if receiver is not a local variable or not an AutoCloseable + if (trackingVariable.methodScope == currentScope.methodScope()) { + flowInfo.markAsDefinitelyNonNull(trackingVariable.binding); +// if (flowContext.initsOnFinally != null) +// flowContext.initsOnFinally.markAsDefinitelyNonNull(trackingVariable.binding); + } else { + trackingVariable.closedInNestedMethod = true; + } + } + } if (nonStatic) { this.receiver.checkNPE(currentScope, flowContext, flowInfo); // https://bugs.eclipse.org/bugs/show_bug.cgi?id=318682 @@ -84,6 +99,13 @@ if ((this.arguments[i].implicitConversion & TypeIds.UNBOXING) != 0) { this.arguments[i].checkNPE(currentScope, flowContext, flowInfo); } + FakedTrackingVariable trackVar = FakedTrackingVariable.getCloseTrackingVariable(this.arguments[i]); + if (trackVar != null) { + // insert info that the tracked resource *may* be closed (by the target method, i.e.) + FlowInfo infoResourceIsClosed = flowInfo.copy(); + infoResourceIsClosed.markAsDefinitelyNonNull(trackVar.binding); + flowInfo = FlowInfo.conditional(flowInfo, infoResourceIsClosed); + } flowInfo = this.arguments[i].analyseCode(currentScope, flowContext, flowInfo).unconditionalInits(); } } Index: compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java,v retrieving revision 1.79.2.1 diff -u -r1.79.2.1 MethodDeclaration.java --- compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java 5 Mar 2011 18:12:56 -0000 1.79.2.1 +++ compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java 18 Jul 2011 08:34:43 -0000 @@ -134,6 +134,7 @@ } } + this.scope.checkUnclosedCloseables(flowInfo, methodContext, null); } catch (AbortMethod e) { this.ignoreFurtherInvestigation = true; } Index: compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java,v retrieving revision 1.101.2.12 diff -u -r1.101.2.12 QualifiedAllocationExpression.java --- compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java 13 Jul 2011 08:27:07 -0000 1.101.2.12 +++ compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java 18 Jul 2011 08:34:43 -0000 @@ -76,6 +76,13 @@ // process arguments if (this.arguments != null) { for (int i = 0, count = this.arguments.length; i < count; i++) { + FakedTrackingVariable trackVar = FakedTrackingVariable.getCloseTrackingVariable(this.arguments[i]); + if (trackVar != null) { + // insert info that the tracked resource *may* be closed (by the target method, i.e.) + FlowInfo infoResourceIsClosed = flowInfo.copy(); + infoResourceIsClosed.markAsDefinitelyNonNull(trackVar.binding); + flowInfo = FlowInfo.conditional(flowInfo, infoResourceIsClosed); + } flowInfo = this.arguments[i].analyseCode(currentScope, flowContext, flowInfo); if ((this.arguments[i].implicitConversion & TypeIds.UNBOXING) != 0) { this.arguments[i].checkNPE(currentScope, flowContext, flowInfo); Index: compiler/org/eclipse/jdt/internal/compiler/ast/ReturnStatement.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReturnStatement.java,v retrieving revision 1.70.2.1 diff -u -r1.70.2.1 ReturnStatement.java --- compiler/org/eclipse/jdt/internal/compiler/ast/ReturnStatement.java 27 Apr 2011 16:33:51 -0000 1.70.2.1 +++ compiler/org/eclipse/jdt/internal/compiler/ast/ReturnStatement.java 18 Jul 2011 08:34:43 -0000 @@ -40,6 +40,13 @@ if ((this.expression.implicitConversion & TypeIds.UNBOXING) != 0) { this.expression.checkNPE(currentScope, flowContext, flowInfo); } + FakedTrackingVariable trackingVariable = FakedTrackingVariable.getCloseTrackingVariable(this.expression); + if (trackingVariable != null) { + // don't report issues concerning this local, since by returning + // the method passes the responsibility to the caller: + flowInfo.markAsDefinitelyNonNull(trackingVariable.binding); + trackingVariable.hasReportedProblem = true; + } } this.initStateIndex = currentScope.methodScope().recordInitializationStates(flowInfo); @@ -104,6 +111,7 @@ this.expression.bits |= ASTNode.IsReturnedValue; } } + currentScope.checkUnclosedCloseables(flowInfo, null/*ignore exception exits from flowContext*/, this); return FlowInfo.DEAD_END; } Index: compiler/org/eclipse/jdt/internal/compiler/ast/TryStatement.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TryStatement.java,v retrieving revision 1.116.2.27 diff -u -r1.116.2.27 TryStatement.java --- compiler/org/eclipse/jdt/internal/compiler/ast/TryStatement.java 13 Jul 2011 12:34:59 -0000 1.116.2.27 +++ compiler/org/eclipse/jdt/internal/compiler/ast/TryStatement.java 18 Jul 2011 08:34:44 -0000 @@ -15,6 +15,9 @@ *******************************************************************************/ package org.eclipse.jdt.internal.compiler.ast; +import java.util.ArrayList; +import java.util.List; + import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ASTVisitor; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; @@ -120,8 +123,13 @@ for (int i = 0, max = this.resources.length; i < max; i++) { flowInfo = this.resources[i].analyseCode(currentScope, handlingContext, flowInfo.copy()); - this.resources[i].binding.useFlag = LocalVariableBinding.USED; // Is implicitly used anyways. - TypeBinding type = this.resources[i].binding.type; + LocalVariableBinding resourceBinding = this.resources[i].binding; + resourceBinding.useFlag = LocalVariableBinding.USED; // Is implicitly used anyways. + if (resourceBinding.closeTracker != null) { + resourceBinding.closeTracker.isInsideTryWithResources = true; + flowInfo.markAsDefinitelyNonNull(resourceBinding.closeTracker.binding); + } + TypeBinding type = resourceBinding.type; if (type != null && type.isValidBinding()) { ReferenceBinding binding = (ReferenceBinding) type; MethodBinding closeMethod = binding.getExactMethod(ConstantPool.Close, new TypeBinding [0], this.scope.compilationUnitScope()); // scope needs to be tighter @@ -245,8 +253,13 @@ for (int i = 0, max = this.resources.length; i < max; i++) { flowInfo = this.resources[i].analyseCode(currentScope, handlingContext, flowInfo.copy()); - this.resources[i].binding.useFlag = LocalVariableBinding.USED; // Is implicitly used anyways. - TypeBinding type = this.resources[i].binding.type; + LocalVariableBinding resourceBinding = this.resources[i].binding; + resourceBinding.useFlag = LocalVariableBinding.USED; // Is implicitly used anyways. + if (resourceBinding.closeTracker != null) { + resourceBinding.closeTracker.isInsideTryWithResources = true; + flowInfo.markAsDefinitelyNonNull(resourceBinding.closeTracker.binding); + } + TypeBinding type = resourceBinding.type; if (type != null && type.isValidBinding()) { ReferenceBinding binding = (ReferenceBinding) type; MethodBinding closeMethod = binding.getExactMethod(ConstantPool.Close, new TypeBinding [0], this.scope.compilationUnitScope()); // scope needs to be tighter @@ -267,6 +280,13 @@ this.bits |= ASTNode.IsTryBlockExiting; } + // superimpose status for tracking variables: + // don't complain on initsOnException if finally has a better state. + List trackVars = new ArrayList(); + currentScope.getTrackVars(trackVars, subInfo); + if (trackVars.size() > 0) + flowContext.improveNullInfoForExceptionExits(trackVars, subInfo); + // check unreachable catch blocks handlingContext.complainIfUnusedExceptionHandlers(this.scope, this); Index: compiler/org/eclipse/jdt/internal/compiler/flow/ExceptionHandlingFlowContext.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/ExceptionHandlingFlowContext.java,v retrieving revision 1.45.8.6 diff -u -r1.45.8.6 ExceptionHandlingFlowContext.java --- compiler/org/eclipse/jdt/internal/compiler/flow/ExceptionHandlingFlowContext.java 18 May 2011 04:48:07 -0000 1.45.8.6 +++ compiler/org/eclipse/jdt/internal/compiler/flow/ExceptionHandlingFlowContext.java 18 Jul 2011 08:34:44 -0000 @@ -15,18 +15,21 @@ package org.eclipse.jdt.internal.compiler.flow; import java.util.ArrayList; +import java.util.List; -import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.Argument; -import org.eclipse.jdt.internal.compiler.ast.UnionTypeReference; +import org.eclipse.jdt.internal.compiler.ast.FakedTrackingVariable; import org.eclipse.jdt.internal.compiler.ast.SubRoutineStatement; import org.eclipse.jdt.internal.compiler.ast.TryStatement; import org.eclipse.jdt.internal.compiler.ast.TypeReference; +import org.eclipse.jdt.internal.compiler.ast.UnionTypeReference; import org.eclipse.jdt.internal.compiler.codegen.ObjectCache; import org.eclipse.jdt.internal.compiler.lookup.BlockScope; import org.eclipse.jdt.internal.compiler.lookup.CatchParameterBinding; import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers; +import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; import org.eclipse.jdt.internal.compiler.lookup.MethodScope; import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; import org.eclipse.jdt.internal.compiler.lookup.Scope; @@ -184,7 +187,15 @@ return node; } - +public int getNullStatusAfter(LocalVariableBinding local, FlowInfo flowInfo) { + // collect info from normal flow and exceptional flows: + int status = flowInfo.nullStatus(local); + if (this.initsOnExceptions != null) + for (int i = 0; i < this.initsOnExceptions.length; i++) + status |= (this.initsOnExceptions[i].nullStatus(local) & (FlowInfo.NULL|FlowInfo.POTENTIALLY_NULL)); + // merge collected info: + return status; +} public String individualToString() { StringBuffer buffer = new StringBuffer("Exception flow context"); //$NON-NLS-1$ @@ -302,4 +313,31 @@ } return null; } +/** + * {@inheritDoc} + */ +public void improveNullInfoForExceptionExits(List moreTrackVars, FlowInfo otherInfo) { + if (this.initsOnExceptions == null) return; + for (int j = 0; j < this.initsOnExceptions.length; j++) { + UnconditionalFlowInfo copy = null; + for (int i = 0; i < moreTrackVars.size(); i++) { + LocalVariableBinding trackVar = ((FakedTrackingVariable) moreTrackVars.get(i)).binding; + int status = otherInfo.nullStatus(trackVar); + if ((status & FlowInfo.NULL) == 0) { + if ((status & FlowInfo.NON_NULL) != 0) { + this.initsOnExceptions[j].markAsDefinitelyNonNull(trackVar); + } else if ((status & FlowInfo.POTENTIALLY_NON_NULL) != 0) { + if (this.initsOnExceptions[j].isDefinitelyNull(trackVar)) { + // cannot directly set to pot.nn, need to construct and merge infos for this task: + if (copy == null) + copy = this.initsOnExceptions[j].unconditionalCopy(); + copy.markAsDefinitelyNonNull(trackVar); + } + } + } + } + if (copy != null) + this.initsOnExceptions[j] = this.initsOnExceptions[j].mergedWith(copy); + } +} } 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.2.8 diff -u -r1.68.2.8 FlowContext.java --- compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java 27 Jun 2011 11:58:08 -0000 1.68.2.8 +++ compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java 18 Jul 2011 08:34:45 -0000 @@ -15,9 +15,11 @@ package org.eclipse.jdt.internal.compiler.flow; import java.util.ArrayList; +import java.util.List; + import org.eclipse.jdt.core.compiler.CharOperation; -import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.LabeledStatement; import org.eclipse.jdt.internal.compiler.ast.Reference; @@ -395,6 +397,14 @@ return null; } +/** + * Answer the combined null status that local will have after this flow context. + * Subclasses will respect break and exception exits. + */ +public int getNullStatusAfter(LocalVariableBinding local, FlowInfo flowInfo) { + return flowInfo.nullStatus(local); +} + /* * lookup through break labels */ @@ -736,4 +746,40 @@ buffer.append(individualToString()).append('\n'); return buffer.toString(); } +/** + * For all tracking variables in the given list improve the null info in initsOnException (if existent) + * with information from the given otherInfo. + */ +public void improveNullInfoForExceptionExits(List moreTrackVars, FlowInfo otherInfo) { + // nothing here, overridden in ExceptionHandlingFlowContext +} + +/** + * Get the null status that local will have after the current point as indicated by flowInfo and flowContext + * @param local + * @param flowInfo + * @param flowContext may be null + * @return a bitset from the constants FlowInfo.{NULL,POTENTIALLY_NULL,POTENTIALLY_NON_NULL,NON_NULL}. + */ +public static int getNullStatusAfter(LocalVariableBinding local, FlowInfo flowInfo, FlowContext flowContext) { + int status = (flowContext != null) + ? flowContext.getNullStatusAfter(local, flowInfo) + : flowInfo.nullStatus(local); + return mergeNullStatus(status); +} + +/** Merge the bits NULL, NON_NULL, POTENTIALLY_NULL, POTENTIALLY_NON_NULL to a one-bit answer. */ +protected static int mergeNullStatus(int status) { + if ((status & FlowInfo.NULL) != 0) { + if ((status & (FlowInfo.NON_NULL | FlowInfo.POTENTIALLY_NON_NULL)) != 0) + return FlowInfo.POTENTIALLY_NULL; // null + doubt = pot null + return FlowInfo.NULL; + } else if ((status & FlowInfo.NON_NULL) != 0) { + if ((status & FlowInfo.POTENTIALLY_NULL) != 0) + return FlowInfo.POTENTIALLY_NULL; // non-null + doubt = pot null + return FlowInfo.NON_NULL; + } else if ((status & FlowInfo.POTENTIALLY_NULL) != 0) + return FlowInfo.POTENTIALLY_NULL; + return status; +} } Index: compiler/org/eclipse/jdt/internal/compiler/flow/SwitchFlowContext.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/SwitchFlowContext.java,v retrieving revision 1.32.8.1 diff -u -r1.32.8.1 SwitchFlowContext.java --- compiler/org/eclipse/jdt/internal/compiler/flow/SwitchFlowContext.java 8 Mar 2011 17:20:08 -0000 1.32.8.1 +++ compiler/org/eclipse/jdt/internal/compiler/flow/SwitchFlowContext.java 18 Jul 2011 08:34:45 -0000 @@ -12,6 +12,7 @@ import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.codegen.BranchLabel; +import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; /** * Reflects the context of code analysis, keeping track of enclosing @@ -31,6 +32,14 @@ return this.breakLabel; } +public int getNullStatusAfter(LocalVariableBinding local, FlowInfo flowInfo) { + // collect info from normal flow and breaks flows: + int status = flowInfo.nullStatus(local); + if (this.initsOnBreak != null) + status |= this.initsOnBreak.nullStatus(local); + return status; +} + public String individualToString() { StringBuffer buffer = new StringBuffer("Switch flow context"); //$NON-NLS-1$ buffer.append("[initsOnBreak -").append(this.initsOnBreak.toString()).append(']'); //$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.72.2.3 diff -u -r1.72.2.3 UnconditionalFlowInfo.java --- compiler/org/eclipse/jdt/internal/compiler/flow/UnconditionalFlowInfo.java 12 Apr 2011 08:35:58 -0000 1.72.2.3 +++ compiler/org/eclipse/jdt/internal/compiler/flow/UnconditionalFlowInfo.java 18 Jul 2011 08:34:46 -0000 @@ -770,7 +770,7 @@ } int vectorIndex; if ((vectorIndex = (position / BitCacheSize) - 1) - >= this.extra[0].length) { + >= this.extra[2].length) { return false; // if not enough room in vector, then not initialized } return ((this.extra[2][vectorIndex] & this.extra[4][vectorIndex] @@ -797,7 +797,7 @@ } int vectorIndex; if ((vectorIndex = (position / BitCacheSize) - 1) >= - this.extra[0].length) { + this.extra[2].length) { return false; // if not enough room in vector, then not initialized } return ((this.extra[2][vectorIndex] & this.extra[3][vectorIndex] @@ -822,7 +822,7 @@ } int vectorIndex; if ((vectorIndex = (position / BitCacheSize) - 1) >= - this.extra[0].length) { + this.extra[2].length) { return false; // if not enough room in vector, then not initialized } return ((this.extra[2][vectorIndex] & this.extra[5][vectorIndex] @@ -882,7 +882,7 @@ } int vectorIndex; if ((vectorIndex = (position / BitCacheSize) - 1) >= - this.extra[0].length) { + this.extra[2].length) { return false; // if not enough room in vector, then not initialized } return ((this.extra[4][vectorIndex] @@ -908,7 +908,7 @@ } int vectorIndex; if ((vectorIndex = (position / BitCacheSize) - 1) >= - this.extra[0].length) { + this.extra[2].length) { return false; // if not enough room in vector, then not initialized } return ((this.extra[3][vectorIndex] @@ -934,7 +934,7 @@ } int vectorIndex; if ((vectorIndex = (position / BitCacheSize) - 1) >= - this.extra[0].length) { + this.extra[2].length) { return false; // if not enough room in vector, then not initialized } return (this.extra[5][vectorIndex] @@ -1889,7 +1889,6 @@ } return count; } - public UnconditionalFlowInfo nullInfoLessUnconditionalCopy() { if (this == DEAD_END) { return this; Index: compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java,v retrieving revision 1.241.2.4 diff -u -r1.241.2.4 CompilerOptions.java --- compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java 13 Jul 2011 08:27:06 -0000 1.241.2.4 +++ compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java 18 Jul 2011 08:34:47 -0000 @@ -141,6 +141,9 @@ public static final String OPTION_ReportMethodCanBeStatic = "org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic"; //$NON-NLS-1$ public static final String OPTION_ReportMethodCanBePotentiallyStatic = "org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic"; //$NON-NLS-1$ public static final String OPTION_ReportRedundantSpecificationOfTypeArguments = "org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments"; //$NON-NLS-1$ + public static final String OPTION_ReportUnclosedCloseable = "org.eclipse.jdt.core.compiler.problem.unclosedCloseable"; //$NON-NLS-1$ + public static final String OPTION_ReportPotentiallyUnclosedCloseable = "org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable"; //$NON-NLS-1$ + public static final String OPTION_ReportExplicitlyClosedAutoCloseable = "org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable"; //$NON-NLS-1$ /** * Possible values for configurable options */ @@ -244,6 +247,9 @@ public static final int MethodCanBeStatic = IrritantSet.GROUP2 | ASTNode.Bit5; public static final int MethodCanBePotentiallyStatic = IrritantSet.GROUP2 | ASTNode.Bit6; public static final int RedundantSpecificationOfTypeArguments = IrritantSet.GROUP2 | ASTNode.Bit7; + public static final int UnclosedCloseable = IrritantSet.GROUP2 | ASTNode.Bit8; + public static final int PotentiallyUnclosedCloseable = IrritantSet.GROUP2 | ASTNode.Bit9; + public static final int ExplicitlyClosedAutoCloseable = IrritantSet.GROUP2 | ASTNode.Bit10; // Severity level for handlers /** @@ -555,6 +561,12 @@ return OPTION_ReportMethodCanBePotentiallyStatic; case RedundantSpecificationOfTypeArguments : return OPTION_ReportRedundantSpecificationOfTypeArguments; + case UnclosedCloseable : + return OPTION_ReportUnclosedCloseable; + case PotentiallyUnclosedCloseable : + return OPTION_ReportPotentiallyUnclosedCloseable; + case ExplicitlyClosedAutoCloseable : + return OPTION_ReportExplicitlyClosedAutoCloseable; } return null; } @@ -718,6 +730,9 @@ OPTION_ReportUnusedTypeArgumentsForMethodInvocation, OPTION_ReportUnusedWarningToken, OPTION_ReportVarargsArgumentNeedCast, + OPTION_ReportUnclosedCloseable, + OPTION_ReportPotentiallyUnclosedCloseable, + OPTION_ReportExplicitlyClosedAutoCloseable, }; return result; } @@ -984,6 +999,9 @@ optionsMap.put(OPTION_ReportMethodCanBeStatic, getSeverityString(MethodCanBeStatic)); optionsMap.put(OPTION_ReportMethodCanBePotentiallyStatic, getSeverityString(MethodCanBePotentiallyStatic)); optionsMap.put(OPTION_ReportRedundantSpecificationOfTypeArguments, getSeverityString(RedundantSpecificationOfTypeArguments)); + optionsMap.put(OPTION_ReportUnclosedCloseable, getSeverityString(UnclosedCloseable)); + optionsMap.put(OPTION_ReportPotentiallyUnclosedCloseable, getSeverityString(PotentiallyUnclosedCloseable)); + optionsMap.put(OPTION_ReportExplicitlyClosedAutoCloseable, getSeverityString(ExplicitlyClosedAutoCloseable)); return optionsMap; } @@ -1414,6 +1432,9 @@ if ((optionValue = optionsMap.get(OPTION_ReportMethodCanBeStatic)) != null) updateSeverity(MethodCanBeStatic, optionValue); if ((optionValue = optionsMap.get(OPTION_ReportMethodCanBePotentiallyStatic)) != null) updateSeverity(MethodCanBePotentiallyStatic, optionValue); if ((optionValue = optionsMap.get(OPTION_ReportRedundantSpecificationOfTypeArguments)) != null) updateSeverity(RedundantSpecificationOfTypeArguments, optionValue); + if ((optionValue = optionsMap.get(OPTION_ReportUnclosedCloseable)) != null) updateSeverity(UnclosedCloseable, optionValue); + if ((optionValue = optionsMap.get(OPTION_ReportPotentiallyUnclosedCloseable)) != null) updateSeverity(PotentiallyUnclosedCloseable, optionValue); + if ((optionValue = optionsMap.get(OPTION_ReportExplicitlyClosedAutoCloseable)) != null) updateSeverity(ExplicitlyClosedAutoCloseable, optionValue); // Javadoc options if ((optionValue = optionsMap.get(OPTION_DocCommentSupport)) != null) { Index: compiler/org/eclipse/jdt/internal/compiler/impl/IrritantSet.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/IrritantSet.java,v retrieving revision 1.13.2.2 diff -u -r1.13.2.2 IrritantSet.java --- compiler/org/eclipse/jdt/internal/compiler/impl/IrritantSet.java 13 Jul 2011 08:27:06 -0000 1.13.2.2 +++ compiler/org/eclipse/jdt/internal/compiler/impl/IrritantSet.java 18 Jul 2011 08:34:47 -0000 @@ -103,7 +103,8 @@ // group-2 warnings enabled by default .set( CompilerOptions.DeadCode - |CompilerOptions.Tasks); + |CompilerOptions.Tasks + |CompilerOptions.UnclosedCloseable); ALL.setAll(); HIDING 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.136 diff -u -r1.136 BinaryTypeBinding.java --- compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java 17 Jan 2011 13:00:56 -0000 1.136 +++ compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java 18 Jul 2011 08:34:48 -0000 @@ -971,6 +971,12 @@ variable.resolve(); return variable; } +public boolean hasTypeBit(int bit) { + // ensure hierarchy is resolved + superclass(); + superInterfaces(); + return (this.typeBits & bit) != 0; +} private void initializeTypeVariable(TypeVariableBinding variable, TypeVariableBinding[] existingVariables, SignatureWrapper wrapper, char[][][] missingTypeNames) { // ParameterSignature = Identifier ':' TypeSignature // or Identifier ':' TypeSignature(optional) InterfaceBound(s) @@ -1139,8 +1145,13 @@ // finish resolving the type this.superclass = (ReferenceBinding) resolveType(this.superclass, this.environment, true /* raw conversion */); this.tagBits &= ~TagBits.HasUnresolvedSuperclass; - if (this.superclass.problemId() == ProblemReasons.NotFound) + if (this.superclass.problemId() == ProblemReasons.NotFound) { this.tagBits |= TagBits.HierarchyHasProblems; // propagate type inconsistency + } else { + this.superclass.superclass(); + this.superclass.superInterfaces(); + } + this.typeBits |= this.superclass.typeBits; return this.superclass; } // NOTE: superInterfaces of binary types are resolved when needed @@ -1150,8 +1161,13 @@ for (int i = this.superInterfaces.length; --i >= 0;) { this.superInterfaces[i] = (ReferenceBinding) resolveType(this.superInterfaces[i], this.environment, true /* raw conversion */); - if (this.superInterfaces[i].problemId() == ProblemReasons.NotFound) + if (this.superInterfaces[i].problemId() == ProblemReasons.NotFound) { this.tagBits |= TagBits.HierarchyHasProblems; // propagate type inconsistency + } else { + this.superInterfaces[i].superclass(); + this.superInterfaces[i].superInterfaces(); + } + this.typeBits |= this.superInterfaces[i].typeBits; } this.tagBits &= ~TagBits.HasUnresolvedSuperinterfaces; return this.superInterfaces; Index: compiler/org/eclipse/jdt/internal/compiler/lookup/BlockScope.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BlockScope.java,v retrieving revision 1.123.2.3 diff -u -r1.123.2.3 BlockScope.java --- compiler/org/eclipse/jdt/internal/compiler/lookup/BlockScope.java 5 May 2011 14:31:10 -0000 1.123.2.3 +++ compiler/org/eclipse/jdt/internal/compiler/lookup/BlockScope.java 18 Jul 2011 08:34:48 -0000 @@ -10,10 +10,15 @@ *******************************************************************************/ package org.eclipse.jdt.internal.compiler.lookup; +import java.util.ArrayList; +import java.util.List; + import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ast.*; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.codegen.CodeStream; +import org.eclipse.jdt.internal.compiler.flow.FlowContext; +import org.eclipse.jdt.internal.compiler.flow.FlowInfo; import org.eclipse.jdt.internal.compiler.impl.Constant; import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; @@ -957,4 +962,93 @@ } } } + +private List trackingVariables; +public int registerTrackingVariable(FakedTrackingVariable fakedTrackingVariable) { + if (this.trackingVariables == null) + this.trackingVariables = new ArrayList(); + this.trackingVariables.add(fakedTrackingVariable); + MethodScope outerMethodScope = outerMostMethodScope(); + return outerMethodScope.analysisIndex + (++outerMethodScope.trackVarCount); + +} +public void checkUnclosedCloseables(FlowInfo flowInfo, FlowContext flowContext, ASTNode location) { + if (this.trackingVariables == null) return; + for (int i=0; i= ClassFileConstants.JDK1_7 + && !trackingVar.isInsideTryWithResources) + problemReporter().explicitlyClosedAutoCloseable(trackingVar); + } + } +} +/** + * Find all tracking variables in scope that have relevant null status. + * @param moreTrackVars list for collecting all found tracking variables + * @param flowInfo use to check if tracking variable has interesting status + */ +public void getTrackVars(List moreTrackVars, FlowInfo flowInfo) { + if (this.trackingVariables != null) { + for (int i=0; i always closed + } + else if ( elseFlowInfo.isDefinitelyNonNull(trackingVar.binding) // closed in else branch + && thenFlowInfo.isDefinitelyNull(trackingVar.originalBinding)) // null in then branch + { + thenFlowInfo.markAsDefinitelyNonNull(trackingVar.binding); // -> always closed + } + } + } + if (this.parent instanceof BlockScope) + ((BlockScope) this.parent).correlateTrackingVarsIfElse(thenFlowInfo, elseFlowInfo); +} } Index: compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java,v retrieving revision 1.183.2.1 diff -u -r1.183.2.1 ClassScope.java --- compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java 20 Apr 2011 20:40:00 -0000 1.183.2.1 +++ compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java 18 Jul 2011 08:34:49 -0000 @@ -907,6 +907,7 @@ } else { // only want to reach here when no errors are reported sourceType.superclass = superclass; + sourceType.typeBits |= superclass.typeBits; return true; } } @@ -1017,6 +1018,7 @@ noProblems &= superInterfaceRef.resolvedType.isValidBinding(); } // only want to reach here when no errors are reported + sourceType.typeBits |= superInterface.typeBits; interfaceBindings[count++] = superInterface; } // hold onto all correctly resolved superinterfaces Index: compiler/org/eclipse/jdt/internal/compiler/lookup/LocalVariableBinding.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LocalVariableBinding.java,v retrieving revision 1.49.2.2 diff -u -r1.49.2.2 LocalVariableBinding.java --- compiler/org/eclipse/jdt/internal/compiler/lookup/LocalVariableBinding.java 27 Apr 2011 16:33:51 -0000 1.49.2.2 +++ compiler/org/eclipse/jdt/internal/compiler/lookup/LocalVariableBinding.java 18 Jul 2011 08:34:49 -0000 @@ -19,6 +19,7 @@ import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.Annotation; +import org.eclipse.jdt.internal.compiler.ast.FakedTrackingVariable; import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.impl.Constant; @@ -39,6 +40,8 @@ public int[] initializationPCs; public int initializationCount = 0; + public FakedTrackingVariable closeTracker; // track closing of instances of type AutoCloseable + // for synthetic local variables // if declaration slot is not positionned, the variable will not be listed in attribute // note that the name of a variable should be chosen so as not to conflict with user ones (usually starting with a space char is all needed) Index: compiler/org/eclipse/jdt/internal/compiler/lookup/MethodScope.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodScope.java,v retrieving revision 1.78.2.1 diff -u -r1.78.2.1 MethodScope.java --- compiler/org/eclipse/jdt/internal/compiler/lookup/MethodScope.java 8 Mar 2011 17:20:09 -0000 1.78.2.1 +++ compiler/org/eclipse/jdt/internal/compiler/lookup/MethodScope.java 18 Jul 2011 08:34:50 -0000 @@ -49,6 +49,9 @@ // inner-emulation public SyntheticArgumentBinding[] extraSyntheticArguments; + // count number of tracking variables, see FakedTrackingVariable + int trackVarCount; + public MethodScope(ClassScope parent, ReferenceContext context, boolean isStatic) { super(METHOD_SCOPE, parent); this.locals = new LocalVariableBinding[5]; 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.120.2.2 diff -u -r1.120.2.2 ParameterizedTypeBinding.java --- compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java 9 May 2011 14:30:21 -0000 1.120.2.2 +++ compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java 18 Jul 2011 08:34:50 -0000 @@ -629,6 +629,13 @@ return this.type.hasMemberTypes(); } + public boolean hasTypeBit(int bit) { + TypeBinding erasure = erasure(); + if (erasure instanceof ReferenceBinding) + return ((ReferenceBinding) erasure).hasTypeBit(bit); + return false; + } + /** * @see org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding#implementsMethod(MethodBinding) */ Index: compiler/org/eclipse/jdt/internal/compiler/lookup/ProblemReferenceBinding.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ProblemReferenceBinding.java,v retrieving revision 1.22 diff -u -r1.22 ProblemReferenceBinding.java --- compiler/org/eclipse/jdt/internal/compiler/lookup/ProblemReferenceBinding.java 27 Jun 2008 16:04:02 -0000 1.22 +++ compiler/org/eclipse/jdt/internal/compiler/lookup/ProblemReferenceBinding.java 18 Jul 2011 08:34:50 -0000 @@ -40,6 +40,12 @@ return this.closestMatch; } +public boolean hasTypeBit(int bit) { + if (this.closestMatch != null) + return this.closestMatch.hasTypeBit(bit); + return false; +} + /* API * Answer the problem id associated with the receiver. * NoError if the receiver is a valid binding. 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.140.2.6 diff -u -r1.140.2.6 ReferenceBinding.java --- compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java 28 Jun 2011 15:35:07 -0000 1.140.2.6 +++ compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java 18 Jul 2011 08:34:51 -0000 @@ -17,6 +17,7 @@ import java.util.Arrays; import java.util.Comparator; +import org.eclipse.core.runtime.Assert; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; @@ -45,6 +46,8 @@ private SimpleLookupTable compatibleCache; + int typeBits; + public static final ReferenceBinding LUB_GENERIC = new ReferenceBinding() { /* used for lub computation */}; private static final Comparator FIELD_COMPARATOR = new Comparator() { @@ -428,8 +431,10 @@ case 'A' : switch(typeName.length) { case 13 : - if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_AUTOCLOSEABLE[2])) + if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_AUTOCLOSEABLE[2])) { this.id = TypeIds.T_JavaLangAutoCloseable; + this.typeBits |= TypeIds.BitAutoCloseable; + } return; case 14: if (CharOperation.equals(typeName, TypeConstants.JAVA_LANG_ASSERTIONERROR[2])) @@ -928,6 +933,12 @@ return (this.modifiers & ExtraCompilerModifiers.AccRestrictedAccess) != 0; } +public boolean hasTypeBit(int bit) { + // overridden in relevant subclasses + Assert.isTrue(false, "Unsupported query for type "+getClass().getName()); //$NON-NLS-1$ + return false; // never reached +} + /** Answer true if the receiver implements anInterface or is identical to anInterface. * If searchHierarchy is true, then also search the receiver's superclasses. * Index: compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java,v retrieving revision 1.186.2.5 diff -u -r1.186.2.5 SourceTypeBinding.java --- compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java 20 Apr 2011 20:40:00 -0000 1.186.2.5 +++ compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java 18 Jul 2011 08:34:52 -0000 @@ -18,6 +18,7 @@ import java.util.HashMap; import java.util.Iterator; +import org.eclipse.core.runtime.Assert; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; @@ -1066,6 +1067,13 @@ return accessors[1]; } +public boolean hasTypeBit(int bit) { + // source types initialize type bits during connectSuperclass/interfaces() + if (!isLocalType()) + Assert.isTrue((this.tagBits & TagBits.EndHierarchyCheck) != 0, "Hierarchy should be connected"); //$NON-NLS-1$ + return (this.typeBits & bit) != 0; +} + /** * @see org.eclipse.jdt.internal.compiler.lookup.Binding#initializeDeprecatedAnnotationTagBits() */ Index: compiler/org/eclipse/jdt/internal/compiler/lookup/TypeConstants.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeConstants.java,v retrieving revision 1.51.2.6 diff -u -r1.51.2.6 TypeConstants.java --- compiler/org/eclipse/jdt/internal/compiler/lookup/TypeConstants.java 28 Jun 2011 15:35:07 -0000 1.51.2.6 +++ compiler/org/eclipse/jdt/internal/compiler/lookup/TypeConstants.java 18 Jul 2011 08:34:52 -0000 @@ -154,6 +154,7 @@ "MethodHandle$PolymorphicSignature".toCharArray() //$NON-NLS-1$ }; char[][] JAVA_LANG_AUTOCLOSEABLE = {JAVA, LANG, "AutoCloseable".toCharArray()}; //$NON-NLS-1$ + char[] CLOSE = "close".toCharArray(); //$NON-NLS-1$ // Constraints for generic type argument inference int CONSTRAINT_EQUAL = 0; // Actual = Formal Index: compiler/org/eclipse/jdt/internal/compiler/lookup/TypeIds.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeIds.java,v retrieving revision 1.36.2.5 diff -u -r1.36.2.5 TypeIds.java --- compiler/org/eclipse/jdt/internal/compiler/lookup/TypeIds.java 28 Jun 2011 15:35:07 -0000 1.36.2.5 +++ compiler/org/eclipse/jdt/internal/compiler/lookup/TypeIds.java 18 Jul 2011 08:34:53 -0000 @@ -173,4 +173,6 @@ final int Object2Object = T_JavaLangObject + (T_JavaLangObject << 4); final int BOXING = 0x200; final int UNBOXING = 0x400; + + final int BitAutoCloseable = 1; } Index: compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java,v retrieving revision 1.75 diff -u -r1.75 TypeVariableBinding.java --- compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java 25 Oct 2010 08:50:02 -0000 1.75 +++ compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java 18 Jul 2011 08:34:53 -0000 @@ -42,6 +42,7 @@ this.modifiers = ClassFileConstants.AccPublic | ExtraCompilerModifiers.AccGenericSignature; // treat type var as public this.tagBits |= TagBits.HasTypeVariable; this.environment = environment; + this.typeBits = -1; } /** @@ -307,6 +308,19 @@ return true; } + public boolean hasTypeBit(int bit) { + if (this.typeBits == -1) { + // initialize from bounds + this.typeBits = 0; + if (this.superclass != null) + this.typeBits |= this.superclass.typeBits; + if (this.superInterfaces != null) + for (int i = 0, l = this.superInterfaces.length; i < l; i++) + this.typeBits |= this.superInterfaces[i].typeBits; + } + return (this.typeBits & bit) != 0; + } + /** * Returns true if the type variable is directly bound to a given type */ Index: compiler/org/eclipse/jdt/internal/compiler/lookup/WildcardBinding.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/WildcardBinding.java,v retrieving revision 1.76 diff -u -r1.76 WildcardBinding.java --- compiler/org/eclipse/jdt/internal/compiler/lookup/WildcardBinding.java 24 Jun 2009 18:21:59 -0000 1.76 +++ compiler/org/eclipse/jdt/internal/compiler/lookup/WildcardBinding.java 18 Jul 2011 08:34:53 -0000 @@ -54,6 +54,7 @@ if (bound instanceof UnresolvedReferenceBinding) ((UnresolvedReferenceBinding) bound).addWrapper(this, environment); this.tagBits |= TagBits.HasUnresolvedTypeVariables; // cleared in resolve() + this.typeBits = -1; } public int kind() { @@ -420,6 +421,19 @@ return this.genericType.hashCode(); } + public boolean hasTypeBit(int bit) { + if (this.typeBits == -1) { + // initialize from upper bounds + this.typeBits = 0; + if (this.superclass != null) + this.typeBits |= this.superclass.typeBits; + if (this.superInterfaces != null) + for (int i = 0, l = this.superInterfaces.length; i < l; i++) + this.typeBits |= this.superInterfaces[i].typeBits; + } + return (this.typeBits & bit) != 0; + } + void initialize(ReferenceBinding someGenericType, TypeBinding someBound, TypeBinding[] someOtherBounds) { this.genericType = someGenericType; this.bound = someBound; 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.430.2.35 diff -u -r1.430.2.35 ProblemReporter.java --- compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java 13 Jul 2011 08:27:07 -0000 1.430.2.35 +++ compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java 18 Jul 2011 08:34:59 -0000 @@ -55,6 +55,7 @@ import org.eclipse.jdt.internal.compiler.ast.EqualExpression; import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall; import org.eclipse.jdt.internal.compiler.ast.Expression; +import org.eclipse.jdt.internal.compiler.ast.FakedTrackingVariable; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; import org.eclipse.jdt.internal.compiler.ast.FieldReference; import org.eclipse.jdt.internal.compiler.ast.ImportReference; @@ -430,6 +431,15 @@ case IProblem.MethodCanBePotentiallyStatic: return CompilerOptions.MethodCanBePotentiallyStatic; + + case IProblem.UnclosedCloseable: + case IProblem.UnclosedCloseableAtExit: + return CompilerOptions.UnclosedCloseable; + case IProblem.PotentiallyUnclosedCloseable: + case IProblem.PotentiallyUnclosedCloseableAtExit: + return CompilerOptions.PotentiallyUnclosedCloseable; + case IProblem.ExplicitlyClosedAutoCloseable: + return CompilerOptions.ExplicitlyClosedAutoCloseable; case IProblem.RedundantSpecificationOfTypeArguments: return CompilerOptions.RedundantSpecificationOfTypeArguments; @@ -465,6 +475,7 @@ case CompilerOptions.ParameterAssignment : case CompilerOptions.MethodCanBeStatic : case CompilerOptions.MethodCanBePotentiallyStatic : + case CompilerOptions.ExplicitlyClosedAutoCloseable : return CategorizedProblem.CAT_CODE_STYLE; case CompilerOptions.MaskedCatchBlock : @@ -486,6 +497,8 @@ case CompilerOptions.ShouldImplementHashcode : case CompilerOptions.DeadCode : case CompilerOptions.UnusedObjectAllocation : + case CompilerOptions.UnclosedCloseable : + case CompilerOptions.PotentiallyUnclosedCloseable : return CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM; case CompilerOptions.OverriddenPackageDefaultMethod : @@ -7911,4 +7924,51 @@ } } } +public void potentiallyUnclosedCloseable(FakedTrackingVariable trackVar, ASTNode location) { + if (location == null) { + this.handle( + IProblem.PotentiallyUnclosedCloseable, + NoArgument, + NoArgument, + trackVar.sourceStart, + trackVar.sourceEnd); + } else { + String[] args = { String.valueOf(trackVar.name) }; + this.handle( + IProblem.PotentiallyUnclosedCloseableAtExit, + args, + args, + location.sourceStart, + location.sourceEnd); + } + trackVar.hasReportedProblem = true; +} +public void unclosedCloseable(FakedTrackingVariable trackVar, ASTNode location) { + if (location == null) { + this.handle( + IProblem.UnclosedCloseable, + NoArgument, + NoArgument, + trackVar.sourceStart, + trackVar.sourceEnd); + } else { + String[] args = { String.valueOf(trackVar.name) }; + this.handle( + IProblem.UnclosedCloseableAtExit, + args, + args, + location.sourceStart, + location.sourceEnd); + } + trackVar.hasReportedProblem = true; +} +public void explicitlyClosedAutoCloseable(FakedTrackingVariable trackVar) { + String[] args = { String.valueOf(trackVar.name) }; + this.handle( + IProblem.ExplicitlyClosedAutoCloseable, + args, + args, + trackVar.sourceStart, + trackVar.sourceEnd); +} } \ No newline at end of file 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.262.2.32 diff -u -r1.262.2.32 messages.properties --- compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties 13 Jul 2011 08:27:07 -0000 1.262.2.32 +++ compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties 18 Jul 2011 08:35:00 -0000 @@ -647,6 +647,11 @@ 882 = Unhandled exception type {0} thrown by automatic close() invocation on {1} 883 = '<>' operator is not allowed for source level below 1.7 884 = Redundant specification of type arguments <{0}> +885 = Value of type AutoCloseable is not closed on all paths. +886 = Instance '{0}' of type AutoCloseable may be unclosed at this point. +887 = Value of type AutoCloseable is not closed neither explicitly nor using a try-with-resources. +888 = Instance '{0}' of type AutoCloseable is not closed at this point. +889 = Instance '{0}' should be managed by try-with-resource. ### ELABORATIONS ## Access restrictions Index: model/org/eclipse/jdt/core/JavaCore.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java,v retrieving revision 1.659.2.3 diff -u -r1.659.2.3 JavaCore.java --- model/org/eclipse/jdt/core/JavaCore.java 13 Jul 2011 08:27:06 -0000 1.659.2.3 +++ model/org/eclipse/jdt/core/JavaCore.java 18 Jul 2011 08:35:03 -0000 @@ -1357,6 +1357,52 @@ */ public static final String COMPILER_PB_POTENTIALLY_MISSING_STATIC_ON_METHOD = PLUGIN_ID + ".compiler.problem.reportMethodCanBePotentiallyStatic"; //$NON-NLS-1$ /** + * Compiler option ID: Reporting a resource that is not closed properly. + *

When enabled, that compiler will issue an error or a warning if a local variable + * holds a value of type AutoCloseable (or Closeable if compliance is < 1.7) + * and if flow analysis shows that the method close() is not invoked locally on that value. + *

+ *
Option id:
"org.eclipse.jdt.core.compiler.problem.reportUnclosedCloseable"
+ *
Possible values:
{ "error", "warning", "ignore" }
+ *
Default:
"warning"
+ *
+ * @since 3.7 + * @category CompilerOptionID + */ + public static final String COMPILER_PB_UNCLOSED_CLOSEABLE = PLUGIN_ID + ".compiler.problem.unclosedCloseable"; //$NON-NLS-1$ + /** + * Compiler option ID: Reporting a resource that may not be closed properly. + *

When enabled, that compiler will issue an error or a warning if a local variable + * holds a value of type AutoCloseable (or Closeable if compliance is < 1.7) + * and if flow analysis shows that the method close() is not invoked locally + * on that value for all execution paths. + *

+ *
Option id:
"org.eclipse.jdt.core.compiler.problem.reportPotentiallyUnclosedCloseable"
+ *
Possible values:
{ "error", "warning", "ignore" }
+ *
Default:
"ignore"
+ *
+ * @since 3.7 + * @category CompilerOptionID + */ + public static final String COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE = PLUGIN_ID + ".compiler.problem.potentiallyUnclosedCloseable"; //$NON-NLS-1$ + /** + * Compiler option ID: Reporting a resource that is not managed by try-with-resources. + *

When enabled, that compiler will issue an error or a warning if a local variable + * holds a value of type AutoCloseable, and if the method close() is + * explicitly invoked on that resource, but the resource is not managed by a + * try-with-resources block. + *

Note that this option is not surfaced in the UI, as it is intended only for internal + * use for computing quick assists / cleanups. + *

+ *
Option id:
"org.eclipse.jdt.core.compiler.problem.reportPotentiallyUnclosedCloseable"
+ *
Possible values:
{ "error", "warning", "ignore" }
+ *
Default:
"ignore"
+ *
+ * @since 3.7 + * @category CompilerOptionID + */ + public static final String COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE = PLUGIN_ID + ".compiler.problem.explicitlyClosedAutoCloseable"; //$NON-NLS-1$ + /** * Compiler option ID: Setting Source Compatibility Mode. *

Specify whether which source level compatibility is used. From 1.4 on, 'assert' is a keyword * reserved for assertion support. Also note, than when toggling to 1.4 mode, the target VM #P org.eclipse.jdt.core.tests.compiler Index: src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java,v retrieving revision 1.224.2.3 diff -u -r1.224.2.3 BatchCompilerTest.java --- src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java 13 Jul 2011 08:27:36 -0000 1.224.2.3 +++ src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java 18 Jul 2011 08:35:11 -0000 @@ -1820,6 +1820,7 @@ "