Repository: openrewrite/rewrite-static-analysis
Pull request #563: Enhance RemoveUnusedLocalVariables to support filtering by type
Author: login: bgalek
Labels: name: enhancement
description: New feature or request
Created: 2025-05-19T14:24:48Z | Merged: 2025-05-19T17:02:24Z

## What's changed?

This PR enhances the `RemoveUnusedLocalVariables` recipe in OpenRewrite by introducing a new configuration option: `onlyRemoveVariablesOfType`. This option allows users to specify a list of fully qualified class names; only unused local variables of those types will be considered for removal.

## What's your motivation?

This change addresses an important scalability concern in large codebases. Removing **all** unused local variables often results in extensive, unrelated changes across a project, making code reviews cumbersome.

### Checklist

* [x] I've added unit tests to cover both positive and negative cases
* [x] I've read and applied the [[recipe conventions and best practices](https://docs.openrewrite.org/authoring-recipes/recipe-conventions-and-best-practices)](https://docs.openrewrite.org/authoring-recipes/recipe-conventions-and-best-practices)
* [x] I've used the IntelliJ IDEA auto-formatter on affected files


Diff:
diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java
index 82c6ff045f..1f514c5478 100644
--- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java
+++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java
@@ -15,6 +15,8 @@
  */
 package org.openrewrite.staticanalysis;
 
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.google.errorprone.annotations.InlineMe;
 import lombok.EqualsAndHashCode;
 import lombok.Value;
 import org.jspecify.annotations.Nullable;
@@ -42,12 +44,38 @@ public class RemoveUnusedLocalVariables extends Recipe {
             example = "[unused, notUsed, IGNORE_ME]")
     String @Nullable [] ignoreVariablesNamed;
 
+    @Option(displayName = "Only remove variables of a given type",
+            description = "A fully qualified class name. Only unused local variables whose type matches this will be removed. " +
+                    "If empty or not set, all unused local variables are considered for removal.",
+            required = false,
+            example = "java.lang.String")
+    @Nullable
+    String withType;
+
     @Option(displayName = "Remove unused local variables with side effects in initializer",
             description = "Whether to remove unused local variables despite side effects in the initializer. Default false.",
             required = false)
     @Nullable
     Boolean withSideEffects;
 
