Fixed bug 1808110 - PreserveStackTrace

(backport from trunk)

git-svn-id: https://pmd.svn.sourceforge.net/svnroot/pmd/branches/pmd/4.2.x@6006 51baf565-9d33-0410-a72c-fc3788e3496d
This commit is contained in:
Romain Pelisse 2008-04-11 17:47:30 +00:00
parent 2f4cdbd6c3
commit adfd316184
3 changed files with 183 additions and 38 deletions

View File

@ -3,6 +3,8 @@
'41' and '42' shortcuts for rulesets added
Fixed bug 1928009 - Error using migration ruleset in PMD 4.2
Fixed bug 1932242 - EmptyMethodInAbstractClassShouldBeAbstract false +
Fixed bug 1808110 - PreserveStackTrace
AvoidDuplicateLiteralRule now has 'skipAnnotations' boolean property
ruleset.dtd and ruleset_xml_schema.xsd added to jar file in rulesets directory
Update RuleSetWriter to handle non-Apache TRAX implementations, add an option to not use XML Namespaces

View File

@ -162,8 +162,8 @@ public class Foo {
public void foo(String a) {
try {
int i = Integer.parseInt(a);
} catch(Exception e){
Exception e1 = new Exception(e);
} catch(Exception e10){
Exception e1 = new Exception(e10);
throw e1;
}
}
@ -243,4 +243,63 @@ public class B {
}
]]></code>
</test-code>
<test-code>
<description><![CDATA[
15, IllegalStateException does not have any constructor which takes original exception.
]]></description>
<expected-problems>0</expected-problems>
<code><![CDATA[
public class Foo {
public void bar() {
try {
;
} catch (Exception excep) {
throw new IllegalStateException();
}
}
}
]]></code>
</test-code>
<test-code>
<description><![CDATA[
16, False -, No violations reported by PreserveStackTrace Rule
]]></description>
<expected-problems>1</expected-problems>
<code><![CDATA[
public class Foo {
public void bar() {
try {
;
} catch (Exception notUsed) {
RuntimeException ex = new RuntimeException();
throw ex;
}
}
}
]]></code>
</test-code>
<test-code>
<description><![CDATA[
17, PreserveStackTrace Rule should exclude this as initCause is used.
]]></description>
<expected-problems>0</expected-problems>
<code><![CDATA[
public class Foo {
public void bar() {
try {
;
} catch (Exception e) {
IllegalStateException ex = new IllegalStateException();
ex.initCause(e);
throw ex;
}
}
}
]]></code>
</test-code>
</test-data>

View File

