Skip to content

[kotlin] Add AST improvements, KotlinAstUtil#6670

Merged
adangel merged 24 commits into
pmd:mainfrom
stokpop:feature/kotlin-ast-improvements
May 26, 2026
Merged

[kotlin] Add AST improvements, KotlinAstUtil#6670
adangel merged 24 commits into
pmd:mainfrom
stokpop:feature/kotlin-ast-improvements

Conversation

@stokpop
Copy link
Copy Markdown
Contributor

@stokpop stokpop commented May 13, 2026

Describe the PR

See issue #6669 for a full description of the three improvements: AST node improvements, the new KotlinAstUtil utility class.

Related issues

Ready?

  • Added unit tests for fixed bug/feature
  • Passing all unit tests
  • Complete build ./mvnw clean verify passes (checked automatically by github actions)
  • Added (in-code) documentation (if needed)

stokpop added 5 commits May 13, 2026 08:15
…es and KotlinAstUtil

- KotlinNode: add default getIdentifier() and getModifiers() methods
- KotlinInnerNode: implement getIdentifier(), getModifiers(), getImage();
  add NonNullAttributeIterator to skip null XPath attributes in PMD Designer
- KotlinTerminalNode: add null-attribute filter for getXPathAttributesIterator()
- KotlinAstUtil: new static utility class for navigating Kotlin AST from rule code
  (getIdentifierText, getPrimaryExpressionText, typeContainsName,
  isDirectChildOfFunctionBody, isDirectDescendantOf, getLhsVarName,
  collectAllParamNames, collectLocalVarNames, collectClassVarFieldNames)
- Tests: 5 new inline tests for AST attributes; new KotlinAstUtilTest with 8 tests
Detects local variable declarations and lambda explicit parameters
that shadow a parameter of any enclosing named function.

Visits each KtFunctionDeclaration once and collects a combined set
of params from that function and all its enclosing named functions.
Uses KotlinAstUtil.isWithin to scope checks so each declaration is
processed exactly once by its nearest enclosing function visit.

Also adds collectLambdaParamNames utility method to KotlinAstUtil.

Test cases cover: basic shadowing, var (mutable), vararg params,
destructuring (no false positive), extension functions, inner blocks,
nested named functions, lambdas with explicit params.
The {0} placeholder in the rule message was not substituted because
addViolation was called without arguments. Pass the variable name as
the format argument.

Also add expected-messages assertion to the first test case so that
unformatted message placeholders cannot regress undetected.
… overloads; remove unused typeContainsName from KotlinAstUtil
@pmd-actions-helper
Copy link
Copy Markdown
Contributor

pmd-actions-helper Bot commented May 13, 2026

Documentation Preview

Compared to main:
This changeset changes 0 violations,
introduces 0 new violations, 0 new errors and 0 new configuration errors,
removes 0 violations, 0 errors and 0 configuration errors.
There are 0 changed duplications, 0 new duplications and 0 removed duplications.
There are 0 changed CPD errors, 0 new CPD errors and 0 removed CPD errors.

Regression Tester Report

(comment created at 2026-05-26 18:31:04+00:00 for 89fe9b8)

Copy link
Copy Markdown
Member

@adangel adangel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR!

I' not a fan, that we add attributes on KotlinNode, that are not useful for all nodes and we need to filter them out later. This is just a manifestation of missing solution for #4338.

I spent now some time to implement a different solution, which I will push shortly to this PR.

