From 167aa96ffe01161262749dd66638f4960ba73a43 Mon Sep 17 00:00:00 2001 From: Stephan Herrmann Date: Wed, 24 Apr 2024 00:10:57 +0200 Subject: [PATCH] ecj: Duplicate resource leak warnings (#2380) fixes #1867 Detect the leak already when analyzing the MessageSend whose receiver is the leaking expression. --- .../compiler/ast/FakedTrackingVariable.java | 2 +- .../internal/compiler/ast/MessageSend.java | 8 ++- .../regression/ResourceLeakTests.java | 61 +++++++++++++++++++ 3 files changed, 68 insertions(+), 3 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/FakedTrackingVariable.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/FakedTrackingVariable.java index 3349e817939..2d20957884c 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/FakedTrackingVariable.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/FakedTrackingVariable.java @@ -552,7 +552,7 @@ public static FlowInfo analyseCloseableAcquisition(BlockScope scope, FlowInfo fl } } - private static boolean isFluentMethod(MethodBinding binding) { + static boolean isFluentMethod(MethodBinding binding) { if (binding.isStatic()) return false; ReferenceBinding declaringClass = binding.declaringClass; diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/MessageSend.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/MessageSend.java index 8135e3167e7..56aac67bf0a 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/MessageSend.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/MessageSend.java @@ -265,8 +265,12 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl // NullReferenceTest#test0510 } // after having analysed exceptions above start tracking newly allocated resource: - if (analyseResources && FakedTrackingVariable.isAnyCloseable(this.resolvedType)) - flowInfo = FakedTrackingVariable.analyseCloseableAcquisition(currentScope, flowInfo, flowContext, this); + if (analyseResources) { + if (FakedTrackingVariable.isAnyCloseable(this.resolvedType)) + flowInfo = FakedTrackingVariable.analyseCloseableAcquisition(currentScope, flowInfo, flowContext, this); + if (!FakedTrackingVariable.isFluentMethod(this.binding)) + FakedTrackingVariable.cleanUpUnassigned(currentScope, this.receiver, flowInfo, false); + } manageSyntheticAccessIfNecessary(currentScope, flowInfo); // account for pot. exceptions thrown by method execution diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ResourceLeakTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ResourceLeakTests.java index ba482460738..915fc6d2db4 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ResourceLeakTests.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ResourceLeakTests.java @@ -7135,6 +7135,67 @@ void test(int sw) { "----------\n", options); } +public void testGH1867_dupes() { + if (this.complianceLevel < ClassFileConstants.JDK1_8) // uses lambda + return; + Map options = getCompilerOptions(); + options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR); + options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); + options.put(CompilerOptions.OPTION_ReportExplicitlyClosedAutoCloseable, CompilerOptions.ERROR); + runLeakTest( + new String[] { + "GH1867.java", + """ + import java.io.IOException; + import java.nio.file.*; + import java.util.Map; + + class CtSym { + public CtSym(Path x) throws IOException { } + public FileSystem getFs() { + return null; + } + } + class RuntimeIOException extends RuntimeException { + private static final long serialVersionUID = 1L; + public RuntimeIOException(IOException cause) { + super(cause); + } + @Override + public synchronized IOException getCause() { + return (IOException) super.getCause(); + } + } + public class GH1867 { + public static CtSym getCtSym(Path jdkHome, Map ctSymFiles) throws IOException { + CtSym ctSym; + try { + ctSym = ctSymFiles.compute(jdkHome, (Path x, CtSym current) -> { + if (current == null || !current.getFs().isOpen()) { + try { + return new CtSym(x); + } catch (IOException e) { + throw new RuntimeIOException(e); + } + } + return current; + }); + } catch (RuntimeIOException rio) { + throw rio.getCause(); + } + return ctSym; + } + } + """ + }, + "----------\n" + + "1. ERROR in GH1867.java (at line 26)\n" + + " if (current == null || !current.getFs().isOpen()) {\n" + + " ^^^^^^^^^^^^^^^\n" + + potentialOrDefiniteLeak("") + + "----------\n", + options); +} public void testGH2207_1() { if (this.complianceLevel < ClassFileConstants.JDK1_8) return;