View | Details | Raw Unified | Return to bug 358903 | Differences between
and this patch

Collapse All | Expand All

(-)a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TryWithResourcesStatementTest.java (-1 / +209 lines)
Lines 23-29 Link Here
23
public class TryWithResourcesStatementTest extends AbstractRegressionTest {
23
public class TryWithResourcesStatementTest extends AbstractRegressionTest {
24
24
25
static {
25
static {
26
//	TESTS_NAMES = new String[] { "test056throw"};
26
//	TESTS_NAMES = new String[] { "test059"};
27
//	TESTS_NUMBERS = new int[] { 50 };
27
//	TESTS_NUMBERS = new int[] { 50 };
28
//	TESTS_RANGE = new int[] { 11, -1 };
28
//	TESTS_RANGE = new int[] { 11, -1 };
29
}
29
}
Lines 5226-5231 Link Here
5226
			"}\n"
5226
			"}\n"
5227
		},  "");	
5227
		},  "");	
5228
}
5228
}
5229
// Bug 358903 - Filter practically unimportant resource leak warnings
5230
// Bug 360908 - Avoid resource leak warning when the underlying/chained resource is closed explicitly
5231
// a resource wrapper is not closed but the underlying resource is
5232
public void test059a() {
5233
	Map options = getCompilerOptions();
5234
	options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
5235
	options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.WARNING);
5236
	this.runConformTest(
5237
		new String[] {
5238
			"X.java",
5239
			"import java.io.File;\n" +
5240
			"import java.io.BufferedInputStream;\n" +
5241
			"import java.io.FileInputStream;\n" +
5242
			"import java.io.IOException;\n" +
5243
			"public class X {\n" +
5244
			"    void foo() throws IOException {\n" +
5245
			"        File file = new File(\"somefile\");\n" +
5246
			"        FileInputStream fileStream  = new FileInputStream(file);\n" +
5247
			"        BufferedInputStream bis = new BufferedInputStream(fileStream);\n" +
5248
			"        BufferedInputStream doubleWrap = new BufferedInputStream(bis);\n" +
5249
			"        System.out.println(bis.available());\n" +
5250
			"        fileStream.close();\n" +
5251
			"    }\n" +
5252
			"    void inline() throws IOException {\n" +
5253
			"        File file = new File(\"somefile\");\n" +
5254
			"        FileInputStream fileStream;\n" +
5255
			"        BufferedInputStream bis = new BufferedInputStream(fileStream = new FileInputStream(file));\n" +
5256
			"        System.out.println(bis.available());\n" +
5257
			"        fileStream.close();\n" +
5258
			"    }\n" +
5259
			"    public static void main(String[] args) throws IOException {\n" +
5260
			"        try {\n" +
5261
			"            new X().foo();\n" +
5262
			"        } catch (IOException ex) {" +
5263
			"            System.out.println(\"Got IO Exception\");\n" +
5264
			"        }\n" +
5265
			"    }\n" +
5266
			"}\n"
5267
		},
5268
		"Got IO Exception",
5269
		null,
5270
		true,
5271
		null,
5272
		options,
5273
		null);
5274
}
5275
// Bug 358903 - Filter practically unimportant resource leak warnings
5276
// a closeable without OS resource is not closed
5277
public void test059b() {
5278
	Map options = getCompilerOptions();
5279
	options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
5280
	options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.WARNING);
5281
	this.runConformTest(
5282
		new String[] {
5283
			"X.java",
5284
			"import java.io.StringReader;\n" +
5285
			"import java.io.IOException;\n" +
5286
			"public class X {\n" +
5287
			"    void foo() throws IOException {\n" +
5288
			"        StringReader string  = new StringReader(\"content\");\n" +
5289
			"        System.out.println(string.read());\n" +
5290
			"    }\n" +
5291
			"    public static void main(String[] args) throws IOException {\n" +
5292
			"        new X().foo();\n" +
5293
			"    }\n" +
5294
			"}\n"
5295
		},
5296
		"99", // character 'c'
5297
		null,
5298
		true,
5299
		null,
5300
		options,
5301
		null);
