??-?????-2023 - 7.0.0-SNAPSHOT
The PMD team is pleased to announce PMD 7.0.0-SNAPSHOT.
This is a major release.
New and noteworthy
Revamped Command Line Interface
PMD now ships with a unified Command Line Interface for both Linux/Unix and Windows. Instead of having a collection of scripts
for the different utilities shipped with PMD, a single script pmd
(pmd.bat
for Windows) can now launch all
utilities using subcommands, e.g. pmd check
, pmd designer
. All commands and options are thoroughly documented in the help,
with full color support where available. Moreover, efforts were made to provide consistency in the usage of all PMD utilities.
$ Usage: pmd [-hV] [COMMAND]
-h, --help Show this help message and exit.
-V, --version Print version information and exit.
Commands:
check The PMD standard source code analyzer
cpd Copy/Paste Detector - find duplicate code
designer The PMD visual rule designer
cpd-gui GUI for the Copy/Paste Detector
Warning: May not support the full CPD feature set
ast-dump Experimental: dumps the AST of parsing source code
Exit Codes:
0 Succesful analysis, no violations found
1 An unexpected error occurred during execution
2 Usage error, please refer to the command help
4 Successful analysis, at least 1 violation found
For instance, where you previously would have run
run.sh pmd -d src -R ruleset.xml
you should now use
pmd check -d src -R ruleset.xml
or even better, omit using -d
/ --dir
and simply pass the sources at the end of the parameter list
pmd check -R ruleset.xml src
Multiple source directories can passed, such as:
pmd check -R ruleset.xml src/main/java src/test/java
And the exact same applies to CPD:
pmd cpd --minimum-tokens 100 src/main/java
Additionally, the CLI for the check
command has been enhanced with a progress bar, which interactively displays the
current progress of the analysis.
TODO screenshot (take it right before releasing, because other changes to the CLI will occur until then)
This can be disabled with the --no-progress
flag.
Finally, we now provide a completion script for Bash/Zsh to further help daily usage.
This script can be found under shell/pmd-completion.sh
in the binary distribution.
To use it, edit your ~/.bashrc
/ ~/.zshrc
file and add the following line:
source *path_to_pmd*/shell/pmd-completion.sh
Full Antlr support
Languages backed by an Antlr grammar are now fully supported. This means, it’s now possible not only to use Antlr grammars for CPD, but we can actually build full-fledged PMD rules for them as well. Both the traditional Java visitor rules, and the simpler XPath rules are available to users.
We expect this to enable both our dev team and external contributors to largely extend PMD usage for more languages.
Swift support
Given the full Antlr support, PMD now fully supports Swift. We are pleased to announce we are shipping a number of rules starting with PMD 7.
ForceCast
(swift-errorprone
) flags all force casts, making sure you are defensively considering all types. Having the application crash shouldn’t be an option.ForceTry
(swift-errorprone
) flags all force tries, making sure you are defensively handling exceptions. Having the application crash shouldn’t be an option.ProhibitedInterfaceBuilder
(swift-bestpractices
) flags any usage of interface builder. Interface builder files are prone to merge conflicts, and are impossible to code review, so larger teams usually try to avoid it or reduce it’s usage.UnavailableFunction
(swift-bestpractices
) flags any function throwing afatalError
not marked as@available(*, unavailable)
to ensure no calls are actually performed in the codebase.
Contributors: @lsoncini, @matifraga, @tomidelucca
Kotlin support (experimental)
PMD now supports Kotlin as an additional language for analyzing source code. It is based on the official kotlin Antlr grammar. Java-based rules and XPath-based rules are supported.
Kotlin support has experimental stability level, meaning no compatibility should be expected between even incremental releases. Any functionality can be added, removed or changed without warning.
We are shipping the following rules:
FunctionNameTooShort
(kotlin-bestpractices
) finds functions with a too short name.OverrideBothEqualsAndHashcode
(kotlin-errorprone
) finds classes with only eitherequals
orhashCode
overridden, but not both. This leads to unexpected behavior once instances of such classes are used in collections (Lists, HashMaps, …).
Contributors: @jborgers, @stokpop
XPath 3.1 support
Support for XPath versions 1.0, 1.0-compatibility was removed, support for XPath 2.0 is deprecated. The default (and only) supported XPath version is now XPath 3.1. This version of the XPath language is mostly identical to XPath 2.0. Notable changes:
- The deprecated support for sequence-valued attributes is removed. Sequence-valued properties are still supported.
- Refer to the Saxonica documentation for an introduction to new features in XPath 3.1.
Node stream API
This version includes a powerful API to navigate trees, similar in usage to the Java 8 Stream API:
node.descendants(ASTMethodCall.class)
.filter(m -> "toString".equals(m.getMethodName()))
.map(m -> m.getQualifier())
.filter(q -> TypeTestUtil.isA(String.class, q))
.foreach(System.out::println);
A pipeline like shown here traverses the tree lazily, which is more efficient than traversing eagerly to put all descendants in a list. It is also much easier to change than the old imperative way.
To make this API as accessible as possible, the Node
interface has been fitted with new methods producing node streams. Those methods replace previous tree traversal methods like Node#findDescendantsOfType
. In all cases, they should be more efficient and more convenient.
See NodeStream
for more details.
JavaScript support
The JS specific parser options have been removed. The parser now always retains comments and uses version ES6. The language module registers a couple of different versions. The latest version, which supports ES6 and also some new constructs (see Rhino]), is the default. This should be fine for most use cases.
New Rules
Apex
- The Apex rule
UnusedMethod
finds unused methods in your code.
Java
UnnecessaryBoxing
reports boxing and unboxing conversions that may be made implicit.
Changed Rules
Java
UnnecessaryFullyQualifiedName
: the rule has two new properties, to selectively disable reporting on static field and method qualifiers. The rule also has been improved to be more precise.UselessParentheses
: the rule has two new properties which control how strict the rule should be applied. WithignoreClarifying
(default: true) parentheses that are strictly speaking not necessary are allowed, if they separate expressions of different precedence. The other propertyignoreBalancing
(default: true) is similar, in that it allows parentheses that help reading and understanding the expressions.LooseCoupling
: the rule has a new property to allow some types to be coupled to (allowedTypes
).EmptyCatchBlock
:CloneNotSupportedException
andInterruptedException
are not special-cased anymore. Rename the exception parameter toignored
to ignore them.DontImportSun
:sun.misc.Signal
is not special-cased anymore.UseDiamondOperator
: the propertyjava7Compatibility
is removed. The rule now handles Java 7 properly without a property.SingularField
: PropertiescheckInnerClasses
anddisallowNotAssignment
are removed. The rule is now more precise and will check these cases properly.UseUtilityClass
: The propertyignoredAnnotations
has been removed.LawOfDemeter
: the rule has a new propertytrustRadius
. This defines the maximum degree of trusted data. The default of 1 is the most restrictive.CommentContent
: The propertiescaseSensitive
anddisallowedTerms
are removed. The new propertyfobiddenRegex
can be used now to define the disallowed terms with a single regular expression.
Deprecated Rules
Removed Rules
The following previously deprecated rules have been finally removed:
- AbstractNaming (java-codestyle) -> use
ClassNamingConventions
- AvoidFinalLocalVariable (java-codestyle) -> not replaced
- AvoidPrefixingMethodParameters (java-codestyle) -> use
FormalParameterNamingConventions
- AvoidUsingShortType (java-performance) -> not replaced
- BadComparison (java-errorprone) -> use
ComparisonWithNaN
- BooleanInstantiation (java-performance) -> use
UnnecessaryBoxing
andPrimitiveWrapperInstantiation
- ByteInstantiation (java-performance) -> use
UnnecessaryBoxing
andPrimitiveWrapperInstantiation
- CloneThrowsCloneNotSupportedException (java-errorprone) -> not replaced
- DataflowAnomalyAnalysis (java-errorprone) -> not replaced
- DefaultPackage (java-codestyle) -> use
CommentDefaultAccessModifier
- DoNotCallSystemExit (java-errorprone) -> use
DoNotTerminateVM
- ForLoopsMustUseBraces (java-codestyle) -> use
ControlStatementBraces
- IfElseStmtsMustUseBraces (java-codestyle) -> use
ControlStatementBraces
- IfStmtsMustUseBraces (java-codestyle) -> use
ControlStatementBraces
- IntegerInstantiation (java-performance) -> use
UnnecessaryBoxing
andPrimitiveWrapperInstantiation
- InvalidSlf4jMessageFormat (java-errorprone) -> use
InvalidLogMessageFormat
- LoggerIsNotStaticFinal (java-errorprone)
- LongInstantiation (java-performance) -> use
UnnecessaryBoxing
andPrimitiveWrapperInstantiation
- MIsLeadingVariableName (java-codestyle) -> use
FieldNamingConventions
- MissingBreakInSwitch (java-errorprone) -> use
ImplicitSwitchFallThrough
- ModifiedCyclomaticComplexity (java-design) -> use
CyclomaticComplexity
- PositionLiteralsFirstInCaseInsensitiveComparisons (java-bestpractices) -> use
LiteralsFirstInComparisons
- PositionLiteralsFirstInComparisons (java-bestpractices) -> use
LiteralsFirstInComparisons
- ReturnEmptyArrayRatherThanNull (java-errorprone) -> use
ReturnEmptyCollectionRatherThanNull
- ShortInstantiation (java-performance) -> use
UnnecessaryBoxing
andPrimitiveWrapperInstantiation
- SimplifyBooleanAssertion (java-design) -> use
SimplifiableTestAssertion
- SimplifyStartsWith (java-performance) -> not replaced
- StdCyclomaticComplexity (java-design) -> use
CyclomaticComplexity
- SuspiciousConstantFieldName (java-codestyle)
- UnnecessaryWrapperObjectCreation (java-performance) -> use the new rule
UnnecessaryBoxing
- UnsynchronizedStaticDateFormatter (java-multithreading)
- UseAssertEqualsInsteadOfAssertTrue (java-bestpractices) -> use
SimplifiableTestAssertion
- UseAssertNullInsteadOfAssertEquals (java-bestpractices) -> use
SimplifiableTestAssertion
- UseAssertSameInsteadOfAssertEquals (java-bestpractices) -> use
SimplifiableTestAssertion
- UseAssertTrueInsteadOfAssertEquals (java-bestpractices) -> use
SimplifiableTestAssertion
- VariableNamingConventions (apex-codestyle)
- VariableNamingConventions (java-codestyle) -> use
FieldNamingConventions
and such - WhileLoopsMustUseBraces (java-codestyle) -> use
ControlStatementBraces
Fixed Issues
- miscellaneous
- ant
- #4080: [ant] Split off Ant integration into a new submodule
- core
- #2234: [core] Consolidate PMD CLI into a single command
- #2518: [core] Language properties
- #2873: [core] Utility classes in pmd 7
- #3203: [core] Replace RuleViolationFactory implementations with ViolationDecorator
- #3782: [core] Language lifecycle
- #3902: [core] Violation decorators
- #4035: [core] ConcurrentModificationException in DefaultRuleViolationFactory
- #4120: [core] Explicitly name all language versions
- cli
- apex-design
- #2667: [apex] Integrate nawforce/ApexLink to build robust Unused rule
- java
- java-bestpractices
- #342: [java] AccessorMethodGeneration: Name clash with another public field not properly handled
- #755: [java] AccessorClassGeneration false positive for private constructors
- #770: [java] UnusedPrivateMethod yields false positive for counter-variant arguments
- #807: [java] AccessorMethodGeneration false positive with overloads
- #833: [java] ForLoopCanBeForeach should consider iterating on this
- #1189: [java] UnusedPrivateMethod false positive from inner class via external class
- #1212: [java] Don’t raise JUnitTestContainsTooManyAsserts on JUnit 5’s assertAll
- #1422: [java] JUnitTestsShouldIncludeAssert false positive with inherited @Rule field
- #1565: [java] JUnitAssertionsShouldIncludeMessage false positive with AssertJ
- #1747: [java] PreserveStackTrace false-positive
- #1969: [java] MissingOverride false-positive triggered by package-private method overwritten in another package by extending class
- #1998: [java] AccessorClassGeneration false-negative: subclass calls private constructor
- #2130: [java] UnusedLocalVariable: false-negative with array
- #2147: [java] JUnitTestsShouldIncludeAssert - false positives with lambdas and static methods
- #2464: [java] LooseCoupling must ignore class literals: ArrayList.class
- #2542: [java] UseCollectionIsEmpty can not detect the case
foo.bar().size()
- #2650: [java] UseTryWithResources false positive when AutoCloseable helper used
- #2796: [java] UnusedAssignment false positive with call chains
- #2797: [java] MissingOverride long-standing issues
- #2806: [java] SwitchStmtsShouldHaveDefault false-positive with Java 14 switch non-fallthrough branches
- #2822: [java] LooseCoupling rule: Extend to cover user defined implementations and interfaces
- #2882: [java] UseTryWithResources - false negative for explicit close
- #2883: [java] JUnitAssertionsShouldIncludeMessage false positive with method call
- #2890: [java] UnusedPrivateMethod false positive with generics
- java-codestyle
- #1208: [java] PrematureDeclaration rule false-positive on variable declared to measure time
- #1429: [java] PrematureDeclaration as result of method call (false positive)
- #1673: [java] UselessParentheses false positive with conditional operator
- #1790: [java] UnnecessaryFullyQualifiedName false positive with enum constant
- #1918: [java] UselessParentheses false positive with boolean operators
- #2134: [java] PreserveStackTrace not handling
Throwable.addSuppressed(...)
- #2299: [java] UnnecessaryFullyQualifiedName false positive with similar package name
- #2391: [java] UseDiamondOperator FP when expected type and constructed type have a different parameterization
- #2528: [java] MethodNamingConventions - JUnit 5 method naming not support ParameterizedTest
- #2739: [java] UselessParentheses false positive for string concatenation
- #2748: [java] UnnecessaryCast false positive with unchecked cast
- #3195: [java] Improve rule UnnecessaryReturn to detect more cases
- #3218: [java] Generalize UnnecessaryCast to flag all unnecessary casts
- #3221: [java] PrematureDeclaration false positive for unused variables
- #3238: [java] Improve ExprContext, fix FNs of UnnecessaryCast
- java-design
- #1014: [java] LawOfDemeter: False positive with lambda expression
- #1605: [java] LawOfDemeter: False positive for standard UTF-8 charset name
- #2175: [java] LawOfDemeter: False positive for chained methods with generic method call
- #2179: [java] LawOfDemeter: False positive with static property access - should treat class-level property as global object, not dot-accessed property
- #2180: [java] LawOfDemeter: False positive with Thread and ThreadLocalRandom
- #2182: [java] LawOfDemeter: False positive with package-private access
- #2188: [java] LawOfDemeter: False positive with fields assigned to local vars
- #2536: [java] ClassWithOnlyPrivateConstructorsShouldBeFinal can’t detect inner class
- #3786: [java] SimplifyBooleanReturns should consider operator precedence
- java-errorprone
- #659: [java] MissingBreakInSwitch - last default case does not contain a break
- #1005: [java] CloneMethodMustImplementCloneable triggers for interfaces
- #1669: [java] NullAssignment - FP with ternay and null as constructor argument
- #1899: [java] Recognize @SuppressWanings(“fallthrough”) for MissingBreakInSwitch
- #2320: [java] NullAssignment - FP with ternary and null as method argument
- #2532: [java] AvoidDecimalLiteralsInBigDecimalConstructor can not detect the case
new BigDecimal(Expression)
- #2579: [java] MissingBreakInSwitch detects the lack of break in the last case
- #2880: [java] CompareObjectsWithEquals - false negative with type res
- #2894: [java] Improve MissingBreakInSwitch
- #3071: [java] BrokenNullCheck FP with PMD 6.30.0
- #3087: [java] UnnecessaryBooleanAssertion overlaps with SimplifiableTestAssertion
- #3100: [java] UseCorrectExceptionLogging FP in 6.31.0
- #3173: [java] UseProperClassLoader false positive
- #3351: [java] ConstructorCallsOverridableMethod ignores abstract methods
- java-multithreading
- java-performance
- kotlin
- #419: [kotlin] Add support for Kotlin
API Changes
-
#1648: [apex,vf] Remove CodeClimate dependency - Robert Sösemann Properties “cc_categories”, “cc_remediation_points_multiplier”, “cc_block_highlighting” can no longer be overridden in rulesets. They were deprecated without replacement.
-
The old GUI applications accessible through
run.sh designerold
andrun.sh bgastviewer
(and corresponding Batch scripts) have been removed from the PMD distribution. Please use the newer rule designer withrun.sh designer
. The corresponding classes in packagesjava.net.sourceforge.pmd.util.viewer
andjava.net.sourceforge.pmd.util.designer
have all been removed. -
All API related to XPath support has been moved to the package
net.sourceforge.pmd.lang.rule.xpath
. This includes API that was previously dispersed overnet.sourceforge.pmd.lang
,net.sourceforge.pmd.lang.ast.xpath
,net.sourceforge.pmd.lang.rule.xpath
,net.sourceforge.pmd.lang.rule
, and various language-specific packages (which were made internal). -
The implementation of the Ant integration has been moved from the module
pmd-core
to a new modulepmd-ant
. This involves classes in packagenet.sourceforge.pmd.ant
. The ant CPDTask classnet.sourceforge.pmd.cpd.CPDTask
has been moved into the same packagenet.sourceforge.pmd.ant
. You’ll need to update your taskdef entries in your build.xml files with the FQCNnet.sourceforge.pmd.ant.CPDTask
if you use it anywhere. -
Utility classes in
net.sourceforge.pmd.util
, that have previously marked as@InternalApi
have been finally moved tonet.sourceforge.pmd.internal.util
. This includes ClasspathClassLoader, FileFinder, FileUtil, and IOUtil. -
The following utility classes in
net.sourceforge.pmd.util
are now considered public API:
Metrics framework
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
orDouble
. It avoids widening the result to adouble
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 returnnull
in that case (or a garbage value). The valuenull
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 genericMetricsUtil
, 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 toMetricsUtil::computeMetric
with something likeif (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 theMetric
interface and removed. So theMetric
interface has the new methodString 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
, andJavaOperationMetricKey
. Metric constants are now all inside theJavaMetrics
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 theMetric
interface to create a metric easily. -
This makes it so, that
LanguageMetricsProvider
does not need type parameters. It can just return aSet<Metric<?, ?>>
to list available metrics. -
Signature
s, their implementations, and the interfaceSignedNode
have been removed. Node streams allow replacing their usages very easily.
Testing framework
- PMD 7 has been upgraded to use JUnit 5 only. That means, that JUnit4 related classes have been removed, namely
net.sourceforge.pmd.testframework.PMDTestRunner
net.sourceforge.pmd.testframework.RuleTestRunner
net.sourceforge.pmd.testframework.TestDescriptor
- Rule tests, that use
SimpleAggregatorTst
orPmdRuleTst
work as before without change, but use now JUnit5 under the hood. If you added additional JUnit4 tests to your rule test classes, then you’ll need to upgrade them to use JUnit5.
Language Modules
In order to support language properties and provide a proper lifecycle for languages, there were some changes needed in this area:
- The class
BaseLanguageModule
has been removed. - Individual language modules should now extend
SimpleLanguageModuleBase
. Like before this class is registered via the service loader mechanism viaMETA-INF/services/net.sourceforge.pmd.lang.Language
. - The implementation of a language version handler has been simplified by providing default implementations for most aspects. The minimum requirement is now to provide an own parser for the language version handler.
- Language modules can define custom language properties which can be set via environment variables. This allows to add and use language specific configuration options without the need to change pmd-core.
- For each PMD analysis run a new
LanguageProcessor
instance is created and destroyed afterwards. This allows to store global information without using static fields. This enables the implementation of multifile analysis. - Rules have access to this language processor instance during initialization.
External Contributions
- #1658: [core] Node support for Antlr-based languages - Matías Fraga
- #1698: [core] [swift] Antlr Base Parser adapter and Swift Implementation - Lucas Soncini
- #1774: [core] Antlr visitor rules - Lucas Soncini
- #1877: [swift] Feature/swift rules - Matías Fraga
- #1881: [doc] Add ANTLR documentation - Matías Fraga
- #1882: [swift] UnavailableFunction Swift rule - Tomás de Lucca
- #2830: [apex] Apexlink POC - Kevin Jones
- #3866: [core] Add CLI Progress Bar - @JerritEic