Skip to content

Commit

Permalink
Eclipse 2024-03 reports unclosed closeable for whitelisted StreamEx (#…
Browse files Browse the repository at this point in the history
…2199)

fixes #2191

+ resolve confusion about superclass/interface vs. current type
  • Loading branch information
stephan-herrmann committed Mar 26, 2024
1 parent bb58548 commit 19e8e90
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 27 deletions.
Expand Up @@ -2477,7 +2477,7 @@ public ReferenceBinding superclass() {
}
this.typeBits |= (this.superclass.typeBits & TypeIds.InheritableBits);
if ((this.typeBits & (TypeIds.BitAutoCloseable|TypeIds.BitCloseable)) != 0) // avoid the side-effects of hasTypeBit()!
this.typeBits |= applyCloseableClassWhitelists(this.environment.globalOptions);
this.typeBits |= applyCloseableWhitelists(this.environment.globalOptions);
detectCircularHierarchy();
return this.superclass;
}
Expand Down Expand Up @@ -2566,7 +2566,7 @@ public ReferenceBinding[] superInterfaces() {
}
this.typeBits |= (this.superInterfaces[i].typeBits & TypeIds.InheritableBits);
if ((this.typeBits & (TypeIds.BitAutoCloseable|TypeIds.BitCloseable)) != 0) // avoid the side-effects of hasTypeBit()!
this.typeBits |= applyCloseableInterfaceWhitelists(this.environment.globalOptions);
this.typeBits |= applyCloseableWhitelists(this.environment.globalOptions);
}
this.tagBits &= ~TagBits.HasUnresolvedSuperinterfaces;
return this.superInterfaces;
Expand Down
Expand Up @@ -1202,9 +1202,6 @@ private boolean connectSuperclass() {
// only want to reach here when no errors are reported
sourceType.setSuperClass(superclass);
sourceType.typeBits |= (superclass.typeBits & TypeIds.InheritableBits);
// further analysis against white lists for the unlikely case we are compiling java.io.*:
if ((sourceType.typeBits & (TypeIds.BitAutoCloseable|TypeIds.BitCloseable)) != 0)
sourceType.typeBits |= sourceType.applyCloseableClassWhitelists(this.compilerOptions());
return true;
}
}
Expand Down Expand Up @@ -1458,9 +1455,6 @@ private boolean connectSuperInterfaces() {
}
// only want to reach here when no errors are reported
sourceType.typeBits |= (superInterface.typeBits & TypeIds.InheritableBits);
// further analysis against white lists for the unlikely case we are compiling java.util.stream.Stream:
if ((sourceType.typeBits & (TypeIds.BitAutoCloseable|TypeIds.BitCloseable)) != 0)
sourceType.typeBits |= sourceType.applyCloseableInterfaceWhitelists(compilerOptions());
interfaceBindings[count++] = superInterface;
}
// hold onto all correctly resolved superinterfaces
Expand All @@ -1479,6 +1473,9 @@ void connectTypeHierarchy() {
environment().typesBeingConnected.add(sourceType);
boolean noProblems = connectSuperclass();
noProblems &= connectSuperInterfaces();
if ((sourceType.typeBits & (TypeIds.BitAutoCloseable|TypeIds.BitCloseable)) != 0) {
sourceType.typeBits |= sourceType.applyCloseableWhitelists(compilerOptions());
}
environment().typesBeingConnected.remove(sourceType);
sourceType.tagBits |= TagBits.EndHierarchyCheck;
// connectPermittedTypes();
Expand Down
Expand Up @@ -1516,7 +1516,7 @@ public ReferenceBinding superclass() {
this.superclass = (ReferenceBinding) Scope.substitute(this, genericSuperclass);
this.typeBits |= (this.superclass.typeBits & TypeIds.InheritableBits);
if ((this.typeBits & (TypeIds.BitAutoCloseable|TypeIds.BitCloseable)) != 0) // avoid the side-effects of hasTypeBit()!
this.typeBits |= applyCloseableClassWhitelists(this.environment.globalOptions);
this.typeBits |= applyCloseableWhitelists(this.environment.globalOptions);
}
return this.superclass;
}
Expand All @@ -1534,7 +1534,7 @@ public ReferenceBinding[] superInterfaces() {
for (int i = this.superInterfaces.length; --i >= 0;) {
this.typeBits |= (this.superInterfaces[i].typeBits & TypeIds.InheritableBits);
if ((this.typeBits & (TypeIds.BitAutoCloseable|TypeIds.BitCloseable)) != 0) // avoid the side-effects of hasTypeBit()!
this.typeBits |= applyCloseableInterfaceWhitelists(this.environment.globalOptions);
this.typeBits |= applyCloseableWhitelists(this.environment.globalOptions);
}
}
}
Expand Down
Expand Up @@ -2167,6 +2167,16 @@ protected int applyCloseableClassWhitelists(CompilerOptions options) {
}
}
}
for (int i=0; i<3; i++) {
if (!CharOperation.equals(this.compoundName[i], TypeConstants.ONE_UTIL_STREAMEX[i])) {
return 0;
}
}
for (char[] streamName : TypeConstants.RESOURCE_FREE_CLOSEABLE_STREAMEX) {
if (CharOperation.equals(this.compoundName[3], streamName)) {
return TypeIds.BitResourceFreeCloseable;
}
}
break;
}
int l = TypeConstants.OTHER_WRAPPER_CLOSEABLES.length;
Expand Down Expand Up @@ -2199,7 +2209,11 @@ protected boolean hasMethodWithNumArgs(char[] selector, int numArgs) {
}
return false;
}