The Java API for this feels a bit clunky, but I couldn't see a different way if we don't want to throw away the current Kotlin AST (-> #4337). This would be a bigger effort and in all possible ways incompatible (but hey - who uses pmd-kotlin anyway??)

Maybe someone sees a simpler, more elegant solution.

Let me know, what you think of this - it adds another level of indirection for accessing information from the AST (because we can't modify the AST classes).

I've seen, you sneaked in LocalVariableShadowsParameter. This is a new rule and not part of the migration effort of migrating Java rules to Kotlin. It is new - and it deserves its own PR + issue using the new-rule-issue-template.

This PR now does two things: Improve AST and add a new rule - too much IMHO.
Sure, the rule depends on the AST improvements (especially the KotlinAstUtil), so can be done only after. But that's how you do it: First improve the foundation, then use the foundation to do new things.

Please extract the rule into it's own PR.

I guess, we should rebase this PR in the end to have it clean.

Just started the Designer with the new changes. Here are the things, we need to solve:

  • The default attributes (like Image, BeginLine, ...) are appearing twice: Once from the node itself and once of the attribute view (which mostly delegates).
  • We need to add a DesignerBinding for some nodes

Comment thread pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinNode.java Outdated
Comment thread pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinNode.java Outdated
Comment thread pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinInnerNode.java Outdated
Comment thread pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinInnerNode.java Outdated
Comment thread pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinInnerNode.java Outdated
Comment thread pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/util/KotlinAstUtil.java Outdated
Comment thread pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinInnerNode.java Outdated
Comment thread pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/util/KotlinAstUtil.java Outdated
Comment thread pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/util/KotlinAstUtil.java Outdated
@adangel adangel added the needs:user-input Maintainers are waiting for feedback from author label May 16, 2026
stokpop added 6 commits May 22, 2026 08:22
AttributeView implements Node so PMD reflection picks up all delegated
get* methods, causing duplicates when concatenating base and view
attributes. Null-valued attributes (e.g. @modifiers with no modifiers)
are also filtered out. Update tree dump reference files accordingly.
@stokpop
Copy link
Copy Markdown
Contributor Author

stokpop commented May 22, 2026

Thanks for the detailed review — and especially for the AttributeView code, much cleaner than what we had before!

Addressed points:

KotlinDesignerBindings added (KotlinDesignerBindings.java) and wired into KotlinHandler.getDesignerBindings(). Shows @Text for terminal nodes, @Identifier for nodes with a simple identifier, @Modifiers for nodes with modifiers. (A more complete version with @TypeName/@ReturnTypeName lives in the kotlin-type-mapper branch where type info is available.)

Duplicate and null XPath attributes fixed in three commits:

  • KotlinInnerNode.getXPathAttributesIterator() now deduplicates view attributes and filters null values (single-pass, two while-loops; IteratorUtil.concat cannot be used here because its optimization calls bs.hasNext() eagerly at construction time — see comment in code)
  • Nodes without an AttributeView (e.g. KtKotlinFile) also filter null attributes
  • KotlinTerminalNode.getXPathAttributesIterator() likewise filters null attributes (@Image null was visible on all terminal nodes like T-Identifier, T-DOT)

The Designer now no longer shows duplicate attribute entries, and attributes with a null value (like @Image null) are no longer shown at all.

Regression tests added for no-null and no-duplicate invariants, covering inner nodes with and without an AttributeView, and terminal nodes.

LocalVariableShadowsParameter — the rule was mainly there as a means to validate and discover the changes needed in the AST. Those insights have been turned into unit tests instead. Totally fine to keep PRs distinct; the rule will follow as a separate PR.

@stokpop stokpop changed the title [kotlin] Add AST improvements, KotlinAstUtil, and LocalVariableShadowsParameter rule [kotlin] Add AST improvements, KotlinAstUtil May 22, 2026
@stokpop stokpop requested a review from adangel May 22, 2026 14:00
Comment thread pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinInnerNode.java Outdated
Comment thread pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinInnerNode.java Outdated
Comment thread pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinInnerNode.java Outdated
@adangel adangel added an:enhancement An improvement on existing features / rules and removed needs:user-input Maintainers are waiting for feedback from author labels May 25, 2026
* Returns the text of the first {@code SimpleIdentifier} direct child,
* or {@code null} if none is present.
*/
default String getIdentifier() {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might make sense to actually name this attribute "Name" and not "Identifier". But this can be done later as well in a separate PR when we have rules (the attribute is experimental).

@adangel adangel added this to the 7.25.0 milestone May 25, 2026
Copy link
Copy Markdown
Member

@adangel adangel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stokpop This is almost ready - just some release notes and deprecations are missing.
I'm planning to include this in this week's release.

Comment thread docs/pages/release_notes.md Outdated
@adangel adangel merged commit 6aedcea into pmd:main May 26, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

an:enhancement An improvement on existing features / rules

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[kotlin] Add AST improvements, KotlinAstUtil

2 participants