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/ResourceLeakTests.java (-1 / +113 lines)
Lines 26-32 import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; Link Here
26
public class ResourceLeakTests extends AbstractRegressionTest {
26
public class ResourceLeakTests extends AbstractRegressionTest {
27
27
28
static {
28
static {
29
//	TESTS_NAMES = new String[] { "test056q"};
29
//	TESTS_NAMES = new String[] { "test061a"};
30
//	TESTS_NUMBERS = new int[] { 50 };
30
//	TESTS_NUMBERS = new int[] { 50 };
31
//	TESTS_RANGE = new int[] { 11, -1 };
31
//	TESTS_RANGE = new int[] { 11, -1 };
32
}
32
}
Lines 2097-2102 public void test061f() throws IOException { Link Here
2097
		null);
2097
		null);
2098
}
2098
}
2099
// Bug 358903 - Filter practically unimportant resource leak warnings
2099
// Bug 358903 - Filter practically unimportant resource leak warnings
2100
// Bug 361073 - Avoid resource leak warning when the top level resource is closed explicitly
2101
// a resource wrapper is closed closing also the underlying resource - from a real-world example
2102
public void test061f2() throws IOException {
2103
	Map options = getCompilerOptions();
2104
	options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
2105
	options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR);
2106
	this.runConformTest(
2107
		new String[] {
2108
			"X.java",
2109
			"import java.io.OutputStream;\n" +
2110
			"import java.io.FileOutputStream;\n" +
2111
			"import java.io.BufferedOutputStream;\n" +
2112
			"import java.io.IOException;\n" +
2113
			"public class X {\n" +
2114
			"    void zork() throws IOException {\n" +
2115
			"		try {\n" + 
2116
			"			OutputStream os = null;\n" + 
2117
			"			try {\n" + 
2118
			"				os = new BufferedOutputStream(new FileOutputStream(\"somefile\"));\n" + 
2119
			"				String externalForm = \"externalPath\";\n" + 
2120
			"			} finally {\n" + 
2121
			"				if (os != null)\n" + 
2122
			"					os.close();\n" + 
2123
			"			}\n" + 
2124
			"		} catch (IOException e) {\n" + 
2125
			"			e.printStackTrace();\n" + 
2126
			"		}\n" +
2127
			"    }\n" +
2128
			"}\n"
2129
		},
2130
		"",
2131
		null,
2132
		true,
2133
		null,
2134
		options,
2135
		null);
2136
}
2137
// Bug 358903 - Filter practically unimportant resource leak warnings
2138
// Bug 361073 - Avoid resource leak warning when the top level resource is closed explicitly
2139
// a resource wrapper is sent to another method affecting also the underlying resource - from a real-world example
2140
public void test061f3() throws IOException {
2141
	Map options = getCompilerOptions();
2142
	options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
2143
	options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR);
2144
	this.runNegativeTest(
2145
		new String[] {
2146
			"X.java",
2147
			"import java.io.File;\n" +
2148
			"import java.io.FileInputStream;\n" +
2149
			"import java.io.FileNotFoundException;\n" +
2150
			"import java.io.InputStream;\n" +
2151
			"import java.io.BufferedInputStream;\n" +
2152
			"public class X {\n" +
2153
			"    String loadProfile(File profileFile) {\n" + 
2154
			"		try {\n" + 
2155
			"			InputStream stream = new BufferedInputStream(new FileInputStream(profileFile));\n" + 
2156
			"			return loadProfile(stream);\n" + 
2157
			"		} catch (FileNotFoundException e) {\n" + 
2158
			"			//null\n" + 
2159
			"		}\n" + 
2160
			"		return null;\n" + 
2161
			"	}\n" + 
2162
			"	private String loadProfile(InputStream stream) {\n" + 
2163
			"		return null;\n" + 
2164
			"	}\n" +
2165
			"}\n"
2166
		},
2167
		"----------\n" + 
2168
		"1. ERROR in X.java (at line 10)\n" + 
2169
		"	return loadProfile(stream);\n" + 
2170
		"	^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + 
2171
		"Potential resource leak: \'stream\' may not be closed at this location\n" + 
2172
		"----------\n",
2173
		null,
2174
		true,
2175
		options);
