28-August-2026 - 7.27.0
The PMD team is pleased to announce PMD 7.27.0.
This is a minor release.
Table Of Contents
- 🚀️ New and noteworthy
- Java 27 Support
- Updated Apex Support
- Kotlin type-aware analysis
- 🌟️ New and Changed Rules
- New Rules
- Renamed Rules
- Changed Rules
- Deprecated Rules
- 🐛️ Fixed Issues
- 🚨️ API Changes
- Deprecations
- Experimental API
- ✨️ Merged pull requests
- 📦️ Dependency updates
- 📈️ Stats
🚀️ New and noteworthy
Java 27 Support
This release of PMD brings support for Java 27.
There are no new standard language features.
There is one preview language feature:
In order to analyze a project with PMD that uses these preview language features,
you'll need to select the new language version 27-preview:
pmd check --use-version java-27-preview ...
Note: Support for Java 25 preview language features have been removed. The version "25-preview"
is no longer available.
Updated Apex Support
The Apex language support has been bumped to version 67.0 (Summer '26). It supports the new
Multiline String literals.
Kotlin type-aware analysis
Kotlin now supports type-aware analysis via the auxClasspath language property (see #6677).
Resolved type names, return types, and annotation FQNs are available through
KotlinNodeTypeData for use in Java-based rules.
Note: Type data is not yet accessible in XPath rules or the PMD Rule Designer. This will be added in the next version.
🌟️ New and Changed Rules
New Rules
- The new java rule
UnusedReturnValue(Java Error Prone) finds method calls whose result is not used,
although ignoring the result of these method calls is likely a mistake.
The rule is referenced in the quickstart.xml ruleset for Java. - New rule
ProtectedMemberInFinalClass(Java Design) finds protected members defined in final classes.
Such members should use package or private visibility to clarify their intended scope.
The rule replaces now deprecated rulesAvoidProtectedFieldInFinalClassandAvoidProtectedMethodInFinalClassNotExtending
and flags members that were previously not detected by either of these rules, such as nested types or constructors.
The rule is referenced in the quickstart.xml ruleset for Java.
Renamed Rules
- The rule
InstantiableUtilityClass(Java Design) was renamed fromUseUtilityClassto better reflect the problem.
The old name still works but is deprecated.
Changed Rules
- The rule
CommentRequired(Java Documentation)
has a new propertypackageMethodCommentRequirement. It controls whether Javadoc comments are required (or
unwanted) for package-private methods and constructors. Previously, onlypublicandprotectedmethods could
be configured (viapublicMethodCommentRequirementandprotectedMethodCommentRequirement). The new property
defaults toIgnored, so existing rule configurations are unaffected.
This was implemented in #6880. - The rule
BooleanGetMethodName(Java Codestyle) has a new property
includeWrappedType. If set to true (default), the rule treats Boolean and boolean identical.
If set to false, the rule follows the bean convention and treats Boolean like any other object.
Deprecated Rules
- The java rule
CheckSkipResulthas been deprecated for removal
in favor of the new ruleUnusedReturnValue. - The java rule
UselessPureMethodCallhas been deprecated for removal
in favor of the new ruleUnusedReturnValue.
🐛️ Fixed Issues
- apex
- apex-bestpractices
- #5904: [apex] ApexUnitTestShouldNotUseSeeAllDataTrue violation range should only be the annotation and not the entire test method
- java
- #5041: [java] Parsing failed in ParseLock#doParse(): IndexOutOfBoundsException
- #6010: [java] java.lang.OutOfMemoryError: Java heap space when accessing big Jar files with PMD 7
- #6374: [java] Support Java 27
- #6768: [java] Disambiguation IllegalStateException resolving a synthesized record accessor used as a call argument alongside an anonymous class
- #6932: [java] AssertionError when outer class is parsed before inner class with conflicting visibility
- java-bestpractices
- #1237: [java] AbstractClassWithoutAnyMethod: False positive for empty subclasses that inherit methods
- #1287: [java] GuardLogStatement: False positive when using negative guard conditions
- #2033: [jsp] NoClassAttribute: False positive for jsp:useBean
- #5514: [java] ExhaustiveSwitchHasDefault: False positive for non-exhaustive switch statements
- #5670: [java] ExhaustiveSwitchHasDefault: False positive with final fields not initialized in constructor
- #6200: [java] UnusedAssignment: False positive about the ++ unary operator
- #6393: [java] UnusedPrivateMethod: False positive with overloaded private methods called with values returned from methods of an unresolved type
- #6611: [java] UnnecessaryVarargsArrayCreation: False positive when removing the array creates overload ambiguity
- #6965: [java] AbstractClassWithoutAnyMethod: False Positive on derived abstract class
- java-codestyle
- #2974: [java] Merge rules about protected in final class (AvoidProtectedFieldInFinalClass, AvoidProtectedMethodInFinalClassNotExtending)
- #5441: [java] UseDiamondOperator: False positive with interdependent generic vars
- #6958: [java] BooleanGetMethodName should have the option to treat boolean wrapper type differently
- #6274: [java] UselessParentheses: False positive in ternary else expression
- #6651: [java] UnnecessaryImport: False positive when Javadoc {@link} references an array type
- #6709: [java] LambdaCanBeMethodReference: False positive with array creation containing constructor call in receiver
- #6737: [java] TooManyStaticImports: @SuppressWarnings("PMD.TooManyStaticImports") has stopped working
- #6846: [java] VariableDeclarationUsageDistance: False positive with variables grouped at the top of a block
- #6867: [java] UnnecessaryFullyQualifiedName: ContextedAssertionError: This should be unreachable: unknown constant ScopeInfo: MODULE_IMPORT
- #6943: [java] UnnecessaryCast: False positives related to generics
- java-design
- java-documentation
- java-errorprone
- #2840: [java] CloseResource: False positive on mocks
- #3880: [java] ReturnEmptyCollectionRatherThanNull: False negative when a null value is assigned to a local that is later returned
- #4623: [java] CloseResource: False positive with resource being closed in method
- #6435: [java] UnconditionalIfStatement: False negative for negated boolean constant
- #6537: [java] StaticEJBFieldShouldBeFinal: False Negative when using @stateless etc.
- #6547: [java] NonSerializableClass: False negative for generic element/value types of collections and maps
- #6625: [java] New rule: UnusedReturnValue
- #6695: [java] ReturnEmptyCollectionRatherThanNull: False negative when null is returned through a local variable
- #6742: [java] CloseResource: False positive when a correctly-closed resource is declared without initializer
- #6744: [java] ReturnEmptyCollectionRatherThanNull: False negatives when a returned expression can evaluate to null
- #6826: [java] AssertEqualsArgumentOrder: False positive for double assertEquals
- #6900: [java] DoubleCheckedLocking: False negative when the outer null check is written as !(x != null)
- java-multithreading
- #6747: [java] NonThreadSafeSingleton: False negative with ternary conditional operator
- kotlin
- miscellaneous
- #1995: [core] PMD should display number of rules violated or errors found
- #2527: [doc] CPD: Invalid link to String Tiling Algorithm
- #4952: [doc] Improve doc around PMDConfiguration#prependAuxclasspath #setClassloader
- #4953: [core] Deprecate PMDConfiguration#setClassloader and #getClassloader
- #6837: [ci] chore: actions/create-github-app-token: Input 'app-id' has been deprecated with message: Use 'client-id' instead
- #6865: [core] Include the running PMD version in the "Unable to find referenced rule" error
- #6913: [core] RuleSetLoader#loadFromString ignores previously configured Resource/ClassLoader
- #6952: [core] Ruleset references are not resolved relative to the referencing ruleset
🚨️ API Changes
Deprecations
- core
PMDConfiguration#getClassLoaderandPMDConfiguration#setClassLoaderare deprecated.
UseprependAuxClasspathorsetAuxClasspathto
configure the auxClasspath for analyzing Java code.
Note: In order to read back the currently configured auxClasspath, usegetAuxClasspathand not the
deprecatedgetClassLoader()anymore.
Using ClassLoaders directly is discouraged, as it is unclear, if and when the ClassLoaders should be closed to release their resources.
By just configuring the auxClasspath, PMD internally can deal with that.
- core
net.sourceforge.pmd.lang.JvmLanguagePropertyBundle.setClassLoaderand
net.sourceforge.pmd.lang.JvmLanguagePropertyBundle.getAnalysisClassLoaderare deprecated. Use the language property
JvmLanguagePropertyBundle#AUX_CLASSPATHinstead viagetProperty()andsetProperty(). This language property
is now set correctly when providing the auxClasspath via CLI parameter--aux-classpath.- The internal class
net.sourceforge.pmd.internal.util.ClasspathClassLoaderhas been explicitly marked as deprecated.
Using ClassLoaders directly is discouraged. UsePMDConfiguration#setAuxClasspathinstead.
- java
TypeSystem#usingClassLoaderClasspathis deprecated. Using
ClassLoaders directly is discouraged. UseusingClasspath
instead.
Experimental API
- core
- The new
AuxClasspathLoaderis a replacement for the deprecatedClasspathClassLoader.
It deals with a typical classpath to load classes need for Java's type resolution. It has the static method
enableReuse(int)which enables caching of AuxClasspathLoader instances. This is useful for unit tests
or IDE plugins, when PMD is executed multiple times within one JVM instance. Don't forget to call
disableReuse()when you're done to close all cached instances.
- The new
- kotlin
KotlinNodeTypeData: Provides the initial API to access type information
on Kotlin AST nodes. It's part of the new Kotlin type-aware analysis.
✨️ Merged pull requests
- #6795: [kotlin] Add kotlin-type-mapper infrastructure - Peter Paul Bakker (@stokpop)
- #6811: [java] Fix #4623: CloseResource: False positive with resource being closed in method - Lukas Gräf (@lukasgraef)
- #6822: [java] Fix #5670, #5514: ExhaustiveSwitchHasDefault when default is necessary - Sören Glimm (@UncleOwen)
- #6823: [cli] Print PMD analysis summary - DragonFSKY (@DragonFSKY)
- #6825: [doc] Update "Merging pull requests" - Sören Glimm (@UncleOwen)
- #6827: [java] Fix #6826: AssertEqualsArgumentOrder false positive for double/float delta - Dan Halperin (@dhalperi)
- #6828: [java] Fix #6709: Fix false positive: LambdaCanBeMethodReference should not flag lambda… - Subhadeep (@dweep-js)
- #6829: [core] test: cover pmd analysis configuration - amir (@amirdeljouyi)
- #6838: [java] Follow-up on #6809: Add tests - Sören Glimm (@UncleOwen)
- #6840: chore: Fix #6837: Use client id for create-github-app-token - Andreas Dangel (@adangel)
- #6841: [core] refactor: AnalysisCache based on Path - Andreas Dangel (@adangel)
- #6842: [java] #4730: Add a test for FinalFieldCouldBeStatic that shows that #4730 was already fixed - Sören Glimm (@UncleOwen)
- #6843: [java] Fix #6714: Rename UseUtilityClass to InstantiableUtilityClass - Sören Glimm (@UncleOwen)
- #6845: [core] Fix #4953: Deprecate PMDConfiguration#getClassLoader - Andreas Dangel (@adangel)
- #6858: [java] Fix AvoidThrowingNewInstanceOfSameException false positive - Subhadeep (@dweep-js)
- #6859: [java] Fix #6010: Add replacement for ClasspathClassloader - Andreas Dangel (@adangel)
- #6860: [java] Fix #6846: VariableDeclarationUsageDistance: False positive with variables grouped at the top of a block - Gamja-rani (@onetuks)
- #6863: [java] chore: Add test for ReportStatsListener - Sören Glimm (@UncleOwen)
- #6866: [java] Fix #5041: IndexOutOfBoundsException for type annotations on inner class method signatures - Niklas Keller (@kelunik)
- #6868: [java] Fix #6867: Handle module imports in UnnecessaryFullyQualifiedName - DragonFSKY (@DragonFSKY)
- #6870: [core] Fix #6865: Improve missing rule reference error - DragonFSKY (@DragonFSKY)
- #6871: [java] Fix #6768: Resolve record component types before inference - DragonFSKY (@DragonFSKY)
- #6880: [java] CommentRequired: add packageMethodCommentRequirement property - legacynode (@legacynode)
- #6883: [java] CognitiveComplexity: count switch expressions - Kurath (@KurathSec)
- #6884: [java] Fix #6651: UnnecessaryImport: False positive for array-typed Javadoc {@link} parameters - hexonal (@hexonal)
- #6886: [doc] chore: release_notes - Use 4 space indentation - Andreas Dangel (@adangel)
- #6892: [kotlin] Fix #6891: Use KtModifiers container in AnnotationFqnAnnotator - Peter Paul Bakker (@stokpop)
- #6894: [java] Support Java 27 - Andreas Dangel (@adangel)
- #6902: [java] Fix #6737: Use next annotatable sibling for supressing top level - Andreas Dangel (@adangel)
- #6905: Fix Regression-Tester config: Replace Schedul-o-matic-9000 with declarative-lookup-rollup-summaries - Sören Glimm (@UncleOwen)
- #6906: [jsp] Fix #2033: NoClassAttribute for jsp:useBean - Columbus Labs (@ColumbusLabs)
- #6916: [java] Fix #6625: New rule: UnusedReturnValue - Sören Glimm (@UncleOwen)
- #6917: [java] Fix #6900: Modifies isNullCheck to accept negated expressions - Will-6543 (@Will-6543)
- #6918: [java] Fix #6742: CloseResource false positive for a wrapped resource assigned without an initializer - Eljees (@Eljees)
- #6919: [java] Prepare deprecation of asCtx in java-bestpractices (part of #4814) - Sören Glimm (@UncleOwen)
- #6920: [java] Fix #1287: GuardLogStatement false positive with a guard clause - Eljees (@Eljees)
- #6921: [core] Fix #6913: Use the configured class loader in RuleSetLoader#loadFromString - renechoi (@renechoi)
- #6933: [java] Fix #6932: Handle conflicting inner class visibility modifiers - Scrates1 (@Scrates1)
- #6934: [java] Fix #5441: Resolve interdependent inference variables simultaneously - Sebastian Lövdahl (@slovdahl)
- #6936: [java] Fix #6925: ImmutableField: false positive on picocli @Option/@Parameters fields - dev_Hakaze (@arimu1)
- #6938: [java] Fix #6747: NonThreadSafeSingleton flags ternary lazy init - fudian (@fudianchn)
- #6939: [java] Fix #6435: UnconditionalIfStatement flags arbitrarily negated boolean literals - fudian (@fudianchn)
- #6941: [java] Fix #6744: ReturnEmptyCollectionRatherThanNull: Analyze possible null return values - fudian (@fudianchn)
- #6942: [java] Fix #6547: NonSerializableClass checks collection/map generic element types - fudian (@fudianchn)
- #6944: chore: Remove unnecessary casts - Sören Glimm (@UncleOwen)
- #6945: [java] Fix #6537: StaticEJBFieldShouldBeFinal detects @Stateless/@Stateful/@Singleton/@MessageDriven EJB classes - fudian (@fudianchn)
- #6947: [java] Fix #6393: UnusedPrivateMethod FP on overloaded methods when overload resolution fails - fudian (@fudianchn)
- #6948: [java] Fix #6270: CommentSize skips file header comments - fudian (@fudianchn)
- #6949: [java] Fix #6611: UnnecessaryVarargsArrayCreation ignores overload ambiguity - fudian (@fudianchn)
- #6950: [java] Fix #6274: UselessParentheses treats ternary else-branch parentheses as clarifying - fudian (@fudianchn)
- #6953: [core] Fix #6952: Resolve ruleset references relative to the referencing ruleset - Lukas Gräf (@lukasgraef)
- #6954: [java] Fix #2840: CloseResource: allow Mockito mocks by default - Eljees (@Eljees)
- #6955: [apex] Fix ApexUnitTestShouldNotUseSeeAllDataTrue violation location - Taran (@tarann26)
- #6957: [java] Fix dataflow state for conditional initializers - subotac (@subotac)
- #6959: [chore] Add oout to allowed list of typos - Sören Glimm (@UncleOwen)
- #6962: [java] Fix #6958: Add configurable Boolean handling to BooleanGetMethodName - Harshit Sinha (@harshitsinha11)
- #6964: [java] New rule: ProtectedMemberInFinalClass - Zbynek Konecny (@zbynek)
- #6966: [java] AssertEqualsArgumentOrder: False negative for assertEquals with delta - Zbynek Konecny (@zbynek)
- #6968: [java] Fix #6967: Make violation message of UnusedReturnValue consistent by removing type parameters - Sören Glimm (@UncleOwen)
- #6972: [java] Fix #6965: AbstractClassWithoutAnyMethod false positive on derived abstract class - Abdullah (@AzazelSensei)
- #6973: chore: Enforce bytecode version JDK 8 - Andreas Dangel (@adangel)
- #6974: [java] Introduce JPackageSymbol - Andreas Dangel (@adangel)
- #6976: chore: Fix AssertEqualsArgumentOrder - Andreas Dangel (@adangel)
- #6978: [java] UnusedReturnValue: Fix description - Sören Glimm (@UncleOwen)
- #6981: [doc] Fix #2527: restore CPD tiling article link - Abdullah (@AzazelSensei)
- #6982: [java] Fix #6943: UnnecessaryCast: false positives related to generics - dev_Hakaze (@arimu1)
- #6989: [doc] Update release notes for 7.27.0 - Andreas Dangel (@adangel)
- #6990: [java] Move ProtectedMemberInFinalClass to design category - Zbynek Konecny (@zbynek)
📦️ Dependency updates
- #6824: Bump PMD from 7.25.0 to 7.26.0
- #6830: chore(deps): bump actions/setup-java from 5.3.0 to 5.4.0
- #6831: chore(deps): bump junit.version from 6.1.0 to 6.1.1
- #6832: chore(deps): bump actions/cache from 6.0.0 to 6.1.0
- #6833: chore(deps): bump actions/cache/restore from 6.0.0 to 6.1.0
- #6834: chore(deps): bump ruby/setup-ruby from 1.314.0 to 1.315.0
- #6835: chore(deps): bump crate-ci/typos from 1.47.2 to 1.48.0
- #6836: chore(deps): bump com.puppycrawl.tools:checkstyle from 13.6.0 to 13.7.0
- #6847: chore(deps): bump marocchino/sticky-pull-request-comment from 3.0.4 to 3.0.5
- #6848: chore(deps): bump actions/setup-java from 5.4.0 to 5.5.0
- #6849: chore(deps): bump ruby/setup-ruby from 1.315.0 to 1.316.0
- #6850: chore(deps): bump scalameta.version from 4.17.0 to 4.17.1
- #6851: chore(deps): bump io.github.apex-dev-tools:vf-parser from 2.0.0-beta.1 to 2.0.0
- #6852: chore(deps-dev): bump log4j.version from 2.26.0 to 2.26.1
- #6854: chore(deps): bump org.apache.groovy:groovy from 5.0.6 to 5.0.7
- #6855: chore(deps-dev): bump byte.buddy.version from 1.18.10 to 1.18.11
- #6857: chore(deps): bump org.checkerframework:checker-qual from 4.2.0 to 4.2.1
- #6862: [apex] Fix #6478: Bump apex-parser from 5.0.0 to 5.1.0
- #6872: chore: Bump maven from 3.9.14 to 3.9.16
- #6873: chore(deps): bump net.sf.saxon:Saxon-HE from 12.9 to 12.10
- #6874: chore(deps): bump ruby/setup-ruby from 1.316.0 to 1.318.0
- #6875: chore(deps): bump scalameta.version from 4.17.1 to 4.17.2
- #6876: chore(deps): bump junit.version from 6.1.1 to 6.1.2
- #6878: chore(deps): bump com.puppycrawl.tools:checkstyle from 13.7.0 to 13.8.0
- #6890: [apex][visualforce] Update apex-ls from 6.0.2 to 6.1.0
- #6897: chore(deps): bump ruby/setup-ruby from 1.318.0 to 1.320.0
- #6898: chore(deps): bump actions/checkout from 7.0.0 to 7.0.1
- #6899: chore(deps): bump actions/setup-java from 5.5.0 to 5.6.0
- #6907: chore(deps): bump ruby/setup-ruby from 1.320.0 to 1.321.0
- #6908: chore(deps): bump scalameta.version from 4.17.2 to 4.17.3
- #6909: chore(deps): bump csv from 3.3.5 to 3.3.6 in /docs
- #6910: chore(deps): bump com.puppycrawl.tools:checkstyle from 13.8.0 to 13.10.0
- #6911: chore(deps): bump org.apache.maven.plugins:maven-jar-plugin from 3.5.0 to 3.5.1
- #6927: chore(deps): bump org.jsoup:jsoup from 1.22.2 to 1.23.1
- #6928: chore(deps): bump org.cyclonedx:cyclonedx-maven-plugin from 2.9.2 to 2.9.3
- #6929: chore(deps): bump actions/setup-java from 5.6.0 to 5.7.0
- #6930: chore(deps): bump org.apache.groovy:groovy from 5.0.7 to 5.0.8
- #6931: chore(deps): bump io.github.apex-dev-tools:apex-ls_2.13 from 6.1.0 to 6.2.0
- #6969: chore(deps): bump junit.version from 6.1.2 to 6.1.3
- #6970: chore(deps): bump crate-ci/typos from 1.48.0 to 1.49.0
- #6971: chore(deps): bump org.checkerframework:checker-qual from 4.2.1 to 4.2.2
- #6979: chore(deps): bump build-tools from 39 to 40
- #6980: chore(deps): bump json from 2.19.2 to 2.19.9 in /docs
- #6985: chore(deps-dev): bump com.google.guava:guava from 33.6.0-jre to 33.7.1-jre
- #6986: chore(deps): bump org.apache.groovy:groovy from 5.0.8 to 5.1.0
- #6997: chore(deps): bump com.google.protobuf:protobuf-java from 4.35.1 to 4.36.0
- #6998: chore(deps-dev): bump byte.buddy.version from 1.18.11 to 1.18.12
- #6999: chore(deps): bump com.puppycrawl.tools:checkstyle from 13.10.0 to 14.0.0
- #7001: chore(deps): bump io.github.apex-dev-tools:apex-parser from 5.1.0 to 5.2.0
📈️ Stats
- 120 commits
- 124 closed tickets & PRs
- Days since last release: 60