openrewrite / rewrite-kotlin

Work-in-progress implementation of Kotlin language support for OpenRewrite.
Apache License 2.0
38 stars 11 forks source link

Is there some way to use UAST or PSI APIs in a recipe? #600

Closed drawers closed 2 months ago

drawers commented 2 months ago

Thank you for this project. Some features really stand out:

  1. The Gradle tasks are really easy to use
  2. The BOM is handy

What problem are you trying to solve?

I am trying to perform Open Rewrite migrations in large Android projects.

Android platform development teams usually have a lot of experience with the UAST APIs or PSI APIs.

Example lint rule here: https://github.com/slackhq/slack-lints/blob/main/slack-lint-checks/src/main/java/slack/lint/denylistedapis/DenyListedApiDetector.kt

Describe the solution you'd like

Is there some solution that would allow limited code sharing between lint rules and OpenRewrite recipes? At least for element inspection if not rewriting of code? In other words, some way to visit the tree in a recipe that uses PSI or UAST as opposed to the foreign tree.J and tree.K APIs.

The way it is right now, if there is a problematic pattern in the codebase I would have to write a lint rule using UAST or PSI to prohibit further usages, and then follow up with a Open Rewrite Recipe in the less familiar J and K APIs to complete the migration.

Have you considered any alternatives or workarounds?

The alternative here is to just to deal with the fact we would need two different ways of navigating the AST in order to use both tools. It's not the end of the world, but could aid OpenRewrite adoption if there were some way to adapt the two trees.

Are you interested in contributing this feature to OpenRewrite?

This would be hard for me to contribute with my current priorities but I'm interested to hear feedback

timtebeek commented 2 months ago

Hi @drawers ; Thanks for the kind words and background as to what's common in Android projects. We don't currently have a way for recipes to use elements from the UAST or PSI APIs, as we indeed map constructs to our own models. There might be some symmetry though, based on past implementations influencing our design. That way I hope the model isn't entirely foreign to users.

Recently we've also taken to allowing foreign models like Refaster rules and Codemods to map to our models to affect code changes, so we might be able to do something similar in the future for this particular case, although there's no concrete plans just yet. We are looking to better support Android projects though; so any help and hints there is appreciated!

If you're looking to keep issues out after fixing them with recipes then there's also the option to integrate recipe runs into your CI. That way you still only have to implement one method to keep issues out, instead of a separate check and fix. Hope that's helpful to you there.

drawers commented 2 months ago

Thank you @timtebeek for this info! The link to the article about recipe runs as a quality gate is useful, as well as the information about mapping between Refaster rules and Codemods.

I had another thought too - if our team is determined to use Android Lint then it would be possible to use a round of lint to write data for the OpenRewrite rule to read. The lint task and the rewrite task could be wired together using Gradle.

I'm closing this issue to help with backlog management.