@ -1,6 +1,13 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.rules.design;
import net.sourceforge.pmd.AbstractRule;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import net.sourceforge.pmd.AbstractJavaRule;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.ast.ASTArgumentList;
import net.sourceforge.pmd.ast.ASTCastExpression;
@ -10,43 +17,68 @@ import net.sourceforge.pmd.ast.ASTName;
import net.sourceforge.pmd.ast.ASTPrimaryExpression;
import net.sourceforge.pmd.ast.ASTPrimaryPrefix;
import net.sourceforge.pmd.ast.ASTThrowStatement;
import net.sourceforge.pmd.ast.Node;
import net.sourceforge.pmd.ast.SimpleNode;
import net.sourceforge.pmd.symboltable.NameOccurrence;
import net.sourceforge.pmd.symboltable.VariableNameDeclaration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.jaxen.JaxenException;
public class PreserveStackTrace extends AbstractRule {
/**
*
* @author Unknown,
* @author Romain PELISSE, belaran@gmail.com, fix for bug 1808110
*
*/
public class PreserveStackTrace extends AbstractJavaRule {
private List<ASTName> nameNodes = new ArrayList<ASTName>();
public Object visit(ASTCatchStatement node, Object data) {
String target = (((SimpleNode) node.jjtGetChild(0).jjtGetChild(1)).getImage());
List<ASTThrowStatement> lstThrowStatements = node.findChildrenOfType(ASTThrowStatement.class);
// FUTURE: This is dectection is name based, it should probably used Type Resolution, to become type "based"
private static final String FIND_THROWABLE_INSTANCE = "//VariableDeclaratorId[" +
"(../descendant::VariableInitializer/Expression/PrimaryExpression/PrimaryPrefix/AllocationExpression/ClassOrInterfaceType" +
"[" +
"contains(@Image,'Exception')" + // Assuming the Exception class does contains 'Exception' in its name
"and" +
"(not (../Arguments/ArgumentList))" +
"]" +
")]";
private static final String ILLEGAL_STATE_EXCEPTION = "IllegalStateException";
private static final String FILL_IN_STACKTRACE = ".fillInStackTrace";
public Object visit(ASTCatchStatement catchStmt, Object data) {
String target = ((SimpleNode)catchStmt.jjtGetChild(0).jjtGetChild(1)).getImage();
// Gather every variable used to store exception instance created without any argument, inside the catch
gatherVariableWithExceptionRef(catchStmt,data);
// Inspect all the throw stmt inside the catch stmt
List<ASTThrowStatement> lstThrowStatements = catchStmt.findChildrenOfType(ASTThrowStatement.class);
for (ASTThrowStatement throwStatement : lstThrowStatements) {
SimpleNode sn = (SimpleNode) throwStatement.jjtGetChild(0).jjtGetChild(0);
if (sn.getClass().equals(ASTCastExpression.class)) {
ASTPrimaryExpression expr = (ASTPrimaryExpression) sn.jjtGetChild(1);
Node n = throwStatement.jjtGetChild(0).jjtGetChild(0);
if (n.getClass().equals(ASTCastExpression.class)) {
ASTPrimaryExpression expr = (ASTPrimaryExpression) n.jjtGetChild(1);
if (expr.jjtGetNumChildren() > 1 && expr.jjtGetChild(1).getClass().equals(ASTPrimaryPrefix.class)) {
RuleContext ctx = (RuleContext) data;
addViolation(ctx, throwStatement);
}
continue;
}
// If the thrown exception is IllegalStateException, no way to preserve the exception (the constructor has no args)
if ( ! isThrownExceptionOfType(throwStatement,ILLEGAL_STATE_EXCEPTION) ) {
// Retrieve all argument for the throw exception (to see if the original exception is preserved)
ASTArgumentList args = throwStatement.getFirstChildOfType(ASTArgumentList.class);
if (args != null) {
ck(data, target, throwStatement, args);
} else {
SimpleNode child = (SimpleNode) throwStatement.jjtGetChild(0);
}
else {
Node child = throwStatement.jjtGetChild(0);
while (child != null && child.jjtGetNumChildren() > 0
&& !child.getClass().equals(ASTName.class)) {
child = (SimpleNode) child.jjtGetChild(0);
child = child.jjtGetChild(0);
}
if (child != null){
if( child.getClass().equals(ASTName.class) && (!target.equals(child.getImage()) && !child.hasImageEqualTo(target + ".fillInStackTrace"))) {
if( child.getClass().equals(ASTName.class) && (!target.equals(((SimpleNode)child).getImage()) && !((SimpleNode)child).hasImageEqualTo(target + FILL_IN_STACKTRACE))) {
Map<VariableNameDeclaration, List<NameOccurrence>> vars = ((ASTName) child).getScope().getVariableDeclarations();
for (VariableNameDeclaration decl: vars.keySet()) {
args = ((SimpleNode)decl.getNode().jjtGetParent())
@ -61,7 +93,59 @@ public class PreserveStackTrace extends AbstractRule {
}
}
}
return super.visit(node, data);
}
return super.visit(catchStmt, data);
}
/*
* Search Catch stmt nodes for variable used to store unproperly created throwable or exception
*/
private void gatherVariableWithExceptionRef(ASTCatchStatement catchStmt, Object data) {
try {
List<Node> nodes = catchStmt.findChildNodesWithXPath(FIND_THROWABLE_INSTANCE);
for ( Node node : nodes ) {
List <Node> violations = catchStmt.findChildNodesWithXPath("//Expression/PrimaryExpression/PrimaryPrefix/Name[@Image = '" + ((SimpleNode)node).getImage() + "']");
if ( violations != null && violations.size() > 0 ) {
// If, after this allocation, the 'initCause' method is called, and the ex passed
// this is not a violation
if ( ! useInitCause((Node)violations.get(0),catchStmt) ) //FIXME: iterate, better than get(0) ?
super.addViolation(data,(SimpleNode) node);
}
}
} catch (JaxenException e) {
// XPath is valid, this should never happens...
e.printStackTrace();
}
}
private boolean useInitCause(Node node, ASTCatchStatement catchStmt) throws JaxenException {
// In case of NPE...
if ( node != null && ((SimpleNode)node).getImage() != null )
{
List <Node> nodes = catchStmt.findChildNodesWithXPath("descendant::StatementExpression/PrimaryExpression/PrimaryPrefix/Name[@Image = '" + ((SimpleNode)node).getImage() + ".initCause']");
if ( nodes != null && nodes.size() > 0 )
{
return true;
}
}
return false;
}
private boolean isThrownExceptionOfType(ASTThrowStatement throwStatement,String type) {
boolean status = false;
try {
List<Node> results = throwStatement.findChildNodesWithXPath("Expression/PrimaryExpression/PrimaryPrefix/AllocationExpression/ClassOrInterfaceType[@Image = '" + type + "']");
// If we have a match, return true
if ( results != null && results.size() == 1 ) {
status = true;
}
} catch (JaxenException e) {
// XPath is valid, this should never happens !
e.printStackTrace();
}
return status;
}
private void ck(Object data, String target, ASTThrowStatement throwStatement,