2176
}
2177
// Bug 358903 - Filter practically unimportant resource leak warnings
2100
// Bug 360908 - Avoid resource leak warning when the underlying/chained resource is closed explicitly
2178
// Bug 360908 - Avoid resource leak warning when the underlying/chained resource is closed explicitly
2101
// Different points in a resource chain are closed
2179
// Different points in a resource chain are closed
2102
public void test061g() {
2180
public void test061g() {
Lines 2454-2459 public void test061o() { Link Here
2454
		true,
2532
		true,
2455
		options);
2533
		options);
2456
}
2534
}
2535
// Bug 358903 - Filter practically unimportant resource leak warnings
2536
// a resource wrapper is conditionally allocated but not closed - from a real-world example
2537
public void test061f4() throws IOException {
2538
	Map options = getCompilerOptions();
2539
	options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
2540
	options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR);
2541
	this.runNegativeTest(
2542
		new String[] {
2543
			"X.java",
2544
			"import java.io.File;\n" +
2545
			"import java.io.FileInputStream;\n" +
2546
			"import java.io.FileNotFoundException;\n" +
2547
			"import java.io.InputStream;\n" +
2548
			"import java.io.BufferedInputStream;\n" +
2549
			"public class X {\n" +
2550
			"    	void foo(File location, String adviceFilePath) throws FileNotFoundException {\n" + 
2551
			"		InputStream stream = null;\n" + 
2552
			"		if (location.isDirectory()) {\n" + 
2553
			"			File adviceFile = new File(location, adviceFilePath);\n" + 
2554
			"			stream = new BufferedInputStream(new FileInputStream(adviceFile));\n" + 
2555
			"		}\n" + 
2556
			"	}\n" + 
2557
			"}\n"
2558
		},
2559
		"----------\n" +
2560
		"1. ERROR in X.java (at line 11)\n" +
2561
		"	stream = new BufferedInputStream(new FileInputStream(adviceFile));\n" +
2562
		"	^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
2563
		"Potential resource leak: \'stream\' may not be closed\n" + // message could be stronger, but the enclosing if blurs the picture
2564
		"----------\n",
2565
		null,
2566
		true,
2567
		options);
2568
}
2457
// Bug 362331 - Resource leak not detected when closeable not assigned to variable
2569
// Bug 362331 - Resource leak not detected when closeable not assigned to variable
2458
// a resource is never assigned
2570
// a resource is never assigned
2459
public void test062a() throws IOException {
2571
public void test062a() throws IOException {
(-)a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FakedTrackingVariable.java (-16 / +77 lines)
Lines 14-19 import java.util.HashMap; Link Here
14
import java.util.Iterator;
14
import java.util.Iterator;
15
import java.util.Map;
15
import java.util.Map;
16
import java.util.Map.Entry;
16
import java.util.Map.Entry;
17
import java.util.Set;
17
18
18
import org.eclipse.jdt.internal.compiler.codegen.CodeStream;
19
import org.eclipse.jdt.internal.compiler.codegen.CodeStream;
19
import org.eclipse.jdt.internal.compiler.flow.FlowContext;
20
import org.eclipse.jdt.internal.compiler.flow.FlowContext;
Lines 107-112 public class FakedTrackingVariable extends LocalDeclaration { Link Here
107
				scope.getJavaLangObject(),  // dummy, just needs to be a reference type
108
				scope.getJavaLangObject(),  // dummy, just needs to be a reference type
108
				0,
109
				0,
109
				false);
110
				false);
111
		this.binding.declaringScope = scope;
110
		this.binding.setConstant(Constant.NotAConstant);
112
		this.binding.setConstant(Constant.NotAConstant);
111
		this.binding.useFlag = LocalVariableBinding.USED;
113
		this.binding.useFlag = LocalVariableBinding.USED;
112
		// use a free slot without assigning it:
114
		// use a free slot without assigning it:
Lines 225-230 public class FakedTrackingVariable extends LocalDeclaration { Link Here
225
						FakedTrackingVariable currentTracker = innerTracker;
227
						FakedTrackingVariable currentTracker = innerTracker;
226
						while (currentTracker != null) {
228
						while (currentTracker != null) {
227
							flowInfo.markNullStatus(currentTracker.binding, newStatus);
229
							flowInfo.markNullStatus(currentTracker.binding, newStatus);
230
							currentTracker.globalClosingState |= allocation.closeTracker.globalClosingState;
228
							currentTracker = currentTracker.innerTracker;
231
							currentTracker = currentTracker.innerTracker;
229
						}
232
						}
230
					}
233
					}