5302
}
5303
// Bug 358903 - Filter practically unimportant resource leak warnings
5304
// a resource wrapper is not closed but the underlying closeable is resource-free
5305
public void test059c() {
5306
	Map options = getCompilerOptions();
5307
	options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
5308
	options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.WARNING);
5309
	this.runConformTest(
5310
		new String[] {
5311
			"X.java",
5312
			"import java.io.BufferedReader;\n" +
5313
			"import java.io.StringReader;\n" +
5314
			"import java.io.IOException;\n" +
5315
			"public class X {\n" +
5316
			"    void foo() throws IOException {\n" +
5317
			"        StringReader input = new StringReader(\"content\");\n" +
5318
			"        BufferedReader br = new BufferedReader(input);\n" +
5319
			"        BufferedReader doubleWrap = new BufferedReader(br);\n" +
5320
			"        System.out.println(br.read());\n" +
5321
			"    }\n" +
5322
			"    void inline() throws IOException {\n" +
5323
			"        BufferedReader br = new BufferedReader(new StringReader(\"content\"));\n" +
5324
			"        System.out.println(br.read());\n" +
5325
			"    }\n" +
5326
			"    public static void main(String[] args) throws IOException {\n" +
5327
			"        new X().foo();\n" +
5328
			"    }\n" +
5329
			"}\n"
5330
		},
5331
		"99",
5332
		null,
5333
		true,
5334
		null,
5335
		options,
5336
		null);
5337
}
5338
// Bug 358903 - Filter practically unimportant resource leak warnings
5339
// a resource wrapper is not closed neither is the underlying resource
5340
public void test059d() {
5341
	Map options = getCompilerOptions();
5342
	options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
5343
	options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.WARNING);
5344
	this.runNegativeTest(
5345
		new String[] {
5346
			"X.java",
5347
			"import java.io.File;\n" +
5348
			"import java.io.BufferedInputStream;\n" +
5349
			"import java.io.FileInputStream;\n" +
5350
			"import java.io.IOException;\n" +
5351
			"public class X {\n" +
5352
			"    void foo() throws IOException {\n" +
5353
			"        File file = new File(\"somefile\");\n" +
5354
			"        FileInputStream fileStream  = new FileInputStream(file);\n" +
5355
			"        BufferedInputStream bis = new BufferedInputStream(fileStream);\n" +
5356
			"        BufferedInputStream doubleWrap = new BufferedInputStream(bis);\n" +
5357
			"        System.out.println(bis.available());\n" +
5358
			"    }\n" +
5359
			"    void inline() throws IOException {\n" +
5360
			"        File file = new File(\"somefile\");\n" +
5361
			"        BufferedInputStream bis2 = new BufferedInputStream(new FileInputStream(file));\n" +
5362
			"        System.out.println(bis2.available());\n" +
5363
			"    }\n" +
5364
			"    public static void main(String[] args) throws IOException {\n" +
5365
			"        try {\n" +
5366
			"            new X().foo();\n" +
5367
			"        } catch (IOException ex) {" +
5368
			"            System.out.println(\"Got IO Exception\");\n" +
5369
			"        }\n" +
5370
			"    }\n" +
5371
			"}\n"
5372
		},
5373
		"----------\n" + 
5374
		"1. ERROR in X.java (at line 9)\n" + 
5375
		"	BufferedInputStream bis = new BufferedInputStream(fileStream);\n" + 
5376
		"	                    ^^^\n" + 
5377
		"Resource leak: \'bis\' is never closed\n" + 
5378
		"----------\n" + 
5379
		"2. ERROR in X.java (at line 15)\n" + 
5380
		"	BufferedInputStream bis2 = new BufferedInputStream(new FileInputStream(file));\n" + 
5381
		"	                    ^^^^\n" + 
5382
		"Resource leak: \'bis2\' is never closed\n" + 
5383
		"----------\n",
5384
		null,
5385
		true,
5386
		options);
