What version of OpenRewrite are you using?
rewrite 8.80.0-SNAPSHOT (rewrite-java / rewrite-kotlin)
How are you running OpenRewrite?
Via JavaTemplate.apply / KotlinTemplate.apply with an Expression parameter whose declared type is JavaType.Parameterized or JavaType.GenericTypeVariable bound to a caller-scope type variable.
What is the smallest, simplest way to reproduce the problem?
In a recipe, pass an Expression parameter whose type carries an unbound type variable. For example, when replacing builder.default { ... } where builder has type PolymorphicModuleBuilder<T> (caller-side <T : Any>), the parameter is serialized by Substitutions/TypeUtils.toString() into:
class Template {
/*__TEMPLATE__*/__P__./*__p0__*/p<kotlinx.serialization.modules.PolymorphicModuleBuilder<T>>().defaultDeserializer(...)
;
}
The caller's type variable T is undeclared inside the synthetic class Template {}, so the parser rejects the template with Could not parse as Java: ....
What did you expect to see?
Template parameters whose declared type references unbound type variables should be erased (raw type, or bound) during synthetic template generation.
What did you see instead?
IllegalArgumentException: Could not parse as Java: ...
Suggested fix
In Substitutions.getTypeName() / TypeUtils.toString(JavaType) as used by the template parameter rendering path, apply type erasure:
JavaType.Parameterized → its raw getType()
JavaType.GenericTypeVariable → first bound (or java.lang.Object if empty)
- Recurse for
JavaType.Array element types
This mirrors the JVM bytecode erasure and keeps the synthetic template class self-contained.
Workaround
Strip generics from each Expression parameter before calling apply: https://github.com/moderneinc/rewrite-migrate-kotlin/commit/8f2ec1b — see withRawType / toRawType in ReplaceKotlinMethod.java.
What version of OpenRewrite are you using?
rewrite 8.80.0-SNAPSHOT (rewrite-java / rewrite-kotlin)
How are you running OpenRewrite?
Via
JavaTemplate.apply/KotlinTemplate.applywith anExpressionparameter whose declared type isJavaType.ParameterizedorJavaType.GenericTypeVariablebound to a caller-scope type variable.What is the smallest, simplest way to reproduce the problem?
In a recipe, pass an Expression parameter whose type carries an unbound type variable. For example, when replacing
builder.default { ... }wherebuilderhas typePolymorphicModuleBuilder<T>(caller-side<T : Any>), the parameter is serialized bySubstitutions/TypeUtils.toString()into:The caller's type variable
Tis undeclared inside the syntheticclass Template {}, so the parser rejects the template withCould not parse as Java: ....What did you expect to see?
Template parameters whose declared type references unbound type variables should be erased (raw type, or bound) during synthetic template generation.
What did you see instead?
IllegalArgumentException: Could not parse as Java: ...Suggested fix
In
Substitutions.getTypeName()/TypeUtils.toString(JavaType)as used by the template parameter rendering path, apply type erasure:JavaType.Parameterized→ its rawgetType()JavaType.GenericTypeVariable→ first bound (orjava.lang.Objectif empty)JavaType.Arrayelement typesThis mirrors the JVM bytecode erasure and keeps the synthetic template class self-contained.
Workaround
Strip generics from each
Expressionparameter before callingapply: https://github.com/moderneinc/rewrite-migrate-kotlin/commit/8f2ec1b — seewithRawType/toRawTypeinReplaceKotlinMethod.java.