Lines 536-581 public class FakedTrackingVariable extends LocalDeclaration { Link Here
536
				scope.removeTrackingVar(trackVar);
539
				scope.removeTrackingVar(trackVar);
537
				return flowInfo;
540
				return flowInfo;
538
			}
541
			}
539
			trackVar.globalClosingState |= SHARED_WITH_OUTSIDE;
540
			if (scope.methodScope() != trackVar.methodScope)
541
				trackVar.globalClosingState |= CLOSED_IN_NESTED_METHOD;
542
			// insert info that the tracked resource *may* be closed (by the target method, i.e.)
542
			// insert info that the tracked resource *may* be closed (by the target method, i.e.)
543
			FlowInfo infoResourceIsClosed = flowInfo.copy();
543
			FlowInfo infoResourceIsClosed = flowInfo.copy();
544
			infoResourceIsClosed.markAsDefinitelyNonNull(trackVar.binding);
544
			do {
545
				trackVar.globalClosingState |= SHARED_WITH_OUTSIDE;
546
				if (scope.methodScope() != trackVar.methodScope)
547
					trackVar.globalClosingState |= CLOSED_IN_NESTED_METHOD;
548
				infoResourceIsClosed.markAsDefinitelyNonNull(trackVar.binding);
549
			} while ((trackVar = trackVar.innerTracker) != null); 
545
			return FlowInfo.conditional(flowInfo, infoResourceIsClosed);
550
			return FlowInfo.conditional(flowInfo, infoResourceIsClosed);
546
		}
551
		}
547
		return flowInfo;
552
		return flowInfo;
548
	}
553
	}
549
	
554
555
	/** 
556
	 * Pick tracking variables from 'varsOfScope' to establish a proper order of processing:
557
	 * As much as possible pick wrapper resources before their inner resources.
558
	 * Also consider cases of wrappers and their inners being declared at different scopes.
559
	 */
560
	public static FakedTrackingVariable pickVarForReporting(Set varsOfScope, BlockScope scope, boolean atExit) {
561
		if (varsOfScope.isEmpty()) return null;
562
		FakedTrackingVariable trackingVar = (FakedTrackingVariable) varsOfScope.iterator().next();
563
		while (trackingVar.outerTracker != null) {
564
			// resource is wrapped, is wrapper defined in this scope?
565
			if (varsOfScope.contains(trackingVar.outerTracker)) {
566
				// resource from same scope, travel up the wrapper chain
567
				trackingVar = trackingVar.outerTracker;
568
			} else if (atExit) {
569
				// at an exit point we report against inner despite a wrapper that may/may not be closed later
570
				break;
571
			} else {
572
				BlockScope outerTrackerScope = trackingVar.outerTracker.binding.declaringScope;
573
				if (outerTrackerScope == scope) {
574
					// outerTracker is from same scope and already processed -> pick trackingVar now
575
					break;
576
				} else {
577
					// outer resource is from other (outer?) scope
578
					Scope currentScope = scope;
579
					while ((currentScope = currentScope.parent) instanceof BlockScope) {
580
						if (outerTrackerScope == currentScope) {
581
							// at end of block pass responsibility for inner resource to outer scope holding a wrapper
582
							varsOfScope.remove(trackingVar); // drop this one
583
							// pick a next candidate:
584
							return pickVarForReporting(varsOfScope, scope, atExit);
585
						}
586
					}
587
					break; // not parent owned -> pick this var
588
				}
589
			}
590
		}
591
		varsOfScope.remove(trackingVar);
592
		return trackingVar;
593
	}
594
550
	public void recordErrorLocation(ASTNode location, int nullStatus) {
595
	public void recordErrorLocation(ASTNode location, int nullStatus) {
551
		if (this.recordedLocations == null)
596
		if (this.recordedLocations == null)
552
			this.recordedLocations = new HashMap();
597
			this.recordedLocations = new HashMap();
553
		this.recordedLocations.put(location, new Integer(nullStatus));
598
		this.recordedLocations.put(location, new Integer(nullStatus));
554
	}
599
	}
