diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/design/CognitiveComplexityRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/design/CognitiveComplexityRule.java new file mode 100644 index 0000000000..dfc8ddee79 --- /dev/null +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/design/CognitiveComplexityRule.java @@ -0,0 +1,99 @@ +package net.sourceforge.pmd.lang.apex.rule.design; + +import net.sourceforge.pmd.lang.apex.ast.ASTMethod; +import net.sourceforge.pmd.lang.apex.ast.ASTUserClass; +import net.sourceforge.pmd.lang.apex.ast.ASTUserTrigger; +import net.sourceforge.pmd.lang.apex.metrics.ApexMetrics; +import net.sourceforge.pmd.lang.apex.metrics.api.ApexClassMetricKey; +import net.sourceforge.pmd.lang.apex.metrics.api.ApexOperationMetricKey; +import net.sourceforge.pmd.lang.apex.rule.AbstractApexRule; +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; + +import java.util.Stack; + +import static net.sourceforge.pmd.properties.constraints.NumericConstraints.positive; + +public class CognitiveComplexityRule extends AbstractApexRule { + + private static final PropertyDescriptor CLASS_LEVEL_DESCRIPTOR + = PropertyFactory.intProperty("classReportLevel") + .desc("Total class cognitive complexity reporting threshold") + .require(positive()) + .defaultValue(40) + .build(); + + private static final PropertyDescriptor METHOD_LEVEL_DESCRIPTOR + = PropertyFactory.intProperty("methodReportLevel") + .desc("Cognitive complexity reporting threshold") + .require(positive()) + .defaultValue(10) + .build(); + + private Stack classNames = new Stack<>(); + private boolean inTrigger; + + + public CognitiveComplexityRule() { + definePropertyDescriptor(CLASS_LEVEL_DESCRIPTOR); + definePropertyDescriptor(METHOD_LEVEL_DESCRIPTOR); + } + + + @Override + public Object visit(ASTUserTrigger node, Object data) { + inTrigger = true; + super.visit(node, data); + inTrigger = false; + return data; + } + + + @Override + public Object visit(ASTUserClass node, Object data) { + + classNames.push(node.getImage()); + super.visit(node, data); + classNames.pop(); + + if (ApexClassMetricKey.COGNITIVE.supports(node)) { + int classCognitive = (int) MetricsUtil.computeMetric(ApexClassMetricKey.COGNITIVE, node); + + if (classCognitive >= getProperty(CLASS_LEVEL_DESCRIPTOR)) { + int classHighest = (int) ApexMetrics.get(ApexOperationMetricKey.COGNITIVE, node, ResultOption.HIGHEST); + + String[] messageParams = {"class", + node.getImage(), + " total", + classCognitive + " (highest " + classHighest + ")", }; + + addViolation(data, node, messageParams); + } + } + return data; + } + + + @Override + public final Object visit(ASTMethod node, Object data) { + + if (ApexOperationMetricKey.COGNITIVE.supports(node)) { + int cognitive = (int) MetricsUtil.computeMetric(ApexOperationMetricKey.COGNITIVE, node); + if (cognitive >= getProperty(METHOD_LEVEL_DESCRIPTOR)) { + String opType = inTrigger ? "trigger" + : node.getImage().equals(classNames.peek()) ? "constructor" + : "method"; + + addViolation(data, node, new String[] {opType, + node.getQualifiedName().getOperation(), + "", + "" + cognitive, }); + } + } + + return data; + } + +}