+    @JsonCreator
+    public RemoveUnusedLocalVariables(
+            String @Nullable [] ignoreVariablesNamed,
+            @Nullable String withType,
+            @Nullable Boolean withSideEffects) {
+        this.ignoreVariablesNamed = ignoreVariablesNamed;
+        this.withType = withType;
+        this.withSideEffects = withSideEffects;
+    }
+
+    @InlineMe(replacement = "new RemoveUnusedLocalVariables(ignoreVariablesNamed, null, withSideEffects)")
+    @Deprecated
+    public RemoveUnusedLocalVariables(
+            String @Nullable [] ignoreVariablesNamed,
+            @Nullable Boolean withSideEffects) {
+        this(ignoreVariablesNamed, null, withSideEffects);
+    }
+
     @Override
     public String getDisplayName() {
         return "Remove unused local variables";
@@ -108,6 +136,11 @@ private Cursor getCursorToParentScope(Cursor cursor) {
                     return variable;
                 }
 
+                // skip matching ignored variable types right away
+                if (withType != null && !TypeUtils.isOfClassType(variable.getType(), withType)) {
+                    return variable;
+                }
+
                 Cursor parentScope = getCursorToParentScope(getCursor());
                 J parent = parentScope.getValue();
                 if (parentScope.getParent() == null ||
diff --git a/src/main/resources/META-INF/rewrite/examples.yml b/src/main/resources/META-INF/rewrite/examples.yml
index 57aa488cad..3fb2367392 100644
--- a/src/main/resources/META-INF/rewrite/examples.yml
+++ b/src/main/resources/META-INF/rewrite/examples.yml
@@ -2229,6 +2229,7 @@ examples:
   parameters:
   - 'null'
   - 'null'
+  - 'null'
   sources:
   - before: |
       class Test {
diff --git a/src/test/java/org/openrewrite/staticanalysis/MinimumSwitchCasesTest.java b/src/test/java/org/openrewrite/staticanalysis/MinimumSwitchCasesTest.java
index caa3e131ff..6b95c44d92 100644
--- a/src/test/java/org/openrewrite/staticanalysis/MinimumSwitchCasesTest.java
+++ b/src/test/java/org/openrewrite/staticanalysis/MinimumSwitchCasesTest.java
@@ -688,7 +688,7 @@ int test(java.io.ObjectInputFilter filter) {
               import java.io.ObjectInputFilter;
 
               class Test {
-                  int test(java.io.ObjectInputFilter filter) {
+                  int test(ObjectInputFilter filter) {
                       if (filter.checkInput(null) == ObjectInputFilter.Status.ALLOWED) {
                           return 0;
                       } else {
diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariablesTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariablesTest.java
index 5b77f31bb3..2abb49b92a 100644
--- a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariablesTest.java
+++ b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariablesTest.java
@@ -46,7 +46,7 @@ class RemoveUnusedLocalVariablesTest implements RewriteTest {
 
     @Override
     public void defaults(RecipeSpec spec) {
-        spec.recipe(new RemoveUnusedLocalVariables(new String[0], null));
+        spec.recipe(new RemoveUnusedLocalVariables(new String[0], null, null));
     }
 
     @DocumentExample
@@ -100,7 +100,7 @@ static int method(int x) {
     @SuppressWarnings("MethodMayBeStatic")
     void ignoreVariablesNamed() {
         rewriteRun(
-          spec -> spec.recipe(new RemoveUnusedLocalVariables(new String[]{"unused"}, null)),
+          spec -> spec.recipe(new RemoveUnusedLocalVariables(new String[]{"unused"}, null, null)),
           //language=java
           java(
             """
@@ -1076,7 +1076,7 @@ static void method() {
     @Issue("https://github.com/openrewrite/rewrite-feature-flags/pull/35")
     void removeDespiteSideEffects() {
         rewriteRun(
-          spec -> spec.recipe(new RemoveUnusedLocalVariables(null, true)),
+          spec -> spec.recipe(new RemoveUnusedLocalVariables(null, null, true)),
           //language=java
           java(
             """
@@ -1098,6 +1098,122 @@ void method(Object someData) {
         );
     }
 
+    @Test
+    void removeOnlyUnusedLocalVariablesOfTypeString() {
+        rewriteRun(
+          spec -> spec.recipe(new RemoveUnusedLocalVariables(
+            null,
+            "java.lang.String",
+            null
+          )),
+          //language=java
+          java(
+            """
+              class Test {
+                  void method() {
+                      String unusedString = "abc";
+                      Integer unusedInteger = 123;
+                      String usedString = "def";
+                      System.out.println(usedString);
+                  }
+              }
+              """,
+            """
+              class Test {
+                  void method() {
+                      Integer unusedInteger = 123;
+                      String usedString = "def";
+                      System.out.println(usedString);
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removeOnlyUnusedLocalVariablesOfTypeInteger() {
+        rewriteRun(
+          spec -> spec.recipe(new RemoveUnusedLocalVariables(
+            null,
+            "java.lang.Integer",
+            null
+          )),
+          //language=java
+          java(
+            """
+              class Test {
+                  void method() {
+                      String unusedString = "abc";
+                      Integer unusedInteger = 123;
+                      Integer usedInteger = 456;
+                      System.out.println(usedInteger);
+                  }
+              }
+              """,
+            """
+              class Test {
+                  void method() {
+                      String unusedString = "abc";
+                      Integer usedInteger = 456;
+                      System.out.println(usedInteger);
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removeAllUnusedLocalVariablesWhenWithTypeNull() {
+        rewriteRun(
+          spec -> spec.recipe(new RemoveUnusedLocalVariables(
+            null,
+            null,
+            null
+          )),
+          //language=java
+          java(
+            """
+              class Test {
+                  void method() {
+                      String unusedString = "abc";
+                      Integer unusedInteger = 123;
+                  }
+              }
+              """,
+            """
+              class Test {
+                  void method() {
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void doNotRemoveUnusedLocalVariablesWhenWithTypeDoesNotMatch() {
+        rewriteRun(
+          spec -> spec.recipe(new RemoveUnusedLocalVariables(
+            null,
+            "java.lang.BigInteger",
+            null
+          )),
+          //language=java
+          java(
+            """
+              class Test {
+                  void method() {
+                      String unusedString = "abc";
+                      Integer unusedInteger = 123;
+                  }
+              }
+              """
+          )
+        );
+    }
+
     @Nested
     class Kotlin {
 
diff --git a/src/test/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReferenceTest.java b/src/test/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReferenceTest.java
index a0215f37e1..a723cb3720 100644
--- a/src/test/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReferenceTest.java
+++ b/src/test/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReferenceTest.java
@@ -613,7 +613,7 @@ List<Object> filter(List<Object> l) {
               class Test {
                   List<Object> filter(List<Object> l) {
                       return l.stream()
-                          .filter(org.test.CheckType.class::isInstance)
+                          .filter(CheckType.class::isInstance)
                           .map(CheckType.class::cast)
                           .collect(Collectors.toList());
                   }


Reviews:
user: login: github-actions[bot]
body: Some suggestions could not be made:
* src/main/resources/META-INF/rewrite/examples.yml
  * lines 328-345
  * lines 660-659
  * lines 705-721
  * lines 1254-1273
  * lines 1306-1305
  * lines 2337-2336
  * lines 2352-2364
  * lines 2801-2800
  * lines 2820-2831
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T15:00:01Z
user: login: timtebeek
body: Thanks again for the help here! I've added a few small polishing commits, but other than that it looks great.
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-19T16:51:55Z
user: login: github-actions[bot]
body: Some suggestions could not be made:
* src/main/resources/META-INF/rewrite/examples.yml
  * lines 328-345
  * lines 660-659
  * lines 705-721
  * lines 1254-1273
  * lines 1306-1305
  * lines 2232-2231
  * lines 2337-2336
  * lines 2352-2364
  * lines 2801-2800
  * lines 2820-2831
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T16:54:10Z
user: login: iddeepak
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T16:54:47Z
user: login: timtebeek
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-19T16:55:59Z
user: login: timtebeek
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-19T16:56:05Z
user: login: timtebeek
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-19T16:56:18Z
user: login: iddeepak
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T16:58:23Z
user: login: iddeepak
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T17:02:35Z
user: login: github-actions[bot]
body: Some suggestions could not be made:
* src/main/resources/META-INF/rewrite/examples.yml
  * lines 328-345
  * lines 660-659
  * lines 705-721
  * lines 1254-1273
  * lines 1306-1305
  * lines 2338-2337
  * lines 2353-2365
  * lines 2802-2801
  * lines 2821-2832
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T17:03:59Z
user: login: knutwannheden
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T18:26:37Z
user: login: timtebeek
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-19T18:39:05Z

Review comments:
diff_hunk: @@ -42,11 +43,37 @@ public class RemoveUnusedLocalVariables extends Recipe {
             example = "[unused, notUsed, IGNORE_ME]")
     String @Nullable [] ignoreVariablesNamed;
 
-    @Option(displayName = "Remove unused local variables with side effects in initializer",
-            description = "Whether to remove unused local variables despite side effects in the initializer. Default false.",
-            required = false)
-    @Nullable
-    Boolean withSideEffects;
+    @Option(displayName = "Only remove variables of type",
path: src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java
user: login: iddeepak
body: ```suggestion
    @Option(displayName = "Only remove variables of a given type",
```
created_at: 2025-05-19T16:14:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -42,11 +43,37 @@ public class RemoveUnusedLocalVariables extends Recipe {
             example = "[unused, notUsed, IGNORE_ME]")
     String @Nullable [] ignoreVariablesNamed;
 
-    @Option(displayName = "Remove unused local variables with side effects in initializer",
-            description = "Whether to remove unused local variables despite side effects in the initializer. Default false.",
-            required = false)
-    @Nullable
-    Boolean withSideEffects;
+    @Option(displayName = "Only remove variables of type",
+            description = "A fully qualified class names. Only unused local variables whose type matches one of these will be removed. " +
path: src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java
user: login: iddeepak
body: ```suggestion
            description = "A fully qualified class name. Only unused local variables whose type matches this will be removed. " +
```
created_at: 2025-05-19T16:19:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -42,11 +43,37 @@ public class RemoveUnusedLocalVariables extends Recipe {
             example = "[unused, notUsed, IGNORE_ME]")
     String @Nullable [] ignoreVariablesNamed;
 
-    @Option(displayName = "Remove unused local variables with side effects in initializer",
-            description = "Whether to remove unused local variables despite side effects in the initializer. Default false.",
-            required = false)
-    @Nullable
-    Boolean withSideEffects;
+    @Option(displayName = "Only remove variables of type",
+            description = "A fully qualified class names. Only unused local variables whose type matches one of these will be removed. " +
+                          "If empty or not set, all unused local variables are considered for removal.",
+            required = false,
+            example = "java.lang.String")
+    @Nullable String withType;
+
+    @Option(displayName = "Ignore possible side effects",
+            description = "If true, variables with possible side effects in their initializer will be ignored and not removed. " +
+                          "This option was previously named 'withSideEffects'.",
+            required = false,
+            example = "true")
+    @Nullable Boolean withSideEffects;
path: src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java
user: login: iddeepak
body: ```suggestion
    @Nullable Boolean ignoreSideEffects;
```
created_at: 2025-05-19T16:28:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -42,11 +43,37 @@ public class RemoveUnusedLocalVariables extends Recipe {
             example = "[unused, notUsed, IGNORE_ME]")
     String @Nullable [] ignoreVariablesNamed;
 
-    @Option(displayName = "Remove unused local variables with side effects in initializer",
-            description = "Whether to remove unused local variables despite side effects in the initializer. Default false.",
-            required = false)
-    @Nullable
-    Boolean withSideEffects;
+    @Option(displayName = "Only remove variables of type",
+            description = "A fully qualified class names. Only unused local variables whose type matches one of these will be removed. " +
+                          "If empty or not set, all unused local variables are considered for removal.",
+            required = false,
+            example = "java.lang.String")
+    @Nullable String withType;
+
+    @Option(displayName = "Ignore possible side effects",
+            description = "If true, variables with possible side effects in their initializer will be ignored and not removed. " +
+                          "This option was previously named 'withSideEffects'.",
+            required = false,
+            example = "true")
+    @Nullable Boolean withSideEffects;
+
+    @JsonCreator
+    public RemoveUnusedLocalVariables(
+            String @Nullable [] ignoreVariablesNamed,
+            @Nullable String withType,
+            @Nullable Boolean withSideEffects
path: src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java
user: login: iddeepak
body: ```suggestion
            @Nullable Boolean ignoreSideEffects
```
created_at: 2025-05-19T16:31:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -42,11 +43,37 @@ public class RemoveUnusedLocalVariables extends Recipe {
             example = "[unused, notUsed, IGNORE_ME]")
     String @Nullable [] ignoreVariablesNamed;
 
-    @Option(displayName = "Remove unused local variables with side effects in initializer",
-            description = "Whether to remove unused local variables despite side effects in the initializer. Default false.",
-            required = false)
-    @Nullable
-    Boolean withSideEffects;
+    @Option(displayName = "Only remove variables of type",
+            description = "A fully qualified class names. Only unused local variables whose type matches one of these will be removed. " +
+                          "If empty or not set, all unused local variables are considered for removal.",
+            required = false,
+            example = "java.lang.String")
+    @Nullable String withType;
+
+    @Option(displayName = "Ignore possible side effects",
+            description = "If true, variables with possible side effects in their initializer will be ignored and not removed. " +
+                          "This option was previously named 'withSideEffects'.",
+            required = false,
+            example = "true")
+    @Nullable Boolean withSideEffects;
+
+    @JsonCreator
+    public RemoveUnusedLocalVariables(
+            String @Nullable [] ignoreVariablesNamed,
+            @Nullable String withType,
+            @Nullable Boolean withSideEffects
+    ) {
+        this.ignoreVariablesNamed = ignoreVariablesNamed;
+        this.withType = withType;
+        this.withSideEffects = withSideEffects;
path: src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java
user: login: iddeepak
body: ```suggestion
        this.ignoreSideEffects = ignoreSideEffects;
```
created_at: 2025-05-19T16:32:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -42,11 +43,37 @@ public class RemoveUnusedLocalVariables extends Recipe {
             example = "[unused, notUsed, IGNORE_ME]")
     String @Nullable [] ignoreVariablesNamed;
 
-    @Option(displayName = "Remove unused local variables with side effects in initializer",
-            description = "Whether to remove unused local variables despite side effects in the initializer. Default false.",
-            required = false)
-    @Nullable
-    Boolean withSideEffects;
+    @Option(displayName = "Only remove variables of type",
+            description = "A fully qualified class names. Only unused local variables whose type matches one of these will be removed. " +
+                          "If empty or not set, all unused local variables are considered for removal.",
+            required = false,
+            example = "java.lang.String")
+    @Nullable String withType;
+
+    @Option(displayName = "Ignore possible side effects",
+            description = "If true, variables with possible side effects in their initializer will be ignored and not removed. " +
+                          "This option was previously named 'withSideEffects'.",
+            required = false,
+            example = "true")
+    @Nullable Boolean withSideEffects;
+
+    @JsonCreator
+    public RemoveUnusedLocalVariables(
+            String @Nullable [] ignoreVariablesNamed,
+            @Nullable String withType,
+            @Nullable Boolean withSideEffects
+    ) {
+        this.ignoreVariablesNamed = ignoreVariablesNamed;
+        this.withType = withType;
+        this.withSideEffects = withSideEffects;
+    }
+
+    public RemoveUnusedLocalVariables(
+            String @Nullable [] ignoreVariablesNamed,
+            @Nullable Boolean withSideEffects
path: src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java
user: login: iddeepak
body: ```suggestion
            @Nullable Boolean ignoreSideEffects
```
created_at: 2025-05-19T16:32:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -108,6 +135,15 @@ private Cursor getCursorToParentScope(Cursor cursor) {
                     return variable;
                 }
 
+                // skip matching ignored variable types right away
+                if (
+                    variable.getType() instanceof JavaType.FullyQualified &&
+                    withType != null &&
+                    !withType.equals(((JavaType.FullyQualified) variable.getType()).getFullyQualifiedName())
+                ) {
path: src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java
user: login: iddeepak
body: `if(withType != null && !TypeUtils.isOfClassType(var.getType(), withType))`
created_at: 2025-05-19T16:46:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -42,11 +43,37 @@ public class RemoveUnusedLocalVariables extends Recipe {
             example = "[unused, notUsed, IGNORE_ME]")
     String @Nullable [] ignoreVariablesNamed;
 
-    @Option(displayName = "Remove unused local variables with side effects in initializer",
-            description = "Whether to remove unused local variables despite side effects in the initializer. Default false.",
-            required = false)
-    @Nullable
-    Boolean withSideEffects;
+    @Option(displayName = "Only remove variables of type",
+            description = "A fully qualified class names. Only unused local variables whose type matches one of these will be removed. " +
+                          "If empty or not set, all unused local variables are considered for removal.",
+            required = false,
+            example = "java.lang.String")
+    @Nullable String withType;
+
+    @Option(displayName = "Ignore possible side effects",
+            description = "If true, variables with possible side effects in their initializer will be ignored and not removed. " +
+                          "This option was previously named 'withSideEffects'.",
+            required = false,
+            example = "true")
+    @Nullable Boolean withSideEffects;
+
+    @JsonCreator
+    public RemoveUnusedLocalVariables(
+            String @Nullable [] ignoreVariablesNamed,
+            @Nullable String withType,
+            @Nullable Boolean withSideEffects
path: src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java
user: login: timtebeek
body: we can't rename fields without negatively affecting folks using declarative recipes with options.
created_at: 2025-05-19T16:55:59Z
author_association: MEMBER
diff_hunk: @@ -42,11 +43,37 @@ public class RemoveUnusedLocalVariables extends Recipe {
             example = "[unused, notUsed, IGNORE_ME]")
     String @Nullable [] ignoreVariablesNamed;
 
-    @Option(displayName = "Remove unused local variables with side effects in initializer",
-            description = "Whether to remove unused local variables despite side effects in the initializer. Default false.",
-            required = false)
-    @Nullable
-    Boolean withSideEffects;
+    @Option(displayName = "Only remove variables of type",
+            description = "A fully qualified class names. Only unused local variables whose type matches one of these will be removed. " +
+                          "If empty or not set, all unused local variables are considered for removal.",
+            required = false,
+            example = "java.lang.String")
+    @Nullable String withType;
+
+    @Option(displayName = "Ignore possible side effects",
+            description = "If true, variables with possible side effects in their initializer will be ignored and not removed. " +
+                          "This option was previously named 'withSideEffects'.",
+            required = false,
+            example = "true")
+    @Nullable Boolean withSideEffects;
+
+    @JsonCreator
+    public RemoveUnusedLocalVariables(
+            String @Nullable [] ignoreVariablesNamed,
+            @Nullable String withType,
+            @Nullable Boolean withSideEffects
+    ) {
+        this.ignoreVariablesNamed = ignoreVariablesNamed;
+        this.withType = withType;
+        this.withSideEffects = withSideEffects;
+    }
+
+    public RemoveUnusedLocalVariables(
+            String @Nullable [] ignoreVariablesNamed,
+            @Nullable Boolean withSideEffects
path: src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java
user: login: timtebeek
body: we can't rename fields without negatively affecting folks using declarative recipes with options.
created_at: 2025-05-19T16:56:05Z
author_association: MEMBER
diff_hunk: @@ -42,11 +43,37 @@ public class RemoveUnusedLocalVariables extends Recipe {
             example = "[unused, notUsed, IGNORE_ME]")
     String @Nullable [] ignoreVariablesNamed;
 
-    @Option(displayName = "Remove unused local variables with side effects in initializer",
-            description = "Whether to remove unused local variables despite side effects in the initializer. Default false.",
-            required = false)
-    @Nullable
-    Boolean withSideEffects;
+    @Option(displayName = "Only remove variables of type",
+            description = "A fully qualified class names. Only unused local variables whose type matches one of these will be removed. " +
+                          "If empty or not set, all unused local variables are considered for removal.",
+            required = false,
+            example = "java.lang.String")
+    @Nullable String withType;
+
+    @Option(displayName = "Ignore possible side effects",
+            description = "If true, variables with possible side effects in their initializer will be ignored and not removed. " +
+                          "This option was previously named 'withSideEffects'.",
+            required = false,
+            example = "true")
+    @Nullable Boolean withSideEffects;
+
+    @JsonCreator
+    public RemoveUnusedLocalVariables(
+            String @Nullable [] ignoreVariablesNamed,
+            @Nullable String withType,
+            @Nullable Boolean withSideEffects
+    ) {
+        this.ignoreVariablesNamed = ignoreVariablesNamed;
+        this.withType = withType;
+        this.withSideEffects = withSideEffects;
path: src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java
user: login: timtebeek
body: we can't rename fields without negatively affecting folks using declarative recipes with options.
created_at: 2025-05-19T16:56:18Z
author_association: MEMBER
diff_hunk: @@ -42,11 +43,37 @@ public class RemoveUnusedLocalVariables extends Recipe {
             example = "[unused, notUsed, IGNORE_ME]")
     String @Nullable [] ignoreVariablesNamed;
 
-    @Option(displayName = "Remove unused local variables with side effects in initializer",
-            description = "Whether to remove unused local variables despite side effects in the initializer. Default false.",
-            required = false)
-    @Nullable
-    Boolean withSideEffects;
+    @Option(displayName = "Only remove variables of type",
+            description = "A fully qualified class names. Only unused local variables whose type matches one of these will be removed. " +
+                          "If empty or not set, all unused local variables are considered for removal.",
+            required = false,
+            example = "java.lang.String")
+    @Nullable String withType;
+
+    @Option(displayName = "Ignore possible side effects",
+            description = "If true, variables with possible side effects in their initializer will be ignored and not removed. " +
+                          "This option was previously named 'withSideEffects'.",
+            required = false,
+            example = "true")
+    @Nullable Boolean withSideEffects;
+
+    @JsonCreator
+    public RemoveUnusedLocalVariables(
+            String @Nullable [] ignoreVariablesNamed,
+            @Nullable String withType,
+            @Nullable Boolean withSideEffects
+    ) {
+        this.ignoreVariablesNamed = ignoreVariablesNamed;
+        this.withType = withType;
+        this.withSideEffects = withSideEffects;
+    }
+
+    public RemoveUnusedLocalVariables(
+            String @Nullable [] ignoreVariablesNamed,
+            @Nullable Boolean withSideEffects
path: src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java
user: login: iddeepak
body: ok, thnx
created_at: 2025-05-19T16:58:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -42,12 +44,38 @@ public class RemoveUnusedLocalVariables extends Recipe {
             example = "[unused, notUsed, IGNORE_ME]")
     String @Nullable [] ignoreVariablesNamed;
 
+    @Option(displayName = "Only remove variables of a given type",
+            description = "A fully qualified class name. Only unused local variables whose type matches this will be removed. " +
+                    "If empty or not set, all unused local variables are considered for removal.",
+            required = false,
+            example = "java.lang.String")
+    @Nullable
+    String withType;
+
     @Option(displayName = "Remove unused local variables with side effects in initializer",
             description = "Whether to remove unused local variables despite side effects in the initializer. Default false.",
             required = false)
     @Nullable
     Boolean withSideEffects;
 
+    @JsonCreator
+    public RemoveUnusedLocalVariables(
+            String @Nullable [] ignoreVariablesNamed,
+            @Nullable String withType,
+            @Nullable Boolean withSideEffects) {
+        this.ignoreVariablesNamed = ignoreVariablesNamed;
+        this.withType = withType;
+        this.withSideEffects = withSideEffects;
+    }
+
+    @InlineMe(replacement = "new RemoveUnusedLocalVariables(ignoreVariablesNamed, null, withSideEffects)")
path: src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java
user: login: knutwannheden
body: The contract is that the value corresponds exactly to the body, so `this(...)` rather than `new RemoveUnusedLocalVariables(...)`.
created_at: 2025-05-19T18:26:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -42,12 +44,38 @@ public class RemoveUnusedLocalVariables extends Recipe {
             example = "[unused, notUsed, IGNORE_ME]")
     String @Nullable [] ignoreVariablesNamed;
 
+    @Option(displayName = "Only remove variables of a given type",
+            description = "A fully qualified class name. Only unused local variables whose type matches this will be removed. " +
+                    "If empty or not set, all unused local variables are considered for removal.",
+            required = false,
+            example = "java.lang.String")
+    @Nullable
+    String withType;
+
     @Option(displayName = "Remove unused local variables with side effects in initializer",
             description = "Whether to remove unused local variables despite side effects in the initializer. Default false.",
             required = false)
     @Nullable
     Boolean withSideEffects;
 
+    @JsonCreator
+    public RemoveUnusedLocalVariables(
+            String @Nullable [] ignoreVariablesNamed,
+            @Nullable String withType,
+            @Nullable Boolean withSideEffects) {
+        this.ignoreVariablesNamed = ignoreVariablesNamed;
+        this.withType = withType;
+        this.withSideEffects = withSideEffects;
+    }
+
+    @InlineMe(replacement = "new RemoveUnusedLocalVariables(ignoreVariablesNamed, null, withSideEffects)")
path: src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java
user: login: timtebeek
body: I had doubted about that, but seeing how this is likely to be called from outside the class I opted for the in-my-eyes safer constructor calls that ought to be a drop in elsewhere. If that's wrong then we can change it still.
created_at: 2025-05-19T18:39:05Z
author_association: MEMBER

PR comments:
user: login: bgalek
created_at: 2025-05-19T14:29:42Z
body: @timtebeek check it out ;)
author_association: CONTRIBUTOR
user: login: bgalek
created_at: 2025-05-19T17:33:07Z
body: Thanks! See You in the next PR :))
author_association: CONTRIBUTOR