555
600
556
	public boolean reportRecordedErrors(Scope scope) {
601
	public boolean reportRecordedErrors(Scope scope, int mergedStatus) {
557
		FakedTrackingVariable current = this;
602
		FakedTrackingVariable current = this;
558
		while (current.globalClosingState == 0) {
603
		while (current.globalClosingState == 0) {
559
			current = current.innerTracker;
604
			current = current.innerTracker;
560
			if (current == null) {
605
			if (current == null) {
561
				// no relevant state found -> report:
606
				// no relevant state found -> report:
562
				reportError(scope.problemReporter(), null, FlowInfo.NULL);
607
				reportError(scope.problemReporter(), null, mergedStatus);
563
				return true;
608
				return true;
564
			}
609
			}
565
		}
610
		}
566
		boolean hasReported = false;
611
		boolean hasReported = false;
567
		if (this.recordedLocations != null) {
612
		if (this.recordedLocations != null) {
568
			Iterator locations = this.recordedLocations.entrySet().iterator();
613
			Iterator locations = this.recordedLocations.entrySet().iterator();
614
			int reportFlags = 0;
569
			while (locations.hasNext()) {
615
			while (locations.hasNext()) {
570
				Map.Entry entry = (Entry) locations.next();
616
				Map.Entry entry = (Entry) locations.next();
571
				reportError(scope.problemReporter(), (ASTNode)entry.getKey(), ((Integer)entry.getValue()).intValue());
617
				reportFlags |= reportError(scope.problemReporter(), (ASTNode)entry.getKey(), ((Integer)entry.getValue()).intValue());
572
				hasReported = true;
618
				hasReported = true;
573
			}
619
			}
620
			if (reportFlags != 0) {
621
				// after all locations have been reported, mark as reported to prevent duplicate report via an outer wrapper
622
				current = this;
623
				do {
624
					current.globalClosingState |= reportFlags;
625
				} while ((current = current.innerTracker) != null);
626
			}
574
		}
627
		}
575
		return hasReported;
628
		return hasReported;
576
	}
629
	}
577
	
630
	
578
	public void reportError(ProblemReporter problemReporter, ASTNode location, int nullStatus) {
631
	public int reportError(ProblemReporter problemReporter, ASTNode location, int nullStatus) {
579
		// which degree of problem?
632
		// which degree of problem?
580
		boolean isPotentialProblem = false;
633
		boolean isPotentialProblem = false;
581
		if (nullStatus == FlowInfo.NULL) {
634
		if (nullStatus == FlowInfo.NULL) {
Lines 587-608 public class FakedTrackingVariable extends LocalDeclaration { Link Here
587
		// report:
640
		// report:
588
		if (isPotentialProblem) {
641
		if (isPotentialProblem) {
589
			if ((this.globalClosingState & (REPORTED_POTENTIAL_LEAK|REPORTED_DEFINITIVE_LEAK)) != 0)
642
			if ((this.globalClosingState & (REPORTED_POTENTIAL_LEAK|REPORTED_DEFINITIVE_LEAK)) != 0)
590
				return;
643
				return 0;
591
			problemReporter.potentiallyUnclosedCloseable(this, location);	
644
			problemReporter.potentiallyUnclosedCloseable(this, location);	
592
		} else {
645
		} else {
593
			if ((this.globalClosingState & (REPORTED_DEFINITIVE_LEAK)) != 0)
646
			if ((this.globalClosingState & (REPORTED_DEFINITIVE_LEAK)) != 0)
594
				return;
647
				return 0;
595
			problemReporter.unclosedCloseable(this, location);			
648
			problemReporter.unclosedCloseable(this, location);			
596
		}
649
		}
597
		// propagate flag to inners:
650
		// propagate flag to inners:
598
		if (location == null) {
651
		int reportFlag = isPotentialProblem ? REPORTED_POTENTIAL_LEAK : REPORTED_DEFINITIVE_LEAK;
599
			int reportFlag = isPotentialProblem ? REPORTED_POTENTIAL_LEAK : REPORTED_DEFINITIVE_LEAK;
652
		if (location == null) { // if location != null flags will be set after the loop over locations 
600
			FakedTrackingVariable current = this;
653
			FakedTrackingVariable current = this;
601
			while (current != null) {
654
			do {
602
				current.globalClosingState |= reportFlag;
655
				current.globalClosingState |= reportFlag;
603
				current = current.innerTracker;
656
			} while ((current = current.innerTracker) != null);
604
			}
605
		}
657
		}
658
		return reportFlag;
606
	}
659
	}
607
660
608
	public void reportExplicitClosing(ProblemReporter problemReporter) {
661
	public void reportExplicitClosing(ProblemReporter problemReporter) {
Lines 611-614 public class FakedTrackingVariable extends LocalDeclaration { Link Here
611
			problemReporter.explicitlyClosedAutoCloseable(this);
664
			problemReporter.explicitlyClosedAutoCloseable(this);
612
		}
665
		}
613
	}
666
	}
667
668
	public void resetReportingBits() {
669
		FakedTrackingVariable current = this;
670
		while (current != null) {
671
			current.globalClosingState &= ~(REPORTED_POTENTIAL_LEAK|REPORTED_DEFINITIVE_LEAK);
672
			current = current.innerTracker;
673
		}
674
	}
614
}
675
}
(-)a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java (-1 / +1 lines)
Lines 1-5 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright (c) 2000, 2012 IBM Corporation and others.
2
 * Copyright (c) 2000, 2011 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
