Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
cbd4deb
migrate recipes as-is
timo-a Dec 11, 2024
eb2ccca
fix year in license
timo-a Dec 15, 2024
8d8ad1d
bring back original helper methods
timo-a Dec 15, 2024
316657f
minor polish
timo-a Dec 15, 2024
28b76ff
remove line in recipe spec
timo-a Dec 15, 2024
04a78bc
Update src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.…
timo-a Dec 15, 2024
ccf978d
Update src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.…
timo-a Dec 15, 2024
daecba7
Update src/test/java/org/openrewrite/java/migrate/lombok/NormalizeSet…
timo-a Dec 15, 2024
45be826
fix build
timo-a Dec 21, 2024
783df6d
fix build
timo-a Dec 21, 2024
28946ff
markdown and comments
timo-a Feb 16, 2025
a1fab56
support no field access
timo-a Feb 16, 2025
772ccba
Inline MethodRecorder & rename list of methods not renamed
timo-a Feb 16, 2025
dfbcb60
Remove unnecessary packages
timo-a Feb 16, 2025
23a98d3
Capture current behavior with a running test
timo-a Feb 16, 2025
29cf350
fix test
timo-a Feb 16, 2025
73f2732
Apply suggestions from code review (of getter pr)
timo-a Jun 30, 2025
ca9f973
Group the tests that expect no change
timo-a Jun 30, 2025
31659ef
Use early returns in `isEffectivelySetter`
timo-a Jun 30, 2025
37be475
Adopt `TypeUtils.isOverride`
timo-a Jun 30, 2025
e99aa5f
Use MethodMatcher.methodPattern(method)
timo-a Jul 2, 2025
080aa67
Use`getTypesInUse().getDeclaredMethods()`
timo-a Jul 2, 2025
ee573ca
Expect to fail for a case not covered yet
timo-a Jul 2, 2025
cc6e71f
Inline MethodAcc
timo-a Jul 2, 2025
54502e8
refactor: rename to sth. less ambiguous
timo-a Jul 2, 2025
afa84e7
Merge branch 'main' into lombok/normalize-setter
timtebeek Jul 26, 2025
00c3727
Update src/main/java/org/openrewrite/java/migrate/lombok/AdoptLombokS…
timtebeek Jul 26, 2025
4fc323a
Minimize changes with Getter equivalent
timtebeek Jul 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* Copyright 2024 the original author or authors.
* <p>
* 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
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.lombok;

