A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://docs.pmd-code.org/latest/pmd_userdocs_migrating_to_pmd7.html below:

Migration Guide for PMD 7

Migrating to PMD 7 from PMD 6.x

Table of Contents Important:

This document might be incomplete and doesn’t answer all questions. In that case please reach out to us by opening a

discussion

so that we can improve this guide.

Before you update

Before updating to PMD 7, you should first update to the latest PMD 6 version 6.55.0 and try to fix all deprecation warnings.

There are a couple of deprecated things in PMD 6, you might encounter:

Approaching 7.0.0

After that, migrate to the release candidates, and fix any problems you encounter. Start with 7.0.0-rc1 via 7.0.0-rc2, 7.0.0-rc3 and 7.0.0-rc4 until you finally use 7.0.0.

You might encounter additionally the following types of problems:

The following topics describe well known migration challenges in more detail.

Use cases I’m using only built-in rules

When you are using only built-in rules, then you should check, whether you use any deprecated rule. With PMD 7 many deprecated rules are finally removed. You can see a complete list of the removed rules in the release notes for PMD 7. The release notes also mention the replacement rule, that should be used instead. For some rules, there is no replacement.

Then many rules have been changed or improved. New properties have been added to make them more versatile or properties have been removed, if they are not necessary anymore. See changed rules in the release notes for PMD 7.

All properties which accept multiple values now use a comma (,) as a delimiter. The previous default was a pipe character (|). The delimiter is not configurable anymore. If needed, the comma can be escaped with a backslash. This affects the following rules: AvoidUsingHardCodedIP, LooseCoupling, UnusedPrivateField, UnusedPrivateMethod, AtLeastOneConstructor, CommentDefaultAccessModifier, FieldNamingConventions, LinguisticNaming, UnnecessaryConstructor, CyclomaticComplexity, NcssCount, SingularField, AvoidBranchingStatementAsLastInLoop, CloseResource.

A handful of rules are new to PMD 7. You might want to check these out: new rules.

Once you have reviewed your ruleset(s), you can switch to PMD 7.

I’m using custom rules Testing

Ideally, you have written good tests already for your custom rules - see Testing your rules. This helps to identify problems early on.

The base test classes PmdRuleTst and SimpleAggregatorTst have been moved out of package net.sourceforge.pmd.testframework. You’ll need to adjust your imports.

Ruleset XML

The <rule> tag, that defines your custom rule, is required to have a language attribute now. This was always the case for XPath rules, but is now a requirement for Java rules.

XPath rules

If you have XPath based rules, the first step will be to migrate to XPath 2.0 and then to XPath 3.1. XPath 2.0 is available in PMD 6 already and can be used right away. PMD 7 will use by default XPath 3.1 and won’t support XPath 1.0 anymore. The difference between XPath 2.0 and XPath 3.1 is not big, so your XPath 2.0 can be expected to work in PMD 7 without any further changes. So the migration path is to simply migrate to XPath 2.0.

After you have migrated your XPath rules to XPath 2.0, remove the “version” property, since that has been removed with PMD 7. PMD 7 by default uses XPath 3.1. See below XPath for details.

Then change the class attribute of your rule to net.sourceforge.pmd.lang.rule.xpath.XPathRule - because the class XPathRule has been moved into subpackage net.sourceforge.pmd.lang.rule.xpath.

There are some general changes for AST nodes regarding the @Image attribute. See below General AST Changes to avoid @Image.

Additional infos:

Java rules

