Skip to content

Rename variables if the name matches class name #5454

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

radoslaw-panuszewski
Copy link
Contributor

@radoslaw-panuszewski radoslaw-panuszewski commented May 19, 2025

What's your motivation?

When I rename a type in IntelliJ IDEA (for example SomeType to AnotherType) it will automatically rename variables with the same name as the class, but starting with lowercase character (so someType to anotherType).

This PR introduces the above feature to org.openrewrite.java.ChangeType recipe.

Checklist

  • I've added unit tests to cover both positive and negative cases
  • I've read and applied the recipe conventions and best practices
  • I've used the IntelliJ IDEA auto-formatter on affected files

@github-project-automation github-project-automation bot moved this to In Progress in OpenRewrite May 19, 2025
@radoslaw-panuszewski radoslaw-panuszewski force-pushed the rename-variables-when-changing-type branch 2 times, most recently from b83c9b8 to 071fb76 Compare May 19, 2025 19:02
@radoslaw-panuszewski radoslaw-panuszewski force-pushed the rename-variables-when-changing-type branch from 071fb76 to 8551095 Compare May 19, 2025 19:07
@radoslaw-panuszewski radoslaw-panuszewski marked this pull request as ready for review May 19, 2025 19:12
Copy link
Member

@timtebeek timtebeek left a comment

Choose a reason for hiding this comment

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

Neat addition, thanks! Couple small requests, but otherwise looks good.

Comment on lines 735 to 744

/**
* @return string with first character changed to lowercase (or empty string)
*/
public static String decapitalize(@Nullable String string) {
if (string != null && !string.isEmpty()) {
return Character.toLowerCase(string.charAt(0)) + string.substring(1);
}
return "";
}
Copy link
Member

Choose a reason for hiding this comment

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

Could we start this out as just a private method inside ChangeType itself? We try to minimize exposed API, as folks will use anything they can see, which limits our ability to evolve for something essentially only used once so far.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved to private method in ChangeType

Comment on lines +1595 to +1596
public A2 method(A2 a2) {
return a2;
Copy link
Member

Choose a reason for hiding this comment

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

Neat idea indeed to rename these as well!

// Rename variable if it matches class name (starting with a lowercase character)
if (ident.getSimpleName().equals(decapitalize(className))) {
if (targetType instanceof JavaType.FullyQualified) {
String newName = decapitalize(((JavaType.FullyQualified) targetType).getClassName());
Copy link
Member

Choose a reason for hiding this comment

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

Would you mind wrapping this in a use of VariableNameUtils.generateVariableName? in 99.9...% of the cases those will be the same, but it's best to avoid conflicts where we can.

public static String generateVariableName(String baseName, Cursor scope, GenerationStrategy strategy) {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done as suggested 😉

*/
public static String decapitalize(@Nullable String string) {
if (string != null && !string.isEmpty()) {
return Character.toLowerCase(string.charAt(0)) + string.substring(1);
Copy link
Contributor

Choose a reason for hiding this comment

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

Pedantic mode: we should probably work on codepoints instead of characters since Java identifiers allow all of Unicode, not just the BMP.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added support for unicode characters + test case for that (Ą1 -> Ą2)

Comment on lines +411 to +413
if (v.getVariableType() != null && !v.getSimpleName().equals(v.getVariableType().getName())) {
return v.withVariableType(v.getVariableType().withName(v.getSimpleName()));
}
Copy link
Member

Choose a reason for hiding this comment

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

Would we be replacing any variable here? Or only when it matched the old variable name? Want to make sure there's no unexpected changes, as the recipe is used so often, and the tests are very minimal.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This piece of code only updates variable type name if the underlying Identifier has been renamed earlier.

I've added a test case doNotRenameRandomVariablesMatchingClassName to verify that random variables with matching name are not renamed.

Copy link
Member

Choose a reason for hiding this comment

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

Great thanks! I'm rushing out the door here, but will ask the wider team to review while I'm out over the next few days.

@timtebeek timtebeek moved this from In Progress to Ready to Review in OpenRewrite May 20, 2025
@timtebeek timtebeek added the enhancement New feature or request label May 20, 2025
@Test
void updateMethodTypeWithUnicodeCharacter() {
rewriteRun(
spec -> spec.recipe(new ChangeType("a.Ą1", "a.Ą2", false)),
Copy link
Contributor

Choose a reason for hiding this comment

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

Since Ą is in the BMP, this test should pass with the old code as well. In order to really see the change going from characters to codepoints, you probably need something like 𐐓 (\uD801\uDC13).

Copy link
Contributor Author

@radoslaw-panuszewski radoslaw-panuszewski May 21, 2025

Choose a reason for hiding this comment

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

I tried changing Ą/ą to 𐐅/𐐭 but I encountered strange issues from unrelated parts of the code (like import statement started disappearing). I've spent some time debugging it but eventually gave up as it seems to be a broader issue and fixing it is out of scope of this PR.

I reverted the code of decapitalize method to the previous version and removed the test case with special characters. All of my experiments are in the git history in case someone would like to continue the fight from there 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
Status: Ready to Review
Development

Successfully merging this pull request may close these issues.

3 participants