Skip to content

Rename underscore class references across source files - #1197

Draft
martinfrancois wants to merge 1 commit into
openrewrite:mainfrom
martinfrancois:repro/rename-underscore-cross-file
Draft

Rename underscore class references across source files#1197
martinfrancois wants to merge 1 commit into
openrewrite:mainfrom
martinfrancois:repro/rename-underscore-cross-file

Conversation

@martinfrancois

@martinfrancois martinfrancois commented Aug 11, 2026

Copy link
Copy Markdown

Suggested review order: 49 of 52 (Score: 1)
Review first: openrewrite/rewrite-testing-frameworks#1084

What's changed?

Adds 2 known-failing tests to RenameUnderscoreIdentifierTest that reproduce two gaps when RenameUnderscoreIdentifier renames a class named _:

  • classReferencesInAnotherSourceFileFollowTheRename: references to the renamed class in another source file keep the old _ spelling.
  • classRenameAvoidsASecondaryClassDeclaredInAnotherSourceFile: the chosen name __ duplicates a package private class declared in another file.

No recipe code changes. The tests are marked @ExpectedToFail so the suite stays green; removing the annotation shows the failure. The test class has 11 tests on main and 13 with these additions.

Both tests build their _ class with a ChangeType("UNDERSCORE", "_", false) setup recipe instead of the file's renameToUnderscore() helper, because the helper cannot produce valid cross-file references to _. A comment in the test file explains this. Before RenameUnderscoreIdentifier runs, the LST truly declares a class _ with bound references from the other file.

What's your motivation?

Recipe: org.openrewrite.java.migrate.lang.RenameUnderscoreIdentifier.

The recipe renames _ identifiers so Java 8 code compiles on Java 9 and later. It runs inside Java8toJava11, which UpgradeToJava17 and UpgradeToJava21 pull in. On current main (01d0fe82), it renames only the class declaration identifier and does not check for names declared in other files.

Case 1: class references in another source file

Before

Two files are parsed together. After the setup recipe, _.java declares class _, and the other file contains bound references to that class:

class User {
    _ field = new _();
}

Actual after the recipe

_.java becomes class __, but User.java is unchanged:

User.java: (unchanged)

Javac on JDK 25 reports error: underscore not allowed here at the type reference and the constructor call. JDK 9 through 21 report the equivalent release-9 keyword error.

Expected after the recipe

References in the other file are renamed with the declaration:

class User {
    __ field = new __();
}

Case 2: the selected name exists in another source file

Before

_.java declares class _. Other.java already declares a package-private class named __:

class Other {
}

class __ {
}

Actual after the recipe

The recipe renames class _ to class __. The two source files then declare the same class name, and javac rejects the result with error: duplicate class: a.__.

Expected after the recipe

(unchanged)

The recipe MUST leave the declaration unchanged when the selected replacement name is already declared in another source file.

Found while preparing #1194, which fixes the same-file cases of both defects.

Affected code in real projects

  • yinwang0/rubysonar Analyzer.java: a Java 7 static analyzer whose utility class is literally named _ (declared in _.java) and is referenced from this and many other source files through calls such as _.newSessionId() and _.msg(..); the recipe from main renames only the declaration in _.java to __, so every cross-file reference keeps the old _ spelling and javac rejects it as a keyword on Java 9 and later.

Anything in particular you'd like reviewers to focus on?

I think both are genuine bugs, because the recipe's whole purpose is to keep the code compiling, and in both cases the output fails to compile. If you agree this should change, I would gladly prepare the fix. If this behavior is intended, feel free to close this and I know it is settled.

Any additional context

Pre-existing tests changed: None.

My open #1194 discloses both cases as explicit limits ("the same as on main") but does not fix them: its collision check sees same-file names and other files' paths only, and it renames class references within the same file only. These tests were executed on current main; per the description of #1194 they are expected to fail identically on top of it.

This reproduction was prepared with AI assistance (Claude Code). I reviewed the tests and this description.

The added reproduction tests and the existing suite together cover changed and unchanged behavior. The known-failing tests remain disabled until implementation. The formatter run was calibrated per file; untouched lines were not reformatted.

Checklist

@github-project-automation github-project-automation Bot moved this to In Progress in OpenRewrite Aug 11, 2026
@martinfrancois martinfrancois changed the title RenameUnderscoreIdentifier: add failing tests for cross-file class rename gaps Rename underscore class references across source files Aug 16, 2026
…name gaps

classReferencesInAnotherSourceFileFollowTheRename pins that a class rename
leaves references in other source files on the old, invalid _ spelling.
classRenameAvoidsASecondaryClassDeclaredInAnotherSourceFile pins that the
chosen name duplicates a package private class from a differently named file.
Both are marked known-failing with @ExpectedToFail.
@martinfrancois
martinfrancois force-pushed the repro/rename-underscore-cross-file branch from 7617c66 to 316df22 Compare August 16, 2026 12:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

1 participant