Merge branch 'pmd/7.0.x' into update-UseTryWithResources

This commit is contained in:
Clément Fournier authored and GitHub committed 2021-04-04 15:58:03 +02:00
commit ae3d7d46bb
264 files changed
+4027 -6578

No files matched your search

+15 -15
View File
@@ -27,23 +27,23 @@
<rule ref="category/java/bestpractices.xml/ForLoopCanBeForeach"/>
<rule ref="category/java/bestpractices.xml/ForLoopVariableCount"/>
<rule ref="category/java/bestpractices.xml/GuardLogStatement"/>
<!-- <rule ref="category/java/bestpractices.xml/JUnit4SuitesShouldUseSuiteAnnotation"/> -->
<!-- <rule ref="category/java/bestpractices.xml/JUnit4TestShouldUseAfterAnnotation"/> -->
<!-- <rule ref="category/java/bestpractices.xml/JUnit4TestShouldUseBeforeAnnotation"/> -->
<!-- <rule ref="category/java/bestpractices.xml/JUnit4TestShouldUseTestAnnotation"/> -->
<rule ref="category/java/bestpractices.xml/JUnit4SuitesShouldUseSuiteAnnotation"/>
<rule ref="category/java/bestpractices.xml/JUnit4TestShouldUseAfterAnnotation"/>
<rule ref="category/java/bestpractices.xml/JUnit4TestShouldUseBeforeAnnotation"/>
<rule ref="category/java/bestpractices.xml/JUnit4TestShouldUseTestAnnotation"/>
<rule ref="category/java/bestpractices.xml/JUnitAssertionsShouldIncludeMessage"/>
<rule ref="category/java/bestpractices.xml/JUnitTestContainsTooManyAsserts"/>
<rule ref="category/java/bestpractices.xml/JUnitTestsShouldIncludeAssert"/>
<rule ref="category/java/bestpractices.xml/JUnitUseExpected"/>
<rule ref="category/java/bestpractices.xml/LiteralsFirstInComparisons"/>
<!-- <rule ref="category/java/bestpractices.xml/LooseCoupling"/> -->
<rule ref="category/java/bestpractices.xml/LooseCoupling"/>
<rule ref="category/java/bestpractices.xml/MethodReturnsInternalArray"/>
<rule ref="category/java/bestpractices.xml/MissingOverride"/>
<!-- <rule ref="category/java/bestpractices.xml/OneDeclarationPerLine"/> -->
<!-- <rule ref="category/java/bestpractices.xml/PreserveStackTrace"/> -->
<!-- <rule ref="category/java/bestpractices.xml/ReplaceEnumerationWithIterator"/> -->
<!-- <rule ref="category/java/bestpractices.xml/ReplaceHashtableWithMap"/> -->
<!-- <rule ref="category/java/bestpractices.xml/ReplaceVectorWithList"/> -->
<rule ref="category/java/bestpractices.xml/ReplaceEnumerationWithIterator"/>
<rule ref="category/java/bestpractices.xml/ReplaceHashtableWithMap"/>
<rule ref="category/java/bestpractices.xml/ReplaceVectorWithList"/>
<rule ref="category/java/bestpractices.xml/SwitchStmtsShouldHaveDefault"/>
<rule ref="category/java/bestpractices.xml/SystemPrintln"/>
<rule ref="category/java/bestpractices.xml/UnusedAssignment"/>
@@ -130,8 +130,8 @@
<!-- <rule ref="category/java/design.xml/ClassWithOnlyPrivateConstructorsShouldBeFinal"/> -->
<!-- <rule ref="category/java/design.xml/CollapsibleIfStatements"/> -->
<!-- <rule ref="category/java/design.xml/CouplingBetweenObjects"/> -->
<!-- <rule ref="category/java/design.xml/CyclomaticComplexity"/> -->
<!-- <rule ref="category/java/design.xml/DataClass"/> -->
<rule ref="category/java/design.xml/CyclomaticComplexity"/>
<rule ref="category/java/design.xml/DataClass"/>
<!-- <rule ref="category/java/design.xml/DoNotExtendJavaLangError"/> -->
<!-- <rule ref="category/java/design.xml/ExceptionAsFlowControl"/> -->
<!-- <rule ref="category/java/design.xml/ExcessiveClassLength"/> -->
@@ -140,13 +140,13 @@
<!-- <rule ref="category/java/design.xml/ExcessiveParameterList"/> -->
<!-- <rule ref="category/java/design.xml/ExcessivePublicCount"/> -->
<!-- <rule ref="category/java/design.xml/FinalFieldCouldBeStatic"/> -->
<!-- <rule ref="category/java/design.xml/GodClass"/> -->
<rule ref="category/java/design.xml/GodClass"/>
<!-- <rule ref="category/java/design.xml/ImmutableField"/> -->
<!-- <rule ref="category/java/design.xml/LawOfDemeter"/> -->
<!-- <rule ref="category/java/design.xml/LogicInversion"/> -->
<!-- <rule ref="category/java/design.xml/LoosePackageCoupling"/> -->
<!-- <rule ref="category/java/design.xml/NPathComplexity"/> -->
<!-- <rule ref="category/java/design.xml/NcssCount"/> -->
<rule ref="category/java/design.xml/NPathComplexity"/>
<rule ref="category/java/design.xml/NcssCount"/>
<!-- <rule ref="category/java/design.xml/SignatureDeclareThrowsException"/> -->
<!-- <rule ref="category/java/design.xml/SimplifiedTernary"/> -->
<!-- <rule ref="category/java/design.xml/SimplifyBooleanAssertion"/> -->
@@ -310,9 +310,9 @@
<!-- <rule ref="category/java/performance.xml/UnnecessaryWrapperObjectCreation"/> -->
<rule ref="category/java/performance.xml/UseArrayListInsteadOfVector"/>
<!-- <rule ref="category/java/performance.xml/UseArraysAsList"/> -->
<!-- <rule ref="category/java/performance.xml/UseIOStreamsWithApacheCommonsFileItem"/> -->
<rule ref="category/java/performance.xml/UseIOStreamsWithApacheCommonsFileItem"/>
<rule ref="category/java/performance.xml/UseIndexOfChar"/>
<!-- <rule ref="category/java/performance.xml/UseStringBufferForStringAppends"/> -->
<rule ref="category/java/performance.xml/UseStringBufferForStringAppends"/>
<rule ref="category/java/performance.xml/UseStringBufferLength"/>
<rule ref="category/java/performance.xml/UselessStringValueOf"/>
+8 -14
View File
@@ -94,9 +94,6 @@ entries:
- title: Defining rule properties
url: /pmd_userdocs_extending_defining_properties.html
output: web, pdf
- title: Using and defining code metrics
url: /pmd_userdocs_extending_metrics_howto.html
output: web, pdf
- title: Rule guidelines
url: /pmd_userdocs_extending_rule_guidelines.html
output: web, pdf
@@ -352,18 +349,18 @@ entries:
- title: Performance
output: web, pdf
url: /pmd_rules_xsl_performance.html
- title: Language Specific Documentation
- title: Language-Specific Documentation
output: web, pdf
folderitems:
- title: JSP Support
- title: Apex
url: /pmd_languages_apex.html
output: web, pdf
- title: Java
url: /pmd_languages_java.html
output: web, pdf
- title: JSP
url: /pmd_languages_jsp.html
output: web, pdf
- title: Java code metrics
url: /pmd_java_metrics_index.html
output: web, pdf
- title: Apex code metrics
url: /pmd_apex_metrics_index.html
output: web, pdf
- title: PLSQL
url: /pmd_languages_plsql.html
output: web, pdf
@@ -406,9 +403,6 @@ entries:
- title: Adding a new CPD language
url: /pmd_devdocs_major_adding_new_cpd_language.html
output: web, pdf
- title: Adding metrics support to a language
url: /pmd_devdocs_major_adding_new_metrics_framework.html
output: web, pdf
- title: Experimental features
output: web, pdf
subfolderitems:
+1 -1
View File
@@ -9,4 +9,4 @@ allowed-tags:
- metrics # About using metrics, and metrics indices
- tools # About tools and integrations, Maven, gradle, etc.
- devdocs # About PMD internals, contributing, building, projects
- languages
- languages # Language-specific documentation pages
+13 -3
View File
@@ -65,12 +65,22 @@ langs:
- name: metric
returnType: "xs:decimal?"
shortDescription: "Computes and returns the value of a metric"
description: "Returns the value of the metric as evaluated on the context node"
notes: "The context node must be a {% jdoc jast::ASTAnyTypeDeclaration %} or a {% jdoc jast::MethodLikeNode %}"
description:
Returns the value of the metric as evaluated on the context node.
If the metric cannot be computed on that node, returns an empty sequence
(which is falsy).
parameters:
- name: "metricKey"
type: "xs:string"
description: "The name of an enum constant in {% jdoc jmx::api.JavaOperationMetricKey %} or {% jdoc jmx::api.JavaClassMetricKey %}"
description: "The name of a metric in {% jdoc jmx::JavaMetrics %} (or an alias thereof)."
examples:
- code: "//ClassOrInterfaceDeclaration[metric('NCSS') > 200]"
outcome: ""
- code: "//MethodDeclaration[metric('CYCLO') > 10 and metric('NCSS') > 20]"
outcome: ""
- code: "//TypeParameter[metric('idontexist') > 50]"
outcome: "Error: no such metric"
- name: hasAnnotation
returnType: "xs:boolean"
+20 -2
View File
@@ -83,6 +83,7 @@ The default version is always ES6.
not necessary are allowed, if they separate expressions of different precedence.
The other property `ignoreBalancing` (default: true) is similar, in that it allows parentheses that help
reading and understanding the expressions.
* The rule {% rule "java/bestpractices/LooseCoupling" %} has a new property to allow some types to be coupled to (`allowedTypes`).
#### Removed Rules
@@ -133,11 +134,13 @@ The following previously deprecated rules have been finally removed:
* [#1998](https://github.com/pmd/pmd/issues/1998): \[java] AccessorClassGeneration false-negative: subclass calls private constructor
* [#2130](https://github.com/pmd/pmd/issues/2130): \[java] UnusedLocalVariable: false-negative with array
* [#2147](https://github.com/pmd/pmd/issues/2147): \[java] JUnitTestsShouldIncludeAssert - false positives with lambdas and static methods
* [#2464](https://github.com/pmd/pmd/issues/2464): \[java] LooseCoupling must ignore class literals: ArrayList.class
* [#2542](https://github.com/pmd/pmd/issues/2542): \[java] UseCollectionIsEmpty can not detect the case `foo.bar().size()`
* [#2650](https://github.com/pmd/pmd/issues/2650): \[java] UseTryWithResources false positive when AutoCloseable helper used
* [#2796](https://github.com/pmd/pmd/issue/2796): \[java] UnusedAssignment false positive with call chains
* [#2797](https://github.com/pmd/pmd/issues/2797): \[java] MissingOverride long-standing issues
* [#2806](https://github.com/pmd/pmd/issues/2806): \[java] SwitchStmtsShouldHaveDefault false-positive with Java 14 switch non-fallthrough branches
* [#2822](https://github.com/pmd/pmd/issues/2822): \[java] LooseCoupling rule: Extend to cover user defined implementations and interfaces
* [#2882](https://github.com/pmd/pmd/issues/2882): \[java] UseTryWithResources - false negative for explicit close
* [#2883](https://github.com/pmd/pmd/issues/2883): \[java] JUnitAssertionsShouldIncludeMessage false positive with method call
* [#2890](https://github.com/pmd/pmd/issues/2890): \[java] UnusedPrivateMethod false positive with generics
@@ -179,8 +182,23 @@ The following previously deprecated rules have been finally removed:
#### Metrics framework
* {% jdoc_old !!core::lang.metrics.MetricKeyUtil#of(java.lang.String, core::lang.metrics.Metric) %} is replaced with {% jdoc_old !!core::lang.metrics.MetricKey#of(java.lang.String, core::lang.metrics.Metric) %}
* {% jdoc_old !!core::lang.metrics.MetricsUtil#computeAggregate(core::lang.metrics.MetricKey, java.lang.Iterable, core::lang.metrics.ResultOption) %} and its overload are replaced with {% jdoc_old !!core::lang.metrics.MetricsUtil#computeStatistics(core::lang.metrics.MetricKey, java.lang.Iterable) %}, {% jdoc_old core::lang.metrics.ResultOption %} is removed
The metrics framework has been made simpler and more general.
* The metric interface takes an additional type parameter, representing the result type of the metric. This is usually `Integer` or `Double`. It avoids widening the result to a `double` just to narrow it down.
This makes it so, that `Double.NaN` is not an appropriate sentinel value to represent "not supported" anymore. Instead, `computeFor` may return `null` in that case (or a garbage value). The value `null` may have caused problems with the narrowing casts, which through unboxing, might have thrown an NPE. But when we deprecated the language-specific metrics façades to replace them with the generic `MetricsUtil`, we took care of making the new methods throw an exception if the metric cannot be computed on the parameter. This forces you to guard calls to `MetricsUtil::computeMetric` with something like `if (metric.supports(node))`. If you're following this pattern, then you won't observe the undefined behavior.
* The `MetricKey` interface is not so useful and has been merged into the `Metric` interface and removed. So the `Metric` interface has the new method `String name()`.
* The framework is not tied to at most 2 node types per language anymore. Previously those were nodes for classes and for methods/constructors. Instead, many metrics support more node types. For example, NCSS can be computed on any code block.
For that reason, keeping around a hard distinction between "class metrics" and "operation metrics" is not useful. So in the Java framework for example, we removed the interfaces `JavaClassMetric`, `JavaOperationMetric`, abstract classes for those, `JavaClassMetricKey`, and `JavaOperationMetricKey`. Metric constants are now all inside the `JavaMetrics` utility class. The same was done in the Apex framework.
We don't really need abstract classes for metrics now. So `AbstractMetric` is also removed from pmd-core. There is a factory method on the `Metric` interface to create a metric easily.
* This makes it so, that {% jdoc core::lang.metrics.LanguageMetricsProvider %} does not need type parameters. It can just return a `Set<Metric<?, ?>>` to list available metrics.
* {% jdoc_old core::lang.metrics.Signature %}s, their implementations, and the interface `SignedNode` have been removed. Node streams allow replacing their usages very easily.
### External Contributions
@@ -146,3 +146,15 @@ This can be achieved with Rule Designer:
* Add a syntax highlighter implementation to `net.sourceforge.pmd.util.fxdesigner.util.codearea.syntaxhighlighting` (you could use Java as an example).
* Register it in the `AvailableSyntaxHighlighters` enumeration.
* Now build your implementation and place the `target/pmd-ui-<version>-SNAPSHOT.jar` to the `lib` directory inside your `pmd-bin-...` distribution (you have to delete old `pmd-ui-*.jar` from there).
# Optional features
## Metrics
If you want to add support for computing metrics:
* Create a package `lang.<langname>.metrics`
* Create a utility class `<langname>Metrics`
* Implement new metrics and add them as static constants. Be sure to document them.
* Implement {% jdoc core::lang.LanguageVersionHandler#getLanguageMetricsProvider() %}, to make the metrics available in the designer.
See {% jdoc java::lang.java.metrics.JavaMetrics %} for an example.
@@ -1,46 +0,0 @@
---
title: Adding support for metrics to a language
short_title: Implement a metrics framework
tags: [devdocs, extending, metrics]
summary: "PMD's Java module has an extensive framework for the calculation of metrics, which allows rule developers
to implement and use new code metrics very simply. Most of the functionality of this framework is abstracted in such
a way that any PMD supported language can implement such a framework without too much trouble. Here's how."
last_updated: February 2020
permalink: pmd_devdocs_major_adding_new_metrics_framework.html
author: Clément Fournier <clement.fournier76@gmail.com>
---
## Internal architecture of the metrics framework
The framework is pretty simple. On a high level, a `Metric<N>` describes some numeric computation on a node of type `N`.
You should wrap it into a `MetricKey<N>`, so that it can be cached on nodes (implemented by {%jdoc core::lang.metrics.MetricsUtil %}).
At the very least, a metrics framework has those two components and is just a convenient way to compute and memoize
metrics on a single file. The expressive power of metrics can be improved by implementing *signature matching* capabilities,
which allows a metric to count signatures matching a specific pattern (a mask) over a whole class. This was originally
designed to work across files, given a working usage resolution. However, making that work with incremental analysis is
harder than it looks, and has been rescheduled to another project.
## Implementation of a new framework
* Implement metrics (typically in an internal package)
* Create some public enums/ utility classes to expose metric keys
* Implement a {%jdoc core::lang.metrics.LanguageMetricsProvider %}, to expose your metrics to the designer
* Use your metric keys in rules with {%jdoc core::lang.metrics.MetricsUtil %}
### Optional: Signature matching
You can match the signature of anything: method, field, class, package... It depends on what's useful for you.
Suppose you want to be able to match signatures for nodes of type `N`. What you have to do then is the following:
* Create a class implementing the interface `Signature<N>`. Signatures describe basic information about the node,
which typically includes most of the modifiers they declare (eg visibility, abstract or virtual, etc.).
It's up to you to define the right level of detail, depending on the accuracy of the pattern matching required.
* Make type `N` implement `SignedNode<N>`. This makes the node capable of giving its signature. Factory methods to
build a `Signature<N>` from a `N` are a good idea.
* Create signature masks. A mask is an object that matches some signatures based on their features. For example, with
the Java framework, you can build a `JavaOperationSigMask` that matches all method signatures with visibility
`public`. A sigmask implements `SigMask<S>`, where `S` is the type of signature your mask handles.
* Create utility methods in your abstract class metric class to count signatures matching a specific mask.
[Example](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/AbstractJavaClassMetric.java#L52)
+10
View File
@@ -0,0 +1,10 @@
---
title: Apex support
permalink: pmd_languages_apex.html
author: Clément Fournier
last_updated: March 2021 (7.0.0)
tags: [languages]
summary: "Apex-specific features and guidance"
---
{% include warning.html content="Todo for pmd 7" %}
@@ -1,71 +0,0 @@
---
title: Index of Apex code metrics
tags: [extending, metrics]
summary: "Index of the code metrics available out of the box to Apex rule developers."
last_updated: July 20, 2017
permalink: pmd_apex_metrics_index.html
toc:
minimumHeaders: 8
---
# Index of code metrics
## Cyclomatic Complexity (CYCLO)
*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
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`, `catch`, `throw`, `do`,
`while`, `for`, `break`, `continue`) and conditional expression (`?:`).
* `else`, `finally` and `default` don't count;
* +1 for every boolean operator (`&&`, `||`) in the guard condition of a control
flow statement.
### Code examples
```java
class Foo {
void baseCyclo() { // Cyclo = 1
highCyclo();
}
void highCyclo() { // Cyclo = 10
int x = 0, y = 2;
boolean a = false, b = true;
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) { // +2
x = a ? y : x; // +1
} else {
x = 2;
}
}
}
}
```
## Weighted Method Count (WMC)
*Class metric.* Can be computed on classes and enums.
# References
<a name="Lanza05">Lanza05:</a> Lanza, Marinescu; Object-Oriented Metrics in Practice, 2005.
<a name="McCabe76">McCabe76:</a> McCabe, A Complexity Measure, in Proceedings of the 2nd ICSE (1976).
+47
View File
@@ -0,0 +1,47 @@
---
title: Java support
permalink: pmd_languages_java.html
author: Clément Fournier
last_updated: March 2021 (7.0.0)
tags: [languages]
summary: "Java-specific features and guidance"
---
{% include warning.html content="WIP, todo for pmd 7" %}
### Type and symbol resolution
Java being a statically typed language, a Java program contains more information that just its syntax tree; for instance, every expression has a static type, and every method call is bound to a method overload statically (even if that overload is virtual). In PMD, much of this information is resolved from the AST by additional passes, which run after parsing, and before rules can inspect the tree.
The semantic analysis roughly works like so:
1. The first passes resolve *symbols*, which are a model of the named entities that Java programs declare, like classes, methods, and variables.
2. Then, each name in the tree is resolved to a symbol, according to the language's scoping rules. This may modify the tree to remove *ambiguous names* (names which could be either a type, package, or variable).
3. The last pass resolves the types of expressions, which performs overload resolution on method calls, and type inference.
TODO describe
* why we need auxclasspath
* how disambiguation can fail
### Type and symbol APIs
TODO describe APIs
### Metrics framework
In order to use code metrics in Java, use the metrics constants in {% jdoc java::lang.java.metrics.JavaMetrics %},
together with {% jdoc core::lang.metrics.MetricsUtil %}. For instance:
```java
@Override
public Object visit(ASTMethodDeclaration node, Object data) {
if (JavaMetrics.NCSS.supports(node)) {
int methodSize = MetricsUtil.computeMetric(JavaMetrics.NCSS, node, ncssOptions);
if (methodSize >= level) {
addViolation(data, node);
}
}
return null;
}
```
The Javadocs are the reference documentation.
File diff suppressed because it is too large. Load diff
+2
View File
@@ -2,6 +2,8 @@
title: JSP Support
permalink: pmd_languages_jsp.html
author: Pieter Vanraemdonck
tags: [languages]
summary: "JSP-specific features and guidance"
---
## What is currently supported and what is not
+3 -1
View File
@@ -1,7 +1,9 @@
---
title: PLSQL
title: PLSQL Support
permalink: pmd_languages_plsql.html
last_updated: March 2021 (6.33.0)
tags: [languages]
summary: "PLSQL-specific features and guidance"
---
## Parsing Exclusions
File diff suppressed because it is too large. Load diff
@@ -27,6 +27,7 @@ your rule as you add test cases.
In this page we'll talk about rules for the Java language, but the process is
very similar for other languages.
{% include note.html content="[Please find an index of language-specific documentation here](tag_languages.html)" %}
## Basics
@@ -133,8 +134,6 @@ The start method is called exactly once per file.
<!-- We don't support language-independent rules anyway... -->
## Rule lifecycle reference
### Construction
+1 -1
View File
@@ -1913,7 +1913,7 @@ For the changes, see [PMD Designer Changelog](https://github.com/pmd/pmd-designe
#### Java Metrics
* The new metric "Class Fan Out Complexity" has been added. See
[Java Metrics Documentation](pmd_java_metrics_index.html#class-fan-out-complexity-class_fan_out) for details.
[Java Metrics Documentation](https://pmd.github.io/pmd-6.19.0/pmd_java_metrics_index.html#class-fan-out-complexity-class_fan_out) for details.
#### Modified Rules
+1 -1
View File
@@ -1,5 +1,5 @@
---
title: "Supported Lanugages"
title: "Supported Languages"
tagName: languages
search: exclude
permalink: tag_languages.html
@@ -4,20 +4,18 @@
package net.sourceforge.pmd.lang.apex;
import java.util.Arrays;
import java.util.List;
import static net.sourceforge.pmd.util.CollectionUtil.setOf;
import java.util.Set;
import net.sourceforge.pmd.annotation.InternalApi;
import net.sourceforge.pmd.lang.AbstractPmdLanguageVersionHandler;
import net.sourceforge.pmd.lang.apex.ast.ASTMethod;
import net.sourceforge.pmd.lang.apex.ast.ASTUserClassOrInterface;
import net.sourceforge.pmd.lang.apex.ast.ApexParser;
import net.sourceforge.pmd.lang.apex.metrics.api.ApexClassMetricKey;
import net.sourceforge.pmd.lang.apex.metrics.api.ApexOperationMetricKey;
import net.sourceforge.pmd.lang.apex.metrics.ApexMetrics;
import net.sourceforge.pmd.lang.apex.rule.internal.ApexRuleViolationFactory;
import net.sourceforge.pmd.lang.ast.Parser;
import net.sourceforge.pmd.lang.metrics.LanguageMetricsProvider;
import net.sourceforge.pmd.lang.metrics.internal.AbstractLanguageMetricsProvider;
import net.sourceforge.pmd.lang.metrics.Metric;
import net.sourceforge.pmd.lang.rule.RuleViolationFactory;
@InternalApi
@@ -38,27 +36,21 @@ public class ApexHandler extends AbstractPmdLanguageVersionHandler {
@Override
public LanguageMetricsProvider<ASTUserClassOrInterface<?>, ASTMethod> getLanguageMetricsProvider() {
public LanguageMetricsProvider getLanguageMetricsProvider() {
return myMetricsProvider;
}
private static class ApexMetricsProvider extends AbstractLanguageMetricsProvider<ASTUserClassOrInterface<?>, ASTMethod> {
private static class ApexMetricsProvider implements LanguageMetricsProvider {
@SuppressWarnings("unchecked")
ApexMetricsProvider() {
// a wild double cast
super((Class<ASTUserClassOrInterface<?>>) (Object) ASTUserClassOrInterface.class, ASTMethod.class);
}
private final Set<Metric<?, ?>> metrics = setOf(
ApexMetrics.COGNITIVE_COMPLEXITY,
ApexMetrics.CYCLO,
ApexMetrics.WEIGHED_METHOD_COUNT
);
@Override
public List<ApexClassMetricKey> getAvailableTypeMetrics() {
return Arrays.asList(ApexClassMetricKey.values());
}
@Override
public List<ApexOperationMetricKey> getAvailableOperationMetrics() {
return Arrays.asList(ApexOperationMetricKey.values());
public Set<Metric<?, ?>> getMetrics() {
return metrics;
}
}
}
@@ -4,14 +4,9 @@
package net.sourceforge.pmd.lang.apex.ast;
import net.sourceforge.pmd.lang.apex.metrics.signature.ApexOperationSignature;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.ast.SignedNode;
import apex.jorje.semantic.ast.member.Method;
public final class ASTMethod extends AbstractApexNode<Method> implements ApexQualifiableNode,
SignedNode<ASTMethod>, Node {
public final class ASTMethod extends AbstractApexNode<Method> implements ApexQualifiableNode {
ASTMethod(Method method) {
super(method);
@@ -100,11 +95,6 @@ public final class ASTMethod extends AbstractApexNode<Method> implements ApexQua
}
@Override
public ApexOperationSignature getSignature() {
return ApexOperationSignature.of(this);
}
/**
* Returns true if this is a synthetic class initializer, inserted
* by the parser.
@@ -1,18 +0,0 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.apex.metrics;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.metrics.AbstractMetric;
/**
* Base class for all Apex metrics.
*
* @author Clément Fournier
*/
public abstract class AbstractApexMetric<N extends Node> extends AbstractMetric<N> {
}
@@ -0,0 +1,135 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.apex.metrics;
import java.util.function.Function;
import java.util.function.Predicate;
import org.apache.commons.lang3.mutable.MutableInt;
import net.sourceforge.pmd.internal.util.PredicateUtil;
import net.sourceforge.pmd.lang.apex.ast.ASTMethod;
import net.sourceforge.pmd.lang.apex.ast.ASTUserClass;
import net.sourceforge.pmd.lang.apex.ast.ASTUserClassOrInterface;
import net.sourceforge.pmd.lang.apex.ast.ApexNode;
import net.sourceforge.pmd.lang.apex.metrics.internal.CognitiveComplexityVisitor;
import net.sourceforge.pmd.lang.apex.metrics.internal.CognitiveComplexityVisitor.State;
import net.sourceforge.pmd.lang.apex.metrics.internal.StandardCycloVisitor;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.metrics.Metric;
import net.sourceforge.pmd.lang.metrics.MetricOptions;
import net.sourceforge.pmd.lang.metrics.MetricsUtil;
/**
* Built-in Apex metrics. See {@link Metric} and {@link MetricsUtil}
* for usage doc.
*/
public final class ApexMetrics {
@SuppressWarnings({"unchecked", "rawtypes"})
private static final Class<ApexNode<?>> GENERIC_APEX_NODE_CLASS =
(Class) ApexNode.class; // this is a Class<ApexNode>, the raw type
/**
* Number of independent paths through a block of code.
* 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 {@code CYCLO = e - n + 2p}. In practice
* it can be calculated by counting control flow statements following
* the standard rules given below.
*
*
* <p>The standard version of the metric complies with McCabes original definition:
* <ul>
* <li>Methods have a base complexity of 1.
* <li>+1 for every control flow statement (if, catch, throw, do, while, for, break, continue) and conditional expression (?:).
* <li>else, finally and default do not count;
* <li>+1 for every boolean operator ({@code &&}, {@code ||}) in
* the guard condition of a control flow statement. Thats because
* Apex has short-circuit evaluation semantics for boolean operators,
* which makes every boolean operator kind of a control flow statement in itself.
* </ul>
*
* <p>Code example:
* <pre>{@code
* class Foo {
* void baseCyclo() { // Cyclo = 1
* highCyclo();
* }
*
* void highCyclo() { // Cyclo = 10
* int x = 0, y = 2;
* boolean a = false, b = true;
*
* 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) { // +2
* x = a ? y : x; // +1
* } else {
* x = 2;
* }
* }
* }
* }
* }</pre>
*/
public static final Metric<ApexNode<?>, Integer> CYCLO =
Metric.of(ApexMetrics::computeCyclo, isRegularApexNode(),
"Cyclomatic Complexity", "Cyclo");
public static final Metric<ApexNode<?>, Integer> COGNITIVE_COMPLEXITY =
Metric.of(ApexMetrics::computeCognitiveComp, isRegularApexNode(),
"Cognitive Complexity");
/**
* Sum of the statistical complexity of the operations in the class.
* We use CYCLO to quantify the complexity of an operation.
*
*/
public static final Metric<ASTUserClassOrInterface<?>, Integer> WEIGHED_METHOD_COUNT =
Metric.of(ApexMetrics::computeWmc, filterMapNode(ASTUserClass.class, PredicateUtil.always()),
"Weighed Method Count", "WMC");
private ApexMetrics() {
// utility class
}
private static Function<Node, ApexNode<?>> isRegularApexNode() {
return filterMapNode(GENERIC_APEX_NODE_CLASS, n -> !(n instanceof ASTMethod && ((ASTMethod) n).isSynthetic()));
}
private static <T extends Node> Function<Node, T> filterMapNode(Class<? extends T> klass, Predicate<? super T> pred) {
return n -> n.asStream().filterIs(klass).filter(pred).first();
}
private static int computeCyclo(ApexNode<?> node, MetricOptions ignored) {
MutableInt result = new MutableInt(1);
node.acceptVisitor(new StandardCycloVisitor(), result);
return result.getValue();
}
private static int computeCognitiveComp(ApexNode<?> node, MetricOptions ignored) {
State state = new State();
node.acceptVisitor(CognitiveComplexityVisitor.INSTANCE, state);
return state.getComplexity();
}
private static int computeWmc(ASTUserClassOrInterface<?> node, MetricOptions options) {
return (int) MetricsUtil.computeStatistics(CYCLO, node.getMethods(), options).getSum();
}
}
@@ -1,24 +0,0 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.apex.metrics;
import net.sourceforge.pmd.lang.apex.ast.ApexQualifiedName;
import net.sourceforge.pmd.lang.apex.metrics.signature.ApexOperationSigMask;
/**
* @author Clément Fournier
*/
public interface ApexSignatureMatcher {
/**
* Returns true if the signature of the operation designated by the qualified name is covered by the mask.
*
* @param qname The operation to test
* @param sigMask The signature mask to use
*
* @return True if the signature of the operation designated by the qualified name is covered by the mask
*/
boolean hasMatchingSig(ApexQualifiedName qname, ApexOperationSigMask sigMask);
}
@@ -1,14 +0,0 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.apex.metrics.api;
import net.sourceforge.pmd.lang.apex.ast.ASTUserClassOrInterface;
import net.sourceforge.pmd.lang.metrics.Metric;
/**
* @author Clément Fournier
*/
public interface ApexClassMetric extends Metric<ASTUserClassOrInterface<?>> {
}
@@ -1,39 +0,0 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.apex.metrics.api;
import net.sourceforge.pmd.lang.apex.ast.ASTUserClassOrInterface;
import net.sourceforge.pmd.lang.apex.metrics.impl.ClassCognitiveComplexityMetric;
import net.sourceforge.pmd.lang.apex.metrics.impl.WmcMetric;
import net.sourceforge.pmd.lang.metrics.MetricKey;
/**
* @author Clément Fournier
*/
public enum ApexClassMetricKey implements MetricKey<ASTUserClassOrInterface<?>> {
COGNITIVE(new ClassCognitiveComplexityMetric()),
WMC(new WmcMetric());
private final ApexClassMetric calculator;
ApexClassMetricKey(ApexClassMetric m) {
calculator = m;
}
@Override
public ApexClassMetric getCalculator() {
return calculator;
}
@Override
public boolean supports(ASTUserClassOrInterface<?> node) {
return calculator.supports(node);
}
}
@@ -1,14 +0,0 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.apex.metrics.api;
import net.sourceforge.pmd.lang.apex.ast.ASTMethod;
import net.sourceforge.pmd.lang.metrics.Metric;
/**
* @author Clément Fournier
*/
public interface ApexOperationMetric extends Metric<ASTMethod> {
}
@@ -1,38 +0,0 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.apex.metrics.api;
import net.sourceforge.pmd.lang.apex.ast.ASTMethod;
import net.sourceforge.pmd.lang.apex.metrics.impl.CognitiveComplexityMetric;
import net.sourceforge.pmd.lang.apex.metrics.impl.CycloMetric;
import net.sourceforge.pmd.lang.metrics.MetricKey;
/**
* @author Clément Fournier
*/
public enum ApexOperationMetricKey implements MetricKey<ASTMethod> {
CYCLO(new CycloMetric()),
COGNITIVE(new CognitiveComplexityMetric());
private final ApexOperationMetric calculator;
ApexOperationMetricKey(ApexOperationMetric m) {
calculator = m;
}
@Override
public ApexOperationMetric getCalculator() {
return calculator;
}
@Override
public boolean supports(ASTMethod node) {
return calculator.supports(node);
}
}
@@ -1,24 +0,0 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.apex.metrics.impl;
import net.sourceforge.pmd.lang.apex.ast.ASTUserClassOrInterface;
import net.sourceforge.pmd.lang.apex.ast.ASTUserClassOrInterface.TypeKind;
import net.sourceforge.pmd.lang.apex.metrics.AbstractApexMetric;
import net.sourceforge.pmd.lang.apex.metrics.api.ApexClassMetric;
/**
* Base class for Apex metrics.
*
* @author Clément Fournier
*/
public abstract class AbstractApexClassMetric extends AbstractApexMetric<ASTUserClassOrInterface<?>>
implements ApexClassMetric {
@Override
public boolean supports(ASTUserClassOrInterface<?> node) {
return node.getTypeKind() == TypeKind.CLASS;
}
}
@@ -1,32 +0,0 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.apex.metrics.impl;
import net.sourceforge.pmd.lang.apex.ast.ASTMethod;
import net.sourceforge.pmd.lang.apex.ast.ASTModifierNode;
import net.sourceforge.pmd.lang.apex.metrics.AbstractApexMetric;
import net.sourceforge.pmd.lang.apex.metrics.api.ApexOperationMetric;
/**
* Base class for Apex operation metrics.
*
* @author Clément Fournier
*/
public abstract class AbstractApexOperationMetric extends AbstractApexMetric<ASTMethod> implements ApexOperationMetric {
/**
* Checks if the metric can be computed on the node. For now, we filter out {@literal <clinit>, <init> and clone},
* which are present in all apex class nodes even if they're not implemented, which may yield unexpected results.
*
* @param node The node to check
*
* @return True if the metric can be computed
*/
@Override
public boolean supports(ASTMethod node) {
return !node.isSynthetic()
&& !node.getFirstChildOfType(ASTModifierNode.class).isAbstract();
}
}
@@ -1,22 +0,0 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.apex.metrics.impl;
import net.sourceforge.pmd.lang.apex.ast.ASTUserClassOrInterface;
import net.sourceforge.pmd.lang.apex.metrics.api.ApexOperationMetricKey;
import net.sourceforge.pmd.lang.metrics.MetricOptions;
import net.sourceforge.pmd.lang.metrics.MetricsUtil;
/**
* The sum of the cognitive complexities of all the methods within a class.
*
* @author Gwilym Kuiper
*/
public class ClassCognitiveComplexityMetric extends AbstractApexClassMetric {
@Override
public double computeFor(ASTUserClassOrInterface<?> node, MetricOptions options) {
return MetricsUtil.computeStatistics(ApexOperationMetricKey.COGNITIVE, node.getMethods()).getSum();
}
}
@@ -1,26 +0,0 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.apex.metrics.impl;
import net.sourceforge.pmd.lang.apex.ast.ASTMethod;
import net.sourceforge.pmd.lang.apex.metrics.impl.visitors.CognitiveComplexityVisitor;
import net.sourceforge.pmd.lang.apex.metrics.impl.visitors.CognitiveComplexityVisitor.State;
import net.sourceforge.pmd.lang.metrics.MetricOptions;
/**
* Measures the cognitive complexity of a Class / Method in Apex.
*
* See https://www.sonarsource.com/docs/CognitiveComplexity.pdf for information about the metric
*
* @author Gwilym Kuiper
*/
public class CognitiveComplexityMetric extends AbstractApexOperationMetric {
@Override
public double computeFor(ASTMethod node, MetricOptions options) {
State state = new State();
node.acceptVisitor(CognitiveComplexityVisitor.INSTANCE, state);
return state.getComplexity();
}
}
Loaded 30 of 264 files, more files were not shown because too many files have changed in this diff. Show more