Merge branch 'pr-523'

This commit is contained in:
Andreas Dangel
2017-07-28 14:52:38 +02:00
15 changed files with 947 additions and 17 deletions

View File

@ -4,6 +4,8 @@
package net.sourceforge.pmd.lang.java.metrics.api;
import java.util.Objects;
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
import net.sourceforge.pmd.lang.java.metrics.impl.AtfdMetric.AtfdClassMetric;
import net.sourceforge.pmd.lang.java.metrics.impl.CycloMetric.CycloClassMetric;
@ -101,7 +103,20 @@ public enum JavaClassMetricKey implements MetricKey<ASTAnyTypeDeclaration> {
public boolean supports(ASTAnyTypeDeclaration node) {
return metric.supports(node);
}
@Override
public boolean equals(Object obj) {
return obj != null && getClass() == obj.getClass()
&& Objects.equals(name(), getClass().cast(obj).name())
&& Objects.equals(getCalculator(), getClass().cast(obj).getCalculator());
}
@Override
public int hashCode() {
return (metric != null ? metric.hashCode() * 31 : 0) + (name != null ? name.hashCode() : 0);
}
};
}
}

View File

@ -4,11 +4,14 @@
package net.sourceforge.pmd.lang.java.metrics.api;
import java.util.Objects;
import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration;
import net.sourceforge.pmd.lang.java.metrics.impl.AtfdMetric.AtfdOperationMetric;
import net.sourceforge.pmd.lang.java.metrics.impl.CycloMetric.CycloOperationMetric;
import net.sourceforge.pmd.lang.java.metrics.impl.LocMetric.LocOperationMetric;
import net.sourceforge.pmd.lang.java.metrics.impl.NcssMetric.NcssOperationMetric;
import net.sourceforge.pmd.lang.java.metrics.impl.NpathMetric;
import net.sourceforge.pmd.lang.metrics.api.Metric;
import net.sourceforge.pmd.lang.metrics.api.MetricKey;
@ -43,7 +46,18 @@ public enum JavaOperationMetricKey implements MetricKey<ASTMethodOrConstructorDe
*
* @see net.sourceforge.pmd.lang.java.metrics.impl.LocMetric
*/
LOC(new LocOperationMetric());
LOC(new LocOperationMetric()),
/**
* N-path complexity.
*
* @see NpathMetric
*/
NPATH(new NpathMetric());
private final JavaOperationMetric calculator;
@ -93,6 +107,21 @@ public enum JavaOperationMetricKey implements MetricKey<ASTMethodOrConstructorDe
public boolean supports(ASTMethodOrConstructorDeclaration node) {
return metric.supports(node);
}
@Override
public boolean equals(Object obj) {
return obj != null && getClass() == obj.getClass()
&& Objects.equals(name(), getClass().cast(obj).name())
&& Objects.equals(getCalculator(), getClass().cast(obj).getCalculator());
}
@Override
public int hashCode() {
return (metric != null ? metric.hashCode() * 31 : 0) + (name != null ? name.hashCode() : 0);
}
};
}

View File

