Optimizations and false positive fixes in PreserveStackTrace

git-svn-id: https://pmd.svn.sourceforge.net/svnroot/pmd/branches/pmd/4.2.x@6232 51baf565-9d33-0410-a72c-fc3788e3496d
This commit is contained in:
Xavier Le Vourch 2008-06-19 03:50:50 +00:00
parent d951795634
commit 9ce5a5be43
4 changed files with 125 additions and 37 deletions

View File

@ -6,6 +6,7 @@ Upgrading UselessOperationOnImmutable to detect more use cases, especially on St
Fixed bug 1988829 - Violation reported without source file name (actually a fix to ConsecutiveLiteralAppends) Fixed bug 1988829 - Violation reported without source file name (actually a fix to ConsecutiveLiteralAppends)
Fixed bug 1989814 - false +: ConsecutiveLiteralAppends Fixed bug 1989814 - false +: ConsecutiveLiteralAppends
Fixed bug 1977230 - false positive: UselessOverridingMethod Fixed bug 1977230 - false positive: UselessOverridingMethod
Optimizations and false positive fixes in PreserveStackTrace
New rule: New rule:
Basic ruleset: EmptyInitializer Basic ruleset: EmptyInitializer

View File

@ -302,4 +302,87 @@ public class Foo {
]]></code> ]]></code>
</test-code> </test-code>
<test-code>
<description><![CDATA[
18, side effects on rules
]]></description>
<expected-problems>1</expected-problems>
<code><![CDATA[
public class Foo {
public void bar1() {
try {
;
} catch (Exception notUsed) {
RuntimeException ex = new RuntimeException();
throw ex;
}
}
public void bar2() {
try {
;
} catch (Exception e) {
IllegalStateException ex = new IllegalStateException();
ex.initCause(e);
throw ex;
}
}
}
]]></code>
</test-code>
<test-code>
<description><![CDATA[
19, False positive
]]></description>
<expected-problems>0</expected-problems>
<code><![CDATA[
public class Foo {
private static boolean isInstanceOf(final MBeanServer mbs,
final ObjectName name,
final String className) {
PrivilegedExceptionAction<Boolean> act =
new PrivilegedExceptionAction<Boolean>() {
public Boolean run() throws InstanceNotFoundException {
return mbs.isInstanceOf(name, className);
}
};
try {
return AccessController.doPrivileged(act);
} catch (Exception e) {
logger.fine("isInstanceOf", "failed: " + e);
logger.debug("isInstanceOf", e);
return false;
}
}
}
]]></code>
</test-code>
<test-code>
<description><![CDATA[
20, False positive
]]></description>
<expected-problems>0</expected-problems>
<code><![CDATA[
public class Foo {
private CodeException[] getCodeExceptions() {
int size = exception_vec.size();
CodeException[] c_exc = new CodeException[size];
try {
for(int i=0; i < size; i++) {
CodeExceptionGen c = (CodeExceptionGen)exception_vec.get(i);
c_exc[i] = c.getCodeException(cp);
}
} catch(ArrayIndexOutOfBoundsException e) {}
return c_exc;
}
}
]]></code>
</test-code>
</test-data> </test-data>

View File