(-)a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BlockScope.java (-8 / +11 lines)
Lines 1006-1018 public void checkUnclosedCloseables(FlowInfo flowInfo, ASTNode location, BlockSc Link Here
1006
	}
1006
	}
1007
	if (location != null && flowInfo.reachMode() != 0) return;
1007
	if (location != null && flowInfo.reachMode() != 0) return;
1008
	Set varSet = new HashSet(this.trackingVariables);
1008
	Set varSet = new HashSet(this.trackingVariables);
1009
	while (!varSet.isEmpty()) {
1009
	FakedTrackingVariable trackingVar;
1010
		// pick one outer-most variable from the set
1010
	// pick one outer-most variable from the set at a time
1011
		FakedTrackingVariable trackingVar = (FakedTrackingVariable) varSet.iterator().next();
1011
	while ((trackingVar = FakedTrackingVariable.pickVarForReporting(varSet, this, location != null)) != null) {
1012
		while (trackingVar.outerTracker != null && varSet.contains(trackingVar.outerTracker)) {
1012
1013
			trackingVar = trackingVar.outerTracker;
1014
		}
1015
		varSet.remove(trackingVar);
1016
		if (location != null && trackingVar.originalBinding != null && flowInfo.isDefinitelyNull(trackingVar.originalBinding))
1013
		if (location != null && trackingVar.originalBinding != null && flowInfo.isDefinitelyNull(trackingVar.originalBinding))
1017
			continue; // reporting against a specific location, resource is null at this flow, don't complain
1014
			continue; // reporting against a specific location, resource is null at this flow, don't complain
1018
		
1015
		
Lines 1027-1033 public void checkUnclosedCloseables(FlowInfo flowInfo, ASTNode location, BlockSc Link Here
1027
		if (location == null) // at end of block and not definitely unclosed
1024
		if (location == null) // at end of block and not definitely unclosed
1028
		{
1025
		{
1029
			// problems at specific locations: medium priority
1026
			// problems at specific locations: medium priority
1030
			if (trackingVar.reportRecordedErrors(this)) // ... report previously recorded errors
1027
			if (trackingVar.reportRecordedErrors(this, status)) // ... report previously recorded errors
1031
				continue;
1028
				continue;
1032
		} 
1029
		} 
1033
		if (status == FlowInfo.POTENTIALLY_NULL) {
1030
		if (status == FlowInfo.POTENTIALLY_NULL) {
Lines 1044-1049 public void checkUnclosedCloseables(FlowInfo flowInfo, ASTNode location, BlockSc Link Here
1044
		for (int i=0; i<this.localIndex; i++)
1041
		for (int i=0; i<this.localIndex; i++)
1045
			this.locals[i].closeTracker = null;		
1042
			this.locals[i].closeTracker = null;		
1046
		this.trackingVariables = null;
1043
		this.trackingVariables = null;
1044
	} else {
1045
		int size = this.trackingVariables.size();
1046
		for (int i=0; i<size; i++) {
1047
			FakedTrackingVariable tracker = (FakedTrackingVariable) this.trackingVariables.get(0);
1048
			tracker.resetReportingBits();
1049
		}
1047
	}
1050
	}
1048
}
1051
}
1049
1052

Return to bug 358903