Skip to content

Commit 5b957bb

Browse files
timo-atimtebeekgithub-actions[bot]
authored
feat: use lombok @NoArgsConstructor annotation (#645)
* migrate recipe as-is * Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Minimize implementation * Add missing AccessLevel imports * Remove unused import --------- Co-authored-by: Tim te Beek <timtebeek@gmail.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Tim te Beek <tim@moderne.io>
1 parent f3baad0 commit 5b957bb

File tree

2 files changed

+220
-0
lines changed

2 files changed

+220
-0
lines changed
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
* <p>
4+
* Licensed under the Moderne Source Available License (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://docs.moderne.io/licensing/moderne-source-available-license
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.openrewrite.java.migrate.lombok;
17+
18+
import lombok.AccessLevel;
19+
import lombok.EqualsAndHashCode;
20+
import lombok.Value;
21+
import org.jspecify.annotations.Nullable;
22+
import org.openrewrite.ExecutionContext;
23+
import org.openrewrite.Recipe;
24+
import org.openrewrite.TreeVisitor;
25+
import org.openrewrite.java.JavaIsoVisitor;
26+
import org.openrewrite.java.JavaParser;
27+
import org.openrewrite.java.JavaTemplate;
28+
import org.openrewrite.java.tree.J;
29+
import org.openrewrite.java.tree.TypeUtils;
30+
31+
import static java.util.Comparator.comparing;
32+
33+
@Value
34+
@EqualsAndHashCode(callSuper = false)
35+
public class UseNoArgsConstructor extends Recipe {
36+
37+
@Override
38+
public String getDisplayName() {
39+
//language=markdown
40+
return "Use `@NoArgsConstructor` where applicable";
41+
}
42+
43+
@Override
44+
public String getDescription() {
45+
//language=markdown
46+
return "Prefer the Lombok `@NoArgsConstructor` annotation over explicitly written out constructors.";
47+
}
48+
49+
@Override
50+
public TreeVisitor<?, ExecutionContext> getVisitor() {
51+
return new JavaIsoVisitor<ExecutionContext>() {
52+
@Override
53+
public J.@Nullable MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
54+
if (method.isConstructor() &&
55+
method.getParameters().get(0) instanceof J.Empty &&
56+
method.getBody() != null && method.getBody().getStatements().isEmpty()) {
57+
J.ClassDeclaration enclosing = getCursor().firstEnclosing(J.ClassDeclaration.class);
58+
AccessLevel accessLevel = LombokUtils.getAccessLevel(method);
59+
doAfterVisit(new JavaIsoVisitor<ExecutionContext>() {
60+
@Override
61+
public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
62+
if (TypeUtils.isOfType(classDecl.getType(), enclosing.getType())) {
63+
String template = "@NoArgsConstructor" + (accessLevel == AccessLevel.PUBLIC ?
64+
"" : "(access = AccessLevel." + accessLevel.name() + ")");
65+
maybeAddImport("lombok.AccessLevel");
66+
maybeAddImport("lombok.NoArgsConstructor");
67+
return JavaTemplate.builder(template)
68+
.imports("lombok.*")
69+
.javaParser(JavaParser.fromJavaVersion().classpath("lombok"))
70+
.build()
71+
.apply(getCursor(), classDecl.getCoordinates().addAnnotation(comparing(J.Annotation::getSimpleName)));
72+
}
73+
return super.visitClassDeclaration(classDecl, ctx);
74+
}
75+
});
76+
return null;
77+
}
78+
return super.visitMethodDeclaration(method, ctx);
79+
}
80+
};
81+
}
82+
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
* Copyright 2024 the original author or authors.
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.openrewrite.java.migrate.lombok;
17+
18+
import org.junit.jupiter.api.Test;
19+
import org.openrewrite.DocumentExample;
20+
import org.openrewrite.test.RecipeSpec;
21+
import org.openrewrite.test.RewriteTest;
22+
23+
import static org.openrewrite.java.Assertions.java;
24+
25+
class UseNoArgsConstructorTest implements RewriteTest {
26+
27+
@Override
28+
public void defaults(RecipeSpec spec) {
29+
spec.recipe(new UseNoArgsConstructor());
30+
}
31+
32+
@DocumentExample
33+
@Test
34+
void replaceEmptyPublicConstructor() {
35+
rewriteRun(// language=java
36+
java(
37+
"""
38+
class A {
39+
public A() {}
40+
}
41+
""",
42+
"""
43+
import lombok.NoArgsConstructor;
44+
45+
@NoArgsConstructor
46+
class A {
47+
}
48+
"""
49+
)
50+
);
51+
}
52+
53+
@Test
54+
void keepNonEmptyPublicConstructor() {
55+
rewriteRun(
56+
//language=java
57+
java(
58+
"""
59+
class A {
60+
61+
int foo;
62+
63+
public A() {
64+
foo = 7;
65+
}
66+
}
67+
"""
68+
)
69+
);
70+
}
71+
72+
@Test
73+
void replaceEmptyProtectedConstructor() {
74+
rewriteRun(
75+
//language=java
76+
java(
77+
"""
78+
class A {
79+
protected A() {}
80+
}
81+
""",
82+
"""
83+
import lombok.AccessLevel;
84+
import lombok.NoArgsConstructor;
85+
86+
@NoArgsConstructor(access = AccessLevel.PROTECTED)
87+
class A {
88+
}
89+
"""
90+
)
91+
);
92+
}
93+
94+
@Test
95+
void replaceEmptyPrivateConstructor() {
96+
rewriteRun(
97+
//language=java
98+
java(
99+
"""
100+
class A {
101+
private A() {}
102+
}
103+
""",
104+
"""
105+
import lombok.AccessLevel;
106+
import lombok.NoArgsConstructor;
107+
108+
@NoArgsConstructor(access = AccessLevel.PRIVATE)
109+
class A {
110+
}
111+
"""
112+
)
113+
);
114+
}
115+
116+
@Test
117+
void replaceEmptyPackageConstructor() {
118+
rewriteRun(
119+
//language=java
120+
java(
121+
"""
122+
class A {
123+
A() {}
124+
}
125+
""",
126+
"""
127+
import lombok.AccessLevel;
128+
import lombok.NoArgsConstructor;
129+
130+
@NoArgsConstructor(access = AccessLevel.PACKAGE)
131+
class A {
132+
}
133+
"""
134+
)
135+
);
136+
}
137+
138+
}

0 commit comments

Comments
 (0)