5387
}
5388
// Bug 358903 - Filter practically unimportant resource leak warnings
5389
// Bug 361073 - Avoid resource leak warning when the top level resource is closed explicitly
5390
// a resource wrapper is closed closing also the underlying resource
5391
public void test059e() {
5392
	Map options = getCompilerOptions();
5393
	options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
5394
	options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.WARNING);
5395
	this.runConformTest(
5396
		new String[] {
5397
			"X.java",
5398
			"import java.io.File;\n" +
5399
			"import java.io.BufferedInputStream;\n" +
5400
			"import java.io.FileInputStream;\n" +
5401
			"import java.io.IOException;\n" +
5402
			"public class X {\n" +
5403
			"    void foo() throws IOException {\n" +
5404
			"        File file = new File(\"somefile\");\n" +
5405
			"        FileInputStream fileStream  = new FileInputStream(file);\n" +
5406
			"        BufferedInputStream bis = new BufferedInputStream(fileStream);\n" +
5407
			"        BufferedInputStream doubleWrap = new BufferedInputStream(bis);\n" +
5408
			"        System.out.println(bis.available());\n" +
5409
			"        bis.close();\n" +
5410
			"    }\n" +
5411
			"    void inline() throws IOException {\n" +
5412
			"        File file = new File(\"somefile\");\n" +
5413
			"        BufferedInputStream bis2 = new BufferedInputStream(new FileInputStream(file));\n" +
5414
			"        System.out.println(bis2.available());\n" +
5415
			"        bis2.close();\n" +
5416
			"        FileInputStream fileStream  = null;\n" +
5417
			"        BufferedInputStream bis3 = new BufferedInputStream(fileStream = new FileInputStream(file));\n" +
5418
			"        System.out.println(bis3.available());\n" +
5419
			"        bis3.close();\n" +
5420
			"    }\n" +
5421
			"    public static void main(String[] args) throws IOException {\n" +
5422
			"        try {\n" +
5423
			"            new X().foo();\n" +
5424
			"        } catch (IOException ex) {" +
5425
			"            System.out.println(\"Got IO Exception\");\n" +
5426
			"        }\n" +
5427
			"    }\n" +
5428
			"}\n"
5429
		},
5430
		"Got IO Exception",
5431
		null,
5432
		true,
5433
		null,
5434
		options,
5435
		null);
5436
}
5229
public static Class testClass() {
5437
public static Class testClass() {
5230
	return TryWithResourcesStatementTest.class;
5438
	return TryWithResourcesStatementTest.class;
5231
}
5439
}
(-)a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FakedTrackingVariable.java (-9 / +91 lines)
Lines 48-53 Link Here
48
	private static final int CLOSED_IN_NESTED_METHOD = 4;
48
	private static final int CLOSED_IN_NESTED_METHOD = 4;
49
	// a location independent issue has been reported already against this resource:
49
	// a location independent issue has been reported already against this resource:
50
	private static final int REPORTED = 8;
50
	private static final int REPORTED = 8;
51
	// a resource is wrapped in another resource:
52
	private static final int WRAPPED = 16;
51
	
53
	