@ -4,9 +4,14 @@
package net.sourceforge.pmd.lang.java.metrics.impl;
import java.util.List;
import org.apache.commons.lang3.mutable.MutableInt;
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTConditionalAndExpression;
import net.sourceforge.pmd.lang.java.ast.ASTConditionalOrExpression;
import net.sourceforge.pmd.lang.java.ast.ASTExpression;
import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration;
import net.sourceforge.pmd.lang.java.ast.JavaParserVisitor;
import net.sourceforge.pmd.lang.java.metrics.JavaMetrics;
@ -38,6 +43,7 @@ import net.sourceforge.pmd.lang.metrics.api.ResultOption;
* fall-through cases in {@code switch} statements are not counted as well.
*
* <p>References:
*
* <ul>
* <li> [1] Lanza, Object-Oriented Metrics in Practice, 2005.
* <li> [2] McCabe, A Complexity Measure, in Proceedings of the 2nd ICSE (1976).
@ -49,8 +55,43 @@ import net.sourceforge.pmd.lang.metrics.api.ResultOption;
*/
public final class CycloMetric {
private CycloMetric() {
}
// TODO:cf Cyclo should develop factorized boolean operators to count them
/**
* Evaluates the number of paths through a boolean expression. This is the total number of {@code &&} and {@code ||}
* operators appearing in the expression. This is used in the calculation of cyclomatic and n-path complexity.
*
* @param expr Expression to analyse
*
* @return The number of paths through the expression
*/
public static int booleanExpressionComplexity(ASTExpression expr) {
if (expr == null) {
return 0;
}
List<ASTConditionalAndExpression> andNodes = expr.findDescendantsOfType(ASTConditionalAndExpression.class);
List<ASTConditionalOrExpression> orNodes = expr.findDescendantsOfType(ASTConditionalOrExpression.class);
int complexity = 0;
for (ASTConditionalOrExpression element : orNodes) {
complexity += element.jjtGetNumChildren() - 1;
}
for (ASTConditionalAndExpression element : andNodes) {
complexity += element.jjtGetNumChildren() - 1;
}
return complexity;
}
/** Variants of CYCLO. */
public enum CycloVersion implements MetricVersion {
/** Do not count the paths in boolean expressions as decision points. */
@ -78,4 +119,5 @@ public final class CycloMetric {
return 1 + JavaMetrics.get(JavaOperationMetricKey.CYCLO, node, version, ResultOption.AVERAGE);
}
}
}

View File

@ -0,0 +1,24 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.metrics.impl;
import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration;
import net.sourceforge.pmd.lang.java.metrics.impl.visitors.DefaultNpathVisitor;
import net.sourceforge.pmd.lang.metrics.api.MetricVersion;
/**
* NPath complexity is a measurement of the acyclic execution paths through a function. See Nejmeh, Communications of
* the ACM Feb 1988 pp 188-200.
*
* @author Clément Fournier
*/
public class NpathMetric extends AbstractJavaOperationMetric {
@Override
public double computeFor(ASTMethodOrConstructorDeclaration node, MetricVersion version) {
return (Integer) node.jjtAccept(new DefaultNpathVisitor(), null);
}
}

View File

@ -0,0 +1,188 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.metrics.impl.visitors;
import java.util.List;
import net.sourceforge.pmd.lang.java.ast.ASTConditionalExpression;
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.ASTMethodOrConstructorDeclaration;
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.ast.JavaParserVisitorReducedAdapter;
import net.sourceforge.pmd.lang.java.metrics.impl.CycloMetric;
/**
* Visitor for the default n-path complexity version.
*
* @author Clément Fournier
* @author Jason Bennett
*/
public class DefaultNpathVisitor extends JavaParserVisitorReducedAdapter {
/* Multiplies the complexity of the children of this node. */
private int multiplyChildrenComplexities(JavaNode node, Object data) {
int product = 1;
for (int i = 0; i < node.jjtGetNumChildren(); i++) {
JavaNode n = (JavaNode) node.jjtGetChild(i);
product *= (Integer) n.jjtAccept(this, data);
}
return product;
}
/* Sums the complexity of the children of the node. */
private int sumChildrenComplexities(JavaNode node, Object data) {
int sum = 0;
for (int i = 0; i < node.jjtGetNumChildren(); i++) {
JavaNode n = (JavaNode) node.jjtGetChild(i);
sum += (Integer) n.jjtAccept(this, data);
}
return sum;
}
@Override
public Object visit(ASTMethodOrConstructorDeclaration node, Object data) {
return multiplyChildrenComplexities(node, data);
}
@Override
public Object visit(JavaNode node, Object data) {
return multiplyChildrenComplexities(node, data);
}
@Override
public Object visit(ASTIfStatement node, Object data) {
// (npath of if + npath of else (or 1) + bool_comp of if) * npath of next
List<ASTStatement> statementChildren = node.findChildrenOfType(ASTStatement.class);
// add path for not taking if
int complexity = node.hasElse() ? 0 : 1;
for (ASTStatement element : statementChildren) {
complexity += (Integer) element.jjtAccept(this, data);
}
int boolCompIf = CycloMetric.booleanExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
return boolCompIf + complexity;
}
@Override
public Object visit(ASTWhileStatement node, Object data) {
// (npath of while + bool_comp of while + 1) * npath of next
int boolCompWhile = CycloMetric.booleanExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
int nPathWhile = (Integer) node.getFirstChildOfType(ASTStatement.class).jjtAccept(this, data);
return boolCompWhile + nPathWhile + 1;
}
@Override
public Object visit(ASTDoStatement node, Object data) {
// (npath of do + bool_comp of do + 1) * npath of next
int boolCompDo = CycloMetric.booleanExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
int nPathDo = (Integer) node.getFirstChildOfType(ASTStatement.class).jjtAccept(this, data);
return boolCompDo + nPathDo + 1;
}
@Override
public Object visit(ASTForStatement node, Object data) {
// (npath of for + bool_comp of for + 1) * npath of next
int boolCompFor = CycloMetric.booleanExpressionComplexity(node.getFirstDescendantOfType(ASTExpression.class));
int nPathFor = (Integer) node.getFirstChildOfType(ASTStatement.class).jjtAccept(this, data);
return 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 1;
}
int boolCompReturn = CycloMetric.booleanExpressionComplexity(expr);
int conditionalExpressionComplexity = multiplyChildrenComplexities(expr, data);
if (conditionalExpressionComplexity > 1) {
boolCompReturn += conditionalExpressionComplexity;
}
return (boolCompReturn > 0) ? boolCompReturn : 1;
}
@Override
public Object visit(ASTSwitchStatement node, Object data) {
// bool_comp of switch + sum(npath(case_range))
int boolCompSwitch = CycloMetric.booleanExpressionComplexity(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 boolCompSwitch + npath;
}
@Override
public Object visit(ASTConditionalExpression node, Object data) {
return node.isTernary() ? sumChildrenComplexities(node, data) + 2 : 1;
}
@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.
*/
return sumChildrenComplexities(node, data);
}
}

