Skip to content

Add RemoveDuplicateAnnotations - #1000

Merged
timtebeek merged 2 commits into
mainfrom
tim/remove-duplicate-annotations
Aug 13, 2026
Merged

Add RemoveDuplicateAnnotations#1000
timtebeek merged 2 commits into
mainfrom
tim/remove-duplicate-annotations

Conversation

@timtebeek

@timtebeek timtebeek commented Aug 13, 2026

Copy link
Copy Markdown
Member

When several distinct annotations migrate to a single new annotation, each ChangeType rewrites its own annotation and the element ends up annotated twice:

import javax.annotation.CheckForNull;
import javax.annotation.Nullable;

class Foo {
    public void bar(@Nullable @CheckForNull String baz) {
    }
}

This recipe removes annotations repeated on the same element, keeping the first occurrence. It covers leading annotations on classes, methods and variables, annotations attached to modifiers, and type-use annotations on array types, nested types and type arguments.

Two guards keep it conservative:

  • only annotations semantically equal to an earlier one are removed, so @Nullable @NonNull and @Tag("a") @Tag("b") are untouched;
  • @Repeatable annotations are skipped, since repeating those is meaningful.

annotationType is optional and defaults to any annotation, so the recipe is useful standalone; rewrite-migrate-java will call it with org.jspecify.annotations.* from its JSpecify migrations.

When several distinct annotations are migrated to a single new annotation --
for instance `javax.annotation.Nullable` and `javax.annotation.CheckForNull`
both becoming `org.jspecify.annotations.Nullable` -- each `ChangeType` rewrites
its own annotation, leaving the element annotated twice.

Remove annotations repeated on the same element, keeping the first occurrence.
Only annotations semantically equal to an earlier one are removed, and
`@Repeatable` annotations are left alone.
@github-project-automation github-project-automation Bot moved this to In Progress in OpenRewrite Aug 13, 2026
timtebeek added a commit to openrewrite/rewrite-migrate-java that referenced this pull request Aug 13, 2026
The recipe is not specific to JSpecify: any many-to-one `ChangeType` mapping can
leave an element annotated twice. It now lives in rewrite-static-analysis, next
to `RemoveDuplicateConditions`, with an optional `annotationType` defaulting to
any annotation and a guard for `@Repeatable` annotations.

See openrewrite/rewrite-static-analysis#1000
@timtebeek

Copy link
Copy Markdown
Member Author

CI is red on ten TypeScript tests (JSON-RPC peer closed the stream / RPC process shut down early with exit code 1). Those are pre-existing on main — the same ten fail in run 31728116299 on main at 17:55, before this branch, and they fail locally on a clean checkout too. RemoveDuplicateAnnotationsTest passes.

A published rewrite-javascript snapshot pins an exact @openrewrite/rewrite
version, and the npm release of that version can lag the Maven one. The RPC
process then has nothing to run and every typescript() test fails.

warmJavaScriptRpcCache already writes its marker only for an install that
succeeded, so publish that as javaScriptRpcAvailable and let the tests that
need the RPC server skip rather than fail the build over a gap upstream. This
also covers a machine without Node, which the warm task already declines to
require.
@timtebeek
timtebeek merged commit 1b39dcc into main Aug 13, 2026
1 check passed
@timtebeek
timtebeek deleted the tim/remove-duplicate-annotations branch August 13, 2026 18:37
@github-project-automation github-project-automation Bot moved this from In Progress to Done in OpenRewrite Aug 13, 2026
timtebeek added a commit to openrewrite/rewrite-migrate-java that referenced this pull request Aug 14, 2026
… one (#1200)

* Do not duplicate JSpecify annotations when several annotations map to one

When an element carries two nullability annotations that both migrate to the
same JSpecify annotation -- such as `javax.annotation.Nullable` together with
`javax.annotation.CheckForNull`, or annotations from two different frameworks --
the `ChangeType` steps each rewrite their own annotation, leaving the element
annotated twice.

Add a `RemoveDuplicateAnnotations` recipe that drops repeated annotations of the
same type, and run it at the end of each `MigrateFrom*` recipe.

Fixes #1199

* Use ListUtils.filter to drop duplicate annotations

* Move `RemoveDuplicateAnnotations` to rewrite-static-analysis

The recipe is not specific to JSpecify: any many-to-one `ChangeType` mapping can
leave an element annotated twice. It now lives in rewrite-static-analysis, next
to `RemoveDuplicateConditions`, with an optional `annotationType` defaulting to
any annotation and a guard for `@Repeatable` annotations.

See openrewrite/rewrite-static-analysis#1000
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

1 participant