52
	/**
54
	/**
53
	 * Bitset of {@link #CLOSE_SEEN}, {@link #PASSED_TO_OUTSIDE}, {@link #CLOSED_IN_NESTED_METHOD} and {@link #REPORTED}.
55
	 * Bitset of {@link #CLOSE_SEEN}, {@link #PASSED_TO_OUTSIDE}, {@link #CLOSED_IN_NESTED_METHOD} and {@link #REPORTED}.
Lines 58-67 Link Here
58
	
60
	
59
	public LocalVariableBinding originalBinding; // the real local being tracked
61
	public LocalVariableBinding originalBinding; // the real local being tracked
60
	
62
	
61
	HashMap recordedLocations; // initially null, ASTNode -> Integer 
63
	HashMap recordedLocations; // initially null, ASTNode -> Integer
64
65
	public FakedTrackingVariable innerTracker; // chained tracking variable of a chained (wrapped) resource
62
66
63
67
64
	public FakedTrackingVariable(LocalVariableBinding original, Statement location) {
68
	public FakedTrackingVariable(LocalVariableBinding original, ASTNode location) {
65
		super(original.name, location.sourceStart, location.sourceEnd);
69
		super(original.name, location.sourceStart, location.sourceEnd);
66
		this.type = new SingleTypeReference(
70
		this.type = new SingleTypeReference(
67
				TypeConstants.OBJECT,
71
				TypeConstants.OBJECT,
Lines 145-151 Link Here
145
				// re-assigning from a fresh, mark as not-closed again:
149
				// re-assigning from a fresh, mark as not-closed again:
146
				flowInfo.markAsDefinitelyNull(previousTrackerBinding);
150
				flowInfo.markAsDefinitelyNull(previousTrackerBinding);
147
			} else {												// 3. no re-use, create a fresh tracking variable:
151
			} else {												// 3. no re-use, create a fresh tracking variable:
148
				local.closeTracker = new FakedTrackingVariable(local, location);
152
				rhsTrackVar = analyseCloseableExpression(local, location, rhs);
153
				if (rhsTrackVar == null)
154
					return;
155
				local.closeTracker = rhsTrackVar;
149
				// a fresh resource, mark as not-closed:
156
				// a fresh resource, mark as not-closed:
150
				flowInfo.markAsDefinitelyNull(local.closeTracker.binding);
157
				flowInfo.markAsDefinitelyNull(local.closeTracker.binding);
151
// TODO(stephan): this might be useful, but I could not find a test case for it: 
158
// TODO(stephan): this might be useful, but I could not find a test case for it: 
Lines 153-158 Link Here
153
//					flowContext.initsOnFinally.markAsDefinitelyNonNull(trackerBinding);
160
//					flowContext.initsOnFinally.markAsDefinitelyNonNull(trackerBinding);
154
			}
161
			}
155
		}
162
		}
163
	}
164
	/* analyse structure of a closeable expression, matching (chained) resources against our white lists. */
165
	private static FakedTrackingVariable analyseCloseableExpression(LocalVariableBinding local, 
166
				ASTNode location, Expression expression) 
