diff --git a/build.gradle.kts b/build.gradle.kts
index 6c05e78f2e..62653bc3d8 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -58,7 +58,8 @@ dependencies {
testImplementation("org.assertj:assertj-core:latest.release")
- testImplementation("com.google.guava:guava:33.0.0-jre")
+ testImplementation("com.google.errorprone:error_prone_annotations:latest.release")
+ testImplementation("com.google.guava:guava:33.4.8-jre")
testImplementation("joda-time:joda-time:2.12.3")
testImplementation("org.threeten:threeten-extra:1.8.0")
diff --git a/src/main/java/org/openrewrite/java/migrate/InlineMethodCalls.java b/src/main/java/org/openrewrite/java/migrate/InlineMethodCalls.java
new file mode 100644
index 0000000000..4fbf10a19f
--- /dev/null
+++ b/src/main/java/org/openrewrite/java/migrate/InlineMethodCalls.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright 2025 the original author or authors.
+ *
+ * Licensed under the Moderne Source Available License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://docs.moderne.io/licensing/moderne-source-available-license
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.migrate;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.Value;
+import org.jspecify.annotations.Nullable;
+import org.openrewrite.Cursor;
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.Recipe;
+import org.openrewrite.TreeVisitor;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.java.JavaTemplate;
+import org.openrewrite.java.JavaVisitor;
+import org.openrewrite.java.tree.*;
+
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static java.util.Collections.emptySet;
+import static java.util.Objects.requireNonNull;
+import static java.util.stream.Collectors.toMap;
+import static java.util.stream.Collectors.toSet;
+
+public class InlineMethodCalls extends Recipe {
+
+ private static final String INLINE_ME = "com.google.errorprone.annotations.InlineMe";
+
+ @Override
+ public String getDisplayName() {
+ return "Inline methods annotated with `@InlineMe`";
+ }
+
+ @Override
+ public String getDescription() {
+ return "Apply inlinings defined by Error Prone's [`@InlineMe` annotation](https://errorprone.info/docs/inlineme).";
+ }
+
+ @Override
+ public TreeVisitor, ExecutionContext> getVisitor() {
+ // XXX Preconditions can not yet pick up the `@InlineMe` annotation on methods used
+ return new JavaVisitor() {
+ @Override
+ public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
+ J.MethodInvocation mi = (J.MethodInvocation) super.visitMethodInvocation(method, ctx);
+ InlineMeValues values = findInlineMeValues(mi.getMethodType());
+ if (values == null) {
+ return mi;
+ }
+ Template template = values.template(mi);
+ if (template == null) {
+ return mi;
+ }
+ removeAndAddImports(method, values.getImports(), values.getStaticImports());
+ J replacement = JavaTemplate.builder(template.getString())
+ .contextSensitive()
+ .imports(values.getImports().toArray(new String[0]))
+ .staticImports(values.getStaticImports().toArray(new String[0]))
+ .javaParser(JavaParser.fromJavaVersion().classpath(JavaParser.runtimeClasspath()))
+ .build()
+ .apply(updateCursor(mi), mi.getCoordinates().replace(), template.getParameters());
+ return avoidMethodSelfReferences(mi, replacement);
+ }
+
+ @Override
+ public J visitNewClass(J.NewClass newClass, ExecutionContext ctx) {
+ J.NewClass nc = (J.NewClass) super.visitNewClass(newClass, ctx);
+ InlineMeValues values = findInlineMeValues(nc.getConstructorType());
+ if (values == null) {
+ return nc;
+ }
+ Template template = values.template(nc);
+ if (template == null) {
+ return nc;
+ }
+ removeAndAddImports(newClass, values.getImports(), values.getStaticImports());
+ J replacement = JavaTemplate.builder(template.getString())
+ .contextSensitive()
+ .imports(values.getImports().toArray(new String[0]))
+ .staticImports(values.getStaticImports().toArray(new String[0]))
+ .javaParser(JavaParser.fromJavaVersion().classpath(JavaParser.runtimeClasspath()))
+ .build()
+ .apply(updateCursor(nc), nc.getCoordinates().replace(), template.getParameters());
+ return avoidMethodSelfReferences(nc, replacement);
+ }
+
+ private @Nullable InlineMeValues findInlineMeValues(JavaType.@Nullable Method methodType) {
+ if (methodType == null) {
+ return null;
+ }
+ List parameterNames = methodType.getParameterNames();
+ if (!parameterNames.isEmpty() && "arg0".equals(parameterNames.get(0))) {
+ return null; // We need `-parameters` before we're able to substitute parameters in the template
+ }
+
+ List annotations = methodType.getAnnotations();
+ for (JavaType.FullyQualified annotation : annotations) {
+ if (INLINE_ME.equals(annotation.getFullyQualifiedName())) {
+ return InlineMeValues.parse((JavaType.Annotation) annotation);
+ }
+ }
+ return null;
+ }
+
+ private void removeAndAddImports(MethodCall method, Set templateImports, Set templateStaticImports) {
+ Set originalImports = findOriginalImports(method);
+
+ // Remove regular and static imports that are no longer needed
+ for (String originalImport : originalImports) {
+ if (!templateImports.contains(originalImport) &&
+ !templateStaticImports.contains(originalImport)) {
+ maybeRemoveImport(originalImport);
+ }
+ }
+
+ // Add new regular imports needed by the template
+ for (String importStr : templateImports) {
+ if (!originalImports.contains(importStr)) {
+ maybeAddImport(importStr);
+ }
+ }
+
+ // Add new static imports needed by the template
+ for (String staticImport : templateStaticImports) {
+ if (!originalImports.contains(staticImport)) {
+ int lastDot = staticImport.lastIndexOf('.');
+ if (0 < lastDot) {
+ maybeAddImport(
+ staticImport.substring(0, lastDot),
+ staticImport.substring(lastDot + 1));
+ }
+ }
+ }
+ }
+
+ private Set findOriginalImports(MethodCall method) {
+ // Collect all regular and static imports used in the original method call
+ return new JavaVisitor>() {
+ @Override
+ public @Nullable JavaType visitType(@Nullable JavaType javaType, Set strings) {
+ JavaType jt = super.visitType(javaType, strings);
+ if (jt instanceof JavaType.FullyQualified) {
+ strings.add(((JavaType.FullyQualified) jt).getFullyQualifiedName());
+ }
+ return jt;
+ }
+
+ @Override
+ public J visitMethodInvocation(J.MethodInvocation methodInvocation, Set staticImports) {
+ J.MethodInvocation mi = (J.MethodInvocation) super.visitMethodInvocation(methodInvocation, staticImports);
+ // Check if this is a static method invocation without a select (meaning it might be statically imported)
+ JavaType.Method methodType = mi.getMethodType();
+ if (mi.getSelect() == null && methodType != null && methodType.hasFlags(Flag.Static)) {
+ staticImports.add(String.format("%s.%s",
+ methodType.getDeclaringType().getFullyQualifiedName(),
+ methodType.getName()));
+ }
+ return mi;
+ }
+
+ @Override
+ public J visitIdentifier(J.Identifier identifier, Set staticImports) {
+ J.Identifier id = (J.Identifier) super.visitIdentifier(identifier, staticImports);
+ // Check if this is a static field reference
+ JavaType.Variable fieldType = id.getFieldType();
+ if (fieldType != null && fieldType.hasFlags(Flag.Static)) {
+ if (fieldType.getOwner() instanceof JavaType.FullyQualified) {
+ staticImports.add(String.format("%s.%s",
+ ((JavaType.FullyQualified) fieldType.getOwner()).getFullyQualifiedName(),
+ fieldType.getName()));
+ }
+ }
+ return id;
+ }
+ }.reduce(method, new HashSet<>());
+ }
+
+ private J avoidMethodSelfReferences(MethodCall original, J replacement) {
+ JavaType.Method replacementMethodType = replacement instanceof MethodCall ?
+ ((MethodCall) replacement).getMethodType() : null;
+ if (replacementMethodType == null) {
+ return replacement;
+ }
+
+ Cursor cursor = getCursor();
+ while ((cursor = cursor.getParent()) != null) {
+ Object value = cursor.getValue();
+
+ JavaType.Method cursorMethodType;
+ if (value instanceof MethodCall) {
+ cursorMethodType = ((MethodCall) value).getMethodType();
+ } else if (value instanceof J.MethodDeclaration) {
+ cursorMethodType = ((J.MethodDeclaration) value).getMethodType();
+ } else {
+ continue;
+ }
+ if (TypeUtils.isOfType(replacementMethodType, cursorMethodType)) {
+ return original;
+ }
+ }
+ return replacement;
+ }
+ };
+ }
+
+ @Value
+ private static class InlineMeValues {
+ private static final Pattern TEMPLATE_IDENTIFIER = Pattern.compile("#\\{(\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*):any\\(.*?\\)}");
+
+ @Getter(AccessLevel.NONE)
+ String replacement;
+
+ Set imports;
+ Set staticImports;
+
+ static InlineMeValues parse(JavaType.Annotation annotation) {
+ Map collect = annotation.getValues().stream().collect(toMap(
+ e -> ((JavaType.Method) e.getElement()).getName(),
+ JavaType.Annotation.ElementValue::getValue
+ ));
+ // Parse imports and static imports from the annotation values
+ return new InlineMeValues(
+ (String) collect.get("replacement"),
+ parseImports(collect.get("imports")),
+ parseImports(collect.get("staticImports")));
+ }
+
+ private static Set parseImports(@Nullable Object importsValue) {
+ if (importsValue instanceof List) {
+ return ((List>) importsValue).stream()
+ .map(Object::toString)
+ .collect(toSet());
+ }
+ return emptySet();
+ }
+
+ @Nullable
+ Template template(MethodCall original) {
+ JavaType.Method methodType = original.getMethodType();
+ if (methodType == null) {
+ return null;
+ }
+ String templateString = createTemplateString(original, replacement, methodType.getParameterNames());
+ List parameters = createParameters(templateString, original);
+ return new Template(templateString, parameters.toArray(new Object[0]));
+ }
+
+ private static String createTemplateString(MethodCall original, String replacement, List originalParameterNames) {
+ String templateString = original instanceof J.MethodInvocation &&
+ ((J.MethodInvocation) original).getSelect() == null &&
+ replacement.startsWith("this.") ?
+ replacement.replaceFirst("^this.\\b", "") :
+ replacement.replaceAll("\\bthis\\b", "#{this:any()}");
+ for (String parameterName : originalParameterNames) {
+ // Replace parameter names with their values in the templateString
+ templateString = templateString.replaceAll(
+ String.format("\\b%s\\b", parameterName),
+ String.format("#{%s:any()}", parameterName)); // TODO 2nd, 3rd etc should use shorthand `#{a}`
+ }
+ return templateString;
+ }
+
+ private static List createParameters(String templateString, MethodCall original) {
+ Map lookup = new HashMap<>();
+ if (original instanceof J.MethodInvocation) {
+ Expression select = ((J.MethodInvocation) original).getSelect();
+ if (select != null) {
+ lookup.put("this", select);
+ }
+ }
+ List originalParameterNames = requireNonNull(original.getMethodType()).getParameterNames();
+ for (int i = 0; i < originalParameterNames.size(); i++) {
+ String originalName = originalParameterNames.get(i);
+ Expression originalValue = original.getArguments().get(i);
+ lookup.put(originalName, originalValue);
+ }
+ List parameters = new ArrayList<>();
+ Matcher matcher = TEMPLATE_IDENTIFIER.matcher(templateString);
+ while (matcher.find()) {
+ Expression o = lookup.get(matcher.group(1));
+ if (o != null) {
+ parameters.add(o);
+ }
+ }
+ return parameters;
+ }
+ }
+
+ @Value
+ private static class Template {
+ String string;
+ Object[] parameters;
+ }
+}
diff --git a/src/main/resources/META-INF/rewrite/no-guava.yml b/src/main/resources/META-INF/rewrite/no-guava.yml
index 56e204bf76..87863a9a47 100644
--- a/src/main/resources/META-INF/rewrite/no-guava.yml
+++ b/src/main/resources/META-INF/rewrite/no-guava.yml
@@ -29,6 +29,7 @@ recipeList:
- org.openrewrite.java.migrate.guava.NoGuavaJava21
- org.openrewrite.java.migrate.guava.NoGuavaCreateTempDir
- org.openrewrite.java.migrate.guava.NoGuavaDirectExecutor
+ - org.openrewrite.java.migrate.guava.NoGuavaInlineMeMethods
- org.openrewrite.java.migrate.guava.NoGuavaListsNewArrayList
- org.openrewrite.java.migrate.guava.NoGuavaListsNewCopyOnWriteArrayList
- org.openrewrite.java.migrate.guava.NoGuavaListsNewLinkedList
@@ -108,6 +109,20 @@ recipeList:
- org.openrewrite.java.migrate.guava.NoMapsAndSetsWithExpectedSize
- org.openrewrite.java.migrate.guava.PreferMathClamp
+---
+type: specs.openrewrite.org/v1beta/recipe
+name: org.openrewrite.java.migrate.guava.NoGuavaInlineMeMethods
+displayName: Inline Guava method calls
+description: >-
+ Inline Guava method calls that are annotated with `@InlineMe` to their replacement method.
+tags:
+ - guava
+preconditions:
+ - org.openrewrite.analysis.search.FindMethods:
+ methodPattern: com.google.common..* *(..)
+recipeList:
+ - org.openrewrite.java.migrate.InlineMethodCalls
+
---
type: specs.openrewrite.org/v1beta/recipe
name: org.openrewrite.java.migrate.guava.PreferJavaNioCharsetStandardCharsets
diff --git a/src/test/java/org/openrewrite/java/migrate/InlineMethodCallsTest.java b/src/test/java/org/openrewrite/java/migrate/InlineMethodCallsTest.java
new file mode 100644
index 0000000000..a046b214e9
--- /dev/null
+++ b/src/test/java/org/openrewrite/java/migrate/InlineMethodCallsTest.java
@@ -0,0 +1,440 @@
+/*
+ * Copyright 2025 the original author or authors.
+ *
+ * Licensed under the Moderne Source Available License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://docs.moderne.io/licensing/moderne-source-available-license
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.migrate;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class InlineMethodCallsTest implements RewriteTest {
+
+ @Override
+ public void defaults(RecipeSpec spec) {
+ spec.recipe(new InlineMethodCalls())
+ .parser(JavaParser.fromJavaVersion().classpath("guava", "error_prone_annotations"));
+ }
+
+ @DocumentExample
+ @Test
+ void inlineMeSimple() {
+ //language=java
+ rewriteRun(
+ java(
+ """
+ import com.google.errorprone.annotations.InlineMe;
+
+ class Lib {
+ @Deprecated
+ @InlineMe(replacement = "this.replacement()")
+ public void deprecated() {}
+ public void replacement() {}
+
+ public static void usage(Lib lib) {
+ lib.deprecated();
+ }
+ }
+ """,
+ """
+ import com.google.errorprone.annotations.InlineMe;
+
+ class Lib {
+ @Deprecated
+ @InlineMe(replacement = "this.replacement()")
+ public void deprecated() {}
+ public void replacement() {}
+
+ public static void usage(Lib lib) {
+ lib.replacement();
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void inlineMeNonStatic() {
+ //language=java
+ rewriteRun(
+ java(
+ """
+ import com.google.errorprone.annotations.InlineMe;
+
+ class Lib {
+ @Deprecated
+ @InlineMe(replacement = "this.replacement()")
+ public void deprecated() {}
+ public void replacement() {}
+
+ public void usage() {
+ deprecated();
+ }
+ }
+ """,
+ """
+ import com.google.errorprone.annotations.InlineMe;
+
+ class Lib {
+ @Deprecated
+ @InlineMe(replacement = "this.replacement()")
+ public void deprecated() {}
+ public void replacement() {}
+
+ public void usage() {
+ replacement();
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void inlineMeChained() {
+ //language=java
+ rewriteRun(
+ java(
+ """
+ import com.google.errorprone.annotations.InlineMe;
+ import java.time.Duration;
+
+ class Lib {
+ private final Duration deadline;
+
+ public Duration getDeadline() {
+ return deadline;
+ }
+
+ @Deprecated
+ @InlineMe(replacement = "this.getDeadline().toMillis()")
+ public long getDeadlineMillis() {
+ return getDeadline().toMillis();
+ }
+
+ long usage() {
+ return getDeadlineMillis();
+ }
+ }
+ """,
+ """
+ import com.google.errorprone.annotations.InlineMe;
+ import java.time.Duration;
+
+ class Lib {
+ private final Duration deadline;
+
+ public Duration getDeadline() {
+ return deadline;
+ }
+
+ @Deprecated
+ @InlineMe(replacement = "this.getDeadline().toMillis()")
+ public long getDeadlineMillis() {
+ return getDeadline().toMillis();
+ }
+
+ long usage() {
+ return getDeadline().toMillis();
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void instanceMethodWithImports() {
+ //language=java
+ rewriteRun(
+ java(
+ """
+ import com.google.errorprone.annotations.InlineMe;
+
+ class MyClass {
+ private java.time.Duration deadline;
+
+ public void setDeadline(java.time.Duration deadline) {
+ this.deadline = deadline;
+ }
+
+ @Deprecated
+ @InlineMe(
+ replacement = "this.setDeadline(Duration.ofMillis(millis))",
+ imports = {"java.time.Duration"})
+ public void setDeadline(long millis) {
+ setDeadline(java.time.Duration.ofMillis(millis));
+ }
+
+ void usage() {
+ setDeadline(1000L);
+ }
+ }
+ """,
+ """
+ import com.google.errorprone.annotations.InlineMe;
+
+ import java.time.Duration;
+
+ class MyClass {
+ private Duration deadline;
+
+ public void setDeadline(Duration deadline) {
+ this.deadline = deadline;
+ }
+
+ @Deprecated
+ @InlineMe(
+ replacement = "this.setDeadline(Duration.ofMillis(millis))",
+ imports = {"java.time.Duration"})
+ public void setDeadline(long millis) {
+ setDeadline(Duration.ofMillis(millis));
+ }
+
+ void usage() {
+ setDeadline(Duration.ofMillis(1000L));
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void staticMethodReplacement() {
+ //language=java
+ rewriteRun(
+ java(
+ """
+ package com.google.frobber;
+
+ import com.google.errorprone.annotations.InlineMe;
+
+ class Frobber {
+
+ public static Frobber fromName(String name) {
+ return new Frobber();
+ }
+
+ @Deprecated
+ @InlineMe(
+ replacement = "Frobber.fromName(name)",
+ imports = {"com.google.frobber.Frobber"})
+ public static Frobber create(String name) {
+ return fromName(name);
+ }
+
+ void usage() {
+ Frobber f = Frobber.create("test");
+ }
+ }
+ """,
+ """
+ package com.google.frobber;
+
+ import com.google.errorprone.annotations.InlineMe;
+
+ class Frobber {
+
+ public static Frobber fromName(String name) {
+ return new Frobber();
+ }
+
+ @Deprecated
+ @InlineMe(
+ replacement = "Frobber.fromName(name)",
+ imports = {"com.google.frobber.Frobber"})
+ public static Frobber create(String name) {
+ return fromName(name);
+ }
+
+ void usage() {
+ Frobber f = Frobber.fromName("test");
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void constructorToFactoryMethod() {
+ //language=java
+ rewriteRun(
+ java(
+ """
+ package com.google.frobber;
+
+ import com.google.errorprone.annotations.InlineMe;
+ import com.google.errorprone.annotations.InlineMeValidationDisabled;
+
+ class MyClass {
+
+ @InlineMeValidationDisabled
+ @Deprecated
+ @InlineMe(
+ replacement = "MyClass.create()",
+ imports = {"com.google.frobber.MyClass"})
+ public MyClass() {
+ }
+
+ public static MyClass create() {
+ return new MyClass();
+ }
+
+ void usage() {
+ MyClass obj = new MyClass();
+ }
+ }
+ """,
+ """
+ package com.google.frobber;
+
+ import com.google.errorprone.annotations.InlineMe;
+ import com.google.errorprone.annotations.InlineMeValidationDisabled;
+
+ class MyClass {
+
+ @InlineMeValidationDisabled
+ @Deprecated
+ @InlineMe(
+ replacement = "MyClass.create()",
+ imports = {"com.google.frobber.MyClass"})
+ public MyClass() {
+ }
+
+ public static MyClass create() {
+ return new MyClass();
+ }
+
+ void usage() {
+ MyClass obj = MyClass.create();
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void multipleParameters() {
+ //language=java
+ rewriteRun(
+ java(
+ """
+ import com.google.errorprone.annotations.InlineMe;
+
+ class Calculator {
+
+ public int addAndMultiply(int a, int b, int c) {
+ return (a + b) * c;
+ }
+
+ @Deprecated
+ @InlineMe(replacement = "this.addAndMultiply(x, y, z)")
+ public int compute(int x, int y, int z) {
+ return addAndMultiply(x, y, z);
+ }
+
+ void foo(Calculator calc) {
+ int result = calc.compute(1, 2, 3);
+ }
+ }
+ """,
+ """
+ import com.google.errorprone.annotations.InlineMe;
+
+ class Calculator {
+
+ public int addAndMultiply(int a, int b, int c) {
+ return (a + b) * c;
+ }
+
+ @Deprecated
+ @InlineMe(replacement = "this.addAndMultiply(x, y, z)")
+ public int compute(int x, int y, int z) {
+ return addAndMultiply(x, y, z);
+ }
+
+ void foo(Calculator calc) {
+ int result = calc.addAndMultiply(1, 2, 3);
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void nestedMethodCalls() {
+ //language=java
+ rewriteRun(
+ java(
+ """
+ import com.google.errorprone.annotations.InlineMe;
+
+ class Builder {
+
+ public Builder withName(String name) {
+ return this;
+ }
+
+ public Builder withAge(int age) {
+ return this;
+ }
+
+ @Deprecated
+ @InlineMe(replacement = "this.withName(name).withAge(age)")
+ public Builder configure(String name, int age) {
+ return withName(name).withAge(age);
+ }
+
+ void foo(Builder builder) {
+ builder.configure("John", 30);
+ }
+ }
+ """,
+ """
+ import com.google.errorprone.annotations.InlineMe;
+
+ class Builder {
+
+ public Builder withName(String name) {
+ return this;
+ }
+
+ public Builder withAge(int age) {
+ return this;
+ }
+
+ @Deprecated
+ @InlineMe(replacement = "this.withName(name).withAge(age)")
+ public Builder configure(String name, int age) {
+ return withName(name).withAge(age);
+ }
+
+ void foo(Builder builder) {
+ builder.withName("John").withAge(30);
+ }
+ }
+ """
+ )
+ );
+ }
+}
diff --git a/src/test/java/org/openrewrite/java/migrate/guava/NoGuavaInlineMeMethods.java b/src/test/java/org/openrewrite/java/migrate/guava/NoGuavaInlineMeMethods.java
new file mode 100644
index 0000000000..a39c82d524
--- /dev/null
+++ b/src/test/java/org/openrewrite/java/migrate/guava/NoGuavaInlineMeMethods.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2025 the original author or authors.
+ *
+ * Licensed under the Moderne Source Available License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://docs.moderne.io/licensing/moderne-source-available-license
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.migrate.guava;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class NoGuavaInlineMeMethods implements RewriteTest {
+
+ @Override
+ public void defaults(RecipeSpec spec) {
+ spec.recipeFromResource(
+ "/META-INF/rewrite/no-guava.yml",
+ "org.openrewrite.java.migrate.guava.NoGuavaInlineMeMethods");
+ }
+
+ @DocumentExample
+ @Test
+ void stringsRegular() {
+ rewriteRun(
+ java(
+ """
+ import com.google.common.base.Strings;
+ class Regular {
+ String repeatString(String s, int n) {
+ return Strings.repeat(s, n);
+ }
+ }
+ """,
+ """
+ class Regular {
+ String repeatString(String s, int n) {
+ return s.repeat(n);
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void stringsStaticImport() {
+ rewriteRun(
+ java(
+ """
+ import static com.google.common.base.Strings.repeat;
+ class StaticImport {
+ String repeatString(String s, int n) {
+ return repeat(s, n);
+ }
+ }
+ """,
+ """
+ class StaticImport {
+ String repeatString(String s, int n) {
+ return s.repeat(n);
+ }
+ }
+ """
+ )
+ );
+ }
+}