Java: support parameterized types in ChangeMethodInvocationReturnType#8043
Draft
MBoegers wants to merge 1 commit into
Draft
Java: support parameterized types in ChangeMethodInvocationReturnType#8043MBoegers wants to merge 1 commit into
ChangeMethodInvocationReturnType#8043MBoegers wants to merge 1 commit into
Conversation
`JavaType.buildType("java.util.Set<java.lang.String>")` does not understand
generics, so the recipe folded the `<...>` suffix into a malformed `ShallowClass`
and emitted a bare `J.Identifier` whose name still contained `<...>`, with no
imports for the type parameters - producing non-compilable output.
Add `TypeUtils.buildTypeTree(String)`, a single string -> TypeTree builder that
handles parameterized types (nested type arguments), arrays, wildcards,
primitives and `java.lang` shortening, and consolidate the near-duplicate
`AddMethodParameter.createTypeTree` into it. The recipe now builds a proper
parameterized type expression, derives its JavaType via `getType()`, shortens
the fully-qualified tree (adding imports for every nested type), and recursively
removes imports for the replaced type.
Closes #7502
sambsnyd
reviewed
Jun 17, 2026
Comment on lines
+113
to
+125
| public static TypeTree buildTypeTree(String typeName) { | ||
| if (typeName.endsWith("]")) { | ||
| TypeTree elementType = buildTypeTree(typeName.substring(0, typeName.lastIndexOf('['))); | ||
| return new J.ArrayType( | ||
| randomId(), | ||
| Space.EMPTY, | ||
| Markers.EMPTY, | ||
| elementType, | ||
| null, | ||
| JLeftPadded.build(Space.EMPTY), | ||
| buildType(typeName) | ||
| ); | ||
| } |
Member
There was a problem hiding this comment.
The challenge I have with this mechanism is that it is not classpath-aware. If you use this to build a type tree for anything not in the standard library you get a stubbed / shallow type.
Is there a reason a type tree produced by a context-insensitive JavaTemplate will not serve?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ChangeMethodInvocationReturnType#7509 — thanks @timtebeek for the original approach and reproducers.Problem
ChangeMethodInvocationReturnTypebreaks on generic return types.JavaType.buildType("java.util.Set<java.lang.String>")doesn't understand generics, so it folds the<...>suffix into a malformedShallowClassand the recipe emits a bareJ.Identifierwhose simple name still contains<...>— non-compilable output with missing imports for the type parameters.Change
TypeUtils.buildTypeTree(String)— a singleString→TypeTreebuilder supporting parameterized types with nested type arguments (Map<String, List<Integer>>), arrays, wildcards (?,? extends X,? super X), primitives andjava.langshortening. ItsJavaTypeis resolved with the same parser, sobuildTypeTree(name).getType()yields the matching type.AddMethodParameter.createTypeTreeintobuildTypeTree, fixing latent gaps: depth-aware splitting of multi-argument nested generics, a space after each comma, and? superbounds.ChangeMethodInvocationReturnTypenow builds a proper parameterized type expression, derives the newJavaTypeviagetType(), shortens the fully-qualified tree viaImportService(importing the raw type and every type parameter), and recursively removes imports for the replaced type. The existinginitializedByMatchguard is preserved.The core
JavaType/TypeTreeinterfaces are intentionally left untouched; the logic lives inTypeUtils. Promoting it intoJavaType.buildTypelater would be a clean follow-up.Test plan
List<String>→Set<String>), nested generics (Map<String, List<Integer>>), a depth-stressing case (Map<String, Map<Integer, Long>>) that the old naive split breaks, and parameterized → raw (List<String>→Object)../gradlew :rewrite-java:testpasses;AddMethodParameterTest, TCKTypeTreeTest,ChangeTypeTestgreen;licenseFormatclean.