167
	{
168
		if (expression.resolvedType instanceof ReferenceBinding) {
169
			ReferenceBinding resourceType = (ReferenceBinding) expression.resolvedType;
170
			
171
			if (resourceType.hasTypeBit(TypeIds.BitResourceFreeCloseable)) {
172
				// (a) resource-free closeable: -> null
173
				return null;
174
			}					
175
			
176
			if (resourceType.hasTypeBit(TypeIds.BitWrapperCloseable)) {
177
				if (expression instanceof Assignment)
178
					expression = ((Assignment)expression).expression;
179
				if (expression instanceof AllocationExpression) {
180
					Expression[] args = ((AllocationExpression) expression).arguments;
181
					if (args != null && args.length == 1) {
182
						Expression arg = args[0];
183
						if (arg instanceof Assignment) {
184
							Expression lhs = ((Assignment)arg).lhs;
185
							if (lhs instanceof SingleNameReference) {
186
								SingleNameReference lhsRef = (SingleNameReference) lhs;
187
								if (lhsRef.binding instanceof LocalVariableBinding) {
188
									FakedTrackingVariable innerTracker = analyseCloseableExpression(((LocalVariableBinding)lhsRef.binding), arg, ((Assignment) arg).expression);
189
									if (innerTracker != null) {
190
										// (b.1) wrapper with relevant inner: -> tracking var, linked to inner tracking var
191
										FakedTrackingVariable tracker = new FakedTrackingVariable(local, location); 
192
										tracker.innerTracker = innerTracker;
193
										innerTracker.globalClosingState |= WRAPPED;
194
										return tracker;
195
									}									
196
								}
197
							}
198
							// FIXME: if we ever get here, what should we do?
199
						}
200
						if (arg instanceof SingleNameReference) {
201
							SingleNameReference ref = (SingleNameReference) arg;
202
							if (ref.binding instanceof LocalVariableBinding) {
203
								LocalVariableBinding innerLocal = (LocalVariableBinding)ref.binding;
204
								FakedTrackingVariable innerTracker = innerLocal.closeTracker;
205
								if (innerTracker != null)
206
									innerTracker = analyseCloseableExpression(innerLocal, expression, arg);
207
								if (innerTracker != null) {
208
									// (b.2) wrapper with relevant inner: -> tracking var, linked to inner tracking var
209
									FakedTrackingVariable tracker = new FakedTrackingVariable(local, location); 
210
									tracker.innerTracker = innerTracker;
211
									innerTracker.globalClosingState |= WRAPPED;
212
									return tracker;
213
								}
214
							}
215
						} else if (arg instanceof AllocationExpression) {
216
							ReferenceBinding innerType = (ReferenceBinding)arg.resolvedType;
217
							if (!innerType.hasTypeBit(TypeIds.BitResourceFreeCloseable|TypeIds.BitWrapperCloseable)) {
218
								// (c) wrapper alloc with direct nested alloc of regular: -> normal track var (no local represents inner)
219
								return new FakedTrackingVariable(local, location);
220
							}
221
						}
222
					}
223
				}
224
				// (d) wrapper with irrelevant inner: -> null
225
				return null;
226
			}
227
		}
228
		if (local.closeTracker != null)
229
			// (e): inner has already been analysed: -> re-use track var
230
			return local.closeTracker;
231
		// (f): normal resource: -> normal tracking var
232
		return new FakedTrackingVariable(local, location);
156
	}
233
	}
157
234
158
	/** Answer wither the given type binding is a subtype of java.lang.AutoCloseable. */
235
	/** Answer wither the given type binding is a subtype of java.lang.AutoCloseable. */
Lines 182-188 Link Here
182
	 */
259
	 */
183
	public static FlowInfo markPassedToOutside(BlockScope scope, Expression expression, FlowInfo flowInfo) {
260
	public static FlowInfo markPassedToOutside(BlockScope scope, Expression expression, FlowInfo flowInfo) {
184
		FakedTrackingVariable trackVar = getCloseTrackingVariable(expression);
261
		FakedTrackingVariable trackVar = getCloseTrackingVariable(expression);
185
		if (trackVar != null) {
262
		if (trackVar != null && trackVar.innerTracker == null) { // ignore when resource is passed to the ctor of a resource wrapper
186
			trackVar.globalClosingState |= PASSED_TO_OUTSIDE;
263
			trackVar.globalClosingState |= PASSED_TO_OUTSIDE;
187
			if (scope.methodScope() != trackVar.methodScope)
264
			if (scope.methodScope() != trackVar.methodScope)
188
				trackVar.globalClosingState |= CLOSED_IN_NESTED_METHOD;
265
				trackVar.globalClosingState |= CLOSED_IN_NESTED_METHOD;
Lines 200-208 Link Here
200
		this.recordedLocations.put(location, new Integer(nullStatus));
277
		this.recordedLocations.put(location, new Integer(nullStatus));
201
	}
278
	}
202
279
203
	public boolean reportRecordedErrors(Scope scope) {
280
	public boolean reportRecordedErrors(Scope scope, int innerStatus) {
204
		if (this.globalClosingState == 0) {
281
		if (this.globalClosingState == 0) {
205
			reportError(scope.problemReporter(), null, FlowInfo.NULL);
282
			reportError(scope.problemReporter(), null, FlowInfo.NULL, innerStatus);
206
			return true;
283
			return true;
207
		}
284
		}
208
		boolean hasReported = false;
285
		boolean hasReported = false;
Lines 210-223 Link Here
210
			Iterator locations = this.recordedLocations.entrySet().iterator();
287
			Iterator locations = this.recordedLocations.entrySet().iterator();
211
			while (locations.hasNext()) {
288
			while (locations.hasNext()) {
212
				Map.Entry entry = (Entry) locations.next();
289
				Map.Entry entry = (Entry) locations.next();
213
				reportError(scope.problemReporter(), (ASTNode)entry.getKey(), ((Integer)entry.getValue()).intValue());
290
				reportError(scope.problemReporter(), (ASTNode)entry.getKey(), ((Integer)entry.getValue()).intValue(), 0); // FIXME: innerStatus
214
				hasReported = true;
291
				hasReported = true;
215
			}
292
			}
216
		}
293
		}
217
		return hasReported;
294
		return hasReported;
218
	}
295
	}
