Skip to content

Commit 62bfd31

Browse files
Add a guide on migrating deprecated methods (#414)
* Add a guide on migrating deprecated methods Based on a suggestion from Tim: openrewrite/rewrite-migrate-java#788 (comment) * Fix spelling warnings * Slight polish * Add markdown around annotation in title --------- Co-authored-by: Mike Solomon <mike-solomon@users.noreply.github.com> Co-authored-by: Tim te Beek <tim@moderne.io>
1 parent 5d1a142 commit 62bfd31

File tree

3 files changed

+231
-0
lines changed

3 files changed

+231
-0
lines changed

.github/actions/spelling/expect.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ httpcomponents
6363
httpcore
6464
IHzb
6565
initscript
66+
inlineme
6667
Intelli
6768
intellij
6869
ipynb
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
---
2+
sidebar_label: Migrate deprecated methods with @InlineMe
3+
description: How to automatically migrate deprecated methods that use the @InlineMe annotation to their replacements.
4+
---
5+
6+
import Tabs from '@theme/Tabs';
7+
import TabItem from '@theme/TabItem';
8+
9+
# Migrate deprecated methods with `@InlineMe`
10+
11+
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.
12+
13+
## What is the `@InlineMe` annotation?
14+
15+
The `@InlineMe` annotation is a powerful tool for API authors to communicate exactly how deprecated methods should be replaced. It includes:
16+
17+
* A `replacement` string showing the new code pattern
18+
* Optional `imports` and `staticImports` fields for any new imports needed
19+
* Support for parameter reordering and transformations
20+
21+
This is particularly useful when:
22+
23+
* Method signatures change (e.g., parameter order)
24+
* Methods move to different classes
25+
* Multiple methods are consolidated into one with different parameters
26+
27+
## Example Configuration
28+
29+
To run the `InlineMethodCalls` recipe that automatically applies these migrations:
30+
31+
<Tabs groupId="projectType">
32+
<TabItem value="gradle" label="Gradle">
33+
34+
```groovy title="build.gradle"
35+
plugins {
36+
id("java")
37+
id("org.openrewrite.rewrite") version("{{VERSION_REWRITE_GRADLE_PLUGIN}}")
38+
}
39+
40+
rewrite {
41+
activeRecipe("org.openrewrite.java.migrate.InlineMethodCalls")
42+
}
43+
44+
repositories {
45+
mavenCentral()
46+
}
47+
48+
dependencies {
49+
rewrite(platform("org.openrewrite.recipe:rewrite-recipe-bom:latest.release"))
50+
rewrite("org.openrewrite.recipe:rewrite-migrate-java")
51+
52+
// Other project dependencies
53+
}
54+
```
55+
56+
</TabItem>
57+
<TabItem value="maven" label="Maven">
58+
59+
```xml title="pom.xml"
60+
<build>
61+
<plugins>
62+
<plugin>
63+
<groupId>org.openrewrite.maven</groupId>
64+
<artifactId>rewrite-maven-plugin</artifactId>
65+
<version>{{VERSION_REWRITE_MAVEN_PLUGIN}}</version>
66+
<configuration>
67+
<activeRecipes>
68+
<recipe>org.openrewrite.java.migrate.InlineMethodCalls</recipe>
69+
</activeRecipes>
70+
</configuration>
71+
<dependencies>
72+
<dependency>
73+
<groupId>org.openrewrite.recipe</groupId>
74+
<artifactId>rewrite-migrate-java</artifactId>
75+
<version>{{VERSION_REWRITE_MIGRATE_JAVA}}</version>
76+
</dependency>
77+
</dependencies>
78+
</plugin>
79+
</plugins>
80+
</build>
81+
```
82+
83+
</TabItem>
84+
</Tabs>
85+
86+
## How it works
87+
88+
When you run this recipe, OpenRewrite will:
89+
90+
1. **Scan for @InlineMe annotations** to find all deprecated methods that have migration instructions
91+
2. **Locate all usages** of these deprecated methods throughout your codebase
92+
3. **Apply the replacements** automatically, rewriting each call according to the pattern specified in the annotation
93+
4. **Update imports** by adding any newly required imports and removing ones that are no longer needed
94+
95+
## Real-world example: Google's S2 Geometry Library
96+
97+
Here's an example from Google's S2 Geometry library showing how `@InlineMe` helps migrate deprecated APIs:
98+
99+
### Deprecated method with @InlineMe
100+
101+
```java
102+
@Deprecated
103+
@InlineMe(
104+
replacement = "S2EdgeUtil.getPointOnLine(a, b, ax)",
105+
imports = "com.google.common.geometry.S2EdgeUtil")
106+
public static S2Point interpolateAtDistance(S1Angle ax, S2Point a, S2Point b) {
107+
return getPointOnLine(a, b, ax);
108+
}
109+
110+
@Deprecated
111+
@InlineMe(
112+
replacement = "S2EdgeUtil.interpolate(a, b, t)",
113+
imports = "com.google.common.geometry.S2EdgeUtil")
114+
public static S2Point interpolate(double t, S2Point a, S2Point b) {
115+
return interpolate(a, b, t);
116+
}
117+
```
118+
119+
### Before and After migration
120+
121+
<Tabs>
122+
<TabItem value="before" label="Before">
123+
124+
```java
125+
import com.google.common.geometry.*;
126+
127+
public class S2ConvexHullQuery {
128+
public S2Loop createLoop(S2Point a, S2Point b) {
129+
// Note the parameter order: t comes first
130+
S2Loop loop = new S2Loop(ImmutableList.of(
131+
a,
132+
b,
133+
S2EdgeUtil.interpolate(0.5, a, b)
134+
));
135+
loop.normalize();
136+
return loop;
137+
}
138+
}
139+
```
140+
141+
</TabItem>
142+
<TabItem value="after" label="After">
143+
144+
```java
145+
import com.google.common.geometry.*;
146+
147+
public class S2ConvexHullQuery {
148+
public S2Loop createLoop(S2Point a, S2Point b) {
149+
// Parameters have been reordered: a, b, then t
150+
S2Loop loop = new S2Loop(ImmutableList.of(
151+
a,
152+
b,
153+
S2EdgeUtil.interpolate(a, b, 0.5)
154+
));
155+
loop.normalize();
156+
return loop;
157+
}
158+
}
159+
```
160+
161+
</TabItem>
162+
</Tabs>
163+
164+
Notice how the recipe automatically:
165+
- Reordered the parameters from `(t, a, b)` to `(a, b, t)`
166+
- Preserved the exact value `0.5` in its new position
167+
- Kept all other code unchanged
168+
169+
## More examples from the change
170+
171+
<Tabs>
172+
<TabItem value="example1" label="Method rename">
173+
174+
```diff
175+
- if (tmp.isEndpoint(p) && !visitor.test(shapeId, edgeId, tmp.a, tmp.b)) {
176+
+ if (tmp.hasEndpoint(p) && !visitor.test(shapeId, edgeId, tmp.a, tmp.b)) {
177+
```
178+
179+
The `isEndpoint` method was renamed to `hasEndpoint` - a simple rename that's perfectly handled by `@InlineMe`.
180+
181+
</TabItem>
182+
<TabItem value="example2" label="Complex parameter reordering">
183+
184+
```diff
185+
- S2Point midpoint = S2EdgeUtil.interpolateAtDistance(S1Angle.radians(0.5), start, end);
186+
+ S2Point midpoint = S2EdgeUtil.getPointOnLine(start, end, S1Angle.radians(0.5));
187+
```
188+
189+
Here the method name changed and parameters were reordered, with the angle moving from first to last position.
190+
191+
</TabItem>
192+
</Tabs>
193+
194+
## Creating your own @InlineMe migrations
195+
196+
If you're a library author, you can use `@InlineMe` in your own APIs:
197+
198+
```java
199+
@Deprecated
200+
@InlineMe(
201+
replacement = "this.newMethod(param2, param1)", // Swap parameter order
202+
imports = {}) // No new imports needed
203+
public void oldMethod(String param1, int param2) {
204+
newMethod(param2, param1);
205+
}
206+
207+
public void newMethod(int number, String text) {
208+
// New implementation
209+
}
210+
```
211+
212+
## Running the recipe
213+
214+
Execute the migration with:
215+
216+
- **Gradle**: `./gradlew rewriteRun`
217+
- **Maven**: `mvn rewrite:run`
218+
219+
After running, review the changes with `git diff` to see all the automated replacements.
220+
221+
## See how this recipe works across multiple open-source repositories
222+
223+
import RecipeCallout from '@site/src/components/ModerneLink';
224+
225+
<RecipeCallout link="https://app.moderne.io/recipes/org.openrewrite.java.migrate.InlineMethodCalls" />
226+
227+
The community edition of the Moderne platform enables you to easily run recipes across thousands of open-source repositories.
228+
229+
Please [contact Moderne](https://moderne.io/product) for more information about safely running the recipes on your own codebase in a private SaaS.

sidebars.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ const sidebars: SidebarsConfig = {
3232
items: [
3333
'running-recipes/popular-recipe-guides/common-static-analysis-issue-remediation',
3434
'running-recipes/popular-recipe-guides/automatically-fix-checkstyle-violations',
35+
'running-recipes/popular-recipe-guides/migrate-deprecated-methods-with-inlineme',
3536
'running-recipes/popular-recipe-guides/migrate-to-java-17',
3637
'running-recipes/popular-recipe-guides/migrate-to-java-21',
3738
'running-recipes/popular-recipe-guides/migrate-from-junit-4-to-junit-5',

0 commit comments

Comments
 (0)