diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index 992c3bf45d..0db8c9bd23 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -63,6 +63,7 @@ httpcomponents httpcore IHzb initscript +inlineme Intelli intellij ipynb diff --git a/docs/running-recipes/popular-recipe-guides/migrate-deprecated-methods-with-inlineme.md b/docs/running-recipes/popular-recipe-guides/migrate-deprecated-methods-with-inlineme.md new file mode 100644 index 0000000000..67dc9bd9db --- /dev/null +++ b/docs/running-recipes/popular-recipe-guides/migrate-deprecated-methods-with-inlineme.md @@ -0,0 +1,229 @@ +--- +sidebar_label: Migrate deprecated methods with @InlineMe +description: How to automatically migrate deprecated methods that use the @InlineMe annotation to their replacements. +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Migrate deprecated methods with `@InlineMe` + +Google's Error Prone library introduced the [`@InlineMe`](https://errorprone.info/docs/inlineme) annotation to help with API migrations. When a method is deprecated and marked with `@InlineMe`, it provides precise instructions on how to replace calls to that method. OpenRewrite can now automatically apply these inline replacements across your codebase. + +## What is the `@InlineMe` annotation? + +The `@InlineMe` annotation is a powerful tool for API authors to communicate exactly how deprecated methods should be replaced. It includes: + +* A `replacement` string showing the new code pattern +* Optional `imports` and `staticImports` fields for any new imports needed +* Support for parameter reordering and transformations + +This is particularly useful when: + +* Method signatures change (e.g., parameter order) +* Methods move to different classes +* Multiple methods are consolidated into one with different parameters + +## Example Configuration + +To run the `InlineMethodCalls` recipe that automatically applies these migrations: + + + + +```groovy title="build.gradle" +plugins { + id("java") + id("org.openrewrite.rewrite") version("{{VERSION_REWRITE_GRADLE_PLUGIN}}") +} + +rewrite { + activeRecipe("org.openrewrite.java.migrate.InlineMethodCalls") +} + +repositories { + mavenCentral() +} + +dependencies { + rewrite(platform("org.openrewrite.recipe:rewrite-recipe-bom:latest.release")) + rewrite("org.openrewrite.recipe:rewrite-migrate-java") + + // Other project dependencies +} +``` + + + + +```xml title="pom.xml" + + + + org.openrewrite.maven + rewrite-maven-plugin + {{VERSION_REWRITE_MAVEN_PLUGIN}} + + + org.openrewrite.java.migrate.InlineMethodCalls + + + + + org.openrewrite.recipe + rewrite-migrate-java + {{VERSION_REWRITE_MIGRATE_JAVA}} + + + + + +``` + + + + +## How it works + +When you run this recipe, OpenRewrite will: + +1. **Scan for @InlineMe annotations** to find all deprecated methods that have migration instructions +2. **Locate all usages** of these deprecated methods throughout your codebase +3. **Apply the replacements** automatically, rewriting each call according to the pattern specified in the annotation +4. **Update imports** by adding any newly required imports and removing ones that are no longer needed + +## Real-world example: Google's S2 Geometry Library + +Here's an example from Google's S2 Geometry library showing how `@InlineMe` helps migrate deprecated APIs: + +### Deprecated method with @InlineMe + +```java +@Deprecated +@InlineMe( + replacement = "S2EdgeUtil.getPointOnLine(a, b, ax)", + imports = "com.google.common.geometry.S2EdgeUtil") +public static S2Point interpolateAtDistance(S1Angle ax, S2Point a, S2Point b) { + return getPointOnLine(a, b, ax); +} + +@Deprecated +@InlineMe( + replacement = "S2EdgeUtil.interpolate(a, b, t)", + imports = "com.google.common.geometry.S2EdgeUtil") +public static S2Point interpolate(double t, S2Point a, S2Point b) { + return interpolate(a, b, t); +} +``` + +### Before and After migration + + + + +```java +import com.google.common.geometry.*; + +public class S2ConvexHullQuery { + public S2Loop createLoop(S2Point a, S2Point b) { + // Note the parameter order: t comes first + S2Loop loop = new S2Loop(ImmutableList.of( + a, + b, + S2EdgeUtil.interpolate(0.5, a, b) + )); + loop.normalize(); + return loop; + } +} +``` + + + + +```java +import com.google.common.geometry.*; + +public class S2ConvexHullQuery { + public S2Loop createLoop(S2Point a, S2Point b) { + // Parameters have been reordered: a, b, then t + S2Loop loop = new S2Loop(ImmutableList.of( + a, + b, + S2EdgeUtil.interpolate(a, b, 0.5) + )); + loop.normalize(); + return loop; + } +} +``` + + + + +Notice how the recipe automatically: +- Reordered the parameters from `(t, a, b)` to `(a, b, t)` +- Preserved the exact value `0.5` in its new position +- Kept all other code unchanged + +## More examples from the change + + + + +```diff +- if (tmp.isEndpoint(p) && !visitor.test(shapeId, edgeId, tmp.a, tmp.b)) { ++ if (tmp.hasEndpoint(p) && !visitor.test(shapeId, edgeId, tmp.a, tmp.b)) { +``` + +The `isEndpoint` method was renamed to `hasEndpoint` - a simple rename that's perfectly handled by `@InlineMe`. + + + + +```diff +- S2Point midpoint = S2EdgeUtil.interpolateAtDistance(S1Angle.radians(0.5), start, end); ++ S2Point midpoint = S2EdgeUtil.getPointOnLine(start, end, S1Angle.radians(0.5)); +``` + +Here the method name changed and parameters were reordered, with the angle moving from first to last position. + + + + +## Creating your own @InlineMe migrations + +If you're a library author, you can use `@InlineMe` in your own APIs: + +```java +@Deprecated +@InlineMe( + replacement = "this.newMethod(param2, param1)", // Swap parameter order + imports = {}) // No new imports needed +public void oldMethod(String param1, int param2) { + newMethod(param2, param1); +} + +public void newMethod(int number, String text) { + // New implementation +} +``` + +## Running the recipe + +Execute the migration with: + +- **Gradle**: `./gradlew rewriteRun` +- **Maven**: `mvn rewrite:run` + +After running, review the changes with `git diff` to see all the automated replacements. + +## See how this recipe works across multiple open-source repositories + +import RecipeCallout from '@site/src/components/ModerneLink'; + + + +The community edition of the Moderne platform enables you to easily run recipes across thousands of open-source repositories. + +Please [contact Moderne](https://moderne.io/product) for more information about safely running the recipes on your own codebase in a private SaaS. diff --git a/sidebars.ts b/sidebars.ts index 08b8b22965..7b64d0990d 100644 --- a/sidebars.ts +++ b/sidebars.ts @@ -32,6 +32,7 @@ const sidebars: SidebarsConfig = { items: [ 'running-recipes/popular-recipe-guides/common-static-analysis-issue-remediation', 'running-recipes/popular-recipe-guides/automatically-fix-checkstyle-violations', + 'running-recipes/popular-recipe-guides/migrate-deprecated-methods-with-inlineme', 'running-recipes/popular-recipe-guides/migrate-to-java-17', 'running-recipes/popular-recipe-guides/migrate-to-java-21', 'running-recipes/popular-recipe-guides/migrate-from-junit-4-to-junit-5',