Skip to content

Fix generic type loss in AutowiredFieldIntoConstructorParameterVisitor (fixes #1005)#1006

Open
dim-kod wants to merge 3 commits intoopenrewrite:mainfrom
dim-kod:fix/autowired-generic-type-loss
Open

Fix generic type loss in AutowiredFieldIntoConstructorParameterVisitor (fixes #1005)#1006
dim-kod wants to merge 3 commits intoopenrewrite:mainfrom
dim-kod:fix/autowired-generic-type-loss

Conversation

@dim-kod
Copy link
Copy Markdown

@dim-kod dim-kod commented Apr 23, 2026

Fixes issue #1005

What's changed?

AutowiredFieldIntoConstructorParameterVisitor now preserves generic type parameters when converting @Autowired fields to constructor parameters.

Both AddConstructorVisitor and AddConstructorParameterAndAssignment were using TypeUtils.asFullyQualified(type).getClassName() to render the field type in the generated constructor, which strips generics — so List<String> ends up as raw List.

The fix switches to TypeTree.toString(), which goes through JavaPrinter and gives you the full source-level type string. I also added addImportsForType/collectFullyQualifiedNames helpers to register type parameter FQNs with the JavaTemplate builder.

A second commit broadens the type guard from TypeUtils.asFullyQualified() == null to null || Primitive — this fixes a separate issue where array-typed fields like String[] a were silently skipped during constructor generation.

What's your motivation?

Any @Autowired field with a parameterized type ends up with a raw-type constructor parameter after the recipe runs, which produces compiler warnings and loses type safety.

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

  • The TypeTree.toString() approach — it uses JavaPrinter.printTrimmed() under the hood and already handles all type forms (parameterized, wildcards, bounded wildcards, nested generics, arrays).
  • The broadened type guard (Primitive check instead of asFullyQualified == null) — this intentionally lets JavaType.Array through.

Have you considered any alternatives or workarounds?

  • A custom renderTypeSimple(JavaType) that recursively builds the simple-name type string from JavaType — works, but duplicates what TypeTree.toString() already does.
  • TypeUtils.toString(JavaType) — produces fully qualified names (e.g. java.util.List<java.lang.String>), not source-level simple names, so it doesn't work for template strings.

Any additional context

Test coverage went from 11 to 22 tests. New tests cover:

  • Generic types into new/existing/parameterized constructors (List<String>)
  • Nested generics (Map<String, List<Integer>>)
  • Wildcards: unbounded (List<?>), upper-bounded (List<? extends Number>), lower-bounded (List<? super Integer>)
  • User-defined generic types across packages (MyService<MyConfig>)
  • Array types (String[])
  • @Autowired(required = false)
  • Multiple annotations (@Autowired @Qualifier("myBean"))

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

dim-kod added 2 commits April 23, 2026 09:32
…r parameters

Both AddConstructorVisitor and AddConstructorParameterAndAssignment used
TypeUtils.asFullyQualified(type).getClassName() to render the field type
in the generated constructor parameter, which dropped generic type
parameters (e.g. List<String> became raw List).

Use TypeTree.toString() instead, which leverages JavaPrinter to produce
the full source-level type string including generics, wildcards, and
nested type parameters. Also add addImportsForType/collectFullyQualifiedNames
helpers to register type parameter FQNs with the JavaTemplate builder.
The type guard used TypeUtils.asFullyQualified() which returns null for
JavaType.Array, preventing constructor generation for array-typed fields.
Replace with a null + primitive check so arrays (and other non-primitive
types) are accepted. Also add tests for @Autowired(required = false)
and fields with multiple annotations.
…-type-loss

# Conflicts:
#	src/main/java/org/openrewrite/java/spring/AutowiredFieldIntoConstructorParameterVisitor.java
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.

2 participants