Table of Contents

25-September-2026 - 7.28.0-SNAPSHOT

The PMD team is pleased to announce PMD 7.28.0-SNAPSHOT.

This is a minor release.

🚀️ New and noteworthy

Kotlin XPath functions and type attributes

Type data is now accessible in XPath rules via new attributes and helper functions (see Kotlin XPath rule support):

  • Attributes: @TypeName, @ReturnTypeName, @AnnotationFqNames, @Modifiers, @Identifier are exposed on declaration nodes (property, function, class, parameter, catch, for-loop, delegation specifier, annotation nodes).
  • pmd-kotlin:typeIs(typeName): matches if the node’s type is typeName or a subtype.
  • pmd-kotlin:typeIsExactly(typeName): matches the exact declared type only (no subtypes).
  • pmd-kotlin:hasAnnotation(name): matches if the node has an annotation with the given simple or FQN.
  • pmd-kotlin:modifiers(): returns the modifier keywords of a declaration as a sequence.
  • pmd-kotlin:isNullable(): returns true if the node’s declared type is nullable (has ?).
  • pmd-kotlin:hasUnresolvedReference(): returns true if the node contains an unresolved reference.
  • pmd-kotlin:matchesSig(signature): matches call sites by method signature pattern (supports wildcards).

🌟️ New and Changed Rules

New Rules

  • The new Java rule OnDemandImport reports on-demand imports, also known as wildcard imports. By default, static imports from JUnit and TestNG are allowed. The allowed static and type import packages can be configured with allowStaticImportsFrom and allowTypeImportsFrom.
  • The new java rule LongLiteralEndingWithLowercaseL finds long literals ending with l. That helps to avoid confusion between numbers ending with 1 and l. Capital L should be used to define long literals.
  • The new java rule TypeNameMismatch finds types that are not defined in a .java file with the same name. Enforcing a match between source file name and type name makes it easier to find source code for given type.
  • The new Java rule CStyleArrayDeclaration finds C-style declarations of arrays (e.g. int numbers[]). That helps you use Java-style declarations (e.g. int[] numbers) consistently throughout the codebase.
  • The new Apex rule ApexUnitTestClassShouldHaveRunRelevantTestsAnnotation finds unit tests that do not use the new @IsTest(critical=true) or @IsTest(testFor='...') annotation parameters for tests. These parameters help to identify which tests should be executed during a RunRelevantTests deployment.

    Changed Rules

  • The property checkNonStaticMethods of the rule NonThreadSafeSingleton is now deprecated and no longer has any effect. Its implementation did the opposite of what the documentation described. The rule now always reports both static and non-static methods; previously it reported only static methods by default.
    This may result in additional violations being reported.
    If you want to suppress violations for non-static methods, you can use suppression via XPath, e.g.
    <property name="violationSuppressXPath" value=".[ancestor-or-self::MethodDeclaration[1][@Static = false()]]" />
    
  • The property statementOrderMatters of the rule VariableCanBeInlined is now deprecated. Setting it to false only risks false negatives, therefore, the property will be removed in PMD 8.0.0.

🐛️ Fixed Issues

  • apex-bestpractices
    • #6988: [apex] New rule: Detect usage of @IsTest(critical=true) / @IsTest(testFor=’…’) annotations (RunRelevantTests, Beta, API 66.0+)
  • html
    • #6135: [html] HtmlCpdLexer giving IndexOutOfBoundsException when script contains unescaped closing tag
  • java
    • #6926: [java] IllegalArgumentException (Mismatched list sizes) with inconsistent unresolved generic arity
  • java-bestpractices
    • #5940: [java] False positive in UnusedAssignment when assignment is in conditional statement
  • java-codestyle
    • #3124: [java] UnnecessaryLocalBeforeReturn/VariableCanBeInlined - remove property statementOrderMatters
    • #5732: [java] UnnecessaryCast false positive with package private methods
  • java-design
    • #6513: [java] SimplifyConditional: False negative when null check and instanceof are separated by other && conditions
    • #6694: [java] SimplifyBooleanReturns triggers inconsistently depending on redundant parentheses in return expression
  • java-documentation
    • #6450: [java] DanglingJavadoc: False positive on /// comments for Java < 23
  • java-errorprone
    • #1050: [java] NullAssignment: False positive inside if statement for first assignment
    • #6693: [java] CloneMethodMustImplementCloneable fires inconsistently between inline throw new and throw-via-local forms
    • #7009: [java] ReplaceJavaUtilDate is suppressed by using pattern variable
    • #7068: [java] UnusedReturnValue reports calls made on Mockito.verify(mock)
  • java-multithreading
    • #6297: [java] AvoidUsingVolatile: Update documentation
    • #6780: [java] NonThreadSafeSingleton: False negative with property checkNonStaticMethods
  • java-security
    • #7007: [java] HardCodedCryptoKey: False negative when a hard-coded key is constructed via new String(char[])
    • #7008: [java] HardCodedCryptoKey: False positive when the key comes from System.getProperty()
  • kotlin
    • #6677: [kotlin] Add XPath functions and type attributes for type-aware XPath rules

🚨️ API Changes

Experimental API

✨️ Merged pull requests

📦️ Dependency updates

📈️ Stats