If you have Java based rules, and you are using rulechain, this works a bit different now. The RuleChain API has changed, see [core] Simplify the rulechain (#2490) for the full details. But in short, you don’t call addRuleChainVisit(...) in the rule’s constructor anymore. Instead, you override the method buildTargetSelector:

    protected RuleTargetSelector buildTargetSelector() {
        return RuleTargetSelector.forTypes(ASTVariableId.class);
    }
Java AST changes

The API to navigate the AST also changed significantly:

Additionally, if you have created rules for Java - regardless whether it is a XPath based rule or a Java based rule - you might need to adjust your queries or visitor methods. The Java AST has been refactored substantially. The easiest way is to use the PMD Rule Designer to see the structure of the AST. See the section Java AST below for details.

I’ve extended PMD with a custom language…

The guides for Adding a new language with JavaCC and Adding a new CPD language have been updated.

Most notable changes are:

I’ve extended PMD with a custom feature…

In that case we can’t provide a general guide unless we know the specific custom feature. If you are having difficulties finding your way around the PMD source code and javadocs and you don’t see the aspect of PMD documented you are using, we are probably missing documentation. Please reach out to us by opening a discussion. We then can enhance the documentation and/or the PMD API.

Special topics Release downloads CLI Changes

The CLI has been revamped completely (see Release Notes: Revamped Command Line Interface).

Most notable changes:

Custom distribution packages

When creating a custom distribution which only integrates the languages you need, there are some changes to apply:

Rule tests are now using JUnit5

When you have custom rules, and you have written rule tests according to the guide Testing your rules, you might want to consider upgrading your other tests to JUnit 5. The tests in PMD 7 have been migrated to JUnit5 - including the rule tests for the built-in rules.

When executing the rule tests, you need to make sure to have JUnit5 on the classpath - which you automatically get when you depend on net.sourceforge.pmd:pmd-test. If you also have JUnit4 tests, you need to make sure to have a junit-vintage-engine as well on the test classpath, so that all tests are executed. That means, you might need to add now a dependency to JUnit4 explicitly if needed.

CPD: Reported endcolumn is now exclusive

In PMD 6, the reported position of the duplicated tokens in CPD where always including, e.g. the following described a duplication of length 4 in PMD 6: beginLine=1, endLine=1, beginColumn=1, endColumn=4 - these are the first 4 character in the first line. With PMD 7, the endColumn is now excluding. The same duplication will be reported in PMD 7 as: beginLine=1, endLine=1, beginColumn=1, endColumn=5.

The reported positions in a file follow now the usual meaning: line numbering starts from 1, begin line and end line are inclusive, begin column is inclusive and end column is exclusive. This is the usual behavior of the most common text editors and the PMD part already used that meaning in RuleViolations for a long time in PMD 6 already.

This only affects the XML report format as the others don’t provide column information.

Node API

Starting from one node in the AST, you can navigate to children or parents with the following methods. This is the “traditional” way for simple cases. For more complex cases, consider to use the new NodeStream API.

Many methods available in PMD 6 have been deprecated and removed for a slicker API with consistent naming, that also integrates tightly with the NodeStream API.

Tip: First use PMD 7.0.0-rc3, which still has these methods. These methods are marked as deprecated, so you can then start to change them. The replacement method is usually provided in the javadocs. That way you avoid being confronted with just compile errors.

Unchanged methods that work as before:

New methods:

New methods that integrate with NodeStream:

Methods removed completely:

See Node for the details.

NodeStream API

In java rule implementations, you often need to navigate the AST to find the interesting nodes. In PMD 6, this was often done by calling jjtGetChild(int) or jjtGetParent(int) and then checking the node type with instanceof. There are also helper methods available, like getFirstChildOfType(Class) or findDescendantsOfType(Class). These methods might return null and you need to check this for every level.

The new NodeStream API provides easy to use methods that follow the Java Stream API (java.util.stream).

Many complex predicates about nodes can be expressed by testing the emptiness of a node stream. E.g. the following tests if the node is a variable declarator id initialized to the value 0:

Example:

     NodeStream.of(someNode)                           // the stream here is empty if the node is null
               .filterIs(ASTVariableId.class)          // the stream here is empty if the node was not a variable id
               .followingSiblings()                    // the stream here contains only the siblings, not the original node
               .children(ASTNumericLiteral.class)
               .filter(ASTNumericLiteral::isIntLiteral)
               .filterMatching(ASTNumericLiteral::getValueAsInt, 0)
               .nonEmpty(); // If the stream is non empty here, then all the pipeline matched

See NodeStream for the details. Note: This was implemented via PR #1622 [core] NodeStream API

XPath: Migrating from 1.0 to 2.0

XPath 1.0 and 2.0 have some incompatibilities. The XPath 2.0 specification describes them precisely. Those are however mostly corner cases and XPath rules usually don’t feature any of them.

The incompatibilities that are most relevant to migrating your rules are not caused by the specification, but by the different engines we use to run XPath 1.0 and 2.0 queries. Here’s a list of known incompatibilities:

General AST Changes to avoid @Image

An abstract syntax tree should be abstract, but in the same time, should not be too abstract. One of the base interfaces for PMD’s AST for all languages is Node, which provides the methods getImage and hasImageEqualTo. However, these methods don’t necessarily make sense for all nodes in all contexts. That’s why getImage() often returns just null. Also, the name is not very describing. AST nodes should try to use more specific names, such as getValue() or getName().

For PMD 7, most languages have been adapted. And when writing XPath rules, you need to replace @Image with whatever is appropriate now (e.g. @Name). See below for details.

Apex and Visualforce

There are many usages of @Image. These will be refactored after PMD 7 is released by deprecating the attribute and providing alternatives.

See also issue Deprecate getImage/@Image #4787.

Html Java

There are still many usages of @Image which are not refactored yet. This will be done after PMD 7 is released by deprecating the attribute and providing alternatives.

See also issue Deprecate getImage/@Image #4787.

Some nodes have already the image attribute (and others) deprecated. These deprecated attributes are removed now:

JavaScript JSP Modelica PLSQL

There are many usages of @Image. These will be refactored after PMD 7 is released by deprecating the attribute and providing alternatives.

See also issue Deprecate getImage/@Image #4787.

Scala XML (and POM)

When using XPathRule, text of text nodes was exposed as @Image of normal element type nodes. Now the attribute is called @Text.

Note: In general, it is recommended to use DomXPathRule instead, which exposes text nodes as real XPath/XML text nodes which conforms to the XPath spec. There is no difference, text of text nodes can be selected using text().

Java AST

The Java grammar has been refactored substantially in order to make it easier to maintain and more correct regarding the Java Language Specification.

Here you can see the most important changes as a comparison between the PMD 6 AST (“Old AST”) and PMD 7 AST (“New AST”) and with some background info about the changes.

When in doubt, it is recommended to use the PMD Designer which can also display the AST.

Renamed classes / interfaces Annotations Annotation AST Examples Code Old AST (PMD 6) New AST (PMD 7)
@A
└─ Annotation "A"
   └─ MarkerAnnotation "A"
      └─ Name "A"
└─ Annotation "A"
   └─ ClassOrInterfaceType "A"
@A()
└─ Annotation "A"
   └─ NormalAnnotation "A"
      └─ Name "A"
└─ Annotation "A"
   ├─ ClassType "A"
   └─ AnnotationMemberList
@A(value="v")
└─ Annotation "A"
   └─ NormalAnnotation "A"
      ├─ Name "A"
      └─ MemberValuePairs
         └─ MemberValuePair "value"
            └─ MemberValue
               └─ PrimaryExpression
                  └─ PrimaryPrefix
                     └─ Literal '"v"'
└─ Annotation "A"
   ├─ ClassType "A"
   └─ AnnotationMemberList
      └─ MemberValuePair "value" [ @Shorthand = false() ]
         └─ StringLiteral '"v"'
@A("v")
└─ Annotation "A"
   └─ SingleMemberAnnotation "A"
      ├─ Name "A"
      └─ MemberValue
         └─ PrimaryExpression
            └─ PrimaryPrefix
               └─ Literal '"v"'
└─ Annotation "A"
   ├─ ClassType "A"
   └─ AnnotationMemberList
      └─ MemberValuePair "value" [ @Shorthand = true() ]
         └─ StringLiteral '"v"'
@A(value="v", on=true)
└─ Annotation "A"
   └─ NormalAnnotation "A"
      ├─ Name "A"
      └─ MemberValuePairs
         ├─ MemberValuePair "value"
         │  └─ MemberValue
         │     └─ PrimaryExpression
         │        └─ PrimaryPrefix
         │           └─ Literal '"v"'
         └─ MemberValuePair "on"
            └─ MemberValue
               └─ PrimaryExpression
                  └─ PrimaryPrefix
                     └─ Literal
                        └─ BooleanLiteral [ @True = true() ]
└─ Annotation "A"
   ├─ ClassType "A"
   └─ AnnotationMemberList
      ├─ MemberValuePair "value" [ @Shorthand = false() ]
      │  └─ StringLiteral '"v"'
      └─ MemberValuePair "on"
         └─ BooleanLiteral [ @True = true() ]
Annotation nesting Annotation nesting Examples Code Old AST (PMD 6) New AST (PMD 7) Method
@A
public void set(int x) { }
└─ ClassOrInterfaceBodyDeclaration
   ├─ Annotation "A"
   │  └─ MarkerAnnotation "A"
   │     └─ Name "A"
   └─ MethodDeclaration
      ├─ ResultType[ @Void = true ]
      ├─ ...
└─ MethodDeclaration
   ├─ ModifierList
   │  └─ Annotation "A"
   │     └─ ClassType "A"
   ├─ VoidType
   ├─ ...
Top-level type declaration
@A class C {}
└─ TypeDeclaration
   ├─ Annotation "A"
   │  └─ MarkerAnnotation "A"
   │     └─ Name "A"
   └─ ClassOrInterfaceDeclaration "C"
      └─ ClassOrInterfaceBody
└─ ClassDeclaration
    ├─ ModifierList
    │  └─ Annotation "A"
    │     └─ ClassType "A"
    └─ ClassBody
Cast expression
var x = (@A T.@B S) expr;
└─ CastExpression
   ├─ Annotation "A"
   │  └─ MarkerAnnotation "A"
   │     └─ Name "A"
   ├─ Type
   │  └─ ReferenceType
   │     └─ ClassOrInterfaceType "T.S"
   │        └─ Annotation "B"
   │           └─ MarkerAnnotation "B"
   │              └─ Name "B"
   └─ PrimaryExpression
      └─ PrimaryPrefix
         └─ Name "expr"
└─ CastExpression
   ├─ ClassType "S"
   │  ├─ ClassType "T"
   │  │  └─ Annotation "A"
   │  │     └─ ClassType "A"
   │  └─ Annotation "B"
   │     └─ ClassType "B"
   └─ VariableAccess "expr"
Cast expression with intersection
var x = (@A T & S) expr;
└─ CastExpression
   ├─ Annotation "A"
   │  └─ MarkerAnnotation "A"
   │     └─ Name "A"
   ├─ Type
   │  └─ ReferenceType
   │     └─ ClassOrInterfaceType "T"
   ├─ ReferenceType
   │  └─ ClassOrInterfaceType "S"
   └─ PrimaryExpression
      └─ PrimaryPrefix
         └─ Name "expr"
└─ CastExpression
  ├─ IntersectionType
  │  ├─ ClassType "T"
  │  │  └─ Annotation "A"
  │  │     └─ ClassType "A"
  │  └─ ClassType "S"
  └─ VariableAccess "expr"
Notice @A binds to T, not T & S Constructor call
new @A T()
└─ AllocationExpression
   ├─ Annotation "A"
   │  └─ MarkerAnnotation "A"
   │     └─ Name "A"
   ├─ ClassOrInterfaceType "T"
   └─ Arguments
└─ ConstructorCall
   ├─ ClassType "T"
   │  └─ Annotation "A"
   │     └─ ClassType "A"
   └─ ArgumentList
Array allocation
new @A int[0]
└─ AllocationExpression
   ├─ Annotation "A"
   │  └─ MarkerAnnotation "A"
   │     └─ Name "A"
   ├─ PrimitiveType "int"
   └─ ArrayDimsAndInits
      └─ Expression
         └─ PrimaryExpression
            └─ PrimaryPrefix
               └─ Literal "0"
└─ ArrayAllocation
   └─ ArrayType
      ├─ PrimitiveType "int"
      │  └─ Annotation "A"
      │     └─ ClassType "A"
      └─ ArrayDimensions
         └─ ArrayDimExpr
            └─ NumericLiteral "0"
Array type
@A int @B[] x;
└─ LocalVariableDeclaration
   ├─ Annotation "A"
   │  └─ MarkerAnnotation "A"
   │     └─ Name "A"
   ├─ Type[ @ArrayType = true() ]
   │  └─ ReferenceType
   │     ├─ PrimitiveType "int"
   │     └─ Annotation "B"
   │        └─ MarkerAnnotation "B"
   │           └─ Name "B"
   └─ VariableDeclarator
      └─ VariableDeclaratorId "x"
└─ LocalVariableDeclaration
  ├─ ModifierList
  │  └─ Annotation "A"
  │     └─ ClassType "A"
  ├─ ArrayType
  │  ├─ PrimitiveType "int"
  │  └─ ArrayDimensions
  │     └─ ArrayTypeDim
  │        └─ Annotation "B"
  │           └─ ClassType "B"
  └─ VariableDeclarator
     └─ VariableId "x"
Type parameters
<@A T, @B S extends @C Object>
└─ TypeParameters
   ├─ TypeParameter "T"
   │  └─ Annotation "A"
   │     └─ MarkerAnnotation "A"
   │        └─ Name "A"
   └─ TypeParameter "S"
      ├─ Annotation "B"
      │  └─ MarkerAnnotation "B"
      │     └─ Name "B"
      └─ TypeBound
         ├─ Annotation "C"
         │  └─ MarkerAnnotation "C"
         │     └─ Name "C"
         └─ ClassOrInterfaceType "Object"
└─ TypeParameters
   ├─ TypeParameter "T"
   │  └─ Annotation "A"
   │     └─ ClassType "A"
   └─ TypeParameter "S" [ @TypeBound = true() ]
      ├─ Annotation "B"
      │  └─ ClassType "B"
      └─ ClassType "Object"
         └─ Annotation "C"
            └─ ClassType "C"
Enum constants
enum E {
  @A E1, @B E2;
}
└─ EnumBody
  ├─ Annotation "A"
  │  └─ MarkerAnnotation "A"
  │     └─ Name "A"
  ├─ EnumConstant "E1"
  ├─ Annotation "B"
  │  └─ MarkerAnnotation "B"
  │     └─ Name "B"
  └─ EnumConstant "E2"
└─ EnumBody
   ├─ EnumConstant "E1"
   │  ├─ ModifierList
   │  │  └─ Annotation "A"
   │  │     └─ ClassType "A"
   │  └─ VariableId "E1"
   └─ EnumConstant "E2"
      ├─ ModifierList
      │  └─ Annotation "B"
      │     └─ ClassType "B"
      └─ VariableId "E2"
Types Type and ReferenceType Type and ReferenceType Examples Code Old AST (PMD 6) New AST (PMD 7)
// in the context of a variable declaration
List<String> strs;
└─ Type (1)
   └─ ReferenceType
      └─ ClassOrInterfaceType "List"
         └─ TypeArguments
            └─ TypeArgument
               └─ ReferenceType (2)
                  └─ ClassOrInterfaceType "String"
  1. Notice that there is a Type node here, since a local var can have a primitive type.
  2. In contrast, notice that there is no Type here, since only reference types are allowed as type arguments.
└─ ClassType "List"
   └─ TypeArguments
      └─ ClassType "String"
Array changes Array Examples Code Old AST (PMD 6) New AST (PMD 7)
String[][] myArray;
└─ Type[ @ArrayType = true() ]
   └─ ReferenceType
      └─ ClassOrInterfaceType[ @Array = true() ][ @ArrayDepth = 2 ] "String"
└─ ArrayType[ @ArrayDepth = 2 ]
   ├─ ClassType "String"
   └─ ArrayDimensions[ @Size = 2 ]
      ├─ ArrayTypeDim
      └─ ArrayTypeDim
String @Annotation1[] @Annotation2[] myArray;
└─ Type[ @ArrayType = true() ]
   └─ ReferenceType
      ├─ ClassOrInterfaceType[ @Array = true() ][ @ArrayDepth = 2 ] "String"
      ├─ Annotation "Annotation1"
      │  └─ MarkerAnnotation "Annotation1"
      │     └─ Name "Annotation1"
      └─ Annotation "Annotation2"
         └─ MarkerAnnotation "Annotation2"
            └─ Name "Annotation2"
└─ ArrayType[ @ArrayDepth = 2 ]
   ├─ ClassType "String"
   └─ ArrayDimensions[ @Size = 2 ]
      ├─ ArrayTypeDim
      │  └─ Annotation "Annotation1"
      │     └─ ClassType "Annotation1"
      └─ ArrayTypeDim
         └─ Annotation "Annotation2"
            └─ ClassType "Annotation2"
new int[2][];
new @Bar int[3][2];
new Foo[] { f, g };
└─ AllocationExpression
   ├─ PrimitiveType "int"
   └─ ArrayDimsAndInits[ @ArrayDepth = 2 ]
      └─ Expression
         └─ PrimaryExpression
            └─ PrimaryPrefix
               └─ Literal "2"

└─ AllocationExpression
   ├─ Annotation "Bar"
   │  └─ MarkerAnnotation "Bar"
   │     └─ Name "Bar"
   ├─ PrimitiveType "int"
   └─ ArrayDimsAndInits[ @ArrayDepth = 2 ]
      ├─ Expression
      │  └─ PrimaryExpression
      │     └─ PrimaryPrefix
      │        └─ Literal "3"
      └─ Expression
         └─ PrimaryExpression
            └─ PrimaryPrefix
               └─ Literal "2"

└─ AllocationExpression
   ├─ ClassOrInterfaceType "Foo"
   └─ ArrayDimsAndInits[ @ArrayDepth = 1 ]
      └─ ArrayInitializer
         ├─ VariableInitializer
         │  └─ Expression
         │     └─ PrimaryExpression
         │        └─ PrimaryPrefix
         │           └─ Name "f"
         └─ VariableInitializer
            └─ Expression
               └─ PrimaryExpression
                  └─ PrimaryPrefix
                     └─ Name "g"
└─ ArrayAllocation[ @ArrayDepth = 2 ]
   └─ ArrayType[ @ArrayDepth = 2 ]
      ├─ PrimitiveType "int"
      └─ ArrayDimensions[ @Size = 2]
         ├─ ArrayDimExpr
         │  └─ NumericLiteral "2"
         └─ ArrayTypeDim

└─ ArrayAllocation[ @ArrayDepth = 2 ]
   └─ ArrayType[ @Array Depth = 2 ]
      ├─ PrimitiveType "int"
      │  └─ Annotation "Bar"
      │     └─ ClassType "Bar"
      └─ ArrayDimensions[ @Size = 2 ]
         ├─ ArrayDimExpr
         │  └─ NumericLiteral "3"
         └─ ArrayDimExpr
            └─ NumericLiteral "2"

└─ ArrayAllocation[ @ArrayDepth = 1 ]
   └─ ArrayType[ @ArrayDepth = 1 ]
   │  ├─ ClassType "Foo"
   │  └─ ArrayDimensions[ @Size = 1 ]
   │     └─ ArrayTypeDim
   └─ ArrayInitializer[ @Length = 2 ]
      ├─ VariableAccess "f"
      └─ VariableAccess "g"
ClassType nesting ClassType Examples Code Old AST (PMD 6) New AST (PMD 7)
Map.Entry<K,V>
└─ ClassOrInterfaceType "Map.Entry"
   └─ TypeArguments
      ├─ TypeArgument
      │  └─ ReferenceType
      │     └─ ClassOrInterfaceType "K"
      └─ TypeArgument
         └─ ReferenceType
            └─ ClassOrInterfaceType "V"
└─ ClassType "Entry"
   ├─ ClassType "Map"
   └─ TypeArguments[ @Size = 2 ]
      ├─ ClassType "K"
      └─ ClassType "V"
First<K>.Second.Third<V>
└─ ClassOrInterfaceType "First.Second.Third"
   ├─ TypeArguments
   │  └─ TypeArgument
   │     └─ ReferenceType
   │        └─ ClassOrInterfaceType "K"
   └─ TypeArguments
      └─ TypeArgument
         └─ ReferenceType
            └─ ClassOrInterfaceType "V"
└─ ClassType "Third"
   ├─  ClassType "Second"
   │   └─ ClassType "First"
   │      └─ TypeArguments[ @Size = 1]
   │         └─ ClassType "K"
   └─ TypeArguments[ @Size = 1 ]
      └─ ClassType "V"
TypeArgument and WildcardType TypeArgument and WildcardType Examples Code Old AST (PMD 6) New AST (PMD 7)
Entry<String, ? extends Node>
└─ ClassOrInterfaceType "Entry"
   └─ TypeArguments
      ├─ TypeArgument
      │  └─ ReferenceType
      │     └─ ClassOrInterfaceType "String"
      └─ TypeArgument[ @Wildcard = true() ]
         └─ WildcardBounds[ @UpperBound = true() ]
            └─ ReferenceType
               └─ ClassOrInterfaceType "Node"
└─ ClassType "Entry"
   └─ TypeArguments[ @Size = 2 ]
      ├─ ClassType "String"
      └─ WildcardType[ @UpperBound = true() ]
         └─ ClassType "Node"
List<?>
└─ ClassOrInterfaceType "List"
   └─ TypeArguments
      └─ TypeArgument[ @Wildcard = true() ]
└─ ClassType "List"
   └─ TypeArguments[ @Size = 1 ]
      └─ WildcardType[ @UpperBound = true() ]
Declarations Import and Package declarations Import and Package declarations Examples Code Old AST (PMD 6) New AST (PMD 7)
import java.util.ArrayList;
import static java.util.Comparator.reverseOrder;
import java.util.*;
├─ ImportDeclaration
│  └─ Name "java.util.ArrayList"
├─ ImportDeclaration[ @Static=true() ]
│  └─ Name "java.util.Comparator.reverseOrder"
└─ ImportDeclaration[ @ImportOnDemand = true() ]
   └─ Name "java.util"
├─ ImportDeclaration "java.util.ArrayList"
├─ ImportDeclaration[ @Static = true() ] "java.util.Comparator.reverseOrder"
└─ ImportDeclaration[ @ImportOnDemand = true() ] "java.util"
package com.example.tool;
└─ PackageDeclaration
   └─ Name "com.example.tool"
└─ PackageDeclaration "com.example.tool"
   └─ ModifierList
Modifier lists Modifier lists Examples Code Old AST (PMD 6) New AST (PMD 7) Method
@A
public void set(final int x, int y) { }
└─ ClassOrInterfaceBodyDeclaration
   ├─ Annotation "A"
   │  └─ MarkerAnnotation "A"
   │     └─ Name "A"
   └─ MethodDeclaration[ @Public = true() ] "set"
      ├─ ResultType[ @Void = true() ]
      └─ MethodDeclarator
         └─ FormalParameters[ @Size = 2 ]
            ├─ FormalParameter[ @Final = true() ]
            │  ├─ Type
            │  │  └─ PrimitiveType "int"
            │  └─ VariableDeclaratorId "x"
            └─ FormalParameter[ @Final = false() ]
               ├─ Type
               │  └─ PrimitiveType "int"
               └─ VariableDeclaratorId "y"
└─ MethodDeclaration[ pmd-java:modifiers() = 'public' ] "set"
   ├─ ModifierList
   │  └─ Annotation "A"
   │     └─ ClassType "A"
   ├─ VoidType
   └─ FormalParameters
      ├─ FormalParameter[ pmd-java:modifiers() = 'final' ]
      │  ├─ ModifierList
      │  └─ VariableId "x"
      └─ FormalParameter[ pmd-java:modifiers() = () ]
         ├─ ModifierList
         └─ VariableId "y"
Top-level type declaration
public @A class C {}
└─ TypeDeclaration
   ├─ Annotation "A"
   │  └─ MarkerAnnotation "A"
   │     └─ Name "A"
   └─ ClassOrInterfaceDeclaration[ @Public = true() ] "C"
      └─ ClassOrInterfaceBody
└─ ClassDeclaration[ pmd-java:modifiers() = 'public' ] "C"
   ├─ ModifierList
   │  └─ Annotation "A"
   │     └─ ClassType "A"
   └─ ClassBody
Flattened body declarations Flattened body declarations Examples Code Old AST (PMD 6) New AST (PMD 7)
public class Flat {
    private int f;
}
└─ CompilationUnit
   └─ TypeDeclaration
      └─ ClassOrInterfaceDeclaration "Flat"
         └─ ClassOrInterfaceBody
            └─ ClassOrInterfaceBodyDeclaration
               └─ FieldDeclaration
                  ├─ Type
                  │  └─ PrimitiveType "int"
                  └─ VariableDeclarator
                     └─ VariableDeclaratorId "f"
└─ CompilationUnit
   └─ ClassDeclaration "Flat"
      ├─ ModifierList
      └─ ClassBody
         └─ FieldDeclaration
            ├─ ModifierList
            ├─ PrimitiveType "int"
            └─ VariableDeclarator
               └─ VariableId "f"
public @interface FlatAnnotation {
    String value() default "";
}
└─ CompilationUnit
   └─ TypeDeclaration
      └─ AnnotationTypeDeclaration "FlatAnnotation"
         └─ AnnotationTypeBody
            └─ AnnotationTypeMemberDeclaration
               └─ AnnotationMethodDeclaration "value"
                  ├─ Type
                  │  └─ ReferenceType
                  │     └─ ClassOrInterfaceType "String"
                  └─ DefaultValue
                     └─ MemberValue
                        └─ PrimaryExpression
                           └─ PrimaryPrefix
                              └─ Literal "\"\""
└─ CompilationUnit
   └─ AnnotationTypeDeclaration "FlatAnnotation"
      ├─ ModifierList
      └─ AnnotationTypeBody
         └─ MethodDeclaration "value"
            ├─ ModifierList
            ├─ ClassType "String"
            ├─ FormalParameters
            └─ DefaultValue
               └─ StringLiteral "\"\""
Module declarations Module declarations Examples Code Old AST (PMD 6) New AST (PMD 7)
open module com.example.foo {
    requires com.example.foo.http;
    requires java.logging;
    requires transitive com.example.foo.network;

    exports com.example.foo.bar;
    exports com.example.foo.internal to com.example.foo.probe;

    uses com.example.foo.spi.Intf;

    provides com.example.foo.spi.Intf with com.example.foo.Impl;
}
└─ CompilationUnit
   └─ ModuleDeclaration[ @Image = 'com.example.foo' ][ @Open = true() ]
      ├─ ModuleDirective[ @Type = 'REQUIRES' ]
      │  └─ ModuleName[ @Image = 'com.example.foo.http' ]
      ├─ ModuleDirective[ @Type = 'REQUIRES' ]
      │  └─ ModuleName[ @Image = 'java.logging' ]
      ├─ ModuleDirective[ @Type = 'REQUIRES' ][ @RequiresModifier = 'TRANSITIVE' ]
      │  └─ ModuleName[ @Image = 'com.example.foo.network' ]
      ├─ ModuleDirective[ @Type = 'EXPORTS' ]
      │  └─ Name[ @Image = 'com.example.foo.bar' ]
      ├─ ModuleDirective[ @Type = 'EXPORTS' ]
      │  ├─ Name[ @Image = 'com.example.foo.internal' ]
      │  └─ ModuleName[ @Image = 'com.example.foo.probe' ]
      ├─ ModuleDirective[ @Type = 'USES' ]
      │  └─ Name[ @Image = 'com.example.foo.spi.Intf' ]
      └─ ModuleDirective[ @Type = 'PROVIDES' ]
         ├─ Name[ @Image = 'com.example.foo.spi.Intf' ]
         └─ Name[ @Image = 'com.example.foo.Impl' ]
└─ CompilationUnit
   └─ ModuleDeclaration[ @Name = 'com.example.foo' ][ @Open = true() ]
      ├─ ModuleName[ @Name = 'com.example.foo' ]
      ├─ ModuleRequiresDirective
      │  └─ ModuleName[ @Name = 'com.example.foo.http' ]
      ├─ ModuleRequiresDirective
      │  └─ ModuleName[ @Name = 'java.logging' ]
      ├─ ModuleRequiresDirective[ @Transitive = true ]
      │  └─ ModuleName[ @Name = 'com.example.foo.network' ]
      ├─ ModuleExportsDirective[ @PackageName = 'com.example.foo.bar' ]
      ├─ ModuleExportsDirective[ @PackageName = 'com.example.foo.internal' ]
      │  └─ ModuleName [ @Name = 'com.example.foo.probe' ]
      ├─ ModuleUsesDirective
      │  └─ ClassType[ pmd-java:typeIs("com.example.foo.spi.Intf") ]
      └─ ModuleProvidesDirective
         ├─ ClassType[ pmd-java:typeIs("com.example.foo.spi.Intf") ]
         └─ ClassType[ pmd-java:typeIs("com.example.foo.Impl") ]
Anonymous class declarations Anonymous class declarations Examples Code Old AST (PMD 6) New AST (PMD 7)
Object anonymous = new Object() {  };
└─ LocalVariableDeclaration
   ├─ Type
   │  └─ ReferenceType
   │     └─ ClassOrInterfaceType[ @Image = 'Object' ]
   └─ VariableDeclarator
      ├─ VariableDeclaratorId "anonymous"
      └─ VariableInitializer
         └─ Expression
            └─ PrimaryExpression
               └─ PrimaryPrefix
                  └─ AllocationExpression
                     ├─ ClassOrInterfaceType[ @AnonymousClass = true() ][ @Image = 'Object' ]
                     ├─ Arguments
                     └─ ClassOrInterfaceBody
└─ LocalVariableDeclaration
   ├─ ModifierList
   ├─ ClassType[ @SimpleName = 'Object' ]
   └─ VariableDeclarator
      ├─ VariableId[ @Name = 'anonymous' ]
      └─ ConstructorCall
         ├─ ClassType[ @SimpleName = 'Object' ]
         ├─ ArgumentList
         └─ AnonymousClassDeclaration
            ├─ ModifierList
            └─ ClassBody
Method and Constructor declarations Method grammar simplification Method grammar Examples Code Old AST (PMD 6) New AST (PMD 7)
public class Sample {
    public Sample(int arg) throws Exception {
        super();
        greet(arg);
    }
    public void greet(int arg) throws Exception {
        System.out.println("Hello");
    }
}
└─ ClassOrInterfaceBody
   ├─ ClassOrInterfaceBodyDeclaration
   │  └─ ConstructorDeclaration[ @Image = 'Sample' ]
   │     ├─ FormalParameters
   │     │  └─ FormalParameter
   │     │     ├─ ...
   │     ├─ NameList
   │     │  └─ Name[ @Image = 'Exception' ]
   │     ├─ ExplicitConstructorInvocation
   │     │  └─ Arguments
   │     └─ BlockStatement
   │        └─ Statement
   │           └─ ...
   └─ ClassOrInterfaceBodyDeclaration
      └─ MethodDeclaration[ @Name = 'greet' ]
         ├─ ResultType
         ├─ MethodDeclarator[ @Image = 'greet' ]
         │  └─ FormalParameters
         │     └─ FormalParameter
         │        ├─ ...
         ├─ NameList
         │  └─ Name[ @Image = 'Exception' ]
         └─ Block
            └─ BlockStatement
               └─ Statement
                  └─ ...
└─ ClassBody
   ├─ ConstructorDeclaration[ @Name = 'Sample' ]
   │  ├─ ModifierList
   │  ├─ FormalParameters
   │  │  └─ FormalParameter
   │  │     ├─ ...
   │  ├─ ThrowsList
   │  │  └─ ClassType[ @SimpleName = 'Exception' ]
   │  └─ Block
   │     ├─ ExplicitConstructorInvocation
   │     │  └─ ArgumentList
   │     └─ ExpressionStatement
   │        └─ ...
   └─ MethodDeclaration[ @Name = 'greet' ]
      ├─ ModifierList
      ├─ VoidType
      ├─ FormalParameters
      │  └─ FormalParameter
      │     ├─ ...
      ├─ ThrowsList
      │  └─ ClassType[ @SimpleName = 'Exception' ]
      └─ Block
         └─ ExpressionStatement
            └─ ...
public @interface MyAnnotation {
    int value() default 1;
}
└─ AnnotationTypeDeclaration[ @SimpleName = 'MyAnnotation' ]
   └─ AnnotationTypeBody
      └─ AnnotationTypeMemberDeclaration
         └─ AnnotationMethodDeclaration[ @Image = 'value' ]
            ├─ Type ...
            └─ DefaultValue ...
└─ AnnotationTypeDeclaration[ @SimpleName = 'MyAnnotation' ]
   ├─ ModifierList
   └─ AnnotationTypeBody
      └─ MethodDeclaration[ @Name = 'value' ]
         ├─ ModifierList
         ├─ PrimitiveType
         ├─ FormalParameters
         └─ DefaultValue ...
Formal parameters Formal parameters Examples Code Old AST (PMD 6) New AST (PMD 7)
try {

} catch (@A IOException | IllegalArgumentException e) {

}
└─ TryStatement
   ├─ Block
   └─ CatchStatement
      ├─ FormalParameter
      │  ├─ Annotation[ @AnnotationName = 'A' ]
      │  │  └─ MarkerAnnotation[ @AnnotationName = 'A' ]
      │  │     └─ Name[ @Image = 'A' ]
      │  ├─ Type
      │  │  └─ ReferenceType
      │  │     └─ ClassOrInterfaceType[ @Image = 'IOException' ]
      │  ├─ Type
      │  │  └─ ReferenceType
      │  │     └─ ClassOrInterfaceType[ @Image = 'IllegalArgumentException' ]
      │  └─ VariableDeclaratorId[ @Name = 'e' ]
      └─ Block
└─ TryStatement
   ├─ Block
   └─ CatchClause
      ├─ CatchParameter
      │  ├─ ModifierList
      │  │  └─ Annotation[ @SimpleName = 'A' ]
      │  │     └─ ClassType[ @SimpleName = 'A' ]
      │  ├─ UnionType
      │  │  ├─ ClassType[ @SimpleName = 'IOException' ]
      │  │  └─ ClassType[ @SimpleName = 'IllegalArgumentException' ]
      │  └─ VariableId[ @Name = 'e' ]
      └─ Block
(a, b) -> {};
c -> {};
(@A var d) -> {};
(@A int e) -> {};
└─ StatementExpression
   └─ PrimaryExpression
      └─ PrimaryPrefix
         └─ LambdaExpression
            ├─ VariableDeclaratorId[ @Name = 'a' ]
            ├─ VariableDeclaratorId[ @Name = 'b' ]
            └─ Block

└─ StatementExpression
   └─ PrimaryExpression
      └─ PrimaryPrefix
         └─ LambdaExpression
            ├─ VariableDeclaratorId[ @Name = 'c' ]
            └─ Block

└─ StatementExpression
   └─ PrimaryExpression
      └─ PrimaryPrefix
         └─ LambdaExpression
            ├─ FormalParameters
            │  └─ FormalParameter
            │     ├─ Annotation[ @AnnotationName = 'A' ]
            │     │  └─ MarkerAnnotation[ @AnnotationName = 'A' ]
            │     │     └─ Name[ @Image = 'A' ]
            │     └─ VariableDeclaratorId[ @Name = 'd' ]
            └─ Block

└─ StatementExpression
   └─ PrimaryExpression
      └─ PrimaryPrefix
         └─ LambdaExpression
            ├─ FormalParameters
            │  └─ FormalParameter
            │     ├─ Annotation[ @AnnotationName = 'A' ]
            │     │  └─ MarkerAnnotation[ @AnnotationName = 'A' ]
            │     │     └─ Name[ @Image = 'A' ]
            │     ├─ Type
            │     │  └─ PrimitiveType[ @Image = 'int' ]
            │     └─ VariableDeclaratorId[ @Name = 'e' ]
            └─ Block
└─ ExpressionStatement
   └─ LambdaExpression
      ├─ LambdaParameterList
      │  ├─ LambdaParameter
      │  │  ├─ ModifierList
      │  │  └─ VariableId[ @Name = 'a' ]
      │  └─ LambdaParameter
      │     ├─ ModifierList
      │     └─ VariableId[ @Name = 'b' ]
      └─ Block

└─ ExpressionStatement
   └─ LambdaExpression
      ├─ LambdaParameterList
      │  └─ LambdaParameter
      │     ├─ ModifierList
      │     └─ VariableId[ @Name = 'c' ]
      └─ Block

└─ ExpressionStatement
   └─ LambdaExpression
      ├─ LambdaParameterList
      │  └─ LambdaParameter
      │     ├─ ModifierList
      │     │  └─ Annotation[ @SimpleName = 'A' ]
      │     │     └─ ClassType[ @SimpleName = 'A' ]
      │     └─ VariableId[ @Name = 'd' ]
      └─ Block

└─ ExpressionStatement
   └─ LambdaExpression
      ├─ LambdaParameterList
      │  └─ LambdaParameter
      │     ├─ ModifierList
      │     │  └─ Annotation[ @SimpleName = 'A' ]
      │     │     └─ ClassType[ @SimpleName = 'A' ]
      │     ├─ PrimitiveType[ @Kind = 'int' ]
      │     └─ VariableId[ @Name = 'e' ]
      └─ Block
New node for explicit receiver parameter explicit receiver parameter Examples Code Old AST (PMD 6) New AST (PMD 7)
void myMethod(@A Foo this, Foo other) {}
└─ FormalParameters (1)
   ├─ FormalParameter[ @ExplicitReceiverParameter = true() ]
   │  ├─ Annotation "A"
   │  │  └─ MarkerAnnotation "A"
   │  │     └─ Name "A"
   │  ├─ Type
   │  │  └─ ReferenceType
   │  │     └─ ClassOrInterfaceType "Foo"
   │  └─ VariableDeclaratorId[ @ExplicitReceiverParameter = true() ] "this"
   └─ FormalParameter
      ├─ Type
      │  └─ ReferenceType
      │     └─ ClassOrInterfaceType "Foo"
      └─ VariableDeclaratorId "other"
└─ FormalParameters (1)
   ├─ ReceiverParameter
   │  └─ ClassType "Foo"
   │     └─ Annotation "A"
   │        └─ ClassType "A"
   └─ FormalParameter
      ├─ ModifierList
      ├─ ClassType "Foo"
      └─ VariableId "other"
Varargs Varargs Examples Code Old AST (PMD 6) New AST (PMD 7)
void myMethod(int... is) {}
└─ FormalParameter[ @Varargs = true() ]
   ├─ Type
   │  └─ PrimitiveType "int"
   └─ VariableDeclaratorId "is"
└─ FormalParameter[ @Varargs = true() ]
   ├─ ModifierList
   ├─ ArrayType
   │  ├─ PrimitiveType "int"
   │  └─ ArrayDimensions
   │     └─ ArrayTypeDim[ @Varargs = true() ]
   └─ VariableId "is"
void myMethod(int @A ... is) {}
└─ FormalParameter[ @Varargs = true() ]
   ├─ Type
   │  └─ PrimitiveType "int"
   ├─ Annotation "A"
   │  └─ MarkerAnnotation "A"
   │     └─ Name "A"
   └─ VariableDeclaratorId "is"
└─ FormalParameter[ @Varargs = true() ]
   ├─ ModifierList
   ├─ ArrayType
   │  ├─ PrimitiveType "int"
   │  └─ ArrayDimensions
   │     └─ ArrayTypeDim[ @Varargs = true() ]
   │        └─ Annotation "A"
   │           └─ ClassType "A"
   └─ VariableId "is"
void myMethod(int[]... is) {}
└─ FormalParameter[ @Varargs = true() ]
   ├─ Type[ @ArrayType = true() ]
   │  └─ ReferenceType
   │     └─ PrimitiveType "int"
   └─ VariableDeclaratorId "is"
└─ FormalParameter[ @Varargs = true() ]
   ├─ ModifierList
   ├─ ArrayType (2)
   │  ├─ PrimitiveType "int"
   │  └─ ArrayDimensions (2)
   │     ├─ ArrayTypeDim
   │     └─ ArrayTypeDim[ @Varargs = true() ]
   └─ VariableId "is"
Add void type node to replace ResultType Void Type Examples Code Old AST (PMD 6) New AST (PMD 7)
void foo();
└─ MethodDeclaration "foo"
   ├─ ResultType[ @Void = true() ]
   └─ MethodDeclarator
      └─ FormalParameters
└─ MethodDeclaration "foo"
   ├─ ModifierList
   ├─ VoidType
   └─ FormalParameters
int foo();
└─ MethodDeclaration "foo"
   ├─ ResultType[ @Void = false() ]
   │  └─ Type
   │     └─ PrimitiveType "int"
   └─ MethodDeclarator
      └─ FormalParameters
└─ MethodDeclaration "foo"
   ├─ ModifierList
   ├─ PrimitiveType "int"
   └─ FormalParameters
Statements Statements are flattened Statements Examples Code Old AST (PMD 6) New AST (PMD 7)
int i;
i = 1;
└─ Block
   ├─ BlockStatement
   │  └─ LocalVariableDeclaration
   │     ├─ Type
   │     │  └─ PrimitiveType "int"
   │     └─ VariableDeclarator
   │        └─ VariableDeclaratorId "i"
   └─ BlockStatement
      └─ Statement
         └─ StatementExpression
            ├─ PrimaryExpression
            │  └─ PrimaryPrefix
            │     └─ Name "i"
            ├─ AssignmentOperator "="
            └─ Expression
               └─ PrimaryExpression
                  └─ PrimaryPrefix
                     └─ Literal "1"
└─ Block
   ├─ LocalVariableDeclaration
   │  ├─ ModifierList
   │  ├─ PrimitiveType "int"
   │  └─ VariableDeclarator
   │     └─ VariableId "i"
   └─ ExpressionStatement
      └─ AssignmentExpression "="
         ├─ VariableAccess "i"
         └─ NumericLiteral "1"
New node for For-each statements For-each statement Examples Code Old AST (PMD 6) New AST (PMD 7)
for (String s : List.of("a", "b")) { }
└─ BlockStatement
   └─ Statement
      └─ ForStatement[ @Foreach = true() ]
         ├─ LocalVariableDeclaration
         │  ├─ Type
         │  │  └─ ReferenceType
         │  │     └─ ClassOrInterfaceType "String"
         │  └─ VariableDeclarator
         │     └─ VariableDeclaratorId "s"
         ├─ Expression
         │  └─ PrimaryExpression
         │     ├─ PrimaryPrefix
         │     │  └─ Name "List.of"
         │     └─ PrimarySuffix
         │        └─ Arguments (2)
         │           └─ ArgumentList (2)
         │              ├─ Expression
         │              │  └─ PrimaryExpression
         │              │     └─ PrimaryPrefix
         │              │        └─ Literal[ @StringLiteral = true() ][ @Image = '"a"' ]
         │              └─ Expression
         │                 └─ PrimaryExpression
         │                    └─ PrimaryPrefix
         │                       └─ Literal[ @StringLiteral = true() ][ @Image = '"b"' ]
         └─ Statement
            └─ Block
└─ Block
   └─ ForeachStatement
      ├─ LocalVariableDeclaration
      │  ├─ ModifierList
      │  ├─ ClassType "String"
      │  └─ VariableDeclarator "s"
      │     └─ VariableId "s"
      ├─ MethodCall "of"
      │  ├─ TypeExpression
      │  │  └─ ClassType "List"
      │  └─ ArgumentList (2)
      │     ├─ StringLiteral[ @Image = '"a"' ]
      │     └─ StringLiteral[ @Image = '"b"' ]
      └─ Block
New nodes for ExpressionStatement, LocalClassStatement ExpressionStatement, LocalClassStatement Examples Code Old AST (PMD 6) New AST (PMD 7)
i++;
class LocalClass {}
└─ Block
   ├─ BlockStatement
   │  └─ Statement
   │     └─ StatementExpression
   │        └─ PostfixExpression "++"
   │           └─ PrimaryExpression
   │              └─ PrimaryPrefix
   │                 └─ Name "i"
   └─ BlockStatement
      └─ ClassOrInterfaceDeclaration[ @Local = true() ] "LocalClass"
         └─ ClassOrInterfaceBody
└─ Block
   ├─ ExpressionStatement
   │  └─ UnaryExpression "++"
   │     └─ VariableAccess "i"
   └─ LocalClassStatement
      └─ ClassDeclaration "LocalClass"
         ├─ ModifierList
         └─ ClassBody
Improve try-with-resources grammar Try-With-Resources Examples Code Old AST (PMD 6) New AST (PMD 7)
try (InputStream in = new FileInputStream(); OutputStream out = new FileOutputStream();) { }
└─ TryStatement
   └─ ResourceSpecification
      └─ Resources
         ├─ Resource
         │  ├─ Type
         │  │  └─ ReferenceType
         │  │     └─ ClassOrInterfaceType "InputStream"
         │  ├─ VariableDeclaratorId "in"
         │  └─ Expression
         │     └─ ...
         └─ Resource
            ├─ Type
            │  └─ ReferenceType
            │     └─ ClassOrInterfaceType "OutputStream"
            ├─ VariableDeclaratorId "out"
            └─ Expression
               └─ ...
└─ TryStatement
   └─ ResourceList[ @TrailingSemiColon = true() ] (2)
      ├─ Resource[ @ConciseResource = false() ] "in"
      │  └─ LocalVariableDeclaration
      │     ├─ ModifierList
      │     ├─ ClassType "InputStream"
      │     └─ VariableDeclarator
      │        ├─ VariableId "in"
      │        └─ ConstructorCall
      │           ├─ ClassType "FileInputStream"
      │           └─ ArgumentList (0)
      └─ Resource[ @ConciseResource = false() ] "out"
         └─ LocalVariableDeclaration
            ├─ ModifierList
            ├─ ClassType "OutputStream"
            └─ VariableDeclarator
               ├─ VariableId "out"
               └─ ConstructorCall
                  ├─ ClassType "FileOutputStream"
                  └─ ArgumentList (0)
InputStream in = new FileInputStream();
try (in) {}
└─ TryStatement
   └─ ResourceSpecification
      └─ Resources
         └─ Resource "in"
            └─ Name "in"
└─ TryStatement
   └─ ResourceList[ @TrailingSemiColon = false() ] (1)
      └─ Resource[ @ConciseResource = true() ] "in"
         └─ VariableAccess "in"
Expressions New nodes for different literals types Literals Examples Code Old AST (PMD 6) New AST (PMD 7)
char c = 'c';
boolean b = true;
int i = 1;
double d = 1.0;
String s = "s";
Object n = null;
└─ Literal[ @CharLiteral = true() ] "'c'"
└─ Literal
   └─ BooleanLiteral[ @True = true() ]
└─ Literal[ @IntLiteral = true() ] "1"
└─ Literal[ @DoubleLiteral = true() ] "1.0"
└─ Literal[ @StringLiteral = true() ] "\"s\""
└─ Literal
   └─ NullLiteral
└─ CharLiteral "'c'"
└─ BooleanLiteral[ @True = true() ]
└─ NumericLiteral[ @IntLiteral = true() ] "1"
└─ NumericLiteral[ @DoubleLiteral = true() ] "1.0"
└─ StringLiteral "\"s\""
└─ NullLiteral
Method calls, constructor calls, array allocations Method calls, constructor calls, array allocations Examples Code Old AST (PMD 6) New AST (PMD 7)
o.myMethod("a");
new Object("b");
new int[10];
new int[] { 1, 2, 3 };
└─ PrimaryExpression
   ├─ PrimaryPrefix
   │  └─ Name "o.myMethod"
   └─ PrimarySuffix
      └─ Arguments
         └─ ArgumentList (1)
            └─ Expression
               └─ PrimaryExpression
                  └─ PrimaryPrefix
                     └─ Literal "\"a\""

└─ PrimaryExpression
   └─ PrimaryPrefix
      └─ AllocationExpression
         ├─ ClassOrInterfaceType "Object"
         └─ Arguments
            └─ ArgumentList
               └─ Expression
                  └─ PrimaryExpression
                     └─ PrimaryPrefix
                        └─ Literal "\"b\""

└─ PrimaryExpression
   └─ PrimaryPrefix
      └─ AllocationExpression
         ├─ PrimitiveType "int"
         └─ ArrayDimsAndInits
            └─ Expression
               └─ PrimaryExpression
                  └─ PrimaryPrefix
                     └─ Literal "10"

└─ PrimaryPrefix
   └─ AllocationExpression
      ├─ PrimitiveType "int"
      └─ ArrayDimsAndInits
         └─ ArrayInitializer
            ├─ VariableInitializer
            │  └─ Expression
            │     └─ PrimaryExpression
            │        └─ PrimaryPrefix
            │           └─ Literal "1"
            ├─ VariableInitializer
            │  └─ Expression
            │     └─ PrimaryExpression
            │        └─ PrimaryPrefix
            │           └─ Literal "2"
            └─ VariableInitializer
               └─ Expression
                  └─ PrimaryExpression
                     └─ PrimaryPrefix
                        └─ Literal "3"
└─ MethodCall "myMethod"
   ├─ VariableAccess "o"
   └─ ArgumentList (1)
      └─ StringLiteral "\"a\""

└─ ConstructorCall
   ├─ ClassType "Object"
   └─ ArgumentList (1)
      └─ StringLiteral "\"b\""

└─ ArrayAllocation[ @ArrayDepth = 1 ]
   └─ ArrayType
      ├─ PrimitiveType "int"
      └─ ArrayDimensions (1)
         └─ ArrayDimExpr
            └─ NumericLiteral "10"

└─ ArrayAllocation[ @ArrayDepth = 1 ]
   ├─ ArrayType
   │  ├─ PrimitiveType "int"
   │  └─ ArrayDimensions (1)
   │     └─ ArrayTypeDim
   └─ ArrayInitializer[ @Length = 3 ]
      ├─ NumericLiteral "1"
      ├─ NumericLiteral "2"
      └─ NumericLiteral "3"
Method call chains are left-recursive Method call chain Examples Code Old AST (PMD 6) New AST (PMD 7)
new Foo().bar.foo(1);
└─ StatementExpression
   └─ PrimaryExpression
      ├─ PrimaryPrefix
      │  └─ AllocationExpression
      │     ├─ ClassOrInterfaceType "Foo"
      │     └─ Arguments (0)
      ├─ PrimarySuffix "bar"
      ├─ PrimarySuffix "foo"
      └─ PrimarySuffix[ @Arguments = true() ]
         └─ Arguments (1)
            └─ ArgumentList
               └─ Expression
                  └─ PrimaryExpression
                     └─ PrimaryPrefix
                        └─ Literal "1"
└─ ExpressionStatement
   └─ MethodCall "foo"
      ├─ FieldAccess "bar"
      │  └─ ConstructorCall
      │     ├─ ClassType "Foo"
      │     └─ ArgumentList (0)
      └─ ArgumentList (1)
         └─ NumericLiteral "1"

Instead of being flat, the subexpressions are now nested within one another. The nesting follows the naturally recursive structure of expressions:

new Foo().bar.foo(1)
└───────┘   │      │ ConstructorCall
└───────────┘      │ FieldAccess
└──────────────────┘ MethodCall

This makes the AST more regular and easier to navigate. Each node contains the other nodes that are relevant to it (e.g. arguments) instead of them being spread out over several siblings. The API of all nodes has been enriched with high-level accessors to query the AST in a semantic way, without bothering with the placement details.

The amount of changes in the grammar that this change entails is enormous, but hopefully firing up the designer to inspect the new structure should give you the information you need quickly.

Note: this also affect binary expressions like ASTInfixExpression. E.g. a+b+c is not parsed as

AdditiveExpression
+ (a)
+ (b)
+ (c)

But it is now (note: AdditiveExpression is now InfixExpression)

InfixExpression
+ InfixExpression
  + (a)
  + (b)
+ (c)
Field access, array access, variable access Field access, array access, variable access Examples Code Old AST (PMD 6) New AST (PMD 7)
field = 1;
localVar = 1;
array[0] = 1;
Foo.staticField = localVar;
└─ BlockStatement
   └─ Statement
      └─ StatementExpression
         ├─ PrimaryExpression
         │  └─ PrimaryPrefix
         │     └─ Name "field"
         ├─ AssignmentOperator "="
         └─ Expression
            └─ PrimaryExpression
               └─ PrimaryPrefix
                  └─ Literal "1"

└─ BlockStatement
   └─ Statement
      └─ StatementExpression
         ├─ PrimaryExpression
         │  └─ PrimaryPrefix
         │     └─ Name "localVar"
         ├─ AssignmentOperator "="
         └─ Expression
            └─ PrimaryExpression
               └─ PrimaryPrefix
                  └─ Literal "1"

└─ BlockStatement
   └─ Statement
      └─ StatementExpression
         ├─ PrimaryExpression
         │  ├─ PrimaryPrefix
         │  │  └─ Name "array"
         │  └─ PrimarySuffix[ @ArrayDereference = true() ]
         │     └─ Expression
         │        └─ PrimaryExpression
         │           └─ PrimaryPrefix
         │              └─ Literal "0"
         ├─ AssignmentOperator "="
         └─ Expression
            └─ PrimaryExpression
               └─ PrimaryPrefix
                  └─ Literal "1"

└─ BlockStatement
   └─ Statement
      └─ StatementExpression
         ├─ PrimaryExpression
         │  └─ PrimaryPrefix
         │     └─ Name "Foo.staticField"
         ├─ AssignmentOperator "="
         └─ Expression
            └─ PrimaryExpression
               └─ PrimaryPrefix
                  └─ Name "localVar"
└─ ExpressionStatement
   └─ AssignmentExpression "="
      ├─ VariableAccess "field"
      └─ NumericLiteral "1"

└─ ExpressionStatement
   └─ AssignmentExpression "="
      ├─ VariableAccess "localVar"
      └─ NumericLiteral "1"

└─ ExpressionStatement
   └─ AssignmentExpression "="
      ├─ ArrayAccess[ @AccessType = "WRITE" ]
      │  ├─ VariableAccess "array"
      │  └─ NumericLiteral "0"
      └─ NumericLiteral "1"

└─ ExpressionStatement
   └─ AssignmentExpression "="
      ├─ FieldAccess[ @AccessType = "WRITE" ] "staticField"
      │  └─ TypeExpression
      │     └─ ClassType "Foo"
      └─ VariableAccess[ @AccessType = "READ" ] "localVar"
Explicit nodes for this/super expressions this/super expressions Examples Code Old AST (PMD 6) New AST (PMD 7)
this.field = 1;
super.field = 1;

this.method();
super.method();
└─ BlockStatement
   └─ Statement
      └─ StatementExpression
         ├─ PrimaryExpression
         │  ├─ PrimaryPrefix[ @ThisModifier = true() ]
         │  └─ PrimarySuffix "field"
         ├─ AssignmentOperator "="
         └─ Expression
            └─ PrimaryExpression
               └─ PrimaryPrefix
                  └─ Literal "1"

└─ BlockStatement
   └─ Statement
      └─ StatementExpression
         ├─ PrimaryExpression
         │  ├─ PrimaryPrefix[ @SuperModifier = true() ]
         │  └─ PrimarySuffix "field"
         ├─ AssignmentOperator "="
         └─ Expression
            └─ PrimaryExpression
               └─ PrimaryPrefix
                  └─ Literal "1"

└─ BlockStatement
   └─ Statement
      └─ StatementExpression
         └─ PrimaryExpression
            ├─ PrimaryPrefix[ @ThisModifier = true() ]
            ├─ PrimarySuffix "method"
            └─ PrimarySuffix[ @Arguments = true() ]
               └─ Arguments (0)

└─ BlockStatement
   └─ Statement
      └─ StatementExpression
         └─ PrimaryExpression
            ├─ PrimaryPrefix[ @SuperModifier = true() ]
            ├─ PrimarySuffix "method"
            └─ PrimarySuffix[ @Arguments = true() ]
               └─ Arguments (0)
└─ ExpressionStatement
   └─ AssignmentExpression "="
      ├─ FieldAccess[ @AccessType = "WRITE" ] "field"
      │  └─ ThisExpression
      └─ NumericLiteral "1"

└─ ExpressionStatement
   └─ AssignmentExpression "="
      ├─ FieldAccess[ @AcessType = "WRITE" ] "field"
      │  └─ SuperExpression
      └─ NumericLiteral "1"

└─ ExpressionStatement
   └─ MethodCall "method"
      ├─ ThisExpression
      └─ ArgumentList (0)

└─ ExpressionStatement
   └─ MethodCall "method"
      ├─ SuperExpression
      └─ ArgumentList (0)
Type expressions Type expressions Examples Code Old AST (PMD 6) New AST (PMD 7)
Foo.staticMethod();
if (x instanceof Foo) {}
var x = Foo::method;
└─ BlockStatement
   └─ Statement
      └─ StatementExpression
         └─ PrimaryExpression
            ├─ PrimaryPrefix
            │  └─ Name "Foo.staticMethod"
            └─ PrimarySuffix[ @Arguments = true() ]
               └─ Arguments (0)

└─ BlockStatement
   └─ Statement
      └─ IfStatement
         ├─ Expression
         │  └─ InstanceOfExpression
         │     ├─ PrimaryExpression
         │     │  └─ PrimaryPrefix
         │     │     └─ Name "x"
         │     └─ Type
         │        └─ ReferenceType
         │           └─ ClassOrInterfaceType "Foo"
         └─ Statement
            └─ Block

└─ BlockStatement
   └─ LocalVariableDeclaration
      └─ VariableDeclarator
         ├─ VariableDeclaratorId "x"
         └─ VariableInitializer
            └─ Expression
               └─ PrimaryExpression
                  ├─ PrimaryPrefix
                  │  └─ Name "Foo"
                  └─ PrimarySuffix
                     └─ MemberSelector
                        └─ MethodReference "method"
└─ ExpressionStatement
   └─ MethodCall "staticMethod"
      ├─ TypeExpression
      │  └─ ClassType "Foo"
      └─ ArgumentList (0)

└─ IfStatement
   ├─ InfixExpression "instanceof"
   │  ├─ VariableAccess[ @AccessType = "READ" ] "x"
   │  └─ TypeExpression
   │     └─ ClassType "Foo"
   └─ Block

└─ LocalVariableDeclaration
   ├─ ModifierList
   └─ VariableDeclarator
      ├─ VariableId "x"
      └─ MethodReference "method"
         └─ TypeExpression
            └─ ClassType "Foo"
Merge unary expressions Unary Expressions Examples Code Old AST (PMD 6) New AST (PMD 7)
++a;
--b;
c++;
d--;
└─ StatementExpression
   └─ PreIncrementExpression
      └─ PrimaryExpression
         └─ PrimaryPrefix
            └─ Name "a"

└─ StatementExpression
   └─ PreDecrementExpression
      └─ PrimaryExpression
         └─ PrimaryPrefix
            └─ Name "b"

└─ StatementExpression
   └─ PostfixExpression "++"
      └─ PrimaryExpression
         └─ PrimaryPrefix
            └─ Name "c"

└─ StatementExpression
   └─ PostfixExpression "--"
      └─ PrimaryExpression
         └─ PrimaryPrefix
            └─ Name "d"
└─ ExpressionStatement
   └─ UnaryExpression[ @Prefix = true() ][ @Operator = '++' ]
      └─ VariableAccess[ @AccessType = "WRITE" ] "a"

└─ ExpressionStatement
   └─ UnaryExpression[ @Prefix = true() ][ @Operator = '--' ]
      └─ VariableAccess[ @AccessType = "WRITE" ] "b"

└─ ExpressionStatement
   └─ UnaryExpression[ @Prefix = false() ][ @Operator = '++' ]
      └─ VariableAccess[ @AccessType = "WRITE" ] "c"

└─ ExpressionStatement
   └─ UnaryExpression[ @Prefix = false() ][ @Operator = '--' ]
      └─ VariableAccess[ @AccessType = "WRITE" ] "d"
x = ~a;
x = +a;
└─ UnaryExpressionNotPlusMinus "~"
   └─ PrimaryExpression
      └─ PrimaryPrefix
         └─ Name "a"

└─ UnaryExpression "+"
   └─ PrimaryExpression
      └─ PrimaryPrefix
         └─ Name "a"
└─ UnaryExpression[ @Prefix = true() ] "~"
   └─ VariableAccess "a"

└─ UnaryExpression[ @Prefix = true() ] "+"
   └─ VariableAccess "a"
Binary operators are left-recursive Binary operators Examples Code Old AST (PMD 6) New AST (PMD 7)
int i = 1 * 2 * 3 % 4;
└─ Expression
   └─ MultiplicativeExpression "%"
      ├─ PrimaryExpression
      │  └─ PrimaryPrefix
      │     └─ Literal "1"
      ├─ PrimaryExpression
      │  └─ PrimaryPrefix
      │     └─ Literal "2"
      ├─ PrimaryExpression
      │  └─ PrimaryPrefix
      │     └─ Literal "3"
      └─ PrimaryExpression
         └─ PrimaryPrefix
            └─ Literal "4"
└─ InfixExpression[ @Operator = '%' ]
   ├─ InfixExpression[@Operator='*']
   │  ├─ InfixExpression[@Operator='*']
   │  │  ├─ NumericLiteral[@ValueAsInt=1]
   │  │  └─ NumericLiteral[@ValueAsInt=2]
   │  └─ NumericLiteral[@ValueAsInt=3]
   └─ NumericLiteral[@ValueAsInt=4]
Parenthesized expressions Parenthesized expressions Examples Code Old AST (PMD 6) New AST (PMD 7)
a = (((1)));
└─ StatementExpression
   ├─ PrimaryExpression
   │  └─ PrimaryPrefix
   │     └─ Name "a"
   ├─ AssignmentOperator "="
   └─ Expression
      └─ PrimaryExpression
         └─ PrimaryPrefix
            └─ Expression
               └─ PrimaryExpression
                  └─ PrimaryPrefix
                     └─ Expression
                        └─ PrimaryExpression
                           └─ PrimaryPrefix
                              └─ Expression
                                 └─ PrimaryExpression
                                    └─ PrimaryPrefix
                                       └─ Literal "1"
└─ ExpressionStatement
   └─ AssignmentExpression
      ├─ VariableAccess "a"
      └─ NumericLiteral[ @Parenthesized = true() ][ @ParenthesisDepth = 3 ] "1"
Apex AST

PMD 7.0.0 switched the underlying parser for Apex code from Jorje to Summit AST, which is based on an open source grammar for Apex: apex-parser.

The produced AST is mostly compatible, there are some unavoidable changes however:

Language versions Migrating custom CPD language modules

This is only relevant, if you are maintaining a CPD language module for a custom language.

Build Tools

Note:

When you switch from PMD 6.x to PMD 7 in your build tools, you most likely need to review your ruleset(s) as well and check for removed rules. See the use case I’m using only built-in rules above.

Ant Maven Gradle XML Report Format

The XML Report format supports rendering suppressed violations.

The content of the attribute suppressiontype is changed in PMD 7.0.0:


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4