219
	
296
	
220
	public void reportError(ProblemReporter problemReporter, ASTNode location, int nullStatus) {
297
	public void reportError(ProblemReporter problemReporter, ASTNode location, int nullStatus, int innerStatus) {
298
		if (this.innerTracker != null && innerStatus == FlowInfo.NON_NULL) {
299
			return; // inner resource is closed, don't complain against the wrapper
300
		}
301
		if ((this.globalClosingState & WRAPPED) != 0)
302
			return;
221
		if (nullStatus == FlowInfo.NULL) {
303
		if (nullStatus == FlowInfo.NULL) {
222
			if ((this.globalClosingState & CLOSED_IN_NESTED_METHOD) != 0)
304
			if ((this.globalClosingState & CLOSED_IN_NESTED_METHOD) != 0)
223
				problemReporter.potentiallyUnclosedCloseable(this, location);
305
				problemReporter.potentiallyUnclosedCloseable(this, location);
Lines 225-231 Link Here
225
				problemReporter.unclosedCloseable(this, location);
307
				problemReporter.unclosedCloseable(this, location);
226
		} else if (nullStatus == FlowInfo.POTENTIALLY_NULL) {
308
		} else if (nullStatus == FlowInfo.POTENTIALLY_NULL) {
227
			problemReporter.potentiallyUnclosedCloseable(this, location);
309
			problemReporter.potentiallyUnclosedCloseable(this, location);
228
		}		
310
		}
229
	}
311
	}
230
312
231
	public void reportExplicitClosing(ProblemReporter problemReporter) {
313
	public void reportExplicitClosing(ProblemReporter problemReporter) {
(-)a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java (+2 lines)
Lines 1276-1281 Link Here
1276
		}
1276
		}
1277
	}
1277
	}
1278
	this.typeBits |= this.superclass.typeBits;
1278
	this.typeBits |= this.superclass.typeBits;
1279
	if (this.superclass.hasTypeBit(TypeIds.BitAutoCloseable|TypeIds.BitCloseable))
1280
		this.typeBits |= applyCloseableWhitelists();
1279
	return this.superclass;
1281
	return this.superclass;
1280
}
1282
}
1281
// NOTE: superInterfaces of binary types are resolved when needed
1283
// NOTE: superInterfaces of binary types are resolved when needed
(-)a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BlockScope.java (-5 / +13 lines)
Lines 1007-1026 Link Here
1007
		// try to improve info if a close() inside finally was observed:
1007
		// try to improve info if a close() inside finally was observed:
1008
		if (locationScope != null) // only check at method exit points
1008
		if (locationScope != null) // only check at method exit points
1009
			status = locationScope.mergeCloseStatus(status, trackingVar.binding, this);
