2929import org .sonar .java .reporting .JavaQuickFix ;
3030import org .sonar .java .reporting .JavaTextEdit ;
3131import org .sonar .plugins .java .api .IssuableSubscriptionVisitor ;
32- import org .sonar .plugins .java .api .JavaVersion ;
3332import org .sonar .plugins .java .api .semantic .Symbol ;
3433import org .sonar .plugins .java .api .tree .AssignmentExpressionTree ;
3534import org .sonar .plugins .java .api .tree .CaseLabelTree ;
@@ -76,7 +75,7 @@ public void leaveNode(Tree tree) {
7675 IdentifierTree simpleName = variable .simpleName ();
7776 if (!simpleName .isUnnamedVariable ()) {
7877 boolean unresolved = UNRESOLVED_IDENTIFIERS_AND_SWITCH_CASE_VISITOR .isUnresolved (simpleName .name ());
79- if (!unresolved && isProperLocalVariable (variable ) && isUnused (variable .symbol ()) && ! isMandatoryForeachVariable ( context . getJavaVersion (), variable )) {
78+ if (!unresolved && isProperLocalVariable (variable ) && isUnused (variable .symbol ()) && canBeReplaced ( variable )) {
8079 QuickFixHelper .newIssue (context )
8180 .forRule (this )
8281 .onTree (simpleName )
@@ -90,11 +89,12 @@ public void leaveNode(Tree tree) {
9089 }
9190
9291 /**
93- * Before Java 22 it was not possible to remove the variable in a foreach statement even it is unused.
92+ * Before Java 22 it was not possible to remove the variable in a foreach statement or try with resources even it is unused.
9493 * For instance in {@code for (String element : list) {}}, it is only since Java 22 that it can be rewritten {@code for (var _ : list) {}}.
9594 */
96- private static boolean isMandatoryForeachVariable (JavaVersion javaVersion , VariableTree variable ) {
97- return variable .parent () instanceof ForEachStatement && !javaVersion .isJava22Compatible ();
95+ private boolean canBeReplaced (VariableTree variable ) {
96+ return context .getJavaVersion ().isJava22Compatible ()
97+ || (!isForeachVariable (variable ) && !isTryResource (variable ));
9898 }
9999
100100 private static boolean isUnused (Symbol symbol ) {
@@ -119,7 +119,6 @@ private static boolean isProperLocalVariable(VariableTree variable) {
119119 return symbol .isLocalVariable ()
120120 && !symbol .isParameter ()
121121 && !isDefinedInCatchClause (variable )
122- && !isTryResource (variable )
123122 && !UNRESOLVED_IDENTIFIERS_AND_SWITCH_CASE_VISITOR .isSwitchPatternVariable (variable );
124123 }
125124
@@ -131,9 +130,13 @@ private static boolean isTryResource(VariableTree variable) {
131130 return variable .parent ().is (Tree .Kind .LIST ) && variable .parent ().parent ().is (Tree .Kind .TRY_STATEMENT );
132131 }
133132
133+ private static boolean isForeachVariable (VariableTree variable ) {
134+ return variable .parent () instanceof ForEachStatement ;
135+ }
136+
134137 private static List <JavaQuickFix > computeQuickFix (VariableTree variable ) {
135- if (variable . parent () instanceof ForEachStatement ) {
136- return makeQuickFixReplacingWithUnnamedVariable (variable );
138+ if (isForeachVariable ( variable ) || isTryResource ( variable ) ) {
139+ return List . of ( makeQuickFixReplacingWithUnnamedVariable (variable ) );
137140 }
138141 return getQuickFixTextSpan (variable ).map (textSpan -> Collections .singletonList (
139142 JavaQuickFix .newQuickFix ("Remove unused local variable" )
@@ -143,11 +146,14 @@ private static List<JavaQuickFix> computeQuickFix(VariableTree variable) {
143146 ).orElseGet (Collections ::emptyList );
144147 }
145148
146- private static List <JavaQuickFix > makeQuickFixReplacingWithUnnamedVariable (VariableTree variable ) {
147- var textSpan = AnalyzerMessage .textSpanBetween (variable .firstToken (), variable .lastToken ());
148- return List .of (JavaQuickFix .newQuickFix ("Replace unused local variable with _" )
149- .addTextEdit (JavaTextEdit .replaceTextSpan (textSpan , "var _" ))
150- .build ());
149+ private static JavaQuickFix makeQuickFixReplacingWithUnnamedVariable (VariableTree variable ) {
150+ return JavaQuickFix .newQuickFix ("Replace unused local variable with _" )
151+ // This works with both enhanced for loop and try-with-resources.
152+ // In the latter case we keep the initializer:
153+ // `for(int elem: elems)` turns into `for(var _: elems)`
154+ // `try(Resource res = initializer())` turns into `try(var _ = initializer ())`
155+ .addTextEdit (JavaTextEdit .replaceBetweenTree (variable .type (), true , variable .simpleName (), true , "var _" ))
156+ .build ();
151157 }
152158
153159 private static Optional <AnalyzerMessage .TextSpan > getQuickFixTextSpan (VariableTree variable ) {
0 commit comments