PMD 7.21.0 released

30 January 2026


30-January-2026 - 7.21.0

The PMD team is pleased to announce PMD 7.21.0.

This is a minor release.

Table Of Contents

🚀️ New and noteworthy

🚀️ New: Java 26 Support

This release of PMD brings support for Java 26.

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 26-preview:

pmd check --use-version java-26-preview ...

Note: Support for Java 24 preview language features have been removed. The version “24-preview” is no longer available.

Build Requirement is Java 21

From now on, Java 21 or newer is required to build PMD. PMD itself still remains compatible with Java 8, so that it still can be used in a pure Java 8 environment. This allows us to use the latest checkstyle version during the build.

CPD

  • The Apex module now supports suppression through CPD-ON/CPD-OFF comment pairs. See #6417

🌟️ New and Changed Rules

New Rules

  • The new Java rule PublicMemberInNonPublicType detects public members (such as methods or fields) within non-public types. Non-public types should not declare public members, as their effective visibility is limited, and using the public modifier can create confusion.
  • The new Java rule UnsupportedJdkApiUsage flags the use of unsupported and non-portable JDK APIs, including sun.* packages, sun.misc.Unsafe, and jdk.internal.misc.Unsafe. These APIs are unstable, intended for internal use, and may change or be removed. The rule complements Java compiler warnings by highlighting such usage during code reviews and encouraging migration to official APIs like VarHandle and the Foreign Function & Memory API.

Changed Rules

The following rules have been changed to use a consistent implementation of enum based rule properties:

  • The property checkAddressTypes of rule AvoidUsingHardCodedIP has changed:
    • Instead of IPv4 use ipv4
    • Instead of IPv6 use ipv6
    • Instead of IPv4 mapped IPv6 use ipv4MappedIpv6
    • The old values still work, but you’ll see a deprecation warning.
  • The property nullCheckBranch of rule ConfusingTernary has changed:
    • Instead of Any use any
    • Instead of Then use then
    • Instead of Else use else
    • The old values still work, but you’ll see a deprecation warning.
  • The property typeAnnotations of rule ModifierOrder has changed:
    • Instead of ontype use onType
    • Instead of ondecl use onDecl
    • The old values still work, but you’ll see a deprecation warning.
  • The values of the properties of rule CommentRequired have changed:
    • Instead of Required use required
    • Instead of Ignored use ignored
    • Instead of Unwanted use unwanted
    • The old values still work, but you’ll see a deprecation warning.

Deprecated Rules

🐛️ Fixed Issues

  • core
    • #6184: [core] Consistent implementation of enum properties
  • apex
    • #6417: [apex] Support CPD suppression with “CPD-OFF” & “CPD-ON”
  • apex-codestyle
    • #6349: [apex] FieldDeclarationsShouldBeAtStart: False positive with properties
  • cli
    • #6290: [cli] Improve Designer start script
  • java
    • #5871: [java] Support Java 26
    • #6364: [java] Parse error with yield lambda inside switch
  • java-design
    • #6231: [java] New Rule: PublicMemberInNonPublicType
  • java-errorprone
    • #3601: [java] InvalidLogMessageFormat: False positive when final parameter is Supplier<Throwable>
    • #5882: [java] UnconditionalIfStatement: False negative when true/false is not literal but local variable
    • #5923: [java] New Rule: Catch usages of sun.misc.Unsafe or jdk.internal.misc.Unsafe
  • java-performance
    • #3857: [java] InsufficientStringBufferDeclaration: False negatives with String constants

🚨️ API Changes

Deprecations