1009
			status = locationScope.mergeCloseStatus(status, trackingVar.binding, this);
1010
		// consider wrappers (per white list) encapsulating an inner resource:
1011
		int innerStatus = 0;
1012
		if (trackingVar.innerTracker != null) {
1013
			LocalVariableBinding innerVar = trackingVar.innerTracker.binding;
1014
			innerStatus = getNullStatusAggressively(innerVar, flowInfo);
1015
			if (locationScope != null) // only check at method exit points
1016
				innerStatus = locationScope.mergeCloseStatus(innerStatus, innerVar, this);
1017
		}
1010
		if (status == FlowInfo.NULL) {
1018
		if (status == FlowInfo.NULL) {
1011
			// definitely unclosed: highest priority
1019
			// definitely unclosed: highest priority
1012
			reportResourceLeak(trackingVar, location, status);
1020
			reportResourceLeak(trackingVar, location, status, innerStatus);
1013
			continue;
1021
			continue;
1014
		}
1022
		}
1015
		if (location == null) // at end of block and not definitely unclosed
1023
		if (location == null) // at end of block and not definitely unclosed
1016
		{
1024
		{
1017
			// problems at specific locations: medium priority
1025
			// problems at specific locations: medium priority
1018
			if (trackingVar.reportRecordedErrors(this)) // ... report previously recorded errors
1026
			if (trackingVar.reportRecordedErrors(this, innerStatus)) // ... report previously recorded errors
1019
				continue;
1027
				continue;
1020
		} 
1028
		} 
1021
		if (status == FlowInfo.POTENTIALLY_NULL) {
1029
		if (status == FlowInfo.POTENTIALLY_NULL) {
1022
			// potentially unclosed: lower priority
1030
			// potentially unclosed: lower priority
1023
			reportResourceLeak(trackingVar, location, status);
1031
			reportResourceLeak(trackingVar, location, status, innerStatus);
1024
		} else if (status == FlowInfo.NON_NULL) {
1032
		} else if (status == FlowInfo.NON_NULL) {
1025
			// properly closed but not managed by t-w-r: lowest priority 
1033
			// properly closed but not managed by t-w-r: lowest priority 
1026
			if (environment().globalOptions.complianceLevel >= ClassFileConstants.JDK1_7)
1034
			if (environment().globalOptions.complianceLevel >= ClassFileConstants.JDK1_7)
Lines 1053-1063 Link Here
1053
	return status;
1061
	return status;
1054
}
1062
}
1055
1063
1056
private void reportResourceLeak(FakedTrackingVariable trackingVar, ASTNode location, int nullStatus) {
1064
private void reportResourceLeak(FakedTrackingVariable trackingVar, ASTNode location, int nullStatus, int innerStatus) {
1057
	if (location != null)
1065
	if (location != null)
1058
		trackingVar.recordErrorLocation(location, nullStatus);
1066
		trackingVar.recordErrorLocation(location, nullStatus);
1059
	else
1067
	else
1060
		trackingVar.reportError(problemReporter(), null, nullStatus);
1068
		trackingVar.reportError(problemReporter(), null, nullStatus, innerStatus);
1061
}
1069
}
1062
1070
1063
/** 
1071
/** 
(-)a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java (+27 lines)
Lines 1469-1472 Link Here
1469
public FieldBinding[] unResolvedFields() {
1469
public FieldBinding[] unResolvedFields() {
1470
	return Binding.NO_FIELDS;
1470
	return Binding.NO_FIELDS;
1471
}
1471
}
1472
1473
/*
1474
 * If a type - known to be a Closeable - is mentioned in one of our white lists
1475
 * answer the typeBit for the white list (BitWrapperCloseable or BitResourceFreeCloseable).
1476
 */
