Throw on metric not supported

This commit is contained in:
Clément Fournier
2020-01-20 21:45:08 +01:00
parent 8fe0fe8bc7
commit 08544a7539
11 changed files with 156 additions and 89 deletions

View File

@ -55,7 +55,7 @@ public final class ApexMetrics {
* @return The value of the metric, or {@code Double.NaN} if the value couldn't be computed
*/
public static double get(MetricKey<ASTUserClassOrInterface<?>> key, ASTUserClass node) {
return MetricsUtil.computeMetric(key, node, MetricOptions.emptyOptions());
return get(key, node, MetricOptions.emptyOptions());
}
@ -70,7 +70,7 @@ public final class ApexMetrics {
* @return The value of the metric, or {@code Double.NaN} if the value couldn't be computed
*/
public static double get(MetricKey<ASTUserClassOrInterface<?>> key, ASTUserClass node, MetricOptions options) {
return MetricsUtil.computeMetric(key, node, options);
return MetricsUtil.computeMetricOrNaN(key, node, options);
}
@ -83,7 +83,7 @@ public final class ApexMetrics {
* @return The value of the metric, or {@code Double.NaN} if the value couldn't be computed
*/
public static double get(MetricKey<ASTMethod> key, ASTMethod node) {
return MetricsUtil.computeMetric(key, node, MetricOptions.emptyOptions());
return get(key, node, MetricOptions.emptyOptions());
}
@ -98,7 +98,7 @@ public final class ApexMetrics {
* @return The value of the metric, or {@code Double.NaN} if the value couldn't be computed
*/
public static double get(MetricKey<ASTMethod> key, ASTMethod node, MetricOptions options) {
return MetricsUtil.computeMetric(key, node, options);
return MetricsUtil.computeMetricOrNaN(key, node, options);
}

View File

@ -90,16 +90,18 @@ public class CyclomaticComplexityRule extends AbstractApexRule {
@Override
public final Object visit(ASTMethod node, Object data) {
int cyclo = (int) MetricsUtil.computeMetric(ApexOperationMetricKey.CYCLO, node);
if (cyclo >= getProperty(METHOD_LEVEL_DESCRIPTOR)) {
String opType = inTrigger ? "trigger"
: node.getImage().equals(classNames.peek()) ? "constructor"
: "method";
if (ApexOperationMetricKey.CYCLO.supports(node)) {
int cyclo = (int) MetricsUtil.computeMetric(ApexOperationMetricKey.CYCLO, node);
if (cyclo >= getProperty(METHOD_LEVEL_DESCRIPTOR)) {
String opType = inTrigger ? "trigger"
: node.getImage().equals(classNames.peek()) ? "constructor"
: "method";
addViolation(data, node, new String[]{opType,
node.getQualifiedName().getOperation(),
"",
"" + cyclo, });
addViolation(data, node, new String[] {opType,
node.getQualifiedName().getOperation(),
"",
"" + cyclo, });
}
}
return data;

View File

@ -16,6 +16,7 @@ import net.sourceforge.pmd.lang.apex.metrics.api.ApexOperationMetricKey;
import net.sourceforge.pmd.lang.apex.rule.AbstractApexRule;
import net.sourceforge.pmd.lang.metrics.MetricOption;
import net.sourceforge.pmd.lang.metrics.MetricOptions;
import net.sourceforge.pmd.lang.metrics.MetricsUtil;
import net.sourceforge.pmd.lang.metrics.ResultOption;
import net.sourceforge.pmd.properties.PropertyDescriptor;
import net.sourceforge.pmd.properties.PropertyFactory;
@ -134,7 +135,7 @@ public abstract class AbstractApexMetricTestRule extends AbstractApexRule {
}
if (classKey != null && reportClasses && classKey.supports(node)) {
int classValue = (int) ApexMetrics.get(classKey, node, metricOptions);
int classValue = (int) MetricsUtil.computeMetric(classKey, node, metricOptions);
String valueReport = String.valueOf(classValue);
@ -153,7 +154,7 @@ public abstract class AbstractApexMetricTestRule extends AbstractApexRule {
@Override
public Object visit(ASTMethod node, Object data) {
if (opKey != null && reportMethods && opKey.supports(node)) {
int methodValue = (int) ApexMetrics.get(opKey, node, metricOptions);
int methodValue = (int) MetricsUtil.computeMetric(opKey, node, metricOptions);
if (methodValue >= reportLevel) {
addViolation(data, node, new String[] {node.getQualifiedName().toString(), "" + methodValue});
}

View File

@ -23,6 +23,15 @@ public final class MetricsUtil {
// util class
}
public static <N extends Node> boolean supportsAll(N node, MetricKey<N>... metrics) {
for (MetricKey<N> metric : metrics) {
if (!metric.supports(node)) {
return false;
}
}
return true;
}
public static <O extends Node> double computeAggregate(MetricKey<? super O> key, Iterable<? extends O> ops, ResultOption resultOption) {
return computeAggregate(key, ops, MetricOptions.emptyOptions(), resultOption);
}
@ -50,9 +59,7 @@ public final class MetricsUtil {
for (O op : ops) {
if (key.supports(op)) {
double val = computeMetric(key, op, options);
if (!Double.isNaN(val)) {
values.add(val);
}
values.add(val);
}
}
@ -65,7 +72,7 @@ public final class MetricsUtil {
case AVERAGE:
return average(values);
default:
return Double.NaN;
throw new IllegalArgumentException("Unknown result option " + resultOption);
}
}
@ -90,6 +97,33 @@ public final class MetricsUtil {
* @param options The options of the metric
*
* @return The value of the metric, or {@code Double.NaN} if the value couldn't be computed
*
* @deprecated This is provided for compatibility with pre 6.21.0
* behavior. Users of a metric should always check beforehand if
* the metric supports the argument.
*/
@Deprecated
public static <N extends Node> double computeMetricOrNaN(MetricKey<? super N> key, N node, MetricOptions options) {
if (!key.supports(node)) {
return Double.NaN;
}
return computeMetric(key, node, options, false);
}
/**
* Computes a metric identified by its code on a node, possibly
* selecting a variant with the {@code options} parameter.
*
* <p>Note that contrary to the previous behaviour, this method
* throws an exception if the metric does not support the node.
*
* @param key The key identifying the metric to be computed
* @param node The node on which to compute the metric
* @param options The options of the metric
*
* @return The value of the metric
*
* @throws IllegalArgumentException If the metric does not support the given node
*/
public static <N extends Node> double computeMetric(MetricKey<? super N> key, N node, MetricOptions options) {
return computeMetric(key, node, options, false);
@ -99,12 +133,17 @@ public final class MetricsUtil {
* Computes a metric identified by its code on a node, possibly
* selecting a variant with the {@code options} parameter.
*
* <p>Note that contrary to the previous behaviour, this method
* throws an exception if the metric does not support the node.
*
* @param key The key identifying the metric to be computed
* @param node The node on which to compute the metric
* @param options The options of the metric
* @param forceRecompute Force recomputation of the result
*
* @return The value of the metric, or {@code Double.NaN} if the value couldn't be computed
* @return The value of the metric
*
* @throws IllegalArgumentException If the metric does not support the given node
*/
public static <N extends Node> double computeMetric(MetricKey<? super N> key, N node, MetricOptions options, boolean forceRecompute) {
Objects.requireNonNull(key, NULL_KEY_MESSAGE);
@ -113,7 +152,7 @@ public final class MetricsUtil {
if (!key.supports(node)) {
return Double.NaN;
throw new IllegalArgumentException(key + " cannot be computed on " + node);
}
ParameterizedMetricKey<? super N> paramKey = ParameterizedMetricKey.getInstance(key, options);

View File

@ -42,7 +42,7 @@ public final class JavaMetrics {
* @return The value of the metric, or {@code Double.NaN} if the value couldn't be computed
*/
public static double get(MetricKey<ASTAnyTypeDeclaration> key, ASTAnyTypeDeclaration node) {
return MetricsUtil.computeMetric(key, node);
return get(key, node, MetricOptions.emptyOptions());
}
@ -57,7 +57,7 @@ public final class JavaMetrics {
* @return The value of the metric, or {@code Double.NaN} if the value couldn't be computed
*/
public static double get(MetricKey<ASTAnyTypeDeclaration> key, ASTAnyTypeDeclaration node, MetricOptions options) {
return MetricsUtil.computeMetric(key, node, options);
return MetricsUtil.computeMetricOrNaN(key, node, options);
}
@ -70,7 +70,7 @@ public final class JavaMetrics {
* @return The value of the metric, or {@code Double.NaN} if the value couldn't be computed
*/
public static double get(MetricKey<MethodLikeNode> key, MethodLikeNode node) {
return MetricsUtil.computeMetric(key, node);
return get(key, node, MetricOptions.emptyOptions());
}
@ -104,7 +104,7 @@ public final class JavaMetrics {
* @return The value of the metric, or {@code Double.NaN} if the value couldn't be computed
*/
public static double get(MetricKey<MethodLikeNode> key, MethodLikeNode node, MetricOptions options) {
return MetricsUtil.computeMetric(key, node, options);
return MetricsUtil.computeMetricOrNaN(key, node, options);
}

View File

@ -145,25 +145,26 @@ public class CyclomaticComplexityRule extends AbstractJavaMetricsRule {
@Override
public final Object visit(MethodLikeNode node, Object data) {
int cyclo = (int) MetricsUtil.computeMetric(JavaOperationMetricKey.CYCLO, node, cycloOptions);
if (cyclo >= methodReportLevel) {
if (JavaOperationMetricKey.CYCLO.supports(node)) {
int cyclo = (int) MetricsUtil.computeMetric(JavaOperationMetricKey.CYCLO, node, cycloOptions);
if (cyclo >= methodReportLevel) {
String opname = node instanceof ASTMethodOrConstructorDeclaration
? PrettyPrintingUtil.displaySignature((ASTMethodOrConstructorDeclaration) node)
: "lambda";
String opname = node instanceof ASTMethodOrConstructorDeclaration
? PrettyPrintingUtil.displaySignature((ASTMethodOrConstructorDeclaration) node)
: "lambda";
String kindname = node instanceof ASTMethodOrConstructorDeclaration
? node instanceof ASTConstructorDeclaration ? "constructor" : "method"
: "lambda";
String kindname = node instanceof ASTMethodOrConstructorDeclaration
? node instanceof ASTConstructorDeclaration ? "constructor" : "method"
: "lambda";
addViolation(data, node, new String[] {kindname,
opname,
"",
"" + cyclo, });
addViolation(data, node, new String[] {kindname,
opname,
"",
"" + cyclo, });
}
}
return data;
}

View File

@ -4,8 +4,12 @@
package net.sourceforge.pmd.lang.java.rule.design;
import static net.sourceforge.pmd.lang.java.metrics.api.JavaClassMetricKey.NOAM;
import static net.sourceforge.pmd.lang.java.metrics.api.JavaClassMetricKey.NOPA;
import static net.sourceforge.pmd.lang.java.metrics.api.JavaClassMetricKey.WMC;
import static net.sourceforge.pmd.lang.java.metrics.api.JavaClassMetricKey.WOC;
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
import net.sourceforge.pmd.lang.java.metrics.api.JavaClassMetricKey;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaMetricsRule;
import net.sourceforge.pmd.lang.metrics.MetricsUtil;
import net.sourceforge.pmd.util.StringUtil;
@ -27,13 +31,17 @@ public class DataClassRule extends AbstractJavaMetricsRule {
@Override
public Object visit(ASTAnyTypeDeclaration node, Object data) {
if (!MetricsUtil.supportsAll(node, NOAM, NOPA, WMC, WOC)) {
return super.visit(node, data);
}
boolean isDataClass = interfaceRevealsData(node) && classRevealsDataAndLacksComplexity(node);
if (isDataClass) {
double woc = MetricsUtil.computeMetric(JavaClassMetricKey.WOC, node);
int nopa = (int) MetricsUtil.computeMetric(JavaClassMetricKey.NOPA, node);
int noam = (int) MetricsUtil.computeMetric(JavaClassMetricKey.NOAM, node);
int wmc = (int) MetricsUtil.computeMetric(JavaClassMetricKey.WMC, node);
double woc = MetricsUtil.computeMetric(WOC, node);
int nopa = (int) MetricsUtil.computeMetric(NOPA, node);
int noam = (int) MetricsUtil.computeMetric(NOAM, node);
int wmc = (int) MetricsUtil.computeMetric(WMC, node);
addViolation(data, node, new Object[] {node.getSimpleName(),
StringUtil.percentageString(woc, 3),
@ -45,15 +53,15 @@ public class DataClassRule extends AbstractJavaMetricsRule {
private boolean interfaceRevealsData(ASTAnyTypeDeclaration node) {
double woc = MetricsUtil.computeMetric(JavaClassMetricKey.WOC, node);
double woc = MetricsUtil.computeMetric(WOC, node);
return woc < WOC_LEVEL;
}
private boolean classRevealsDataAndLacksComplexity(ASTAnyTypeDeclaration node) {
int nopa = (int) MetricsUtil.computeMetric(JavaClassMetricKey.NOPA, node);
int noam = (int) MetricsUtil.computeMetric(JavaClassMetricKey.NOAM, node);
int wmc = (int) MetricsUtil.computeMetric(JavaClassMetricKey.WMC, node);
int nopa = (int) MetricsUtil.computeMetric(NOPA, node);
int noam = (int) MetricsUtil.computeMetric(NOAM, node);
int wmc = (int) MetricsUtil.computeMetric(WMC, node);
return nopa + noam > ACCESSOR_OR_FIELD_FEW_LEVEL && wmc < WMC_HIGH_LEVEL
|| nopa + noam > ACCESSOR_OR_FIELD_MANY_LEVEL && wmc < WMC_VERY_HIGH_LEVEL;

View File

@ -5,8 +5,11 @@
package net.sourceforge.pmd.lang.java.rule.design;
import static net.sourceforge.pmd.lang.java.metrics.api.JavaClassMetricKey.ATFD;
import static net.sourceforge.pmd.lang.java.metrics.api.JavaClassMetricKey.TCC;
import static net.sourceforge.pmd.lang.java.metrics.api.JavaClassMetricKey.WMC;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
import net.sourceforge.pmd.lang.java.metrics.api.JavaClassMetricKey;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
import net.sourceforge.pmd.lang.metrics.MetricsUtil;
import net.sourceforge.pmd.util.StringUtil;
@ -41,9 +44,13 @@ public class GodClassRule extends AbstractJavaRule {
@Override
public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
int wmc = (int) MetricsUtil.computeMetric(JavaClassMetricKey.WMC, node);
double tcc = MetricsUtil.computeMetric(JavaClassMetricKey.TCC, node);
int atfd = (int) MetricsUtil.computeMetric(JavaClassMetricKey.ATFD, node);
if (!MetricsUtil.supportsAll(node, WMC, TCC, ATFD)) {
return super.visit(node, data);
}
int wmc = (int) MetricsUtil.computeMetric(WMC, node);
double tcc = MetricsUtil.computeMetric(TCC, node);
int atfd = (int) MetricsUtil.computeMetric(ATFD, node);
super.visit(node, data);

View File

@ -31,8 +31,8 @@ public class NPathComplexityRule extends AbstractJavaMetricsRule {
@Deprecated
private static final PropertyDescriptor<Double> MINIMUM_DESCRIPTOR
= PropertyFactory.doubleProperty("minimum").desc("Deprecated! Minimum reporting threshold")
.require(positive()).defaultValue(200d).build();
= PropertyFactory.doubleProperty("minimum").desc("Deprecated! Minimum reporting threshold")
.require(positive()).defaultValue(200d).build();
private static final PropertyDescriptor<Integer> REPORT_LEVEL_DESCRIPTOR
@ -71,10 +71,14 @@ public class NPathComplexityRule extends AbstractJavaMetricsRule {
@Override
public final Object visit(ASTMethodOrConstructorDeclaration node, Object data) {
if (!JavaOperationMetricKey.NPATH.supports(node)) {
return data;
}
int npath = (int) MetricsUtil.computeMetric(JavaOperationMetricKey.NPATH, node);
if (npath >= reportLevel) {
addViolation(data, node, new String[]{node instanceof ASTMethodDeclaration ? "method" : "constructor",
PrettyPrintingUtil.displaySignature(node), "" + npath, });
addViolation(data, node, new String[] {node instanceof ASTMethodDeclaration ? "method" : "constructor",
PrettyPrintingUtil.displaySignature(node), "" + npath, });
}
return data;

View File

@ -15,6 +15,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
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.ast.MethodLikeNode;
import net.sourceforge.pmd.lang.java.ast.internal.PrettyPrintingUtil;
import net.sourceforge.pmd.lang.java.metrics.JavaMetrics;
import net.sourceforge.pmd.lang.java.metrics.api.JavaClassMetricKey;
@ -37,40 +38,37 @@ public final class NcssCountRule extends AbstractJavaMetricsRule {
private static final PropertyDescriptor<Integer> METHOD_REPORT_LEVEL_DESCRIPTOR =
PropertyFactory.intProperty("methodReportLevel")
.desc("NCSS reporting threshold for methods")
.require(positive())
.defaultValue(60)
.build();
PropertyFactory.intProperty("methodReportLevel")
.desc("NCSS reporting threshold for methods")
.require(positive())
.defaultValue(60)
.build();
private static final PropertyDescriptor<Integer> CLASS_REPORT_LEVEL_DESCRIPTOR =
PropertyFactory.intProperty("classReportLevel")
.desc("NCSS reporting threshold for classes")
.require(positive())
.defaultValue(1500)
.build();
private static final Map<String, NcssOption> OPTION_MAP;
static {
OPTION_MAP = new HashMap<>();
OPTION_MAP.put(NcssOption.COUNT_IMPORTS.valueName(), NcssOption.COUNT_IMPORTS);
}
private static final PropertyDescriptor<List<NcssOption>> NCSS_OPTIONS_DESCRIPTOR =
PropertyFactory.enumListProperty("ncssOptions", OPTION_MAP)
.desc("Choose options for the computation of Ncss")
.emptyDefaultValue()
.build();
PropertyFactory.intProperty("classReportLevel")
.desc("NCSS reporting threshold for classes")
.require(positive())
.defaultValue(1500)
.build();
private static final PropertyDescriptor<List<NcssOption>> NCSS_OPTIONS_DESCRIPTOR;
private int methodReportLevel;
private int classReportLevel;
private MetricOptions ncssOptions;
static {
Map<String, NcssOption> options = new HashMap<>();
options.put(NcssOption.COUNT_IMPORTS.valueName(), NcssOption.COUNT_IMPORTS);
NCSS_OPTIONS_DESCRIPTOR = PropertyFactory.enumListProperty("ncssOptions", options)
.desc("Choose options for the computation of Ncss")
.emptyDefaultValue()
.build();
}
public NcssCountRule() {
definePropertyDescriptor(METHOD_REPORT_LEVEL_DESCRIPTOR);
definePropertyDescriptor(CLASS_REPORT_LEVEL_DESCRIPTOR);
@ -113,12 +111,14 @@ public final class NcssCountRule extends AbstractJavaMetricsRule {
@Override
public Object visit(ASTMethodOrConstructorDeclaration node, Object data) {
int methodSize = (int) MetricsUtil.computeMetric(JavaOperationMetricKey.NCSS, node, ncssOptions);
if (methodSize >= methodReportLevel) {
addViolation(data, node, new String[] {node instanceof ASTMethodDeclaration ? "method" : "constructor",
PrettyPrintingUtil.displaySignature(node), "" + methodSize, });
if (JavaOperationMetricKey.NCSS.supports((MethodLikeNode) node)) {
int methodSize = (int) MetricsUtil.computeMetric(JavaOperationMetricKey.NCSS, node, ncssOptions);
if (methodSize >= methodReportLevel) {
addViolation(data, node, new String[] {
node instanceof ASTMethodDeclaration ? "method" : "constructor",
PrettyPrintingUtil.displaySignature(node), "" + methodSize, });
}
}
return data;
}

View File

@ -21,6 +21,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
import net.sourceforge.pmd.lang.java.ast.MethodLikeNode;
import net.sourceforge.pmd.lang.java.metrics.api.JavaClassMetricKey;
import net.sourceforge.pmd.lang.java.metrics.api.JavaOperationMetricKey;
import net.sourceforge.pmd.lang.metrics.MetricKey;
import net.sourceforge.pmd.lang.metrics.MetricsUtil;
@ -82,14 +83,18 @@ public class MetricFunction implements Function {
public static double getMetric(Node n, String metricKeyName) {
if (n instanceof ASTAnyTypeDeclaration) {
return MetricsUtil.computeMetric(getClassMetricKey(metricKeyName), (ASTAnyTypeDeclaration) n);
return computeMetric(getClassMetricKey(metricKeyName), (ASTAnyTypeDeclaration) n);
} else if (n instanceof MethodLikeNode) {
return MetricsUtil.computeMetric(getOperationMetricKey(metricKeyName), (MethodLikeNode) n);
return computeMetric(getOperationMetricKey(metricKeyName), (MethodLikeNode) n);
} else {
throw new IllegalStateException(genericBadNodeMessage());
}
}
private static <T extends Node> double computeMetric(MetricKey<T> metricKey, T n) {
return metricKey.supports(n) ? MetricsUtil.computeMetric(metricKey, n) : Double.NaN;
}
private static JavaClassMetricKey getClassMetricKey(String s) {
String constantName = s.toUpperCase(Locale.ROOT);