protected int applyCloseableWhitelists(CompilerOptions options) {
return isInterface()
? applyCloseableInterfaceWhitelists(options)
: applyCloseableClassWhitelists(options);
}
/*
* If a type - known to be a Closeable - is mentioned in one of our white lists
* answer the typeBit for the white list (BitWrapperCloseable or BitResourceFreeCloseable).
Expand All @@ -2218,17 +2232,6 @@ protected int applyCloseableInterfaceWhitelists(CompilerOptions options) {
return TypeIds.BitResourceFreeCloseable;
}
}
} else {
for (int i=0; i<3; i++) {
if (!CharOperation.equals(this.compoundName[i], TypeConstants.ONE_UTIL_STREAMEX[i])) {
return 0;
}
}
for (char[] streamName : TypeConstants.RESOURCE_FREE_CLOSEABLE_STREAMEX) {
if (CharOperation.equals(this.compoundName[3], streamName)) {
return TypeIds.BitResourceFreeCloseable;
}
}
}
break;
}
Expand Down
Expand Up @@ -54,11 +54,28 @@ public class ResourceLeakTests extends AbstractRegressionTest {

// one.util.streamex.StreamEx stub
private static final String STREAMEX_JAVA = "one/util/streamex/StreamEx.java";
private static final String STREAMEX_CONTENT = "package one.util.streamex;\n" +
"import java.util.stream.*;\n" +
"public abstract class StreamEx<T> implements Stream<T> {\n" +
" public static <T> StreamEx<T> create() { return null; }\n" +
"}\n";
private static final String STREAMEX_CONTENT =
"""
package one.util.streamex;
import java.util.Spliterator;
import java.util.stream.*;
import java.util.function.*;
public abstract class StreamEx<T> extends AbstractStreamEx<T, StreamEx<T>> {
public static <T> StreamEx<T> create() { return null; }
public static <T> StreamEx<T> of(T element) { return null; }
@Override public <R> StreamEx<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) { return null; }
}
abstract class AbstractStreamEx<T, S extends AbstractStreamEx<T, S>> extends
BaseStreamEx<T, Stream<T>, Spliterator<T>, S> implements Stream<T>, Iterable<T> {
@Override
public Spliterator<T> spliterator() {
return null;
}
}
abstract class BaseStreamEx<T, S extends BaseStream<T, S>, SPLTR extends Spliterator<T>, B extends BaseStreamEx<T, S, SPLTR, B>>
implements BaseStream<T, S> {
}
""";

static {
// TESTS_NAMES = new String[] { "testBug463320" };
Expand Down Expand Up @@ -4756,6 +4773,27 @@ public void testStreamEx_572707() {
},
options);
}
public void testStreamEx_GH2919() {
if (this.complianceLevel < ClassFileConstants.JDK1_8) return; // uses JRE 8 API

Map options = getCompilerOptions();
options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR);
options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
runConformTest(
new String[] {
STREAMEX_JAVA,
STREAMEX_CONTENT,
"GH2919.java",
"import one.util.streamex.*;\n" +
"\n" +
"public class GH2919 {\n" +
" public void m() {\n" +
" StreamEx<Object> streamEx = StreamEx.of(new Object()).flatMap(obj->StreamEx.of(obj));\n" +
" }\n" +
"}\n"
},
options);
}
// Functions java.nio.file.Files.x() returning *Stream* do produce a resource needing closing
public void testStream2() {
if (this.complianceLevel < ClassFileConstants.JDK1_8)
Expand Down

0 comments on commit 19e8e90

Please sign in to comment.