✨️ Merged pull requests

  • #6231: [java] New Rule: PublicMemberInNonPublicType - Andreas Dangel (@adangel)
  • #6232: [java] New Rule: UnsupportedJdkApiUsage - Thomas Leplus (@thomasleplus)
  • #6233: [core] Fix #6184: More consistent enum properties - Andreas Dangel (@adangel)
  • #6290: [cli] Improve Designer start script - Andreas Dangel (@adangel)
  • #6315: [java] Fix #5882: UnconditionalIfStatement false-negative if true/false is not literal - Marcel (@mrclmh)
  • #6362: chore: Fix typos - Zbynek Konecny (@zbynek)
  • #6366: [java] Fix #3857: InsufficientStringBufferDeclaration should consider constant Strings - Lukas Gräf (@lukasgraef)
  • #6373: [java] Support Java 26 - Andreas Dangel (@adangel)
  • #6377: [doc] chore: update last_updated - Andreas Dangel (@adangel)
  • #6384: chore: helper script check-all-contributors.sh - Andreas Dangel (@adangel)
  • #6386: [core] chore: Bump minimum Java version required for building to 21 - Andreas Dangel (@adangel)
  • #6387: [ci] publish-pull-requests: download latest build result - Andreas Dangel (@adangel)
  • #6389: chore: update javadoc deprecated tags - Andreas Dangel (@adangel)
  • #6390: chore: update javadoc experimental tags - Andreas Dangel (@adangel)
  • #6391: chore: update javadoc internal API tags - Andreas Dangel (@adangel)
  • #6392: [doc] ADR 3: Clarify javadoc tags - Andreas Dangel (@adangel)
  • #6394: [apex] Fix #6349: FieldDeclarationsShouldBeAtStart false positive with properties - Mohamed Hamed (@mdhamed238)
  • #6407: [java] Fix #3601: InvalidLogMessageFormat: False positive when final parameter is Supplier - [Lukas Gräf](https://github.com/lukasgraef) (@lukasgraef)
  • #6417: [apex] Support CPD suppression with “CPD-OFF” & “CPD-ON” - Jade (@goto-dev-null)
  • #6428: [ci] chore: run extensive integration tests under linux only - Andreas Dangel (@adangel)
  • #6429: [doc] chore: add keywords for auxclasspath in Java documentation - Andreas Dangel (@adangel)
  • #6430: [java] Fix #6364: Parse error with yield lambda - Andreas Dangel (@adangel)

📦️ Dependency updates

  • #6367: Bump PMD from 7.19.0 to 7.20.0
  • #6369: chore(deps): bump ruby/setup-ruby from 1.275.0 to 1.277.0
  • #6370: chore(deps): bump org.apache.groovy:groovy from 5.0.2 to 5.0.3
  • #6371: chore(deps-dev): bump net.bytebuddy:byte-buddy from 1.18.2 to 1.18.3
  • #6372: chore(deps): bump org.codehaus.mojo:exec-maven-plugin from 3.6.2 to 3.6.3
  • #6375: chore: Bump maven from 3.9.11 to 3.9.12
  • #6378: chore(deps): bump ruby/setup-ruby from 1.277.0 to 1.279.0
  • #6379: chore(deps): bump scalameta.version from 4.14.2 to 4.14.4
  • #6380: chore(deps): bump junit.version from 6.0.1 to 6.0.2
  • #6381: chore(deps): bump org.jsoup:jsoup from 1.21.2 to 1.22.1
  • #6382: chore(deps): bump org.checkerframework:checker-qual from 3.52.1 to 3.53.0
  • #6383: chore(deps): bump com.puppycrawl.tools:checkstyle from 12.3.0 to 13.0.0
  • #6385: chore(deps): bump uri from 1.0.3 to 1.0.4 in /docs
  • #6399: chore(deps): bump ruby/setup-ruby from 1.279.0 to 1.282.0
  • #6400: chore(deps): bump com.github.siom79.japicmp:japicmp-maven-plugin from 0.25.1 to 0.25.4
  • #6401: chore(deps): bump org.sonatype.central:central-publishing-maven-plugin from 0.9.0 to 0.10.0
  • #6403: chore(deps): bump com.google.protobuf:protobuf-java from 4.33.2 to 4.33.4
  • #6410: chore(deps): bump ruby/setup-ruby from 1.282.0 to 1.285.0
  • #6411: chore(deps): bump actions/cache from 5.0.1 to 5.0.2
  • #6412: chore(deps): bump scalameta.version from 4.14.4 to 4.14.5
  • #6413: chore(deps-dev): bump net.bytebuddy:byte-buddy from 1.18.3 to 1.18.4
  • #6414: chore(deps-dev): bump org.codehaus.mojo:versions-maven-plugin from 2.20.1 to 2.21.0
  • #6415: chore(deps-dev): bump net.bytebuddy:byte-buddy-agent from 1.18.3 to 1.18.4
  • #6419: chore(deps-dev): bump lodash from 4.17.21 to 4.17.23
  • #6421: chore(deps): bump actions/setup-java from 5.1.0 to 5.2.0
  • #6422: chore(deps): bump actions/checkout from 6.0.1 to 6.0.2
  • #6423: chore(deps): bump scalameta.version from 4.14.5 to 4.14.6
  • #6424: chore(deps-dev): bump org.assertj:assertj-core from 3.27.6 to 3.27.7
  • #6425: chore(deps): bump org.apache.groovy:groovy from 5.0.3 to 5.0.4

📈️ Stats

  • 146 commits
  • 30 closed tickets & PRs
  • Days since last release: 30