Move NPathComplexity from metrics to design
This commit is contained in:
@ -18,7 +18,6 @@ import net.sourceforge.pmd.lang.java.ast.ASTWhileStatement;
|
||||
import net.sourceforge.pmd.lang.java.ast.JavaNode;
|
||||
import net.sourceforge.pmd.lang.java.ast.JavaParserVisitorDecorator;
|
||||
import net.sourceforge.pmd.lang.java.metrics.impl.CycloMetric;
|
||||
import net.sourceforge.pmd.lang.java.rule.design.NPathComplexityRule;
|
||||
|
||||
/**
|
||||
* Decorator which counts the complexity of boolean expressions for Cyclo.
|
||||
@ -94,7 +93,7 @@ public class CycloPathAwareDecorator extends JavaParserVisitorDecorator {
|
||||
super.visit(node, data);
|
||||
|
||||
if (node.isTernary()) {
|
||||
int boolCompTern = NPathComplexityRule.sumExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
|
||||
int boolCompTern = CycloMetric.booleanExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
|
||||
((MutableInt) data).add(1 + boolCompTern);
|
||||
}
|
||||
return data;
|
||||
|
@ -1,53 +0,0 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.lang.java.metrics.rule;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.metrics.JavaMetrics;
|
||||
import net.sourceforge.pmd.lang.java.metrics.api.JavaOperationMetricKey;
|
||||
import net.sourceforge.pmd.lang.java.rule.AbstractJavaMetricsRule;
|
||||
import net.sourceforge.pmd.properties.IntegerProperty;
|
||||
|
||||
/**
|
||||
* Simple n-path complexity rule.
|
||||
*
|
||||
* @author Clément Fournier
|
||||
*/
|
||||
public class NPathComplexityRule extends AbstractJavaMetricsRule {
|
||||
|
||||
private static final IntegerProperty REPORT_LEVEL_DESCRIPTOR = new IntegerProperty(
|
||||
"reportLevel", "N-Path Complexity reporting threshold", 1, 30, 200, 1.0f);
|
||||
|
||||
|
||||
private static int reportLevel = 200;
|
||||
|
||||
|
||||
public NPathComplexityRule() {
|
||||
definePropertyDescriptor(REPORT_LEVEL_DESCRIPTOR);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object visit(ASTCompilationUnit node, Object data) {
|
||||
reportLevel = getProperty(REPORT_LEVEL_DESCRIPTOR);
|
||||
|
||||
super.visit(node, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final Object visit(ASTMethodOrConstructorDeclaration node, Object data) {
|
||||
int npath = (int) JavaMetrics.get(JavaOperationMetricKey.NPATH, node);
|
||||
if (npath >= reportLevel) {
|
||||
addViolation(data, node, new String[] {node instanceof ASTMethodDeclaration ? "method" : "constructor",
|
||||
node.getQualifiedName().getOperation(), "" + npath, });
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
@ -11,6 +11,8 @@ import net.sourceforge.pmd.lang.java.ast.ASTForStatement;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTIfStatement;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTSwitchStatement;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTWhileStatement;
|
||||
import net.sourceforge.pmd.lang.java.metrics.impl.CycloMetric;
|
||||
|
||||
|
||||
/**
|
||||
* @author Donald A. Leckie,
|
||||
@ -25,7 +27,7 @@ public class CyclomaticComplexityRule extends StdCyclomaticComplexityRule {
|
||||
public Object visit(ASTIfStatement node, Object data) {
|
||||
super.visit(node, data);
|
||||
|
||||
int boolCompIf = NPathComplexityRule.sumExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
|
||||
int boolCompIf = CycloMetric.booleanExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
|
||||
entryStack.peek().bumpDecisionPoints(boolCompIf);
|
||||
return data;
|
||||
}
|
||||
@ -34,8 +36,7 @@ public class CyclomaticComplexityRule extends StdCyclomaticComplexityRule {
|
||||
public Object visit(ASTForStatement node, Object data) {
|
||||
super.visit(node, data);
|
||||
|
||||
int boolCompFor = NPathComplexityRule
|
||||
.sumExpressionComplexity(node.getFirstDescendantOfType(ASTExpression.class));
|
||||
int boolCompFor = CycloMetric.booleanExpressionComplexity(node.getFirstDescendantOfType(ASTExpression.class));
|
||||
entryStack.peek().bumpDecisionPoints(boolCompFor);
|
||||
return data;
|
||||
}
|
||||
@ -44,7 +45,7 @@ public class CyclomaticComplexityRule extends StdCyclomaticComplexityRule {
|
||||
public Object visit(ASTDoStatement node, Object data) {
|
||||
super.visit(node, data);
|
||||
|
||||
int boolCompDo = NPathComplexityRule.sumExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
|
||||
int boolCompDo = CycloMetric.booleanExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
|
||||
entryStack.peek().bumpDecisionPoints(boolCompDo);
|
||||
return data;
|
||||
}
|
||||
@ -53,7 +54,7 @@ public class CyclomaticComplexityRule extends StdCyclomaticComplexityRule {
|
||||
public Object visit(ASTSwitchStatement node, Object data) {
|
||||
super.visit(node, data);
|
||||
|
||||
int boolCompSwitch = NPathComplexityRule.sumExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
|
||||
int boolCompSwitch = CycloMetric.booleanExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
|
||||
entryStack.peek().bumpDecisionPoints(boolCompSwitch);
|
||||
return data;
|
||||
}
|
||||
@ -62,7 +63,7 @@ public class CyclomaticComplexityRule extends StdCyclomaticComplexityRule {
|
||||
public Object visit(ASTWhileStatement node, Object data) {
|
||||
super.visit(node, data);
|
||||
|
||||
int boolCompWhile = NPathComplexityRule.sumExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
|
||||
int boolCompWhile = CycloMetric.booleanExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
|
||||
entryStack.peek().bumpDecisionPoints(boolCompWhile);
|
||||
return data;
|
||||
}
|
||||
@ -72,8 +73,7 @@ public class CyclomaticComplexityRule extends StdCyclomaticComplexityRule {
|
||||
super.visit(node, data);
|
||||
|
||||
if (node.isTernary()) {
|
||||
int boolCompTern = NPathComplexityRule
|
||||
.sumExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
|
||||
int boolCompTern = CycloMetric.booleanExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
|
||||
entryStack.peek().bumpDecisionPoints(boolCompTern);
|
||||
}
|
||||
return data;
|
||||
|
@ -4,262 +4,52 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.java.rule.design;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTConditionalAndExpression;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTConditionalExpression;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTConditionalOrExpression;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTDoStatement;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTExpression;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTForStatement;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTIfStatement;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTReturnStatement;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTStatement;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTSwitchLabel;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTSwitchStatement;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTTryStatement;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTWhileStatement;
|
||||
import net.sourceforge.pmd.lang.java.ast.JavaNode;
|
||||
import net.sourceforge.pmd.lang.java.rule.AbstractStatisticalJavaRule;
|
||||
import net.sourceforge.pmd.stat.DataPoint;
|
||||
import net.sourceforge.pmd.util.NumericConstants;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.metrics.JavaMetrics;
|
||||
import net.sourceforge.pmd.lang.java.metrics.api.JavaOperationMetricKey;
|
||||
import net.sourceforge.pmd.lang.java.rule.AbstractJavaMetricsRule;
|
||||
import net.sourceforge.pmd.properties.IntegerProperty;
|
||||
|
||||
/**
|
||||
* NPath complexity is a measurement of the acyclic execution paths through a
|
||||
* function. See Nejmeh, Communications of the ACM Feb 1988 pp 188-200.
|
||||
*
|
||||
* Simple n-path complexity rule.
|
||||
*
|
||||
* @author Clément Fournier
|
||||
* @author Jason Bennett
|
||||
*/
|
||||
public class NPathComplexityRule extends AbstractStatisticalJavaRule {
|
||||
public class NPathComplexityRule extends AbstractJavaMetricsRule {
|
||||
|
||||
private static final IntegerProperty REPORT_LEVEL_DESCRIPTOR = new IntegerProperty(
|
||||
"reportLevel", "N-Path Complexity reporting threshold", 1, 30, 200, 1.0f);
|
||||
|
||||
|
||||
private static int reportLevel = 200;
|
||||
|
||||
|
||||
public NPathComplexityRule() {
|
||||
super();
|
||||
setProperty(MINIMUM_DESCRIPTOR, 200d);
|
||||
definePropertyDescriptor(REPORT_LEVEL_DESCRIPTOR);
|
||||
}
|
||||
|
||||
private int complexityMultipleOf(JavaNode node, int npathStart, Object data) {
|
||||
|
||||
int npath = npathStart;
|
||||
JavaNode n;
|
||||
|
||||
for (int i = 0; i < node.jjtGetNumChildren(); i++) {
|
||||
n = (JavaNode) node.jjtGetChild(i);
|
||||
npath *= (Integer) n.jjtAccept(this, data);
|
||||
}
|
||||
|
||||
return npath;
|
||||
}
|
||||
|
||||
private int complexitySumOf(JavaNode node, int npathStart, Object data) {
|
||||
|
||||
int npath = npathStart;
|
||||
JavaNode n;
|
||||
|
||||
for (int i = 0; i < node.jjtGetNumChildren(); i++) {
|
||||
n = (JavaNode) node.jjtGetChild(i);
|
||||
npath += (Integer) n.jjtAccept(this, data);
|
||||
}
|
||||
|
||||
return npath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(ASTMethodDeclaration node, Object data) {
|
||||
int npath = complexityMultipleOf(node, 1, data);
|
||||
public Object visit(ASTCompilationUnit node, Object data) {
|
||||
reportLevel = getProperty(REPORT_LEVEL_DESCRIPTOR);
|
||||
|
||||
DataPoint point = new DataPoint();
|
||||
point.setNode(node);
|
||||
point.setScore(1.0 * npath);
|
||||
point.setMessage(getMessage());
|
||||
addDataPoint(point);
|
||||
|
||||
return Integer.valueOf(npath);
|
||||
super.visit(node, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object visit(JavaNode node, Object data) {
|
||||
int npath = complexityMultipleOf(node, 1, data);
|
||||
return Integer.valueOf(npath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(ASTIfStatement node, Object data) {
|
||||
// (npath of if + npath of else (or 1) + bool_comp of if) * npath of
|
||||
// next
|
||||
|
||||
List<JavaNode> statementChildren = new ArrayList<>();
|
||||
for (int i = 0; i < node.jjtGetNumChildren(); i++) {
|
||||
if (node.jjtGetChild(i).getClass() == ASTStatement.class) {
|
||||
statementChildren.add((JavaNode) node.jjtGetChild(i));
|
||||
}
|
||||
public final Object visit(ASTMethodOrConstructorDeclaration node, Object data) {
|
||||
int npath = (int) JavaMetrics.get(JavaOperationMetricKey.NPATH, node);
|
||||
if (npath >= reportLevel) {
|
||||
addViolation(data, node, new String[] {node instanceof ASTMethodDeclaration ? "method" : "constructor",
|
||||
node.getQualifiedName().getOperation(), "" + npath, });
|
||||
}
|
||||
|
||||
if (statementChildren.isEmpty() || statementChildren.size() == 1 && node.hasElse()
|
||||
|| statementChildren.size() != 1 && !node.hasElse()) {
|
||||
throw new IllegalStateException("If node has wrong number of children");
|
||||
}
|
||||
|
||||
// add path for not taking if
|
||||
int complexity = 0;
|
||||
if (!node.hasElse()) {
|
||||
complexity++;
|
||||
}
|
||||
|
||||
for (JavaNode element : statementChildren) {
|
||||
complexity += (Integer) element.jjtAccept(this, data);
|
||||
}
|
||||
|
||||
int boolCompIf = sumExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
|
||||
return Integer.valueOf(boolCompIf + complexity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(ASTWhileStatement node, Object data) {
|
||||
// (npath of while + bool_comp of while + 1) * npath of next
|
||||
|
||||
int boolCompWhile = sumExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
|
||||
|
||||
Integer nPathWhile = (Integer) ((JavaNode) node.getFirstChildOfType(ASTStatement.class)).jjtAccept(this, data);
|
||||
|
||||
return Integer.valueOf(boolCompWhile + nPathWhile + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(ASTDoStatement node, Object data) {
|
||||
// (npath of do + bool_comp of do + 1) * npath of next
|
||||
|
||||
int boolCompDo = sumExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
|
||||
|
||||
Integer nPathDo = (Integer) ((JavaNode) node.getFirstChildOfType(ASTStatement.class)).jjtAccept(this, data);
|
||||
|
||||
return Integer.valueOf(boolCompDo + nPathDo + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(ASTForStatement node, Object data) {
|
||||
// (npath of for + bool_comp of for + 1) * npath of next
|
||||
|
||||
int boolCompFor = sumExpressionComplexity(node.getFirstDescendantOfType(ASTExpression.class));
|
||||
|
||||
Integer nPathFor = (Integer) ((JavaNode) node.getFirstChildOfType(ASTStatement.class)).jjtAccept(this, data);
|
||||
|
||||
return Integer.valueOf(boolCompFor + nPathFor + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(ASTReturnStatement node, Object data) {
|
||||
// return statements are valued at 1, or the value of the boolean
|
||||
// expression
|
||||
|
||||
ASTExpression expr = node.getFirstChildOfType(ASTExpression.class);
|
||||
|
||||
if (expr == null) {
|
||||
return NumericConstants.ONE;
|
||||
}
|
||||
|
||||
int boolCompReturn = sumExpressionComplexity(expr);
|
||||
int conditionalExpressionComplexity = complexityMultipleOf(expr, 1, data);
|
||||
|
||||
if (conditionalExpressionComplexity > 1) {
|
||||
boolCompReturn += conditionalExpressionComplexity;
|
||||
}
|
||||
|
||||
if (boolCompReturn > 0) {
|
||||
return Integer.valueOf(boolCompReturn);
|
||||
}
|
||||
return NumericConstants.ONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(ASTSwitchStatement node, Object data) {
|
||||
// bool_comp of switch + sum(npath(case_range))
|
||||
|
||||
int boolCompSwitch = sumExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
|
||||
|
||||
int npath = 0;
|
||||
int caseRange = 0;
|
||||
for (int i = 0; i < node.jjtGetNumChildren(); i++) {
|
||||
JavaNode n = (JavaNode) node.jjtGetChild(i);
|
||||
|
||||
// Fall-through labels count as 1 for complexity
|
||||
if (n instanceof ASTSwitchLabel) {
|
||||
npath += caseRange;
|
||||
caseRange = 1;
|
||||
} else {
|
||||
Integer complexity = (Integer) n.jjtAccept(this, data);
|
||||
caseRange *= complexity;
|
||||
}
|
||||
}
|
||||
// add in npath of last label
|
||||
npath += caseRange;
|
||||
return Integer.valueOf(boolCompSwitch + npath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(ASTTryStatement node, Object data) {
|
||||
/*
|
||||
* This scenario was not addressed by the original paper. Based on the
|
||||
* principles outlined in the paper, as well as the Checkstyle NPath
|
||||
* implementation, this code will add the complexity of the try to the
|
||||
* complexities of the catch and finally blocks.
|
||||
*/
|
||||
int npath = complexitySumOf(node, 0, data);
|
||||
|
||||
return Integer.valueOf(npath);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(ASTConditionalExpression node, Object data) {
|
||||
if (node.isTernary()) {
|
||||
int npath = complexitySumOf(node, 0, data);
|
||||
|
||||
npath += 2;
|
||||
return Integer.valueOf(npath);
|
||||
}
|
||||
return NumericConstants.ONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the boolean complexity of the given expression. NPath boolean
|
||||
* complexity is the sum of && and || tokens. This is calculated by summing
|
||||
* the number of children of the &&'s (minus one) and the children of the
|
||||
* ||'s (minus one).
|
||||
*
|
||||
* <p>Note that this calculation applies to Cyclomatic Complexity as well.</p>
|
||||
*
|
||||
* @param expr
|
||||
* control structure expression
|
||||
* @return complexity of the boolean expression
|
||||
*/
|
||||
public static int sumExpressionComplexity(ASTExpression expr) {
|
||||
if (expr == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
List<ASTConditionalAndExpression> andNodes = expr.findDescendantsOfType(ASTConditionalAndExpression.class);
|
||||
List<ASTConditionalOrExpression> orNodes = expr.findDescendantsOfType(ASTConditionalOrExpression.class);
|
||||
|
||||
int children = 0;
|
||||
|
||||
for (ASTConditionalOrExpression element : orNodes) {
|
||||
children += element.jjtGetNumChildren();
|
||||
children--;
|
||||
}
|
||||
|
||||
for (ASTConditionalAndExpression element : andNodes) {
|
||||
children += element.jjtGetNumChildren();
|
||||
children--;
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getViolationParameters(DataPoint point) {
|
||||
return new String[] { ((ASTMethodDeclaration) point.getNode()).getMethodName(),
|
||||
String.valueOf((int) point.getScore()), };
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1025,45 +1025,57 @@ public class Foo extends Bar {
|
||||
|
||||
<rule name="NPathComplexity"
|
||||
since="3.9"
|
||||
message="The method {0}() has an NPath complexity of {1}"
|
||||
message="The {0} ''{1}'' has an NPath complexity of {2}"
|
||||
class="net.sourceforge.pmd.lang.java.rule.design.NPathComplexityRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_design.html#npathcomplexity">
|
||||
<description>
|
||||
The NPath complexity of a method is the number of acyclic execution paths through that method.
|
||||
A threshold of 200 is generally considered the point where measures should be taken to reduce
|
||||
complexity and increase readability.
|
||||
The NPath complexity of a method is the number of acyclic execution paths through that method.
|
||||
While cyclomatic complexity counts the number of decision points in a method, NPath counts the number of
|
||||
full paths from the beginning to the end of the block of the method. That metric grows exponentially, as
|
||||
it multiplies the complexity of statements in the same block. For more details on the calculation, see the
|
||||
documentation of the [NPath metric](/pmd_java_metrics_index.html#npath-complexity-npath).
|
||||
|
||||
A threshold of 200 is generally considered the point where measures should be taken to reduce
|
||||
complexity and increase readability.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
void bar() { // this is something more complex than it needs to be,
|
||||
if (y) { // it should be broken down into smaller methods or functions
|
||||
for (j = 0; j < m; j++) {
|
||||
if (j > r) {
|
||||
doSomething();
|
||||
while (f < 5 ) {
|
||||
anotherThing();
|
||||
f -= 27;
|
||||
}
|
||||
} else {
|
||||
tryThis();
|
||||
}
|
||||
}
|
||||
<![CDATA[
|
||||
public class Foo {
|
||||
public static void bar() { // Ncss = 252: reported!
|
||||
boolean a, b = true;
|
||||
try { // 2 * 2 + 2 = 6
|
||||
if (true) { // 2
|
||||
List buz = new ArrayList();
|
||||
}
|
||||
|
||||
for(int i = 0; i < 19; i++) { // * 2
|
||||
List buz = new ArrayList();
|
||||
}
|
||||
} catch(Exception e) {
|
||||
if (true) { // 2
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if ( r - n > 45) {
|
||||
while (doMagic()) {
|
||||
findRabbits();
|
||||
}
|
||||
|
||||
while (j++ < 20) { // * 2
|
||||
List buz = new ArrayList();
|
||||
}
|
||||
try {
|
||||
doSomethingDangerous();
|
||||
} catch (Exception ex) {
|
||||
makeAmends();
|
||||
} finally {
|
||||
dontDoItAgain();
|
||||
|
||||
switch(j) { // * 7
|
||||
case 1:
|
||||
case 2: break;
|
||||
case 3: j = 5; break;
|
||||
case 4: if (b && a) { bar(); } break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
do { // * 3
|
||||
List buz = new ArrayList();
|
||||
} while (a && j++ < 30);
|
||||
}
|
||||
}
|
||||
]]>
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
|
@ -62,60 +62,5 @@ class Foo {
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="NPathComplexity"
|
||||
since="3.9"
|
||||
message="The {0} ''{1}'' has an NPath complexity of {2}"
|
||||
class="net.sourceforge.pmd.lang.java.metrics.rule.NPathComplexityRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_metrics.html#NPathComplexity">
|
||||
<description>
|
||||
The NPath complexity of a method is the number of acyclic execution paths through that method.
|
||||
While cyclomatic complexity counts the number of decision points in a method, NPath counts the number of
|
||||
full paths from the beginning to the end of the block of the method. That metric grows exponentially, as
|
||||
it multiplies the complexity of statements in the same block. For more details on the calculation, see the
|
||||
documentation of the [NPath metric](/pmd_java_metrics_index.html#npath-complexity-npath).
|
||||
|
||||
A threshold of 200 is generally considered the point where measures should be taken to reduce
|
||||
complexity and increase readability.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Foo {
|
||||
public static void bar() { // Ncss = 252: reported!
|
||||
boolean a, b = true;
|
||||
try { // 2 * 2 + 2 = 6
|
||||
if (true) { // 2
|
||||
List buz = new ArrayList();
|
||||
}
|
||||
|
||||
for(int i = 0; i < 19; i++) { // * 2
|
||||
List buz = new ArrayList();
|
||||
}
|
||||
} catch(Exception e) {
|
||||
if (true) { // 2
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
while (j++ < 20) { // * 2
|
||||
List buz = new ArrayList();
|
||||
}
|
||||
|
||||
switch(j) { // * 7
|
||||
case 1:
|
||||
case 2: break;
|
||||
case 3: j = 5; break;
|
||||
case 4: if (b && a) { bar(); } break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
do { // * 3
|
||||
List buz = new ArrayList();
|
||||
} while (a && j++ < 30);
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
</ruleset>
|
||||
|
@ -26,6 +26,5 @@ public class MetricsRulesTest extends SimpleAggregatorTst {
|
||||
@Override
|
||||
public void setUp() {
|
||||
addRule(RULESET, "CyclomaticComplexity");
|
||||
addRule(RULESET, "NPathComplexity");
|
||||
}
|
||||
}
|
||||
|
@ -3,96 +3,197 @@
|
||||
xmlns="http://pmd.sourceforge.net/rule-tests"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://pmd.sourceforge.net/rule-tests http://pmd.sourceforge.net/rule-tests_1_0_0.xsd">
|
||||
<test-code>
|
||||
<description>ok</description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<code><![CDATA[
|
||||
|
||||
<code-fragment id="full-example"><![CDATA[
|
||||
public class Foo {
|
||||
public static void bar() {
|
||||
if (true) {List buz = new ArrayList();}
|
||||
}
|
||||
public static void bar() {
|
||||
boolean a, b = true;
|
||||
try { // 2 * 2 + 2
|
||||
if (true) { // 2
|
||||
List buz = new ArrayList();
|
||||
}
|
||||
|
||||
for(int i = 0; i < 19; i++) { // 2
|
||||
List buz = new ArrayList();
|
||||
}
|
||||
} catch(Exception e) {
|
||||
if (true) { // 2
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
int j = 0;
|
||||
if (true || a && b) { // 4
|
||||
j = 10;
|
||||
return;
|
||||
}
|
||||
|
||||
while (j++ < 20) { // 2
|
||||
List buz = new ArrayList();
|
||||
}
|
||||
|
||||
switch(j) { // 7
|
||||
case 1:
|
||||
case 2: break;
|
||||
case 3: j = 5; break;
|
||||
case 4: if (b && a) { bar(); } break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
do { // 3
|
||||
List buz = new ArrayList();
|
||||
} while (a && j++ < 30);
|
||||
|
||||
if (b) { // 2
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
]]></code-fragment>
|
||||
|
||||
|
||||
<test-code>
|
||||
<description>fail, with minimum</description>
|
||||
<rule-property name="minimum">1.0</rule-property>
|
||||
<description>Full example</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<expected-messages>
|
||||
<message>The method bar() has an NPath complexity of 2</message>
|
||||
<message>The method 'bar()' has an NPath complexity of 2016</message>
|
||||
</expected-messages>
|
||||
<code-ref id="full-example"/>
|
||||
</test-code>
|
||||
|
||||
|
||||
<test-code>
|
||||
<description>Test default report level - report 200</description>
|
||||
<rule-property name="reportLevel">0</rule-property>
|
||||
<expected-problems>1</expected-problems>
|
||||
<expected-messages>
|
||||
<message>The method 'bar()' has an NPath complexity of 200</message>
|
||||
</expected-messages>
|
||||
<code><![CDATA[
|
||||
public class Foo {
|
||||
public static void bar() {
|
||||
if (true) {List buz = new ArrayList();}
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
public class Foo {
|
||||
public static void bar() {
|
||||
boolean a, b = true;
|
||||
int j = 0;
|
||||
|
||||
switch (j) { // 5
|
||||
case 0:
|
||||
case 1:
|
||||
case 3: if (a || b) {} break;
|
||||
}
|
||||
|
||||
switch (j) { // * 5
|
||||
case 0:
|
||||
case 1:
|
||||
case 3: if (a || b) {} break;
|
||||
}
|
||||
|
||||
if (true || a && b); // * 4
|
||||
while (j++ < 20); // * 2
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description><![CDATA[
|
||||
failure case
|
||||
]]></description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<description>Test default report level - ignore 199</description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<code><![CDATA[
|
||||
public class Foo {
|
||||
public static int bar() {
|
||||
try{
|
||||
if (true) {List buz = new ArrayList();}
|
||||
for(int i = 0; i < 19; i++) {List buz = new ArrayList();}
|
||||
int j = 0;
|
||||
if (true) {j = 10;}
|
||||
while (j++ < 20) {List buz = new ArrayList();}
|
||||
if (true) {j = 21;}
|
||||
if(false) {j = 0;}
|
||||
do {List buz = new ArrayList();} while (j++ < 30);
|
||||
} catch(Exception e){
|
||||
if (true) {e.printStackTrace();}
|
||||
}
|
||||
if (true) {return 1;}
|
||||
else {return 2;}
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
class Foo {
|
||||
void bar() {
|
||||
boolean a, b;
|
||||
try {
|
||||
switch(j) { // 7
|
||||
case 1:
|
||||
case 2: break;
|
||||
case 3: j = 5; break;
|
||||
case 4: if (b && a) { bar(); } break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
switch(j) { // * 7
|
||||
case 1:
|
||||
case 2: break;
|
||||
case 3: j = 5; break;
|
||||
case 4: if (b && a) { bar(); } break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (true || a || b); // * 4
|
||||
|
||||
} catch (ScaryException e) {
|
||||
if (true || a); // + 3
|
||||
}
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>Boolean operators</description>
|
||||
<rule-property name="reportLevel">2</rule-property>
|
||||
<expected-problems>1</expected-problems>
|
||||
<expected-messages>
|
||||
<message>The method 'bar()' has an NPath complexity of 4</message>
|
||||
</expected-messages>
|
||||
<code><![CDATA[
|
||||
class Foo {
|
||||
void bar() {
|
||||
if (a && b || c);
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
|
||||
<code-fragment id="bug3484404"><![CDATA[
|
||||
class Bar
|
||||
{
|
||||
public void x(boolean x, boolean y)
|
||||
{
|
||||
z(
|
||||
(x ? 1 : 2),
|
||||
(y ? 3 : 4)
|
||||
);
|
||||
}
|
||||
class Bar {
|
||||
public void x(boolean x, boolean y) {
|
||||
z((x ? 1 : 2), (y ? 3 : 4));
|
||||
}
|
||||
|
||||
public int y(boolean x, boolean y)
|
||||
{
|
||||
return z(
|
||||
(x ? 1 : 2),
|
||||
(y ? 3 : 4)
|
||||
);
|
||||
}
|
||||
public int y(boolean x, boolean y) {
|
||||
return z((x ? 1 : 2), (y ? 3 : 4));
|
||||
}
|
||||
|
||||
public int z(int x, int y)
|
||||
{
|
||||
return x + y;
|
||||
}
|
||||
public int z(int x, int y) {
|
||||
return x + y;
|
||||
}
|
||||
}
|
||||
]]></code-fragment>
|
||||
|
||||
<test-code>
|
||||
<description>test case for bug 3484404 (Invalid NPath calculation in return statement)</description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<code-ref id="bug3484404"/>
|
||||
</test-code>
|
||||
<test-code>
|
||||
<description>test case for bug 3484404 (Invalid NPath calculation in return statement) with minimum 25</description>
|
||||
<rule-property name="minimum">25.0</rule-property>
|
||||
<rule-property name="reportLevel">4</rule-property>
|
||||
<expected-problems>2</expected-problems>
|
||||
<expected-messages>
|
||||
<message>The method x() has an NPath complexity of 25</message>
|
||||
<message>The method y() has an NPath complexity of 25</message>
|
||||
<message>The method 'x(boolean, boolean)' has an NPath complexity of 4</message>
|
||||
<message>The method 'y(boolean, boolean)' has an NPath complexity of 4</message>
|
||||
</expected-messages>
|
||||
<code-ref id="bug3484404"/>
|
||||
</test-code>
|
||||
|
||||
|
||||
<test-code>
|
||||
<description>NPath supports constructors</description>
|
||||
<rule-property name="reportLevel">6</rule-property>
|
||||
<expected-problems>1</expected-problems>
|
||||
<expected-messages>
|
||||
<message>The constructor 'Foo()' has an NPath complexity of 7</message>
|
||||
</expected-messages>
|
||||
<code><![CDATA[
|
||||
class Foo {
|
||||
Foo() {
|
||||
boolean a, b;
|
||||
int j = 23;
|
||||
switch(j) {
|
||||
case 1:
|
||||
case 2: break;
|
||||
case 3: j = 5; break;
|
||||
case 4: if (b && a) { bar(); } break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
</test-data>
|
||||
|
@ -1,199 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<test-data
|
||||
xmlns="http://pmd.sourceforge.net/rule-tests"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://pmd.sourceforge.net/rule-tests http://pmd.sourceforge.net/rule-tests_1_0_0.xsd">
|
||||
|
||||
<code-fragment id="full-example"><![CDATA[
|
||||
public class Foo {
|
||||
public static void bar() {
|
||||
boolean a, b = true;
|
||||
try { // 2 * 2 + 2
|
||||
if (true) { // 2
|
||||
List buz = new ArrayList();
|
||||
}
|
||||
|
||||
for(int i = 0; i < 19; i++) { // 2
|
||||
List buz = new ArrayList();
|
||||
}
|
||||
} catch(Exception e) {
|
||||
if (true) { // 2
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
int j = 0;
|
||||
if (true || a && b) { // 4
|
||||
j = 10;
|
||||
return;
|
||||
}
|
||||
|
||||
while (j++ < 20) { // 2
|
||||
List buz = new ArrayList();
|
||||
}
|
||||
|
||||
switch(j) { // 7
|
||||
case 1:
|
||||
case 2: break;
|
||||
case 3: j = 5; break;
|
||||
case 4: if (b && a) { bar(); } break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
do { // 3
|
||||
List buz = new ArrayList();
|
||||
} while (a && j++ < 30);
|
||||
|
||||
if (b) { // 2
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
]]></code-fragment>
|
||||
|
||||
|
||||
<test-code>
|
||||
<description>Full example</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<expected-messages>
|
||||
<message>The method 'bar()' has an NPath complexity of 2016</message>
|
||||
</expected-messages>
|
||||
<code-ref id="full-example"/>
|
||||
</test-code>
|
||||
|
||||
|
||||
<test-code>
|
||||
<description>Test default report level - report 200</description>
|
||||
<rule-property name="reportLevel">0</rule-property>
|
||||
<expected-problems>1</expected-problems>
|
||||
<expected-messages>
|
||||
<message>The method 'bar()' has an NPath complexity of 200</message>
|
||||
</expected-messages>
|
||||
<code><![CDATA[
|
||||
public class Foo {
|
||||
public static void bar() {
|
||||
boolean a, b = true;
|
||||
int j = 0;
|
||||
|
||||
switch (j) { // 5
|
||||
case 0:
|
||||
case 1:
|
||||
case 3: if (a || b) {} break;
|
||||
}
|
||||
|
||||
switch (j) { // * 5
|
||||
case 0:
|
||||
case 1:
|
||||
case 3: if (a || b) {} break;
|
||||
}
|
||||
|
||||
if (true || a && b); // * 4
|
||||
while (j++ < 20); // * 2
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>Test default report level - ignore 199</description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<code><![CDATA[
|
||||
class Foo {
|
||||
void bar() {
|
||||
boolean a, b;
|
||||
try {
|
||||
switch(j) { // 7
|
||||
case 1:
|
||||
case 2: break;
|
||||
case 3: j = 5; break;
|
||||
case 4: if (b && a) { bar(); } break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
switch(j) { // * 7
|
||||
case 1:
|
||||
case 2: break;
|
||||
case 3: j = 5; break;
|
||||
case 4: if (b && a) { bar(); } break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (true || a || b); // * 4
|
||||
|
||||
} catch (ScaryException e) {
|
||||
if (true || a); // + 3
|
||||
}
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>Boolean operators</description>
|
||||
<rule-property name="reportLevel">2</rule-property>
|
||||
<expected-problems>1</expected-problems>
|
||||
<expected-messages>
|
||||
<message>The method 'bar()' has an NPath complexity of 4</message>
|
||||
</expected-messages>
|
||||
<code><![CDATA[
|
||||
class Foo {
|
||||
void bar() {
|
||||
if (a && b || c);
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
|
||||
<code-fragment id="bug3484404"><![CDATA[
|
||||
class Bar {
|
||||
public void x(boolean x, boolean y) {
|
||||
z((x ? 1 : 2), (y ? 3 : 4));
|
||||
}
|
||||
|
||||
public int y(boolean x, boolean y) {
|
||||
return z((x ? 1 : 2), (y ? 3 : 4));
|
||||
}
|
||||
|
||||
public int z(int x, int y) {
|
||||
return x + y;
|
||||
}
|
||||
}
|
||||
]]></code-fragment>
|
||||
|
||||
<test-code>
|
||||
<description>test case for bug 3484404 (Invalid NPath calculation in return statement)</description>
|
||||
<rule-property name="reportLevel">4</rule-property>
|
||||
<expected-problems>2</expected-problems>
|
||||
<expected-messages>
|
||||
<message>The method 'x(boolean, boolean)' has an NPath complexity of 4</message>
|
||||
<message>The method 'y(boolean, boolean)' has an NPath complexity of 4</message>
|
||||
</expected-messages>
|
||||
<code-ref id="bug3484404"/>
|
||||
</test-code>
|
||||
|
||||
|
||||
<test-code>
|
||||
<description>NPath supports constructors</description>
|
||||
<rule-property name="reportLevel">6</rule-property>
|
||||
<expected-problems>1</expected-problems>
|
||||
<expected-messages>
|
||||
<message>The constructor 'Foo()' has an NPath complexity of 7</message>
|
||||
</expected-messages>
|
||||
<code><![CDATA[
|
||||
class Foo {
|
||||
Foo() {
|
||||
boolean a, b;
|
||||
int j = 23;
|
||||
switch(j) {
|
||||
case 1:
|
||||
case 2: break;
|
||||
case 3: j = 5; break;
|
||||
case 4: if (b && a) { bar(); } break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
</test-data>
|
Reference in New Issue
Block a user