1477
protected int applyCloseableWhitelists() {
1478
	switch (this.compoundName.length) {
1479
		case 3:
1480
			if (CharOperation.equals(TypeConstants.JAVA, this.compoundName[0])) {
1481
				if (CharOperation.equals(TypeConstants.IO, this.compoundName[1])) {
1482
					char[] simpleName = this.compoundName[2];
1483
					int l = TypeConstants.JAVA_IO_WRAPPER_CLOSEABLES.length;
1484
					for (int i = 0; i < l; i++) {
1485
						if (CharOperation.equals(simpleName, TypeConstants.JAVA_IO_WRAPPER_CLOSEABLES[i]))
1486
							return TypeIds.BitWrapperCloseable;
1487
					}
1488
					l = TypeConstants.JAVA_IO_RESOURCE_FREE_CLOSEABLES.length;
1489
					for (int i = 0; i < l; i++) {
1490
						if (CharOperation.equals(simpleName, TypeConstants.JAVA_IO_RESOURCE_FREE_CLOSEABLES[i]))
1491
							return TypeIds.BitResourceFreeCloseable;
1492
					}
1493
				}
1494
			}
1495
			break;
1496
	}
1497
	return 0;
1498
}
1472
}
1499
}
(-)a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeConstants.java (+16 lines)
Lines 154-159 Link Here
154
	};
154
	};
155
	char[][] JAVA_LANG_AUTOCLOSEABLE =  {JAVA, LANG, "AutoCloseable".toCharArray()}; //$NON-NLS-1$
155
	char[][] JAVA_LANG_AUTOCLOSEABLE =  {JAVA, LANG, "AutoCloseable".toCharArray()}; //$NON-NLS-1$
156
	char[] CLOSE = "close".toCharArray(); //$NON-NLS-1$
156
	char[] CLOSE = "close".toCharArray(); //$NON-NLS-1$
157
	// white lists of closeables:
158
	char[][] JAVA_IO_WRAPPER_CLOSEABLES = new char[][] {
159
		"BufferedInputStream".toCharArray(), //$NON-NLS-1$
160
		"BufferedOutputStream".toCharArray(), //$NON-NLS-1$
161
		"BufferedReader".toCharArray(), //$NON-NLS-1$
162
		"BufferedWriter".toCharArray(), //$NON-NLS-1$
163
		"PrintWriter".toCharArray(),  //$NON-NLS-1$
164
	};
165
	char[][] JAVA_IO_RESOURCE_FREE_CLOSEABLES = new char[][] {			
166
		"StringReader".toCharArray(), //$NON-NLS-1$
167
		"StringWriter".toCharArray(), //$NON-NLS-1$
168
		"ByteArrayInputStream".toCharArray(), //$NON-NLS-1$
169
		"ByteArrayOutputStream".toCharArray(), //$NON-NLS-1$
170
		"CharArrayReader".toCharArray(), //$NON-NLS-1$
171
		"CharArrayWriter".toCharArray(), //$NON-NLS-1$
172
	};
157
173
158
	// Constraints for generic type argument inference
174
	// Constraints for generic type argument inference
159
	int CONSTRAINT_EQUAL = 0;		// Actual = Formal
175
	int CONSTRAINT_EQUAL = 0;		// Actual = Formal
(-)a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeIds.java (+10 lines)
Lines 207-210 Link Here
207
	 * @see ReferenceBinding#hasTypeBit(int)
207
	 * @see ReferenceBinding#hasTypeBit(int)
208
	 */
208
	 */
209
	final int BitCloseable = 2;
209
	final int BitCloseable = 2;
210
	/**
211
	 * Bit for members of a white list:
212
	 * Subtypes of Closeable that wrap another resource without directly holding any OS resources. 
213
	 */
214
	final int BitWrapperCloseable = 4;
215
	/**
216
	 * Bit for members of a white list:
217
	 * Subtypes of Closeable that do not hold an OS resource that needs to be released.
218
	 */
219
	final int BitResourceFreeCloseable = 8;
210
}
220
}

Return to bug 358903