forked from phoedos/pmd
Refactored Cyclo into op metric
The rule now uses WMC to report classes
This commit is contained in:
@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.metrics.api;
|
||||
|
||||
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;
|
||||
import net.sourceforge.pmd.lang.java.metrics.impl.LocMetric.LocClassMetric;
|
||||
import net.sourceforge.pmd.lang.java.metrics.impl.NcssMetric.NcssClassMetric;
|
||||
import net.sourceforge.pmd.lang.java.metrics.impl.WmcMetric;
|
||||
@ -31,13 +30,6 @@ public enum JavaClassMetricKey implements MetricKey<ASTAnyTypeDeclaration> {
|
||||
*/
|
||||
WMC(new WmcMetric()),
|
||||
|
||||
/**
|
||||
* Cyclomatic complexity.
|
||||
*
|
||||
* @see net.sourceforge.pmd.lang.java.metrics.impl.CycloMetric
|
||||
*/
|
||||
CYCLO(new CycloClassMetric()),
|
||||
|
||||
/**
|
||||
* Non Commenting Source Statements.
|
||||
*
|
||||
|
@ -6,7 +6,7 @@ package net.sourceforge.pmd.lang.java.metrics.api;
|
||||
|
||||
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.CycloMetric;
|
||||
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;
|
||||
@ -27,9 +27,9 @@ public enum JavaOperationMetricKey implements MetricKey<ASTMethodOrConstructorDe
|
||||
/**
|
||||
* Cyclomatic complexity.
|
||||
*
|
||||
* @see net.sourceforge.pmd.lang.java.metrics.impl.CycloMetric
|
||||
* @see CycloMetric
|
||||
*/
|
||||
CYCLO(new CycloOperationMetric()),
|
||||
CYCLO(new CycloMetric()),
|
||||
|
||||
/**
|
||||
* Non Commenting Source Statements.
|
||||
|
@ -8,18 +8,15 @@ 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;
|
||||
import net.sourceforge.pmd.lang.java.metrics.api.JavaOperationMetricKey;
|
||||
import net.sourceforge.pmd.lang.java.metrics.impl.visitors.CycloPathUnawareOperationVisitor;
|
||||
import net.sourceforge.pmd.lang.java.metrics.impl.visitors.StandardCycloVisitor;
|
||||
import net.sourceforge.pmd.lang.metrics.MetricVersion;
|
||||
import net.sourceforge.pmd.lang.metrics.ResultOption;
|
||||
|
||||
|
||||
/**
|
||||
* McCabe's Cyclomatic Complexity. Number of independent paths through a block of code [1, 2]. Formally, given that the
|
||||
@ -53,15 +50,24 @@ import net.sourceforge.pmd.lang.metrics.ResultOption;
|
||||
* @author Clément Fournier
|
||||
* @since June 2017
|
||||
*/
|
||||
public final class CycloMetric {
|
||||
public final class CycloMetric extends AbstractJavaOperationMetric {
|
||||
|
||||
private CycloMetric() {
|
||||
|
||||
}
|
||||
|
||||
// TODO:cf Cyclo should develop factorized boolean operators to count them
|
||||
|
||||
|
||||
@Override
|
||||
public double computeFor(ASTMethodOrConstructorDeclaration node, MetricVersion version) {
|
||||
|
||||
JavaParserVisitor visitor = (CycloVersion.IGNORE_BOOLEAN_PATHS == version)
|
||||
? new CycloPathUnawareOperationVisitor()
|
||||
: new StandardCycloVisitor();
|
||||
|
||||
MutableInt cyclo = (MutableInt) node.jjtAccept(visitor, new MutableInt(1));
|
||||
return (double) cyclo.getValue();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@ -98,26 +104,4 @@ public final class CycloMetric {
|
||||
IGNORE_BOOLEAN_PATHS
|
||||
}
|
||||
|
||||
public static final class CycloOperationMetric extends AbstractJavaOperationMetric {
|
||||
|
||||
@Override
|
||||
public double computeFor(ASTMethodOrConstructorDeclaration node, MetricVersion version) {
|
||||
|
||||
JavaParserVisitor visitor = (CycloVersion.IGNORE_BOOLEAN_PATHS == version)
|
||||
? new CycloPathUnawareOperationVisitor()
|
||||
: new StandardCycloVisitor();
|
||||
|
||||
MutableInt cyclo = (MutableInt) node.jjtAccept(visitor, new MutableInt(1));
|
||||
return (double) cyclo.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
public static final class CycloClassMetric extends AbstractJavaClassMetric {
|
||||
|
||||
@Override
|
||||
public double computeFor(ASTAnyTypeDeclaration node, MetricVersion version) {
|
||||
return 1 + JavaMetrics.get(JavaOperationMetricKey.CYCLO, node, version, ResultOption.AVERAGE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -91,9 +91,8 @@ public class StandardCycloVisitor extends CycloPathUnawareOperationVisitor {
|
||||
super.visit(node, data);
|
||||
|
||||
if (node.isTernary()) {
|
||||
int boolCompTern = NPathComplexityRule
|
||||
.sumExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
|
||||
((MutableInt) data).add(boolCompTern);
|
||||
int boolCompTern = NPathComplexityRule.sumExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
|
||||
((MutableInt) data).add(1 + boolCompTern);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ import net.sourceforge.pmd.lang.java.rule.AbstractJavaMetricsRule;
|
||||
import net.sourceforge.pmd.lang.metrics.Metric.Version;
|
||||
import net.sourceforge.pmd.lang.metrics.MetricVersion;
|
||||
import net.sourceforge.pmd.lang.metrics.ResultOption;
|
||||
import net.sourceforge.pmd.lang.rule.properties.BooleanProperty;
|
||||
import net.sourceforge.pmd.lang.rule.properties.EnumeratedProperty;
|
||||
import net.sourceforge.pmd.lang.rule.properties.IntegerProperty;
|
||||
|
||||
@ -30,15 +29,11 @@ import net.sourceforge.pmd.lang.rule.properties.IntegerProperty;
|
||||
*/
|
||||
public class CyclomaticComplexityRule extends AbstractJavaMetricsRule {
|
||||
|
||||
private static final IntegerProperty REPORT_LEVEL_DESCRIPTOR = new IntegerProperty(
|
||||
"reportLevel", "Cyclomatic Complexity reporting threshold", 1, 30, 10, 1.0f);
|
||||
|
||||
private static final BooleanProperty REPORT_CLASSES_DESCRIPTOR = new BooleanProperty(
|
||||
"reportClasses", "Add class average violations to the report", true, 2.0f);
|
||||
|
||||
private static final BooleanProperty REPORT_METHODS_DESCRIPTOR = new BooleanProperty(
|
||||
"reportMethods", "Add method average violations to the report", true, 3.0f);
|
||||
private static final IntegerProperty CLASS_LEVEL_DESCRIPTOR = new IntegerProperty(
|
||||
"classReportLevel", "Total class complexity reporting threshold", 1, 200, 40, 1.0f);
|
||||
|
||||
private static final IntegerProperty METHOD_LEVEL_DESCRIPTOR = new IntegerProperty(
|
||||
"methodReportLevel", "Cyclomatic complexity reporting threshold", 1, 30, 10, 1.0f);
|
||||
|
||||
private static final Map<String, MetricVersion> VERSION_MAP;
|
||||
|
||||
@ -54,26 +49,23 @@ public class CyclomaticComplexityRule extends AbstractJavaMetricsRule {
|
||||
"cycloVersion", "Choose a variant of Cyclo or the standard",
|
||||
VERSION_MAP, Version.STANDARD, MetricVersion.class, 3.0f);
|
||||
|
||||
private int reportLevel;
|
||||
private boolean reportClasses = true;
|
||||
private boolean reportMethods = true;
|
||||
private int methodReportLevel;
|
||||
private int classReportLevel;
|
||||
private MetricVersion cycloVersion = Version.STANDARD;
|
||||
|
||||
|
||||
public CyclomaticComplexityRule() {
|
||||
definePropertyDescriptor(REPORT_LEVEL_DESCRIPTOR);
|
||||
definePropertyDescriptor(REPORT_CLASSES_DESCRIPTOR);
|
||||
definePropertyDescriptor(REPORT_METHODS_DESCRIPTOR);
|
||||
definePropertyDescriptor(CLASS_LEVEL_DESCRIPTOR);
|
||||
definePropertyDescriptor(METHOD_LEVEL_DESCRIPTOR);
|
||||
definePropertyDescriptor(CYCLO_VERSION_DESCRIPTOR);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object visit(ASTCompilationUnit node, Object data) {
|
||||
reportLevel = getProperty(REPORT_LEVEL_DESCRIPTOR);
|
||||
methodReportLevel = getProperty(METHOD_LEVEL_DESCRIPTOR);
|
||||
classReportLevel = getProperty(CLASS_LEVEL_DESCRIPTOR);
|
||||
cycloVersion = getProperty(CYCLO_VERSION_DESCRIPTOR);
|
||||
reportClasses = getProperty(REPORT_CLASSES_DESCRIPTOR);
|
||||
reportMethods = getProperty(REPORT_METHODS_DESCRIPTOR);
|
||||
|
||||
super.visit(node, data);
|
||||
return data;
|
||||
@ -85,14 +77,16 @@ public class CyclomaticComplexityRule extends AbstractJavaMetricsRule {
|
||||
|
||||
super.visit(node, data);
|
||||
|
||||
if (reportClasses && JavaClassMetricKey.CYCLO.supports(node)) {
|
||||
int classCyclo = (int) JavaMetrics.get(JavaClassMetricKey.CYCLO, node, cycloVersion);
|
||||
int classHighest = (int) JavaMetrics.get(JavaOperationMetricKey.CYCLO, node, cycloVersion, ResultOption.HIGHEST);
|
||||
if (JavaClassMetricKey.WMC.supports(node)) {
|
||||
int classWmc = (int) JavaMetrics.get(JavaClassMetricKey.WMC, node, cycloVersion);
|
||||
|
||||
if (classWmc >= classReportLevel) {
|
||||
int classHighest = (int) JavaMetrics.get(JavaOperationMetricKey.CYCLO, node, cycloVersion, ResultOption.HIGHEST);
|
||||
|
||||
if (classCyclo >= reportLevel || classHighest >= reportLevel) {
|
||||
String[] messageParams = {node.getTypeKind().name().toLowerCase(),
|
||||
node.getImage(),
|
||||
classCyclo + " (Highest = " + classHighest + ")", };
|
||||
" total",
|
||||
classWmc + " (highest " + classHighest + ")", };
|
||||
|
||||
addViolation(data, node, messageParams);
|
||||
}
|
||||
@ -104,13 +98,14 @@ public class CyclomaticComplexityRule extends AbstractJavaMetricsRule {
|
||||
@Override
|
||||
public final Object visit(ASTMethodOrConstructorDeclaration node, Object data) {
|
||||
|
||||
if (reportMethods) {
|
||||
int cyclo = (int) JavaMetrics.get(JavaOperationMetricKey.CYCLO, node, cycloVersion);
|
||||
if (cyclo >= reportLevel) {
|
||||
addViolation(data, node, new String[] {node instanceof ASTMethodDeclaration ? "method" : "constructor",
|
||||
node.getQualifiedName().getOperation(), "" + cyclo, });
|
||||
}
|
||||
int cyclo = (int) JavaMetrics.get(JavaOperationMetricKey.CYCLO, node, cycloVersion);
|
||||
if (cyclo >= methodReportLevel) {
|
||||
addViolation(data, node, new String[] {node instanceof ASTMethodDeclaration ? "method" : "constructor",
|
||||
node.getQualifiedName().getOperation(),
|
||||
"",
|
||||
"" + cyclo, });
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
</description>
|
||||
|
||||
<rule name="CyclomaticComplexity"
|
||||
message="The {0} ''{1}'' has a Cyclomatic Complexity of {2}."
|
||||
message="The {0} ''{1}'' has a{2} cyclomatic complexity of {3}."
|
||||
since="1.03"
|
||||
class="net.sourceforge.pmd.lang.java.metrics.rule.CyclomaticComplexityRule"
|
||||
metrics="true"
|
||||
|
@ -20,7 +20,7 @@ public class CycloTestRule extends AbstractMetricTestRule {
|
||||
|
||||
@Override
|
||||
protected JavaClassMetricKey getClassKey() {
|
||||
return JavaClassMetricKey.CYCLO;
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
@ -66,21 +66,19 @@
|
||||
|
||||
<test-code>
|
||||
<description>Complicated method - Standard</description>
|
||||
<expected-problems>3</expected-problems>
|
||||
<expected-problems>2</expected-problems>
|
||||
<expected-messages>
|
||||
<message>'.Complicated' has value 13 highest 21.</message>
|
||||
<message>'.Complicated#exception()' has value 4.</message>
|
||||
<message>'.Complicated#example()' has value 21.</message>
|
||||
<message>'.Complicated#example()' has value 23.</message>
|
||||
</expected-messages>
|
||||
<code-ref id="full-example"/>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>Complicated method - Ignore boolean path version</description>
|
||||
<expected-problems>3</expected-problems>
|
||||
<expected-problems>2</expected-problems>
|
||||
<rule-property name="metricVersion">ignoreBooleanPaths</rule-property>
|
||||
<expected-messages>
|
||||
<message>'.Complicated' has value 10 highest 14.</message>
|
||||
<message>'.Complicated#exception()' has value 4.</message>
|
||||
<message>'.Complicated#example()' has value 14.</message>
|
||||
</expected-messages>
|
||||
@ -103,21 +101,6 @@
|
||||
</code>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>Empty class should count 0</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<expected-messages>
|
||||
<message>'.Foo' has value 0 highest 0.</message>
|
||||
</expected-messages>
|
||||
<code>
|
||||
<![CDATA[
|
||||
class Foo {
|
||||
}
|
||||
]]>
|
||||
</code>
|
||||
</test-code>
|
||||
|
||||
|
||||
<code-fragment id="constructor-violation">
|
||||
<![CDATA[
|
||||
public class Test {
|
||||
@ -135,8 +118,7 @@
|
||||
</code-fragment>
|
||||
|
||||
<test-code>
|
||||
<description>#984 Cyclomatic complexity should treat constructors like methods
|
||||
</description>
|
||||
<description>#984 Cyclomatic complexity should treat constructors like methods</description>
|
||||
<rule-property name="reportClasses">false</rule-property>
|
||||
<expected-problems>1</expected-problems>
|
||||
<expected-messages>
|
||||
@ -201,27 +183,21 @@
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>Interfaces are not supported</description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<description>Ternary expression counts 1 + boolean complexity</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<expected-messages>
|
||||
<message>'.Foo#bar()' has value 3.</message>
|
||||
</expected-messages>
|
||||
<code>
|
||||
<![CDATA[
|
||||
interface Koo {
|
||||
void bar();
|
||||
class Foo {
|
||||
void bar() {
|
||||
boolean a, b;
|
||||
boolean c = (a || b) ? a : b;
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</code>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>Avoid division by 0 when averaging a metric over no operations</description>
|
||||
<rule-property name="reportLevel">-1</rule-property>
|
||||
<expected-problems>1</expected-problems>
|
||||
<expected-messages>'.Foo' has value NaN.</expected-messages>
|
||||
<code>
|
||||
<![CDATA[
|
||||
public class Foo { }
|
||||
]]>
|
||||
</code>
|
||||
</test-code>
|
||||
|
||||
</test-data>
|
||||
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user