From e662aef673b60d6cc71382d4c348ec4c9ad62056 Mon Sep 17 00:00:00 2001 From: oowekyala Date: Sun, 30 Jul 2017 23:31:39 +0200 Subject: [PATCH 01/10] Moved the index into language specific doc --- docs/_data/sidebars/pmd_sidebar.yml | 6 ++-- .../java_metrics_index.md} | 32 +++++++++++++++++-- 2 files changed, 33 insertions(+), 5 deletions(-) rename docs/pages/pmd/{devdocs/metrics_index.md => languages/java_metrics_index.md} (89%) diff --git a/docs/_data/sidebars/pmd_sidebar.yml b/docs/_data/sidebars/pmd_sidebar.yml index 9587ef124..85714739c 100644 --- a/docs/_data/sidebars/pmd_sidebar.yml +++ b/docs/_data/sidebars/pmd_sidebar.yml @@ -99,6 +99,9 @@ entries: - title: JSP Support url: /pmd_languages_jsp.html output: web, pdf + - title: Index of Java code metrics + url: /pmd_java_metrics_index.html + output: web, pdf - title: Developer Documentation output: web, pdf folderitems: @@ -138,9 +141,6 @@ entries: - title: Using code metrics in custom rules url: /pmd_devdocs_metrics_howto.html output: web, pdf - - title: Index of code metrics - url: /pmd_devdocs_metrics_index.html - output: web, pdf - title: Making Rulesets url: /pmd_devdocs_making_rulesets.html output: web, pdf diff --git a/docs/pages/pmd/devdocs/metrics_index.md b/docs/pages/pmd/languages/java_metrics_index.md similarity index 89% rename from docs/pages/pmd/devdocs/metrics_index.md rename to docs/pages/pmd/languages/java_metrics_index.md index 4f558f2a6..1d96c3b19 100644 --- a/docs/pages/pmd/devdocs/metrics_index.md +++ b/docs/pages/pmd/languages/java_metrics_index.md @@ -4,8 +4,8 @@ tags: [customizing] summary: "Index of the code metrics available out of the box to Java rule developers." last_updated: July 20, 2017 sidebar: pmd_sidebar -permalink: pmd_devdocs_metrics_index.html -folder: pmd/devdocs +permalink: pmd_java_metrics_index.html +folder: pmd/languages toc: minimumHeaders: 8 --- @@ -39,6 +39,34 @@ conditional expression (`?:`) \[[Sonarqube](#Sonarqube)\]. Notice switch cases c * +1 for every boolean operator (`&&`, `||`) in the guard condition of a control flow statement. That's because Java has short-circuit evaluation semantics for boolean operators, which makes every boolean operator kind of a control flow statement in itself. + +### Code examples + +```java +class Foo { + void baseCyclo() { // Cyclo = 1 + highCyclo(); + } + void highCyclo() { // Cyclo = + int x = 0, y = 2; + boolean a = false, b = true; + + if (a && (y == 1 ? b : true)) { + if (y == x) { + while (true) { + if (x++ < 20) { + break; + } + } + } else if (y == t && !d) { + x = a ? y : x; + } else { + x = 2; + } + } + } +} +``` ### Versions From 02307ca8d5d7a76086d0f4a6a46a96693776b5bb Mon Sep 17 00:00:00 2001 From: oowekyala Date: Mon, 7 Aug 2017 17:08:40 +0200 Subject: [PATCH 02/10] Code examples for doc + fix Ncss ignoring asserts --- .../pages/pmd/languages/java_metrics_index.md | 50 +++++++++++++++---- .../impl/visitors/DefaultNcssVisitor.java | 7 +++ .../lang/java/metrics/impl/xml/NcssTest.xml | 9 ++-- 3 files changed, 52 insertions(+), 14 deletions(-) diff --git a/docs/pages/pmd/languages/java_metrics_index.md b/docs/pages/pmd/languages/java_metrics_index.md index 1d96c3b19..23e503245 100644 --- a/docs/pages/pmd/languages/java_metrics_index.md +++ b/docs/pages/pmd/languages/java_metrics_index.md @@ -44,22 +44,23 @@ control flow statement in itself. ```java class Foo { - void baseCyclo() { // Cyclo = 1 + void baseCyclo() { // Cyclo = 1 highCyclo(); } - void highCyclo() { // Cyclo = + + void highCyclo() { // Cyclo = 10 int x = 0, y = 2; boolean a = false, b = true; - if (a && (y == 1 ? b : true)) { - if (y == x) { - while (true) { - if (x++ < 20) { - break; + if (a && (y == 1 ? b : true)) { // +3 + if (y == x) { // +1 + while (true) { // +1 + if (x++ < 20) { // +1 + break; // +1 } } - } else if (y == t && !d) { - x = a ? y : x; + } else if (y == t && !d) { // +2 + x = a ? y : x; // +1 } else { x = 2; } @@ -67,7 +68,6 @@ class Foo { } } ``` - ### Versions * Version `CycloVersion#IGNORE_BOOLEAN_PATHS`: Boolean operators are not counted, nor are empty @@ -108,6 +108,36 @@ This makes it easier to compare nested classes to outer classes. Besides, it mak actually represent the size of the class and not of the file. If you don't like that behaviour, use the `JAVANCSS` version. +### Code example +```java +import java.util.Collections; // +0 +import java.io.IOException; // +0 + +class Foo { // +1, total Ncss = 12 + + public void bigMethod() // +1 + throws IOException { + int x = 0, y = 2; // +1 + boolean a = false, b = true; // +1 + + if (a || b) { // +1 + try { // +1 + do { // +1 + x += 2; // +1 + } while (x < 12); + + System.exit(0); // +1 + } catch (IOException ioe) { // +1 + throw new PatheticFailException(ioe); // +1 + } + } else { + assert false; // +1 + } + } +} +``` + + ### Versions diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/visitors/DefaultNcssVisitor.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/visitors/DefaultNcssVisitor.java index 082e55cea..79da9a5e2 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/visitors/DefaultNcssVisitor.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/visitors/DefaultNcssVisitor.java @@ -7,6 +7,7 @@ import org.apache.commons.lang3.mutable.MutableInt; import net.sourceforge.pmd.lang.java.ast.ASTAnnotationTypeDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTAssertStatement; import net.sourceforge.pmd.lang.java.ast.ASTBreakStatement; import net.sourceforge.pmd.lang.java.ast.ASTCatchStatement; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; @@ -220,4 +221,10 @@ public Object visit(ASTInitializer node, Object data) { return super.visit(node, data); } + @Override + public Object visit(ASTAssertStatement node, Object data) { + ((MutableInt) data).increment(); + return super.visit(node, data); + } + } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/metrics/impl/xml/NcssTest.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/metrics/impl/xml/NcssTest.xml index ecf84267f..80c26d932 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/metrics/impl/xml/NcssTest.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/metrics/impl/xml/NcssTest.xml @@ -104,6 +104,7 @@ e.printStackTrace(); } catch (ThemAll pokemon) { pokemon.train(); + assert pokemon.level > 12; } finally { // Do nothing } @@ -125,11 +126,11 @@ true 5 - 'com.company.money.Foo' has value 65 highest 20. + 'com.company.money.Foo' has value 66 highest 21. 'com.company.money.Foo#Foo()' has value 2. 'com.company.money.Foo#Foo(int)' has value 13. 'com.company.money.Foo#foo()' has value 14. - 'com.company.money.Foo#main(String)' has value 20. + 'com.company.money.Foo#main(String)' has value 21. @@ -140,11 +141,11 @@ javaNcss 5 - 'com.company.money.Foo' has value 68 highest 20. + 'com.company.money.Foo' has value 69 highest 21. 'com.company.money.Foo#Foo()' has value 2. 'com.company.money.Foo#Foo(int)' has value 13. 'com.company.money.Foo#foo()' has value 14. - 'com.company.money.Foo#main(String)' has value 20. + 'com.company.money.Foo#main(String)' has value 21. From 193ae355bba8dd7c7d5cf8f12d34ff14251d5f90 Mon Sep 17 00:00:00 2001 From: oowekyala Date: Tue, 8 Aug 2017 00:12:29 +0200 Subject: [PATCH 03/10] Documentation --- .../pages/pmd/languages/java_metrics_index.md | 80 +++++++++++++++---- 1 file changed, 64 insertions(+), 16 deletions(-) diff --git a/docs/pages/pmd/languages/java_metrics_index.md b/docs/pages/pmd/languages/java_metrics_index.md index 23e503245..5b685b725 100644 --- a/docs/pages/pmd/languages/java_metrics_index.md +++ b/docs/pages/pmd/languages/java_metrics_index.md @@ -18,27 +18,27 @@ toc: ### Description -## Cyclomatic Complexity (CYCLO) +## Cyclomatic Complexity (CYCLO) -*Operation metric.* Can be calculated on any non-abstract method. +*Operation metric.* Can be calculated on any non-abstract operation. ### Description Number of independent paths through a block of code \[[Lanza05](#Lanza05)\]. Formally, given that the control flow graph of the block has n vertices, e edges and p connected components, the -Cyclomatic complexity of the block is given by `CYCLO = e - n + 2p` \[[McCabe76](#McCabe76)\]. In practice it can be +cyclomatic complexity of the block is given by `CYCLO = e - n + 2p` \[[McCabe76](#McCabe76)\]. In practice it can be calculated by counting control flow statements following the standard rules given below. The standard version of the metric complies with McCabe's original definition: * Methods have a base complexity of 1. * +1 for every control flow statement (`if`, `case`, `catch`, `throw`, `do`, `while`, `for`, `break`, `continue`) and -conditional expression (`?:`) \[[Sonarqube](#Sonarqube)\]. Notice switch cases count as one, but not the switch itself: the point is that - a switch should have the same complexity value as the equivalent series of `if` statements. + conditional expression (`?:`) \[[Sonarqube](#Sonarqube)\]. Notice switch cases count as one, but not the switch + itself: the point is that a switch should have the same complexity value as the equivalent series of `if` statements. * `else`, `finally` and `default` don't count; * +1 for every boolean operator (`&&`, `||`) in the guard condition of a control flow statement. That's because -Java has short-circuit evaluation semantics for boolean operators, which makes every boolean operator kind of a -control flow statement in itself. + Java has short-circuit evaluation semantics for boolean operators, which makes every boolean operator kind of a + control flow statement in itself. ### Code examples @@ -82,7 +82,7 @@ class Foo { ### Description Simply counts the number of lines of code the operation or class takes up in the source. This metric doesn't discount - comments or blank lines. +comments or blank lines. See also [NCSS](#non-commenting-source-statements-ncss). ## Non-commenting source statements (NCSS) @@ -98,15 +98,15 @@ opening braces in the program. Comments and blank lines are ignored, and stateme The standard version of the metric is based off JavaNCSS's version \[[JavaNcss](#JavaNcss)\]: * +1 for any of the following statements: `if`, `else`, `while`, `do`, `for`, `switch`, `break`, `continue`, `return`, -`throw`, `synchronized`, `catch`, `finally`. + `throw`, `synchronized`, `catch`, `finally`. * +1 for each assignment, variable declaration (except `for` loop initializers) or statement expression. We count -variables declared on the same line (e.g. `int a, b, c;`) as a single statement. + variables declared on the same line (e.g. `int a, b, c;`) as a single statement. * Contrary to Sonarqube, but as JavaNCSS, we count type declarations (class, interface, enum, annotation), -and method and field declarations \[[Sonarqube](#Sonarqube)\]. + and method and field declarations \[[Sonarqube](#Sonarqube)\]. * Contrary to JavaNCSS, but as Sonarqube, we do not count package declaration and import declarations as statements. -This makes it easier to compare nested classes to outer classes. Besides, it makes for class metric results that -actually represent the size of the class and not of the file. If you don't like that behaviour, use the `JAVANCSS` -version. + This makes it easier to compare nested classes to outer classes. Besides, it makes for class metric results that + actually represent the size of the class and not of the file. If you don't like that behaviour, use the `JAVANCSS` + version. ### Code example ```java @@ -138,12 +138,60 @@ class Foo { // +1, total Ncss = 12 ``` - ### Versions * Version `NcssVersion#JAVANCSS`: Import and package statements are counted as well. This version fully complies with JavaNCSS. - + +## NPath complexity (NPath) + +*Operation metric.* Can be computed on any non-abstract operation. + +### Description + +Number of acyclic execution paths through a piece of code. This is related to cyclomatic complexity, but the two +metrics don't count the same thing: NPath counts the number of distinct *full* paths from the beginning to the end of +the method, while Cyclo only counts the number of decision points. NPath is not computed as simply as Cyclo. With +NPath, two decision points appearing sequentially have their complexity multiplied. For example: +```java +void fun(boolean a, boolean b, boolean c) { // NPath = 6 + + // block #0 + + if (a) { + // block #1 + } else { + // block #2 + } + + // block #3 + + if (b) { + // block #4 + } else if (c) { + // block #5 + } + + // block #6 +} +``` +After block 0, the control flow can either execute block 1 or 2 before jumping to block 3. From block three, the +control flow will again have the choice between blocks 4 and 5 before jumping to block 6. The first `if` offers two +choices, the second offers 3, so the cyclomatic complexity of this method is 2 + 3 = 5. NPath, however, sees 2 * 3 = +6 full paths from the beginning to the end. + +The fact that NPath multiplies the complexity of statements makes it grow exponentially: 10 `if` - `else` statements in +a row would give an NPath of 1024, while Cyclo would evaluate to 20. + +Methods with an NPath complexity over 200 are generally considered too complex. + +We compute NPath recursively, with the following set of rules: +* A block has a base complexity of 1 +* The complexity of `for`, `do` and `while` statements is 1 + the complexity of its block + the complexity of its + guard condition +* The complexity of an `if` statement is 1 + the complexity of its guard condition + + + ## Weighted Method Count (WMC) From 6abe95d2be540514c7552a60283cc56dee0f9dd8 Mon Sep 17 00:00:00 2001 From: oowekyala Date: Tue, 8 Aug 2017 15:30:40 +0200 Subject: [PATCH 04/10] Metrics rule descriptions --- .../main/resources/rulesets/java/metrics.xml | 110 ++++++++++-------- 1 file changed, 62 insertions(+), 48 deletions(-) diff --git a/pmd-java/src/main/resources/rulesets/java/metrics.xml b/pmd-java/src/main/resources/rulesets/java/metrics.xml index 422a7e56f..38e7c9f72 100644 --- a/pmd-java/src/main/resources/rulesets/java/metrics.xml +++ b/pmd-java/src/main/resources/rulesets/java/metrics.xml @@ -16,51 +16,46 @@ metrics="true" externalInfoUrl="${pmd.website.baseurl}/rules/java/codesize.html#CyclomaticComplexity"> - + The complexity of methods directly affects maintenance costs and readability. Concentrating too much decisional logic + in a single method makes its behaviour hard to read and change. + + Cyclomatic complexity assesses the complexity of a method by counting the number of decision points in a method, + plus one for the method entry. Decision points are places where the control flow jumps to another place in the + program. As such, they include all control flow statements, such as `if`, `while`, `for`, and `case`. For more + details on the calculation, see the documentation of the [Cyclo metric](/pmd_java_metrics_index.html#cyclomatic-complexity-cyclo). + + 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. + + Reported methods should be broken down into several smaller methods. Reported classes should probably be broken down + into subcomponents. 3 @@ -71,24 +66,38 @@ public class Foo { // This has a Cyclomatic Complexity = 12 since="3.9" class="net.sourceforge.pmd.lang.java.metrics.rule.NcssCountRule" metrics="true" - externalInfoUrl="${pmd.website.baseurl}/rules/java/codesize.html#NcssTypeCount"> + externalInfoUrl="${pmd.website.baseurl}/rules/java/codesize.html#NcssCount"> - This rule uses the NCSS (Non-Commenting Source Statements) algorithm to determine the number of lines - of code for a given type. NCSS ignores comments, and counts actual statements. Using this algorithm, - lines of code that are split are counted as one. + This rule uses the NCSS (Non-Commenting Source Statements) metric to determine the number of lines + of code in a class, method or constructor. NCSS ignores comments, blank lines, and only counts actual + statements. For more details on the calculation, see the documentation ofthe[NCSSmetric](/pmd_java_metrics_index.html#non-commenting-source-statements-ncss). 3 @@ -103,6 +112,11 @@ public class Foo extends Bar { externalInfoUrl="${pmd.website.baseurl}/rules/java/codesize.html#NPathComplexity"> 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 oftheblock of the method. That metric grows exponentially, as + it multiplies the complexity of statements inthe 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. From 2bd9dc6a70041fca1e62fe57b5e66de628d07c47 Mon Sep 17 00:00:00 2001 From: oowekyala Date: Tue, 8 Aug 2017 16:39:12 +0200 Subject: [PATCH 05/10] Finished NPath description --- .../pages/pmd/languages/java_metrics_index.md | 41 ++++++++++++------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/docs/pages/pmd/languages/java_metrics_index.md b/docs/pages/pmd/languages/java_metrics_index.md index 5b685b725..f73b31b4e 100644 --- a/docs/pages/pmd/languages/java_metrics_index.md +++ b/docs/pages/pmd/languages/java_metrics_index.md @@ -152,7 +152,31 @@ class Foo { // +1, total Ncss = 12 Number of acyclic execution paths through a piece of code. This is related to cyclomatic complexity, but the two metrics don't count the same thing: NPath counts the number of distinct *full* paths from the beginning to the end of the method, while Cyclo only counts the number of decision points. NPath is not computed as simply as Cyclo. With -NPath, two decision points appearing sequentially have their complexity multiplied. For example: +NPath, two decision points appearing sequentially have their complexity multiplied. + +The fact that NPath multiplies the complexity of statements makes it grow exponentially: 10 `if` - `else` statements in +a row would give an NPath of 1024, while Cyclo would evaluate to 20. Methods with an NPath complexity over 200 are +generally considered too complex. + +We compute NPath recursively, with the following set of rules: +* An empty block has a complexity of 1. +* The complexity of a block is the product of the NPath complexity of its statements, calculated as follows: + * The complexity of `for`, `do` and `while` statements is 1, plus the complexity of the block, plus the complexity of + the guard condition. + * The complexity of a cascading `if` statement (`if .. else if ..`) is the number of `if` statements in the chain, + plus the complexity of their guard condition, plus the complexity of the unguarded `else` block (or 1 if there is + none). + * The complexity of a `switch` statement is the number of cases, plus the complexity of each `case` block. It's + equivalent to the complexity of the equivalent cascade of `if` statements. + * The complexity of a ternary expression (`?:`) is the complexity of the guard condition, plus the + complexity of both expressions. It's equivalent to the complexity of the equivalent `if .. else` construct. + * The complexity of a `try .. catch` statement is the complexity of the `try` block, plus the complexity of each + catch block. + * The complexity of a `return` statement is the complexity of the expression (or 1 if there is none). + * All other statements have a complexity of 1 and are discarded from the product. + +### Code example + ```java void fun(boolean a, boolean b, boolean c) { // NPath = 6 @@ -176,22 +200,9 @@ void fun(boolean a, boolean b, boolean c) { // NPath = 6 } ``` After block 0, the control flow can either execute block 1 or 2 before jumping to block 3. From block three, the -control flow will again have the choice between blocks 4 and 5 before jumping to block 6. The first `if` offers two +control flow will again have the choice between blocks 4 and 5 before jumping to block 6. The first `if` offers 2 choices, the second offers 3, so the cyclomatic complexity of this method is 2 + 3 = 5. NPath, however, sees 2 * 3 = 6 full paths from the beginning to the end. - -The fact that NPath multiplies the complexity of statements makes it grow exponentially: 10 `if` - `else` statements in -a row would give an NPath of 1024, while Cyclo would evaluate to 20. - -Methods with an NPath complexity over 200 are generally considered too complex. - -We compute NPath recursively, with the following set of rules: -* A block has a base complexity of 1 -* The complexity of `for`, `do` and `while` statements is 1 + the complexity of its block + the complexity of its - guard condition -* The complexity of an `if` statement is 1 + the complexity of its guard condition + - - ## Weighted Method Count (WMC) From 1105d62a29d6658e0444f1660313634878714b57 Mon Sep 17 00:00:00 2001 From: oowekyala Date: Tue, 8 Aug 2017 16:50:12 +0200 Subject: [PATCH 06/10] Interface doc --- docs/pages/pmd/devdocs/metrics_howto.md | 77 +++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 4 deletions(-) diff --git a/docs/pages/pmd/devdocs/metrics_howto.md b/docs/pages/pmd/devdocs/metrics_howto.md index 9eb3294a0..71eedd01a 100644 --- a/docs/pages/pmd/devdocs/metrics_howto.md +++ b/docs/pages/pmd/devdocs/metrics_howto.md @@ -1,9 +1,9 @@ --- title: Using code metrics in custom rules tags: [customizing] -summary: "PMD was recently enhanced with the ability to compute code metrics on Java code (the so-called Metrics -Framework). This framework provides developers with a straightforward interface to use code metrics in their rules, and -to extend the framework with their own custom metrics." +summary: "PMD was recently enhanced with the ability to compute code metrics on Java and Apex source (the so-called +Metrics Framework). This framework provides developers with a straightforward interface to use code metrics in their +rules, and to extend the framework with their own custom metrics." last_updated: July 20, 2017 sidebar: pmd_sidebar permalink: pmd_devdocs_metrics_howto.html @@ -13,4 +13,73 @@ folder: pmd/devdocs ## Using the metrics framework -## Writing custom metrics \ No newline at end of file +{%include note.html content="Using the metrics framework is for now restricted to Java rules (with plans to support +XPath rules later)." %} + +To use the metrics framework in a custom rule, the first thing to do would be to enable metrics by adding the +`metrics="true"` attribute to your rule's XML element. + +{%include note.html content="The following explains how to use the Java metrics framework. The Apex framework +differs only by the name of its classes." %} + +In PMD's Metrics framework, a metric is an operation that can be carried out on nodes of a certain type and produces +a numeric result. In the Java framework, metrics can be computed on operation declaration nodes (constructor and +method declaration), and type declaration nodes (class, interface, enum, and annotation declarations). A metric +object in the framework can only handle either types or operations, but not both. + +The framework provides a library of already implemented metrics. These metrics are referenced by `MetricKey` objects, +which are listed in two public enums: `JavaClassMetricKey` and `JavaOperationMetricKey`. Metric keys wrap a metric, and +know which type of node their metric can be computed on. That way, you cannot compute an operation metric on a class +declaration node. Metrics that can be computed on operation and type declarations (e.g. NCSS) have one metric key in +each enum. + +The static façade class `JavaMetrics` is the only entry point to compute metrics in the Java framework. +This class provides the method `get` and its overloads. The following sections describes the interface of this class. + +### Basic usage + +The simplest overloads of `JavaMetrics.get` take two parameters: **a `MetricKey` and a node of the corresponding type.** +Say you want to write a rule to report methods that have a high cyclomatic complexity. In your rule's visitor, you +can get the value of Cyclo for a method node like so: +```java +public Object visit(ASTMethodDeclaration node, Object data) { + int cyclo = (int) JavaMetrics.get(JavaOperationMetricKey.CYCLO, node); + if (cyclo > 10) { + // add violation + } + return data; +} +``` + +The same goes for class metrics: you select one among `JavaClassMetricKey`'s constants and pass it along with the node + to `JavaMetrics.get`. + +### Capability checking + +Metrics are not necessarily computable on any node of the type they handle. For example, Cyclo cannot be computed on +abstract methods. Metric keys provides a `supports(Node)` boolean method to find out if the metric can be computed on +the specified node. **If the metric cannot be computed on the given node, `JavaMetrics.get` will return `Double.NaN` .** +If you're concerned about that, you can condition your call on whether the node is supported or not: +```java +public Object visit(ASTMethodDeclaration node, Object data) { + if (JavaOperationMetricKey.CYCLO.supports(node)) { + int cyclo = (int) JavaMetrics.get(JavaOperationMetricKey.CYCLO, node); + if (cyclo > 10) { + // add violation + } + return data; + } +} +``` + +### Metric versions + +{%include important.html content="Metric versions are about to be revamped into options that can be combined +together." %} + + +### Result options + +## Writing custom metrics + +{%include warning.html content="WIP" %} From 4be56ab27410aa152f3784ea2bb03d73686a2571 Mon Sep 17 00:00:00 2001 From: oowekyala Date: Tue, 8 Aug 2017 21:07:22 +0200 Subject: [PATCH 07/10] Added code example for npath --- .../main/resources/rulesets/java/metrics.xml | 51 ++++++++++--------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/pmd-java/src/main/resources/rulesets/java/metrics.xml b/pmd-java/src/main/resources/rulesets/java/metrics.xml index 38e7c9f72..ec4a95344 100644 --- a/pmd-java/src/main/resources/rulesets/java/metrics.xml +++ b/pmd-java/src/main/resources/rulesets/java/metrics.xml @@ -123,35 +123,40 @@ class Foo { // +1, total Ncss = 12 3 r) { - doSomething(); - while (f < 5 ) { - anotherThing(); - f -= 27; - } - } else { - tryThis(); - } +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); } } - ]]> From 319e6977d177cd1973f8c4ba329727bbdb422025 Mon Sep 17 00:00:00 2001 From: oowekyala Date: Wed, 9 Aug 2017 14:59:10 +0200 Subject: [PATCH 08/10] Fix spacing problem in descriptions --- pmd-java/src/main/resources/rulesets/java/metrics.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pmd-java/src/main/resources/rulesets/java/metrics.xml b/pmd-java/src/main/resources/rulesets/java/metrics.xml index ec4a95344..deb056a96 100644 --- a/pmd-java/src/main/resources/rulesets/java/metrics.xml +++ b/pmd-java/src/main/resources/rulesets/java/metrics.xml @@ -113,8 +113,8 @@ class Foo { // +1, total Ncss = 12 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 oftheblock of the method. That metric grows exponentially, as - it multiplies the complexity of statements inthe same block. For more details on the calculation, see the + 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 From c38961720d74bf48a5e26860ba307979064615b3 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sun, 13 Aug 2017 12:07:15 +0200 Subject: [PATCH 09/10] Update changelog, refs #548 --- src/site/markdown/overview/changelog.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/site/markdown/overview/changelog.md b/src/site/markdown/overview/changelog.md index 4503e587a..5e45ae798 100644 --- a/src/site/markdown/overview/changelog.md +++ b/src/site/markdown/overview/changelog.md @@ -157,8 +157,9 @@ Al existing rules have been updated to reflect these changes. If you have custom * [#524](https://github.com/pmd/pmd/pull/524): \[java] Add support for explicit type arguments with method invocation - [Bendegúz Nagy](https://github.com/WinterGrascph) * [#525](https://github.com/pmd/pmd/pull/525): \[core] Fix line ending and not ignored files issues - [Matias Comercio](https://github.com/MatiasComercio) * [#528](https://github.com/pmd/pmd/pull/528): \[core] Fix typo - [Ayoub Kaanich](https://github.com/kayoub5) +* [#529](https://github.com/pmd/pmd/pull/529): \[java] Abstracted the Java metrics framework - [Clément Fournier](https://github.com/oowekyala) * [#530](https://github.com/pmd/pmd/pull/530): \[java] Fix issue #527: Lombok getter annotation on enum is not recognized correctly - [Clément Fournier](https://github.com/oowekyala) * [#535](https://github.com/pmd/pmd/pull/535): \[apex] Fix broken Apex visitor adapter - [Clément Fournier](https://github.com/oowekyala) -* [#529](https://github.com/pmd/pmd/pull/529): \[java] Abstracted the Java metrics framework - [Clément Fournier](https://github.com/oowekyala) * [#542](https://github.com/pmd/pmd/pull/542): \[java] Metrics abstraction - [Clément Fournier](https://github.com/oowekyala) +* [#548](https://github.com/pmd/pmd/pull/548): \[java] Metrics documentation - [Clément Fournier](https://github.com/oowekyala) From 908e8edc13984c98ee05408c675ded3596302360 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sun, 13 Aug 2017 12:10:01 +0200 Subject: [PATCH 10/10] Update docs --- docs/_data/sidebars/pmd_sidebar.yml | 4 ++-- pmd-java/src/main/resources/rulesets/java/metrics.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/_data/sidebars/pmd_sidebar.yml b/docs/_data/sidebars/pmd_sidebar.yml index 4f81b6ef8..361b1d838 100644 --- a/docs/_data/sidebars/pmd_sidebar.yml +++ b/docs/_data/sidebars/pmd_sidebar.yml @@ -3,7 +3,7 @@ entries: - title: sidebar - product: PMD Project + product: PMD version: "!PMD_VERSION!" folders: @@ -105,7 +105,7 @@ entries: - title: JSP Support url: /pmd_languages_jsp.html output: web, pdf - - title: Index of Java code metrics + - title: Java code metrics url: /pmd_java_metrics_index.html output: web, pdf - title: Developer Documentation diff --git a/pmd-java/src/main/resources/rulesets/java/metrics.xml b/pmd-java/src/main/resources/rulesets/java/metrics.xml index deb056a96..5a35fb5bb 100644 --- a/pmd-java/src/main/resources/rulesets/java/metrics.xml +++ b/pmd-java/src/main/resources/rulesets/java/metrics.xml @@ -15,7 +15,7 @@ class="net.sourceforge.pmd.lang.java.metrics.rule.CyclomaticComplexityRule" metrics="true" externalInfoUrl="${pmd.website.baseurl}/rules/java/codesize.html#CyclomaticComplexity"> - + 3