diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/AvoidThrowingNullPointerExceptionRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/AvoidThrowingNullPointerExceptionRule.java new file mode 100644 index 0000000000..b1e4f541bd --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/AvoidThrowingNullPointerExceptionRule.java @@ -0,0 +1,77 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.design; + +import java.util.ArrayList; +import java.util.List; + +import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBody; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; +import net.sourceforge.pmd.lang.java.ast.ASTThrowStatement; +import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; + +/** + * Finds throw statements containing NullPointerException + * instances as thrown values + * + * @author Mykhailo Palahuta + */ +public class AvoidThrowingNullPointerExceptionRule extends AbstractJavaRule { + + @Override + public Object visit(ASTClassOrInterfaceBody node, Object data) { + if (bodyHasThrowNullPointerExceptionStatement(node)) { + addViolation(data, node); + } + return super.visit(node, data); + } + + private boolean bodyHasThrowNullPointerExceptionStatement(ASTClassOrInterfaceBody body) { + List throwStatements = body.findDescendantsOfType(ASTThrowStatement.class); + List npeInstances = getNullPointerExceptionInstances(body); + for (ASTThrowStatement throwStatement : throwStatements) { + if (throwsNullPointerException(throwStatement, npeInstances)) { + return true; + } + } + return false; + } + + private List getNullPointerExceptionInstances(ASTClassOrInterfaceBody body) { + List allocations = body.findDescendantsOfType(ASTAllocationExpression.class); + List npeInstances = new ArrayList<>(); + for (ASTAllocationExpression allocation : allocations) { + if (allocatesNullPointerException(allocation)) { + String assignedVarName = getNameOfAssignedVariable(allocation); + npeInstances.add(assignedVarName); + } + } + return npeInstances; + } + + private boolean allocatesNullPointerException(ASTAllocationExpression allocation) { + Class allocatedType = getAllocatedInstanceType(allocation); + return allocatedType != null && NullPointerException.class.isAssignableFrom(allocatedType); + } + + private Class getAllocatedInstanceType(ASTAllocationExpression allocation) { + List allocatedTypes = allocation + .findDescendantsOfType(ASTClassOrInterfaceType.class); + return allocatedTypes.isEmpty() ? null : allocatedTypes.get(0).getType(); + } + + private String getNameOfAssignedVariable(ASTAllocationExpression allocation) { + List variableDeclarators = allocation.getParent() + .findDescendantsOfType(ASTVariableDeclarator.class); + return variableDeclarators.isEmpty() ? null : variableDeclarators.get(0).getName(); + } + + private boolean throwsNullPointerException(ASTThrowStatement throwStatement, List npeInstances) { + String thrownImage = throwStatement.getFirstClassOrInterfaceTypeImage(); + return "NullPointerException".equals(thrownImage) || npeInstances.contains(thrownImage); + } +} diff --git a/pmd-java/src/main/resources/category/java/design.xml b/pmd-java/src/main/resources/category/java/design.xml index 069b50e18a..eb5378dd33 100644 --- a/pmd-java/src/main/resources/category/java/design.xml +++ b/pmd-java/src/main/resources/category/java/design.xml @@ -200,7 +200,7 @@ public void bar() { language="java" since="1.8" message="Avoid throwing null pointer exceptions." - class="net.sourceforge.pmd.lang.rule.XPathRule" + class="net.sourceforge.pmd.lang.java.rule.design.AvoidThrowingNullPointerExceptionRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_design.html#avoidthrowingnullpointerexception"> 1 - - - - - - - - --> - + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/AvoidThrowingNullPointerException.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/AvoidThrowingNullPointerException.xml index 843e2e09f9..80243f7c16 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/AvoidThrowingNullPointerException.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/AvoidThrowingNullPointerException.xml @@ -12,6 +12,32 @@ public class Foo { void bar() { throw new NullPointerException(); } +} + ]]> + + + + no problems if NullPointerException is only instantiated but not thrown + 0 + + + + + problem should be detected even if NullPointerException is stored in some intermediate variable + 1 +