import lombok.EqualsAndHashCode;
import lombok.Value;
import org.jspecify.annotations.Nullable;
import org.openrewrite.ExecutionContext;
import org.openrewrite.ScanningRecipe;
import org.openrewrite.Tree;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.ChangeMethodName;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.MethodMatcher;
import org.openrewrite.java.tree.Expression;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.JavaType;
import org.openrewrite.java.tree.TypeUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Value
@EqualsAndHashCode(callSuper = false)
public class AdoptLombokSetterMethodNames extends ScanningRecipe<List<AdoptLombokSetterMethodNames.RenameRecord>> {

private final static String DO_NOT_RENAME = "DO_NOT_RENAME";

@Override
public String getDisplayName() {
return "Rename setter methods to fit Lombok";
}

@Override
public String getDescription() {
return "Rename methods that are effectively setter to the name Lombok would give them.\n" +
"Limitations:\n" +
" - If two methods in a class are effectively the same setter then one's name will be corrected and the others name will be left as it is.\n" +
" - If the correct name for a method is already taken by another method then the name will not be corrected.\n" +
" - Method name swaps or circular renaming within a class cannot be performed because the names block each other.\n" +
"E.g. `int getFoo() { return ba; } int getBa() { return foo; }` stays as it is.";
}

@Value
public static class RenameRecord {
String methodPattern;
String newMethodName;
}

@Override
public List<RenameRecord> getInitialValue(ExecutionContext ctx) {
return new ArrayList<>();
}

@Override
public TreeVisitor<?, ExecutionContext> getScanner(List<RenameRecord> renameRecords) {
return new JavaIsoVisitor<ExecutionContext>() {
@Override
public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionContext ctx) {
// Cheaply collect all declared methods; this also means we do not support clashing nested class methods
Set<JavaType.Method> declaredMethods = cu.getTypesInUse().getDeclaredMethods();
List<String> existingMethodNames = new ArrayList<>();
for (JavaType.Method method : declaredMethods) {
existingMethodNames.add(method.getName());
}
getCursor().putMessage(DO_NOT_RENAME, existingMethodNames);
return super.visitCompilationUnit(cu, ctx);
}

@Override
public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
if (method.getMethodType() == null || method.getBody() == null ||
!LombokUtils.isEffectivelySetter(method) ||
TypeUtils.isOverride(method.getMethodType())) {
return method;
}

JavaType.Variable fieldType;
Expression variable = ((J.Assignment) method.getBody().getStatements().get(0)).getVariable();
if (variable instanceof J.FieldAccess) {
fieldType = ((J.FieldAccess) variable).getName().getFieldType();
} else if (variable instanceof J.Identifier) {
fieldType = ((J.Identifier) variable).getFieldType();
} else {
return method;
}

// If method already has the name it should have, then nothing to be done
String expectedMethodName = LombokUtils.deriveSetterMethodName(fieldType);
if (expectedMethodName.equals(method.getSimpleName())) {
return method;
}

// If the desired method name is already taken by an existing method, the current method cannot be renamed
List<String> doNotRename = getCursor().getNearestMessage(DO_NOT_RENAME);
assert doNotRename != null;
if (doNotRename.contains(expectedMethodName)) {
return method;
}

renameRecords.add(new RenameRecord(MethodMatcher.methodPattern(method), expectedMethodName));
doNotRename.remove(method.getSimpleName()); //actual method name becomes available again
doNotRename.add(expectedMethodName); //expected method name now blocked
return method;
}
};
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor(List<RenameRecord> renameRecords) {
return new TreeVisitor<Tree, ExecutionContext>() {
@Override
public @Nullable Tree visit(@Nullable Tree tree, ExecutionContext ctx) {
for (RenameRecord rr : renameRecords) {
tree = new ChangeMethodName(rr.methodPattern, rr.newMethodName, true, null)
.getVisitor().visit(tree, ctx);
}
return tree;
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ public static boolean isEffectivelyGetter(J.MethodDeclaration method) {
}

public static String deriveGetterMethodName(@Nullable JavaType type, String fieldName) {

if (type == JavaType.Primitive.Boolean) {
boolean alreadyStartsWithIs = fieldName.length() >= 3 &&
fieldName.substring(0, 3).matches("is[A-Z]");
Expand Down Expand Up @@ -173,6 +172,40 @@ private static boolean hasMatchingSetterMethodName(J.MethodDeclaration method, S
return method.getSimpleName().equals("set" + StringUtils.capitalize(simpleName));
}

public static boolean isEffectivelySetter(J.MethodDeclaration method) {
if (method.getType() != JavaType.Primitive.Void) {
return false;
}
if (method.getParameters().size() != 1 || method.getParameters().get(0) instanceof J.Empty) {
return false;
}

J.VariableDeclarations variableDeclarations = (J.VariableDeclarations) method.getParameters().get(0);
J.VariableDeclarations.NamedVariable param = variableDeclarations.getVariables().get(0);
String paramName = param.getName().toString();

if (method.getBody() == null ||
method.getBody().getStatements().size() != 1 ||
!(method.getBody().getStatements().get(0) instanceof J.Assignment)) {
return false;
}
J.Assignment assignment = (J.Assignment) method.getBody().getStatements().get(0);

if (!(assignment.getVariable() instanceof J.FieldAccess) && !(assignment.getVariable() instanceof J.Identifier)) {
return false;
}

JavaType fieldType = assignment.getVariable().getType();
// assigned value is exactly the parameter
return assignment.getAssignment().toString().equals(paramName) &&
param.getType() != null &&
param.getType().equals(fieldType); // type of parameter and field have to match
}

public static String deriveSetterMethodName(JavaType.Variable fieldType) {
return "set" + StringUtils.capitalize(fieldType.getName());
}

static AccessLevel getAccessLevel(J.MethodDeclaration methodDeclaration) {
if (methodDeclaration.hasModifier(Public)) {
return PUBLIC;
Expand Down
Loading