@ -1329,12 +1329,12 @@ public class Foo {
<rule name="PreserveStackTrace" <rule name="PreserveStackTrace"
since="3.7" since="3.7"
message="Caught exception is rethrown, original stack trace may be lost" message="New exception is thrown in catch block, original stack trace may be lost"
class="net.sourceforge.pmd.rules.design.PreserveStackTrace" class="net.sourceforge.pmd.rules.design.PreserveStackTrace"
externalInfoUrl="http://pmd.sourceforge.net/rules/design.html#PreserveStackTrace"> externalInfoUrl="http://pmd.sourceforge.net/rules/design.html#PreserveStackTrace">
<description> <description>
Throwing a new exception from a catch block without passing the original exception into the Throwing a new exception from a catch block without passing the original exception into the
new Exception will cause the true stack trace to be lost, and can make it difficult to new exception will cause the true stack trace to be lost, and can make it difficult to
debug effectively. debug effectively.
</description> </description>
<priority>3</priority> <priority>3</priority>

View File

@ -17,6 +17,7 @@ import net.sourceforge.pmd.ast.ASTName;
import net.sourceforge.pmd.ast.ASTPrimaryExpression; import net.sourceforge.pmd.ast.ASTPrimaryExpression;
import net.sourceforge.pmd.ast.ASTPrimaryPrefix; import net.sourceforge.pmd.ast.ASTPrimaryPrefix;
import net.sourceforge.pmd.ast.ASTThrowStatement; import net.sourceforge.pmd.ast.ASTThrowStatement;
import net.sourceforge.pmd.ast.ASTVariableDeclarator;
import net.sourceforge.pmd.ast.Node; import net.sourceforge.pmd.ast.Node;
import net.sourceforge.pmd.ast.SimpleNode; import net.sourceforge.pmd.ast.SimpleNode;
import net.sourceforge.pmd.symboltable.NameOccurrence; import net.sourceforge.pmd.symboltable.NameOccurrence;
@ -34,23 +35,18 @@ public class PreserveStackTrace extends AbstractJavaRule {
private List<ASTName> nameNodes = new ArrayList<ASTName>(); private List<ASTName> nameNodes = new ArrayList<ASTName>();
// FUTURE: This is dectection is name based, it should probably used Type Resolution, to become type "based" // FUTURE: This detection is name based, it should probably use Type Resolution, to become type "based"
private static final String FIND_THROWABLE_INSTANCE = "//VariableDeclaratorId[" + // it assumes the exception class contains 'Exception' in its name
"(../descendant::VariableInitializer/Expression/PrimaryExpression/PrimaryPrefix/AllocationExpression/ClassOrInterfaceType" + private static final String FIND_THROWABLE_INSTANCE =
"[" + "./VariableInitializer/Expression/PrimaryExpression/PrimaryPrefix/AllocationExpression" +
"contains(@Image,'Exception')" + // Assuming the Exception class does contains 'Exception' in its name "[ClassOrInterfaceType[contains(@Image,'Exception')] and Arguments[count(*)=0]]";
"and" +
"(not (../Arguments/ArgumentList))" +
"]" +
")]";
private static final String ILLEGAL_STATE_EXCEPTION = "IllegalStateException"; private static final String ILLEGAL_STATE_EXCEPTION = "IllegalStateException";
private static final String FILL_IN_STACKTRACE = ".fillInStackTrace"; private static final String FILL_IN_STACKTRACE = ".fillInStackTrace";
@Override
public Object visit(ASTCatchStatement catchStmt, Object data) { public Object visit(ASTCatchStatement catchStmt, Object data) {
String target = ((SimpleNode)catchStmt.jjtGetChild(0).jjtGetChild(1)).getImage(); 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 // Inspect all the throw stmt inside the catch stmt
List<ASTThrowStatement> lstThrowStatements = catchStmt.findChildrenOfType(ASTThrowStatement.class); List<ASTThrowStatement> lstThrowStatements = catchStmt.findChildrenOfType(ASTThrowStatement.class);
for (ASTThrowStatement throwStatement : lstThrowStatements) { for (ASTThrowStatement throwStatement : lstThrowStatements) {
@ -72,13 +68,13 @@ public class PreserveStackTrace extends AbstractJavaRule {
ck(data, target, throwStatement, args); ck(data, target, throwStatement, args);
} }
else { else {
Node child = throwStatement.jjtGetChild(0); SimpleNode child = (SimpleNode)throwStatement.jjtGetChild(0);
while (child != null && child.jjtGetNumChildren() > 0 while (child != null && child.jjtGetNumChildren() > 0
&& !child.getClass().equals(ASTName.class)) { && !child.getClass().equals(ASTName.class)) {
child = child.jjtGetChild(0); child = (SimpleNode)child.jjtGetChild(0);
} }
if (child != null){ if (child != null){
if( child.getClass().equals(ASTName.class) && (!target.equals(((SimpleNode)child).getImage()) && !((SimpleNode)child).hasImageEqualTo(target + FILL_IN_STACKTRACE))) { if( child.getClass().equals(ASTName.class) && !target.equals(child.getImage()) && !child.hasImageEqualTo(target + FILL_IN_STACKTRACE)) {
Map<VariableNameDeclaration, List<NameOccurrence>> vars = ((ASTName) child).getScope().getVariableDeclarations(); Map<VariableNameDeclaration, List<NameOccurrence>> vars = ((ASTName) child).getScope().getVariableDeclarations();
for (VariableNameDeclaration decl: vars.keySet()) { for (VariableNameDeclaration decl: vars.keySet()) {
args = ((SimpleNode)decl.getNode().jjtGetParent()) args = ((SimpleNode)decl.getNode().jjtGetParent())
@ -98,33 +94,41 @@ public class PreserveStackTrace extends AbstractJavaRule {
return super.visit(catchStmt, data); return super.visit(catchStmt, data);
} }
/* @Override
* Search Catch stmt nodes for variable used to store unproperly created throwable or exception public Object visit(ASTVariableDeclarator node, Object data) {
*/ // Search Catch stmt nodes for variable used to store improperly created throwable or exception
private void gatherVariableWithExceptionRef(ASTCatchStatement catchStmt, Object data) {
try { try {
List<Node> nodes = catchStmt.findChildNodesWithXPath(FIND_THROWABLE_INSTANCE); List<Node> nodes = node.findChildNodesWithXPath(FIND_THROWABLE_INSTANCE);
for ( Node node : nodes ) { if (nodes.size() > 0) {
List <Node> violations = catchStmt.findChildNodesWithXPath("//Expression/PrimaryExpression/PrimaryPrefix/Name[@Image = '" + ((SimpleNode)node).getImage() + "']"); String variableName = ((SimpleNode)node.jjtGetChild(0)).getImage(); // VariableDeclatorId
if ( violations != null && violations.size() > 0 ) { ASTCatchStatement catchStmt = node.getFirstParentOfType(ASTCatchStatement.class);
while (catchStmt != null) {
List<SimpleNode> violations = catchStmt.findChildNodesWithXPath("//Expression/PrimaryExpression/PrimaryPrefix/Name[@Image = '" + variableName + "']");
if (violations != null && violations.size() > 0) {
// If, after this allocation, the 'initCause' method is called, and the ex passed // If, after this allocation, the 'initCause' method is called, and the ex passed
// this is not a violation // this is not a violation
if ( ! useInitCause((Node)violations.get(0),catchStmt) ) //FIXME: iterate, better than get(0) ? if (!useInitCause(violations.get(0), catchStmt)) {
super.addViolation(data,(SimpleNode) node); addViolation(data, node);
} }
} }
// check ASTCatchStatement higher up
catchStmt = catchStmt.getFirstParentOfType(ASTCatchStatement.class);
}
}
return super.visit(node, data);
} catch (JaxenException e) { } catch (JaxenException e) {
// XPath is valid, this should never happens... // XPath is valid, this should never happens...
e.printStackTrace(); throw new IllegalStateException(e);
}
} }
} private boolean useInitCause(SimpleNode node, ASTCatchStatement catchStmt) throws JaxenException {
private boolean useInitCause(Node node, ASTCatchStatement catchStmt) throws JaxenException {
// In case of NPE... // In case of NPE...
if ( node != null && ((SimpleNode)node).getImage() != null ) if ( node != null && node.getImage() != null )
{ {
List <Node> nodes = catchStmt.findChildNodesWithXPath("descendant::StatementExpression/PrimaryExpression/PrimaryPrefix/Name[@Image = '" + ((SimpleNode)node).getImage() + ".initCause']"); List <Node> nodes = catchStmt.findChildNodesWithXPath("descendant::StatementExpression/PrimaryExpression/PrimaryPrefix/Name[@Image = '" + node.getImage() + ".initCause']");
if ( nodes != null && nodes.size() > 0 ) if ( nodes != null && nodes.size() > 0 )
{ {
return true; return true;