View File

@ -15,6 +15,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTSwitchLabel;
import net.sourceforge.pmd.lang.java.ast.ASTSwitchStatement;
import net.sourceforge.pmd.lang.java.ast.ASTWhileStatement;
import net.sourceforge.pmd.lang.java.ast.JavaNode;
import net.sourceforge.pmd.lang.java.metrics.impl.CycloMetric;
import net.sourceforge.pmd.lang.java.rule.codesize.NPathComplexityRule;
/**
@ -29,7 +30,7 @@ public class StandardCycloVisitor extends CycloPathUnawareOperationVisitor {
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));
((MutableInt) data).add(boolCompIf);
return data;
}
@ -39,8 +40,7 @@ public class StandardCycloVisitor extends CycloPathUnawareOperationVisitor {
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));
((MutableInt) data).add(boolCompFor);
return data;
}
@ -50,7 +50,7 @@ public class StandardCycloVisitor extends CycloPathUnawareOperationVisitor {
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));
((MutableInt) data).add(boolCompDo);
return data;
}
@ -60,7 +60,7 @@ public class StandardCycloVisitor extends CycloPathUnawareOperationVisitor {
public Object visit(ASTSwitchStatement node, Object data) {
super.visit((JavaNode) node, data); // skip the superclass' treatment
int boolCompSwitch = NPathComplexityRule.sumExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
int boolCompSwitch = CycloMetric.booleanExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
((MutableInt) data).add(boolCompSwitch);
return data;
}
@ -80,7 +80,7 @@ public class StandardCycloVisitor extends CycloPathUnawareOperationVisitor {
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));
((MutableInt) data).add(boolCompWhile);
return data;
}

View File

@ -0,0 +1,53 @@
/**
* 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.lang.rule.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;
}
}

View File

@ -21,12 +21,12 @@ Complexity directly affects maintenance costs is determined by the number of dec
plus one for the method entry. The decision points include 'if', 'while', 'for', and 'case labels' calls.
Generally, numbers ranging from 1-4 denote low complexity, 5-7 denote moderate complexity, 8-10 denote
high complexity, and 11+ is very high complexity.
]]>
]]>
</description>
<priority>3</priority>
<example>
<![CDATA[
public class Foo { // This has a Cyclomatic Complexity = 12
public class Foo { // This has a Cyclomatic Complexity = 12
1 public void example() {
2 if (a == b) {
3 if (a1 == b1) {
@ -66,7 +66,6 @@ public class Foo { // This has a Cyclomatic Complexity = 12
</example>
</rule>
<!-- TODO -->
<rule name="NcssCount"
message="The {0} ''{1}'' has a NCSS line count of {2}."
since="3.9"
@ -82,19 +81,65 @@ public class Foo { // This has a Cyclomatic Complexity = 12
<example>
<![CDATA[
public class Foo extends Bar {
public Foo() {
//this class only has 4 NCSS lines
super();
public Foo() { //this class only has 4 NCSS lines
super();
super.foo();
}
super.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"
metrics="true"
externalInfoUrl="${pmd.website.baseurl}/rules/java/codesize.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.
</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();
}
}
}
if ( r - n > 45) {
while (doMagic()) {
findRabbits();
}
}
try {
doSomethingDangerous();
} catch (Exception ex) {
makeAmends();
} finally {
dontDoItAgain();
}
}
}
]]>
</example>
</rule>
</ruleset>

View File

@ -9,6 +9,8 @@ import net.sourceforge.pmd.lang.java.metrics.MetricsHook;
import net.sourceforge.pmd.testframework.SimpleAggregatorTst;
/**
* Executes the metrics testing rules.
*
* @author Clément Fournier
*/
public class AllMetricsTest extends SimpleAggregatorTst {
@ -30,6 +32,7 @@ public class AllMetricsTest extends SimpleAggregatorTst {
addRule(RULESET, "NcssTest");
addRule(RULESET, "WmcTest");
addRule(RULESET, "LocTest");
addRule(RULESET, "NPathTest");
}
}

