diff --git a/src/main/java/org/checkstyle/autofix/PositionHelper.java b/src/main/java/org/checkstyle/autofix/PositionHelper.java new file mode 100644 index 0000000..8504754 --- /dev/null +++ b/src/main/java/org/checkstyle/autofix/PositionHelper.java @@ -0,0 +1,105 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// checkstyle-openrewrite-recipes: Automatically fix Checkstyle violations with OpenRewrite. +// Copyright (C) 2025 The Checkstyle OpenRewrite Recipes Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// 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.checkstyle.autofix; + +import java.util.concurrent.CancellationException; +import java.util.function.Function; + +import org.openrewrite.Cursor; +import org.openrewrite.PrintOutputCapture; +import org.openrewrite.TreeVisitor; +import org.openrewrite.internal.RecipeRunException; +import org.openrewrite.java.tree.J; + +public final class PositionHelper { + + private PositionHelper() { + // Utility class + } + + public static int computeLinePosition(J tree, J targetElement, Cursor cursor) { + return computePosition(tree, targetElement, cursor, + out -> 1 + Math.toIntExact(out.chars().filter(chr -> chr == '\n').count())); + } + + public static int computeColumnPosition(J tree, J targetElement, Cursor cursor) { + return computePosition(tree, targetElement, cursor, out -> { + int column = calculateColumnOffset(out); + if (targetElement instanceof J.Literal literal + && literal.getValue() instanceof Number + && literal.getValueSource() != null + && literal.getValueSource().matches("^[+-].*")) { + column++; + } + return column; + }); + } + + private static int computePosition( + J tree, + J targetElement, + Cursor cursor, + Function positionCalculator + ) { + final TreeVisitor>> printer = + tree.printer(cursor); + + final PrintOutputCapture> capture = + new PrintOutputCapture<>(printer) { + @Override + public PrintOutputCapture> append(String text) { + if (targetElement.isScope(getContext().getCursor().getValue())) { + super.append(targetElement.getPrefix().getWhitespace()); + throw new CancellationException(); + } + return super.append(text); + } + }; + + final int result; + try { + printer.visit(tree, capture, cursor.getParentOrThrow()); + throw new IllegalStateException("Target element: " + targetElement + + ", not found in the syntax tree."); + } + catch (CancellationException exception) { + result = positionCalculator.apply(capture.getOut()); + } + catch (RecipeRunException exception) { + if (exception.getCause() instanceof CancellationException) { + result = positionCalculator.apply(capture.getOut()); + } + else { + throw exception; + } + } + return result; + } + + private static int calculateColumnOffset(String out) { + final int lineBreakIndex = out.lastIndexOf('\n'); + final int result; + if (lineBreakIndex == -1) { + result = out.length(); + } + else { + result = out.length() - lineBreakIndex; + } + return result; + } +} diff --git a/src/main/java/org/checkstyle/autofix/recipe/FinalLocalVariable.java b/src/main/java/org/checkstyle/autofix/recipe/FinalLocalVariable.java new file mode 100644 index 0000000..ddaf5c7 --- /dev/null +++ b/src/main/java/org/checkstyle/autofix/recipe/FinalLocalVariable.java @@ -0,0 +1,111 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// checkstyle-openrewrite-recipes: Automatically fix Checkstyle violations with OpenRewrite. +// Copyright (C) 2025 The Checkstyle OpenRewrite Recipes Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// 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.checkstyle.autofix.recipe; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +import org.checkstyle.autofix.PositionHelper; +import org.checkstyle.autofix.parser.CheckstyleViolation; +import org.openrewrite.ExecutionContext; +import org.openrewrite.Recipe; +import org.openrewrite.Tree; +import org.openrewrite.TreeVisitor; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.Space; +import org.openrewrite.marker.Markers; + +/** + * Fixes Checkstyle FinalLocalVariable violations by adding 'final' modifier to local variables + * that are never reassigned. + */ +public class FinalLocalVariable extends Recipe { + + private final List violations; + + public FinalLocalVariable(List violations) { + this.violations = violations; + } + + @Override + public String getDisplayName() { + return "FinalLocalVariable recipe"; + } + + @Override + public String getDescription() { + return "Adds 'final' modifier to local variables that never have their values changed."; + } + + @Override + public TreeVisitor getVisitor() { + return new LocalVariableVisitor(); + } + + private final class LocalVariableVisitor extends JavaIsoVisitor { + + private Path sourcePath; + + @Override + public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionContext ctx) { + this.sourcePath = cu.getSourcePath(); + return super.visitCompilationUnit(cu, ctx); + } + + @Override + public J.VariableDeclarations visitVariableDeclarations( + J.VariableDeclarations multiVariable, ExecutionContext ctx) { + + J.VariableDeclarations declarations = super.visitVariableDeclarations(multiVariable, + ctx); + + if (!(getCursor().getParentTreeCursor().getValue() instanceof J.ClassDeclaration) + && declarations.getVariables().size() == 1 + && declarations.getTypeExpression() != null + && !declarations.hasModifier(J.Modifier.Type.Final)) { + final J.VariableDeclarations.NamedVariable variable = declarations + .getVariables().get(0); + if (isAtViolationLocation(variable)) { + final List modifiers = new ArrayList<>(); + modifiers.add(new J.Modifier(Tree.randomId(), Space.EMPTY, + Markers.EMPTY, null, J.Modifier.Type.Final, new ArrayList<>())); + modifiers.addAll(declarations.getModifiers()); + declarations = declarations.withModifiers(modifiers) + .withTypeExpression(declarations.getTypeExpression() + .withPrefix(Space.SINGLE_SPACE)); + } + } + return declarations; + } + + private boolean isAtViolationLocation(J.VariableDeclarations.NamedVariable literal) { + final J.CompilationUnit cursor = getCursor().firstEnclosing(J.CompilationUnit.class); + + final int line = PositionHelper.computeLinePosition(cursor, literal, getCursor()); + final int column = PositionHelper.computeColumnPosition(cursor, literal, getCursor()); + + return violations.stream().anyMatch(violation -> { + return violation.getLine() == line + && violation.getColumn() == column + && Path.of(violation.getFileName()).equals(sourcePath); + }); + } + } +} diff --git a/src/main/java/org/checkstyle/autofix/recipe/UpperEll.java b/src/main/java/org/checkstyle/autofix/recipe/UpperEll.java index 7042dbd..e85bf04 100644 --- a/src/main/java/org/checkstyle/autofix/recipe/UpperEll.java +++ b/src/main/java/org/checkstyle/autofix/recipe/UpperEll.java @@ -19,16 +19,12 @@ import java.nio.file.Path; import java.util.List; -import java.util.concurrent.CancellationException; -import java.util.function.Function; +import org.checkstyle.autofix.PositionHelper; import org.checkstyle.autofix.parser.CheckstyleViolation; -import org.openrewrite.Cursor; import org.openrewrite.ExecutionContext; -import org.openrewrite.PrintOutputCapture; import org.openrewrite.Recipe; import org.openrewrite.TreeVisitor; -import org.openrewrite.internal.RecipeRunException; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.JavaType; @@ -93,8 +89,8 @@ && isAtViolationLocation(result)) { private boolean isAtViolationLocation(J.Literal literal) { final J.CompilationUnit cursor = getCursor().firstEnclosing(J.CompilationUnit.class); - final int line = computeLinePosition(cursor, literal, getCursor()); - final int column = computeColumnPosition(cursor, literal, getCursor()); + final int line = PositionHelper.computeLinePosition(cursor, literal, getCursor()); + final int column = PositionHelper.computeColumnPosition(cursor, literal, getCursor()); return violations.stream().anyMatch(violation -> { final Path absolutePath = Path.of(violation.getFileName()).toAbsolutePath(); @@ -103,88 +99,5 @@ private boolean isAtViolationLocation(J.Literal literal) { && absolutePath.equals(sourcePath); }); } - - /** - * Computes the position of a target element within a syntax tree using position calculator. - * This method traverses the given syntax tree and captures the printed output until the - * target element is encountered. When the target is found, a CancellationException - * is thrown to interrupt traversal, and the captured output is passed to the provided - * positionCalculator to compute the position. - * - * @param tree the root of the syntax tree to traverse - * @param targetElement the element whose position is to be computed - * @param cursor the current cursor in the tree traversal - * @param positionCalculator a function to compute the position from the printed output - * @return the computed position of the target element - * @throws IllegalStateException if the target element is not found in the tree - * @throws RecipeRunException if an error occurs during traversal - */ - private int computePosition( - J tree, - J targetElement, - Cursor cursor, - Function positionCalculator - ) { - final TreeVisitor>> printer = - tree.printer(cursor); - - final PrintOutputCapture> capture = - new PrintOutputCapture<>(printer) { - @Override - public PrintOutputCapture> append(String text) { - if (targetElement.isScope(getContext().getCursor().getValue())) { - super.append(targetElement.getPrefix().getWhitespace()); - throw new CancellationException(); - } - return super.append(text); - } - }; - - final int result; - try { - printer.visit(tree, capture, cursor.getParentOrThrow()); - throw new IllegalStateException("Target element: " + targetElement - + ", not found in the syntax tree."); - } - catch (CancellationException exception) { - result = positionCalculator.apply(capture.getOut()); - } - catch (RecipeRunException exception) { - if (exception.getCause() instanceof CancellationException) { - result = positionCalculator.apply(capture.getOut()); - } - else { - throw exception; - } - } - return result; - } - - private int computeLinePosition(J tree, J targetElement, Cursor cursor) { - return computePosition(tree, targetElement, cursor, - out -> 1 + Math.toIntExact(out.chars().filter(chr -> chr == '\n').count())); - } - - private int computeColumnPosition(J tree, J targetElement, Cursor cursor) { - return computePosition(tree, targetElement, cursor, out -> { - int column = calculateColumnOffset(out); - if (((J.Literal) targetElement).getValueSource().matches("^[+-].*")) { - column++; - } - return column; - }); - } - - private int calculateColumnOffset(String out) { - final int lineBreakIndex = out.lastIndexOf('\n'); - final int result; - if (lineBreakIndex == -1) { - result = out.length(); - } - else { - result = out.length() - lineBreakIndex; - } - return result; - } } } diff --git a/src/test/java/org/checkstyle/autofix/recipe/FinalLocalVariableTest.java b/src/test/java/org/checkstyle/autofix/recipe/FinalLocalVariableTest.java new file mode 100644 index 0000000..6e9cb28 --- /dev/null +++ b/src/test/java/org/checkstyle/autofix/recipe/FinalLocalVariableTest.java @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// checkstyle-openrewrite-recipes: Automatically fix Checkstyle violations with OpenRewrite. +// Copyright (C) 2025 The Checkstyle OpenRewrite Recipes Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// 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.checkstyle.autofix.recipe; + +import java.util.List; + +import org.checkstyle.autofix.parser.CheckConfiguration; +import org.checkstyle.autofix.parser.CheckstyleViolation; +import org.junit.jupiter.api.Test; +import org.openrewrite.Recipe; + +public class FinalLocalVariableTest extends AbstractRecipeTestSupport { + + @Override + protected String getSubpackage() { + return "finallocalvariable"; + } + + @Override + protected Recipe createRecipe(List violations, CheckConfiguration config) { + + return new FinalLocalVariable(violations); + } + + @Test + void singleLocalTest() throws Exception { + verify("SingleLocalTest"); + } + + @Test + void classFieldTest() throws Exception { + verify("ClassFieldTest"); + } + + @Test + void edgeCaseTest() throws Exception { + verify("EdgeCaseTest"); + } + + @Test + void enhancedForLoop() throws Exception { + verify("EnhancedForLoop"); + } + +} diff --git a/src/test/resources/org/checkstyle/autofix/recipe/finallocalvariable/classfieldtest/InputClassFieldTest.java b/src/test/resources/org/checkstyle/autofix/recipe/finallocalvariable/classfieldtest/InputClassFieldTest.java new file mode 100644 index 0000000..ac06187 --- /dev/null +++ b/src/test/resources/org/checkstyle/autofix/recipe/finallocalvariable/classfieldtest/InputClassFieldTest.java @@ -0,0 +1,50 @@ +/*xml + + + + + + +*/ + +package org.checkstyle.autofix.recipe.finallocalvariable.classfieldtest; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class InputClassFieldTest { + private String instanceField = "instance"; + private int counter = 0; + private List dataList = new ArrayList<>(); + private Map cache = new HashMap<>(); + private boolean initialized = false; + + private static String staticField = "static"; + private static int globalCounter = 0; + + public void FieldsParametersLocalsTest(String name, int initialValue, boolean autoStart) { + String normalizedName = name.trim(); // violation, "should be declared final" + int validatedValue = Math.max(0, initialValue); // violation, "should be declared final" + // violation below, "should be declared final" + String logMessage = "Creating instance with: " + normalizedName; + + this.instanceField = normalizedName; + this.counter = validatedValue; + this.initialized = autoStart; + } + + public void complexMethod(String param1, int param2, List param3, + Map param4, boolean param5) { + String workingData = param1 + "_processed"; // violation, "should be declared final" + int calculatedSize = param2 * 2; // violation, "should be declared final" + // violation below, "should be declared final" + List filteredList = new ArrayList<>(param3); + // violation below, "should be declared final" + Map workingMap = new HashMap<>(param4); + boolean processFlag = param5 && !param3.isEmpty(); // violation, "should be declared final" + // violation below, "should be declared final" + String statusMessage = "Processing " + calculatedSize + " items"; + } +} diff --git a/src/test/resources/org/checkstyle/autofix/recipe/finallocalvariable/classfieldtest/OutputClassFieldTest.java b/src/test/resources/org/checkstyle/autofix/recipe/finallocalvariable/classfieldtest/OutputClassFieldTest.java new file mode 100644 index 0000000..74b379e --- /dev/null +++ b/src/test/resources/org/checkstyle/autofix/recipe/finallocalvariable/classfieldtest/OutputClassFieldTest.java @@ -0,0 +1,46 @@ +/*xml + + + + + + +*/ + +package org.checkstyle.autofix.recipe.finallocalvariable.classfieldtest; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class OutputClassFieldTest { + private String instanceField = "instance"; + private int counter = 0; + private List dataList = new ArrayList<>(); + private Map cache = new HashMap<>(); + private boolean initialized = false; + + private static String staticField = "static"; + private static int globalCounter = 0; + + public void FieldsParametersLocalsTest(String name, int initialValue, boolean autoStart) { + final String normalizedName = name.trim(); + final int validatedValue = Math.max(0, initialValue); + final String logMessage = "Creating instance with: " + normalizedName; + + this.instanceField = normalizedName; + this.counter = validatedValue; + this.initialized = autoStart; + } + + public void complexMethod(String param1, int param2, List param3, + Map param4, boolean param5) { + final String workingData = param1 + "_processed"; + final int calculatedSize = param2 * 2; + final List filteredList = new ArrayList<>(param3); + final Map workingMap = new HashMap<>(param4); + final boolean processFlag = param5 && !param3.isEmpty(); + final String statusMessage = "Processing " + calculatedSize + " items"; + } +} diff --git a/src/test/resources/org/checkstyle/autofix/recipe/finallocalvariable/edgecasetest/InputEdgeCaseTest.java b/src/test/resources/org/checkstyle/autofix/recipe/finallocalvariable/edgecasetest/InputEdgeCaseTest.java new file mode 100644 index 0000000..bb10884 --- /dev/null +++ b/src/test/resources/org/checkstyle/autofix/recipe/finallocalvariable/edgecasetest/InputEdgeCaseTest.java @@ -0,0 +1,85 @@ +/*xml + + + + + + +*/ + +package org.checkstyle.autofix.recipe.finallocalvariable.edgecasetest; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +public class InputEdgeCaseTest { + public void varDeclarations() { + var autoString = "inferred string"; // violation, "should be declared final" + var autoNumber = 42; // violation, "should be declared final" + var autoList = new ArrayList(); // violation, "should be declared final" + var autoMap = new HashMap(); // violation, "should be declared final" + + String regularString = "explicit type"; // violation, "should be declared final" + var anotherAuto = "mixed with regular"; // violation, "should be declared final" + int regularInt = 100; // violation, "should be declared final" + } + + public void loopVariables() { + List items = Arrays.asList("a", "b", "c"); // violation, "should be declared final" + + for (String item : items) { + System.out.println(item); + String processed = item.toUpperCase(); // violation, "should be declared final" + } + + for (int i = 0; i < 10; i++) { + String loopVar = "iteration " + i; // violation, "should be declared final" + int doubled = i * 2; // violation, "should be declared final" + } + + int counter = 0; + while (counter < 5) { + String message = "Count: " + counter; // violation, "should be declared final" + counter++; + } + } + + public void lambdaExpressions() { + // violation below, "should be declared final" + List items = Arrays.asList("one", "two", "three"); + + items.forEach(item -> System.out.println(item)); + items.stream().map(item -> item.toUpperCase()).forEach(System.out::println); + + items.forEach((String item) -> { + String processed = item.trim(); // violation, "should be declared final" + System.out.println(processed); + }); + + String prefix = "Item: "; // violation, "should be declared final" + items.forEach(item -> System.out.println(prefix + item)); + } + + public void tryWithResourcesAndExceptions() { + String filename = "test.txt"; // violation, "should be declared final" + + try (FileReader reader = new FileReader(filename); + BufferedReader buffered = new BufferedReader(reader)) { + + String line = buffered.readLine(); // violation, "should be declared final" + // violation below, "should be declared final" + String processed = line != null ? line.trim() : ""; + + } catch (IOException e) { + // violation below, "should be declared final" + String errorMsg = "Error reading file: " + e.getMessage(); + System.err.println(errorMsg); + + } + } +} diff --git a/src/test/resources/org/checkstyle/autofix/recipe/finallocalvariable/edgecasetest/OutputEdgeCaseTest.java b/src/test/resources/org/checkstyle/autofix/recipe/finallocalvariable/edgecasetest/OutputEdgeCaseTest.java new file mode 100644 index 0000000..651244a --- /dev/null +++ b/src/test/resources/org/checkstyle/autofix/recipe/finallocalvariable/edgecasetest/OutputEdgeCaseTest.java @@ -0,0 +1,82 @@ +/*xml + + + + + + +*/ + +package org.checkstyle.autofix.recipe.finallocalvariable.edgecasetest; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +public class OutputEdgeCaseTest { + public void varDeclarations() { + final var autoString = "inferred string"; + final var autoNumber = 42; + final var autoList = new ArrayList(); + final var autoMap = new HashMap(); + + final String regularString = "explicit type"; + final var anotherAuto = "mixed with regular"; + final int regularInt = 100; + } + + public void loopVariables() { + final List items = Arrays.asList("a", "b", "c"); + + for (String item : items) { + System.out.println(item); + final String processed = item.toUpperCase(); + } + + for (int i = 0; i < 10; i++) { + final String loopVar = "iteration " + i; + final int doubled = i * 2; + } + + int counter = 0; + while (counter < 5) { + final String message = "Count: " + counter; + counter++; + } + } + + public void lambdaExpressions() { + final List items = Arrays.asList("one", "two", "three"); + + items.forEach(item -> System.out.println(item)); + items.stream().map(item -> item.toUpperCase()).forEach(System.out::println); + + items.forEach((String item) -> { + final String processed = item.trim(); + System.out.println(processed); + }); + + final String prefix = "Item: "; + items.forEach(item -> System.out.println(prefix + item)); + } + + public void tryWithResourcesAndExceptions() { + final String filename = "test.txt"; + + try (FileReader reader = new FileReader(filename); + BufferedReader buffered = new BufferedReader(reader)) { + + final String line = buffered.readLine(); + final String processed = line != null ? line.trim() : ""; + + } catch (IOException e) { + final String errorMsg = "Error reading file: " + e.getMessage(); + System.err.println(errorMsg); + + } + } +} diff --git a/src/test/resources/org/checkstyle/autofix/recipe/finallocalvariable/enhancedforloop/InputEnhancedForLoop.java b/src/test/resources/org/checkstyle/autofix/recipe/finallocalvariable/enhancedforloop/InputEnhancedForLoop.java new file mode 100644 index 0000000..af3995c --- /dev/null +++ b/src/test/resources/org/checkstyle/autofix/recipe/finallocalvariable/enhancedforloop/InputEnhancedForLoop.java @@ -0,0 +1,46 @@ +/*xml + + + + + + + + + +*/ + +package org.checkstyle.autofix.recipe.finallocalvariable.enhancedforloop; + +public class InputEnhancedForLoop { + public void method1() + { + final java.util.List list = new java.util.ArrayList<>(); + + for(Object a : list){ // violation, "should be declared final" + } + } + + public void method2() + { + final int[] squares = {0, 1, 4, 9, 16, 25}; + int x; // violation, "should be declared final" + for (int i : squares) { // violation, "should be declared final" + } + + } + // violation below, "should be declared final" + public java.util.List method3(java.util.List snippets) { + // violation below, "should be declared final" + java.util.List filteredSnippets = new java.util.ArrayList<>(); + for (String snippet : snippets) { // violation, "should be declared final" + filteredSnippets.add(snippet); + } + if (filteredSnippets.size() == 0) { + String snippet = snippets.get(0); + snippet = new String(snippet); + filteredSnippets.add(snippet); + } + return filteredSnippets; + } +} diff --git a/src/test/resources/org/checkstyle/autofix/recipe/finallocalvariable/enhancedforloop/OutputEnhancedForLoop.java b/src/test/resources/org/checkstyle/autofix/recipe/finallocalvariable/enhancedforloop/OutputEnhancedForLoop.java new file mode 100644 index 0000000..cad8af0 --- /dev/null +++ b/src/test/resources/org/checkstyle/autofix/recipe/finallocalvariable/enhancedforloop/OutputEnhancedForLoop.java @@ -0,0 +1,44 @@ +/*xml + + + + + + + + + +*/ + +package org.checkstyle.autofix.recipe.finallocalvariable.enhancedforloop; + +public class OutputEnhancedForLoop { + public void method1() + { + final java.util.List list = new java.util.ArrayList<>(); + + for(final Object a : list){ + } + } + + public void method2() + { + final int[] squares = {0, 1, 4, 9, 16, 25}; + final int x; + for (final int i : squares) { + } + + } + public java.util.List method3(final java.util.List snippets) { + final java.util.List filteredSnippets = new java.util.ArrayList<>(); + for (final String snippet : snippets) { + filteredSnippets.add(snippet); + } + if (filteredSnippets.size() == 0) { + String snippet = snippets.get(0); + snippet = new String(snippet); + filteredSnippets.add(snippet); + } + return filteredSnippets; + } +} diff --git a/src/test/resources/org/checkstyle/autofix/recipe/finallocalvariable/singlelocaltest/InputSingleLocalTest.java b/src/test/resources/org/checkstyle/autofix/recipe/finallocalvariable/singlelocaltest/InputSingleLocalTest.java new file mode 100644 index 0000000..c8b5b61 --- /dev/null +++ b/src/test/resources/org/checkstyle/autofix/recipe/finallocalvariable/singlelocaltest/InputSingleLocalTest.java @@ -0,0 +1,50 @@ +/*xml + + + + + + +*/ + +package org.checkstyle.autofix.recipe.finallocalvariable.singlelocaltest; + +import java.io.File; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; + +public class InputSingleLocalTest { + public void basicLocalVariables() { + String name = "John Doe"; // violation, "should be declared final" + int age = 25; // violation, "should be declared final" + double salary = 50000.0; // violation, "should be declared final" + boolean isActive = true; // violation, "should be declared final" + char grade = 'A'; // violation, "should be declared final" + Object data = new HashMap<>(); // violation, "should be declared final" + List items = new ArrayList<>(); // violation, "should be declared final" + StringBuilder builder = new StringBuilder(); // violation, "should be declared final" + Date currentDate = new Date(); // violation, "should be declared final" + File tempFile = new File("temp.txt"); // violation, "should be declared final" + } + + public void methodWithCalculations() { + int x = 10; // violation, "should be declared final" + int y = 20; // violation, "should be declared final" + int sum = x + y; // violation, "should be declared final" + int product = x * y; // violation, "should be declared final" + double average = (x + y) / 2.0; // violation, "should be declared final" + // violation below, "should be declared final" + String result = "Sum: " + sum + ", Product: " + product; + boolean isPositive = sum > 0; // violation, "should be declared final" + } + + public String processResult(String input) { + String trimmed = input.trim(); // violation, "should be declared final" + String upperCase = trimmed.toUpperCase(); // violation, "should be declared final" + String formatted = "[" + upperCase + "]"; // violation, "should be declared final" + return formatted; + } + +} diff --git a/src/test/resources/org/checkstyle/autofix/recipe/finallocalvariable/singlelocaltest/OutputSingleLocalTest.java b/src/test/resources/org/checkstyle/autofix/recipe/finallocalvariable/singlelocaltest/OutputSingleLocalTest.java new file mode 100644 index 0000000..1b2c318 --- /dev/null +++ b/src/test/resources/org/checkstyle/autofix/recipe/finallocalvariable/singlelocaltest/OutputSingleLocalTest.java @@ -0,0 +1,49 @@ +/*xml + + + + + + +*/ + +package org.checkstyle.autofix.recipe.finallocalvariable.singlelocaltest; + +import java.io.File; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; + +public class OutputSingleLocalTest { + public void basicLocalVariables() { + final String name = "John Doe"; + final int age = 25; + final double salary = 50000.0; + final boolean isActive = true; + final char grade = 'A'; + final Object data = new HashMap<>(); + final List items = new ArrayList<>(); + final StringBuilder builder = new StringBuilder(); + final Date currentDate = new Date(); + final File tempFile = new File("temp.txt"); + } + + public void methodWithCalculations() { + final int x = 10; + final int y = 20; + final int sum = x + y; + final int product = x * y; + final double average = (x + y) / 2.0; + final String result = "Sum: " + sum + ", Product: " + product; + final boolean isPositive = sum > 0; + } + + public String processResult(String input) { + final String trimmed = input.trim(); + final String upperCase = trimmed.toUpperCase(); + final String formatted = "[" + upperCase + "]"; + return formatted; + } + +}