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
discussionso that we can improve this guide.
Before you updateBefore 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:
Properties: In order to define property descriptors, you should use PropertyFactory
now. This factory can create properties of any type. E.g. instead of StringProperty.named(...)
use PropertyFactory.stringProperty(...)
.
Also note, that uiOrder
is gone. You can just remove it.
See also Defining rule properties
When reporting a violation, you might see a deprecation of the addViolation
methods. These methods have been moved to RuleContext
. E.g. instead of addViolation(data, node, ...)
use asCtx(data).addViolation(node, ...)
.
-no-cache
â¡ï¸ --no-cache
-failOnViolation
â¡ï¸ --fail-on-violation
-reportfile
â¡ï¸ --report-file
-language
â¡ï¸ --use-version
WARNING: Use of deprecated attribute 'VariableId/@Image' by XPath rule 'VariableNaming' (in ruleset 'VariableNamingRule'), please use @Name instead
and often already suggest an alternative.
<lang-name>-<ruleset-name>
, eg java-basic
, which resolves to rulesets/java/basic.xml
600
, which resolves to rulesets/releases/600.xml
Such usages produce deprecation warnings that should be easy to spot, e.g.
Ruleset reference 'java-basic' uses a deprecated form, use 'rulesets/java/basic.xml' instead
Use the explicit forms of these references to be compatible with PMD 7.
Note: Since PMD 6, all rules are sorted into categories (such as âBest Practicesâ, âDesignâ, âError Proneâ) and the old rulesets like basic.xml
have been deprecated and have been removed with PMD 7. It is about time to create a custom ruleset.
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:
@Deprecated
, @DeprecatedUtil700
, @InternalApi
.
category/vf/security.xml
â¡ï¸ category/visualforce/security.xml
category/vm/...
â¡ï¸ category/velocity/...
The following topics describe well known migration challenges in more detail.
Use cases Iâm using only built-in rulesWhen 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 TestingIdeally, 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.
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.
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:
typeOf
has been removed (deprecated since 6.4.0). Use the function pmd-java:typeIs
or pmd-java:typeIsExactly
instead. See PMD extension functions for available functions.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:
javacc-wrapper.xml
. This should be used now.JjtreeParserAdapter
. This is the class that needs to be implemented now.TokenManager
- we have now a common base class for JavaCC generated token managers. This base class is AbstractTokenManager
.ViolationDecorator
that a language can implement. These ViolationDecorators are called when a violation is reported and they can provide the additional information. This information can be used by renderers via RuleViolation#getAdditionalInfo
.AstVisitorBase
.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 downloadspmd-dist-<version>-bin.zip
, pmd-dist-<version>-src.zip
and pmd-dist-<version>-doc.zip
. Keep that in mind, if you have an automated download script.pmd-bin-<version>
.The CLI has been revamped completely (see Release Notes: Revamped Command Line Interface).
Most notable changes:
run.sh
and pmd.bat
, we now have pmd
only (technically on Windows, there is still a pmd.bat
, but it behaves the same).
run.sh pmd
/ pmd.bat
â¡ï¸ pmd check
run.sh cpd
/ cpd.bat
â¡ï¸ pmd cpd
run.sh designer
/ designer.bat
â¡ï¸ pmd designer
run.sh cpd-gui
/ cpdgui.bat
â¡ï¸ pmd cpd-gui
--fail-on-violation false
â¡ï¸ --no-fail-on-violation
If you donât replace this argument, then âfalseâ will be interpreted as a file to analyze. You might see then an error message such as [main] ERROR net.sourceforge.pmd.cli.commands.internal.PmdCommand - No such file false
.
--no-progress
.--no-ruleset-compatibility
has been removed without replacement.--stress
(or -stress
) has been removed without replacement.When creating a custom distribution which only integrates the languages you need, there are some changes to apply:
net.sourceforge.pmd:pmd-cli
in order to get the CLI classes.<includes>scripts/**,LICENSE</includes>
needs to be changed to <includes>scripts/**,LICENSE,conf/**</includes>
.pmd-bin
includes now optionally also a BOM (bill of material). If you want to create this for your custom distribution, simply add the following plugin configuration:
<plugin>
<groupId>org.cyclonedx</groupId>
<artifactId>cyclonedx-maven-plugin</artifactId>
<version>2.7.11</version>
<configuration>
<outputName>pmd-${project.version}-cyclonedx</outputName>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>makeAggregateBom</goal>
</goals>
</execution>
</executions>
</plugin>
net.sourceforge.pmd:pmd-designer
instead of âpmd-uiâ.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.
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 APIStarting 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.
getNthParent(n)
â¡ï¸ ancestors().get(n - 1)
getFirstParentOfType(parentType)
â¡ï¸ ancestors(parentType).first()
getParentsOfType(parentType)
â¡ï¸ ancestors(parentType).toList()
findChildrenOfType(childType)
â¡ï¸ children(childType).toList()
findDescendantsOfType(targetType)
â¡ï¸ descendants(targetType).toList()
getFirstChildOfType(childType)
â¡ï¸ firstChild(childType)
getFirstDescendantOfType(descendantType)
â¡ï¸ descendants(descendantType).first()
hasDescendantOfType(type)
â¡ï¸ descendants(type).nonEmpty()
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:
children
- returns a NodeStream containing all the children of this node. Note: in PMD 6, this method returned an Iterable
descendants
descendantsOrSelf
ancestors
ancestorsOrSelf
children
firstChild
descendants
ancestors
Methods removed completely:
getFirstParentOfAnyType(parentTypes)
:ï¸ There is no direct replacement, but something along the lines:
ancestors()
.filter(n -> Arrays.stream(classes)
.anyMatch(c -> c.isInstance(n)))
.first();
findChildNodesWithXPath
: Has been removed, because it is very inefficient. Use NodeStream instead.hasDescendantMatchingXPath
: Has been removed, because it is very inefficient. Use NodeStream instead.jjt*
like jjtGetParent
. These methods were implementation specific. Use the equivalent methods like getParent()
.See Node
for the details.
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 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:
fn:
and string:
should not be mentioned explicitly. In XPath 2.0 mode, the engine will complain about an undeclared namespace, but the functions are in the default namespace. Removing the namespace prefixes fixes it.
fn:substring("Foo", 1)
â substring("Foo", 1)
typeIs
must be prefixed with the namespace of the declaring module (pmd-java
).
typeIs("Foo")
â pmd-java:typeIs("Foo")
"true"
and "false"
. In 2.0 mode though, boolean values are truly represented as boolean values, which in XPath may only be obtained through the functions true()
and false()
. If your XPath 1.0 rule tests an attribute like @Private="true"
, then it just needs to be changed to @Private=true()
when migrating. A type error will warn you that you must update the comparison. More is explained on issue #1244.
"true"
, 'true'
â true()
"false"
, 'false'
â false()
@BeginLine > "1"
worked âthatâs not the case in 2.0 mode.
@ArgumentCount > '1'
â @ArgumentCount > 1
/Foo
matches the children of the root named Foo
. In XPath 2.0, that expression matches the root, if it is named Foo
. Consider the following tree:
Foo
ââ Foo
ââ Foo
Then /Foo
will match the root in XPath 2.0, and the other nodes (but not the root) in XPath 1.0. See e.g. an issue caused by this in Apex, with nested classes.
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.
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.
HtmlASTHtmlTextNode
: @Image
â¡ï¸ @Text
, @NormalizedText
â¡ï¸ @Text
, @Text
â¡ï¸ @WholeText
.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:
ASTAnnotationTypeDeclaration
: @Image
â¡ï¸ @SimpleName
ASTAnonymousClassDeclaration
: @Image
â¡ï¸ @SimpleName
ASTClassDeclaration
(previously âASTClassOrInterfaceDeclarationâ): @Image
â¡ï¸ @SimpleName
ASTEnumDeclaration
: @Image
â¡ï¸ @SimpleName
ASTFieldDeclaration
: @VariableName
â¡ï¸ VariableId/@Name
ASTMethodDeclaration
: @Image
â¡ï¸ @Name
ASTMethodDeclaration
: @MethodName
â¡ï¸ @Name
ASTRecordDeclaration
: @Image
â¡ï¸ @SimpleName
ASTVariableId
(previously âASTVariableDeclaratorIdâ): @Image
â¡ï¸ @Name
ASTVariableId
(previously âASTVariableDeclaratorIdâ): @VariableName
â¡ï¸ @Name
ASTVariableId
(previously âASTVariableDeclaratorIdâ): @Array
â¡ï¸ @ArrayType
ASTAssignment
: @Image
â¡ï¸ @Operator
ASTBigIntLiteral
: @Image
â¡ï¸ @Value
ASTBreakStatement
: @Image
â¡ï¸ Name/@Identifier
ASTContinueStatement
: @Image
â¡ï¸ Name/@Identifier
ASTErrorNode
: @Image
â¡ï¸ @Message
ASTFunctionNode
: @Image
â¡ï¸ Name/@Identifier
ASTInfixExpression
: @Image
â¡ï¸ @Operator
ASTKeywordLiteral
: @Image
â¡ï¸ @Literal
ASTLabel
: @Image
â¡ï¸ @Name
ASTName
: @Image
â¡ï¸ @Identifier
ASTNumberLiteral
: @Image
â¡ï¸ @Value
ASTObjectProperty
: @Image
â¡ï¸ @Operator
ASTPropertyGet
: @Image
â¡ï¸ @Operator
ASTRegExpLiteral
: @Image
â¡ï¸ @Value
ASTStringLiteral
: @Image
â¡ï¸ @Value
ASTUnaryExpression
: @Image
â¡ï¸ @Operator
ASTUpdateExpression
: @Image
â¡ï¸ @Operator
ASTXmlDotQuery
: @Image
â¡ï¸ @Operator
ASTXmlMemberGet
: @Image
â¡ï¸ @Operator
ASTXmlPropRef
: @Image
â¡ï¸ Name[last()]/@Identifier
ASTXmlString
: @Image
â¡ï¸ @Xml
ASTAttributeValue
: @Image
â¡ï¸ @Value
ASTCData
: @Image
â¡ï¸ @Content
ASTCommentTag
: @Image
â¡ï¸ @Content
ASTElExpression
: @Image
â¡ï¸ @Content
ASTHtmlScript
: @Image
â¡ï¸ @Content
ASTJspComment
: @Image
â¡ï¸ @Content
ASTJspDeclaration
: @Image
â¡ï¸ @Content
ASTJspExpression
: @Image
â¡ï¸ @Content
ASTJspExpressionInAttribute
: @Image
â¡ï¸ @Content
ASTJspScriptlet
: @Image
â¡ï¸ @Content
ASTText
: @Image
â¡ï¸ @Content
ASTUnparsedText
: @Image
â¡ï¸ @Content
ASTValueBinding
: @Image
â¡ï¸ @Content
ASTAddOp
: @Image
â¡ï¸ @Operator
ASTDerClassSpecifier
: @Image
â¡ï¸ @SimpleClassName
ASTEnumerationShortClassSpecifier
: @Image
â¡ï¸ @SimpleClassName
ASTExtendingLongClassSpecifier
: @Image
â¡ï¸ @SimpleClassName
ASTFactor
: @Image
â¡ï¸ @Operator
ASTLanguageSpecification
: @Image
â¡ï¸ @ExternalLanguage
ASTMulOp
: @Image
â¡ï¸ @Operator
ASTName
: @Image
â¡ï¸ @Name
ASTNumberLiteral
: @Image
â¡ï¸ @Value
ASTRelOp
: @Image
â¡ï¸ @Operator
ASTSimpleLongClassSpecifier
: @Image
â¡ï¸ @SimpleClassName
ASTSimpleName
: @Image
â¡ï¸ @Name
ASTSimpleShortClassSpecifier
: @Image
â¡ï¸ @SimpleClassName
ASTStoredDefinition
: @Image
â¡ï¸ @Name
ASTStringComment
: @Image
â¡ï¸ @Comment
ASTStringLiteral
: @Image
â¡ï¸ @Value
ASTWithinClause
: @Image
â¡ï¸ @Name
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.
ScalaASTLitBoolean
: @Image
â¡ï¸ @Value
ASTLitByte
: @Image
â¡ï¸ @Value
ASTLitChar
: @Image
â¡ï¸ @Value
ASTLitDouble
: @Image
â¡ï¸ @Value
ASTLitFloat
: @Image
â¡ï¸ @Value
ASTLitInt
: @Image
â¡ï¸ @Value
ASTLitLong
: @Image
â¡ï¸ @Value
ASTLitNull
: @Image
â¡ï¸ @Value
ASTLitShort
: @Image
â¡ï¸ @Value
ASTLitString
: @Image
â¡ï¸ @Value
ASTLitSymbol
: @Image
â¡ï¸ @Value
ASTLitUnit
: @Image
â¡ï¸ @Value
ASTNameAnonymous
: @Image
â¡ï¸ @Value
ASTNameIndeterminate
: @Image
â¡ï¸ @Value
ASTTermName
: @Image
â¡ï¸ @Value
ASTTypeName
: @Image
â¡ï¸ @Value
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()
.
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 / interfacesModifierOwner
ASTClassType
)ASTClassDeclaration
)ASTTypeDeclaration
)ASTExecutableDeclaration
)ASTVariableId
)ASTClassBody
)SingleMemberAnnotation
, NormalAnnotation
and MarkerAnnotation
are removed in favour of ASTAnnotation
. The Name node is removed, replaced by a ASTClassType
.@A
and @A()
are semantically equivalent, yet they were parsed as MarkerAnnotation resp. NormalAnnotation. Similarly, @A("")
and @A(value="")
were parsed as SingleMemberAnnotation resp. NormalAnnotation. This also makes parsing much simpler. The nested ClassOrInterface type is used to share the disambiguation logic.@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
ASTAnnotation
s are now nested within the node, to which they are applied to. E.g. if a method is annotated, the Annotation node is now a child of a ASTModifierList
, inside the ASTMethodDeclaration
.@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 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"
ASTType
and ASTReferenceType
have been turned into interfaces, implemented by ASTPrimitiveType
, ASTClassType
, and the new node ASTArrayType
. This reduces the depth of the relevant subtrees, and allows to explore them more easily and consistently.Type
and ReferenceType
name tests wonât match anything anymore.Type/ReferenceType/ClassOrInterfaceType
â¡ï¸ ClassType
Type/PrimitiveType
â¡ï¸ PrimitiveType
.Type/ReferenceType[@ArrayDepth > 1]/ClassOrInterfaceType
â¡ï¸ ArrayType/ClassType
.Type/ReferenceType/PrimitiveType
â¡ï¸ ArrayType/PrimitiveType
.VariableId[pmd-java:typeIs("java.lang.String[]")]
because it considers the additional dimensions on declarations like String foo[];
. The Java equivalent is TypeHelper.isA(id, String[].class);
// in the context of a variable declaration
List<String> strs;
ââ Type (1)
ââ ReferenceType
ââ ClassOrInterfaceType "List"
ââ TypeArguments
ââ TypeArgument
ââ ReferenceType (2)
ââ ClassOrInterfaceType "String"
ââ ClassType "List"
ââ TypeArguments
ââ ClassType "String"
ASTArrayType
, ASTArrayTypeDim
, ASTArrayDimensions
, ASTArrayAllocation
.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
ASTClassType
(formerly ASTClassOrInterfaceType) appears to be left recursive now, and encloses its qualifying type.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
ASTTypeArgument
is removed. Instead, the ASTTypeArguments
node contains directly a sequence of ASTType
nodes. To support this, the new node type ASTWildcardType
captures the syntax previously parsed as a TypeArgument.ASTWildcardBounds
node is removed. Instead, the bound is a direct child of the WildcardType.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
ASTAmbiguousName
in that it describes nothing about what it represents. The name in an import may represent a method name, a type name, a field name⦠Itâs too ambiguous to treat in the parser and could just be the image of the import, or package, or module.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
ModifierOwner
(formerly AccessNode) is now based on a node: ASTModifierList
. That node represents modifiers occurring before a declaration. It provides a flexible API to query modifiers, both explicit and implicit. All declaration nodes now have such a modifier list, even if itâs implicit (no explicit modifiers).ASTFieldDeclaration::isSynchronized
makes no sense. Now, these irrelevant methods donât clutter the API. The API of ModifierList is both more general and flexible.@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
ASTClassOrInterfaceBodyDeclaration
, ASTTypeDeclaration
, and ASTAnnotationTypeMemberDeclaration
. These were unnecessary since annotations are nested (see above Annotation nesting).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
ASTClassType
where appropriate. Also uses specific node types for different directives (requires, exports, uses, provides).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
ASTAnonymousClassDeclaration
is introduced for anonymous classes.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
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
ASTFormalParameter
only for method and constructor declaration. Lambdas use ASTLambdaParameter
, catch clauses use ASTCatchParameter
.ASTUnionType
now)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
ASTReceiverParameter
is introduced to differentiate it from formal parameters.ASTFormalParameter
and ASTVariableId
.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
ASTArrayType
.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
ASTVoidType
node to replace ASTResultType
.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
ASTBlock
are by definition ASTStatement
s, which is now an interface implemented by all statements.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
ASTForeachStatement
instead of ForStatement.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
ASTExpressionStatement
. Added new node ASTLocalClassStatement
.ASTStatement
, that can be used as a child in a block. It itself has only one child, which is some kind of ASTExpression
, which can be really any kind of expression (like assignment). In order to allow local class declarations as part of a block, we introduced ASTLocalClassStatement
which is a statement that carries a type declaration. Now blocks are just a list of statements. This allows us to have two distinct hierarchies for expressions and statements.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
ASTLocalVariableDeclaration
unless it is a concise try-with-resources.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
ASTExpression
and ASTPrimaryExpression
have been turned into interfaces. These added no information to the AST and increased its depth unnecessarily. All expressions implement the first interface. Both of those nodes can no more be found in ASTs.
Migrating:
Expression/X
or Expression/PrimaryExpression/X
, just becomes X
Expression
or PrimaryExpression
name tests wonât match anything anymore. However, the axis step *[@Expression=true()] matches any expression.ASTLiteral
has been turned into an interface.ASTNumericLiteral
, ASTCharLiteral
, ASTStringLiteral
, and ASTClassLiteral
are new nodes that implement that interface.ASTPrimaryExpression
ASTNullLiteral
and ASTBooleanLiteral
were nested within it but other literals types were all directly represented by it was inconsistent, and ultimately that level of nesting was unnecessary./Literal/
segments from your XPath expressions/*[self::StringLiteral or self::CharLiteral]/
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
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
ASTPrimaryPrefix
and ASTPrimarySuffix
are removed from the grammar. Subtrees for primary expressions appear to be left-recursive now.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 = 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"
this
and super
are now explicit nodes instead of PrimaryPrefix.
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
ASTTypeExpression
wraps a ASTType
node (such as ASTClassType
) and is used to qualify a method call or field access or method reference.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
ASTUnaryExpression
node. The merged nodes are:
++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
InfixExpression
, which gives access to the operator via getOperator()
and to the operands (getLhs()
, getRhs()
). Additionally, the resulting AST is not flat anymore, but a more structured tree.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
and @ParenthesisDepth
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:
Method
(ASTMethod
)
@Synthetic
anymore. Unlike Jorje, Summit AST doesnât generate synthetic methods anymore, so this attribute would have been always false and is of no use. Therefore it has been removed completely.<clinit>
, <init>
.BridgeMethodCreator
anymore. This was an artificially generated node by Jorje. Since the new parser doesnât generate synthetic methods anymore, this node is not needed anymore.@Namespace
anymore. The attribute has been removed, as it was never fully implemented. It always returned an empty string.ReferenceExpression
(ASTReferenceExpression
)
@Context
anymore. It was not used and always returned null
.minimumLanguageVersion
and maximumLanguageVersion
).--use-version
, then this default version will be used. Usually the latest version is the default version.pmd check --help
.This is only relevant, if you are maintaining a CPD language module for a custom language.
AbstractLanguage
extend now CpdOnlyLanguageModuleBase
.AntlrTokenManager
use now TokenManager
AntlrTokenFilter
also use now TokenManager
AntlrTokenFilter
extend now BaseTokenFilter
src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language
but instead src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language
. This is the unified language interface for both PMD and CPD capable languages. See also the subinterfaces CpdCapableLanguage
and PmdCapableLanguage
.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.
AntPMDTask
and CPDTask
have been moved from the module pmd-core
into the new module pmd-ant
.net.sourceforge.pmd:pmd-ant
) in order to import the tasks into your build file.toolVersion = "7.16.0"
.pmd 'net.sourceforge.pmd:pmd-ant:7.16.0'
pmd 'net.sourceforge.pmd:pmd-java:7.16.0'
The XML Report format supports rendering suppressed violations.
The content of the attribute suppressiontype
is changed in PMD 7.0.0:
nopmd
â¡ï¸ //nopmd
annotation
â¡ï¸ @suppresswarnings
xpath
- new value. Suppressed via property âviolationSuppressXPathâ.regex
- new value. Suppressed via property âviolationSuppressRegexâ.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