View File

@ -0,0 +1,25 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.metrics.impl;
import net.sourceforge.pmd.lang.java.metrics.api.JavaClassMetricKey;
import net.sourceforge.pmd.lang.java.metrics.api.JavaOperationMetricKey;
/**
* @author Clément Fournier
*/
public class NPathTestRule extends AbstractMetricTestRule {
@Override
protected JavaClassMetricKey getClassKey() {
return null;
}
@Override
protected JavaOperationMetricKey getOpKey() {
return JavaOperationMetricKey.NPATH;
}
}

View File

@ -11,7 +11,6 @@ import net.sourceforge.pmd.testframework.SimpleAggregatorTst;
/**
* @author Clément Fournier
*/
public class MetricsRulesTest extends SimpleAggregatorTst {
private static final String RULESET = "java-metrics";
@ -28,5 +27,6 @@ public class MetricsRulesTest extends SimpleAggregatorTst {
public void setUp() {
addRule(RULESET, "CyclomaticComplexity");
addRule(RULESET, "NcssCount");
addRule(RULESET, "NPathComplexity");
}
}

View File

@ -0,0 +1,196 @@
<?xml version="1.0" encoding="UTF-8"?>
<test-data>
<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">5</rule-property>
<expected-problems>2</expected-problems>
<expected-messages>
<message>The method 'x(boolean, boolean)' has an NPath complexity of 25</message>
<message>The method 'y(boolean, boolean)' has an NPath complexity of 25</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>

View File

@ -33,4 +33,10 @@
metrics="true">
</rule>
<rule name="NPathTest"
message = "''{0}'' has value {1}."
class="net.sourceforge.pmd.lang.java.metrics.impl.NPathTestRule"
metrics="true">
</rule>
</ruleset>

View File

@ -103,4 +103,5 @@ Based on those metrics, rules like "GodClass" detection can be implemented more
* [#512](https://github.com/pmd/pmd/pull/512): \[java] Add incorporation to type inference - [Bendegúz Nagy](https://github.com/WinterGrascph)
* [#513](https://github.com/pmd/pmd/pull/513): \[java] Fix for maximally specific method selection - [Bendegúz Nagy](https://github.com/WinterGrascph)
* [#514](https://github.com/pmd/pmd/pull/514): \[java] Add static method type resolution - [Bendegúz Nagy](https://github.com/WinterGrascph)
* [#523](https://github.com/pmd/pmd/pull/523): \[java] Npath complexity metric and rule - [Clément Fournier](https://github.com/oowekyala)