[java] CloseResource: consider chained streams

This assumes that the underlaying stream is always the
first argument in the constructor call.
This commit is contained in:
Andreas Dangel
2019-06-29 14:13:42 +02:00
parent 5825f6190f
commit 40849dda65
2 changed files with 57 additions and 1 deletions

View File

@ -18,6 +18,7 @@ import org.jaxen.JaxenException;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression;
import net.sourceforge.pmd.lang.java.ast.ASTArgumentList;
import net.sourceforge.pmd.lang.java.ast.ASTBlock;
import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement;
@ -160,6 +161,14 @@ public class CloseResourceRule extends AbstractJavaRule {
if (!isMethodCall(expression) && runtimeType != null && runtimeType.getType() != null) {
type = runtimeType;
}
// consider cases, when the streams are chained
// assumes, that the underlaying stream is always the first argument in the
// constructor call.
ASTExpression firstArgument = getAllocationFirstArgument(expression);
if (firstArgument != null) {
type = firstArgument;
}
}
if (!isAllowedResourceType(type)) {
@ -176,11 +185,22 @@ public class CloseResourceRule extends AbstractJavaRule {
}
}
private ASTExpression getAllocationFirstArgument(ASTExpression expression) {
List<ASTAllocationExpression> allocations = expression.findDescendantsOfType(ASTAllocationExpression.class);
if (!allocations.isEmpty()) {
ASTArgumentList argumentList = allocations.get(allocations.size() - 1).getFirstDescendantOfType(ASTArgumentList.class);
if (argumentList != null) {
return argumentList.getFirstChildOfType(ASTExpression.class);
}
}
return null;
}
private boolean isMethodCall(ASTExpression expression) {
return expression != null
&& expression.jjtGetNumChildren() > 0
&& expression.jjtGetChild(0) instanceof ASTPrimaryExpression
&& !expression.jjtGetChild(0).findChildrenOfType(ASTPrimarySuffix.class).isEmpty();
&& expression.jjtGetChild(0).getFirstChildOfType(ASTPrimarySuffix.class) != null;
}
private boolean isResourceTypeOrSubtype(TypeNode refType) {

View File

@ -1002,6 +1002,42 @@ public class CloseResourceTest {
// TODO: close file
}
}
}
]]></code>
</test-code>
<test-code>
<description>PrintWriter based on StringWriter</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
import java.io.*;
public class CloseResourcePrintWriter {
public String run1() {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
pw.println("Foo");
String result = sw.toString();
return result;
}
public String run2() {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw, true);
pw.println("Foo");
String result = sw.toString();
return result;
}
public String run3() {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(new BufferedWriter(sw));
pw.println("Foo");
return sw.toString();
}
}
]]></code>
</test-code>