Skip to content

Move type-use annotations to array brackets during JSpecify migration#1038

Open
timtebeek wants to merge 11 commits intomainfrom
tim/issue-934-investigation
Open

Move type-use annotations to array brackets during JSpecify migration#1038
timtebeek wants to merge 11 commits intomainfrom
tim/issue-934-investigation

Conversation

@timtebeek
Copy link
Copy Markdown
Member

@timtebeek timtebeek commented Mar 31, 2026

Summary

  • Fixes MigrateFromJavaxAnnotationApi does not modify annotations to the type level #934

  • Adds a new MoveAnnotationToArrayType recipe that moves type-use annotations (e.g. @Nullable) from declaration position to the array brackets (e.g. @Nullable byte[]byte @Nullable[] ), as required by the JSpecify spec.

  • Wires the recipe into all 6 JSpecify migration recipes in jspecify.yml, running after MoveFieldAnnotationToType.

  • Handles method return types, method parameters, and field declarations.

Test plan

  • Unit tests for MoveAnnotationToArrayType: return types, parameters, fields, non-array no-ops, multi-dimensional arrays
  • Integration test in JSpecifyBestPracticesTest for end-to-end javax → JSpecify migration with array types
  • All existing jspecify tests continue to pass

Move the recipe before ChangeType in each migration pipeline so it
matches on the old annotation type (e.g. javax.annotation.*). This
avoids incorrectly moving pre-existing JSpecify annotations where
@nullable String[] intentionally means "array of nullable Strings."
…ected

Verifies that a project with both javax annotations (to migrate) and
pre-existing JSpecify @nullable String[] (meaning array of nullable
elements) only migrates the javax annotations without touching the
already-correct JSpecify annotations.

String description = "When an annotation like `@Nullable` is applied to an array type in declaration position, " +
"this recipe moves it to the array brackets. " +
"For example, `@Nullable byte[]` becomes `byte @Nullable[]`. " +
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is there the scenario to handle when the type of the elements in the array is a nested class? Pulled from the implementation below J.@Nullable Annotation[] is the ideal form, but do we need to consider if someone wrote @Nullable J.Annotation[], or would that just not compile to begin with?

Comment on lines +98 to +108
List<J.Annotation> leading = ListUtils.map(mv.getLeadingAnnotations(), a -> {
if (match[0] == null && matchesType(a)) {
match[0] = a;
return null;
}
return a;
});
if (leading == mv.getLeadingAnnotations()) {
return mv;
}
mv = mv.withLeadingAnnotations(leading);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I suppose I'm trying to figure out if we have recipes that are going to be fragile in light of leading annotations being dropped and whether any of our recipes need to be adapted to handle the change in position for the annotations (have not had a chance to dig into this further yet)

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.

MigrateFromJavaxAnnotationApi does not modify annotations to the type level

2 participants