Skip to content

Commit a0a1b50

Browse files
SONARJAVA-6105 Update MethodTreeUtils.isMainMethod to recognize Java 25 instance main (#5448)
Co-authored-by: rombirli <56340680+rombirli@users.noreply.github.com>
1 parent 437aecf commit a0a1b50

17 files changed

Lines changed: 127 additions & 44 deletions
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package checks;
2+
3+
import java.io.IOException;
4+
5+
class MainMethodThrowsExceptionInstanceMainCheckSample {
6+
public void main(String[] args) throws IOException { // Noncompliant {{Remove this throws clause.}}
7+
// ^^^^^^
8+
}
9+
10+
public void main(int a, int b) throws IOException {}
11+
12+
public void example() {}
13+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package checks;
2+
3+
import java.io.IOException;
4+
5+
class MainMethodThrowsExceptionCheckSample {
6+
static class Bad {
7+
public static void main(String[] args) throws IOException { // Noncompliant {{Remove this throws clause.}}
8+
// ^^^^^^
9+
}
10+
}
11+
12+
// Before Java 25, this is not a program entry point.
13+
static class InstanceMain {
14+
public void main(String[] args) throws IOException {
15+
}
16+
}
17+
18+
static class Good {
19+
public static void main(String[] args) {
20+
}
21+
}
22+
}

java-checks-test-sources/default/src/main/java/checks/RawExceptionCheckSample.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,7 @@ public void throws_Exception2() throws Exception { // Compliant because override
103103

104104
public static void main(String[] args) throws Exception { //should not raise issue SONARJAVA-671
105105
}
106+
107+
void main() throws Exception { // Compliant, because it is an instance main.
108+
}
106109
}

java-checks/src/main/java/org/sonar/java/checks/MainMethodThrowsExceptionCheck.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public List<Tree.Kind> nodesToVisit() {
3636
@Override
3737
public void visitNode(Tree tree) {
3838
MethodTree methodTree = (MethodTree) tree;
39-
if (MethodTreeUtils.isMainMethod(methodTree) && !methodTree.throwsClauses().isEmpty()) {
39+
if (MethodTreeUtils.isMainMethod(methodTree, context.getJavaVersion()) && !methodTree.throwsClauses().isEmpty()) {
4040
reportIssue(methodTree.throwsToken(), "Remove this throws clause.");
4141
}
4242
}

java-checks/src/main/java/org/sonar/java/checks/RawExceptionCheck.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.sonar.java.reporting.FluentReporting;
2626
import org.sonar.plugins.java.api.JavaFileScanner;
2727
import org.sonar.plugins.java.api.JavaFileScannerContext;
28+
import org.sonar.plugins.java.api.JavaVersion;
2829
import org.sonar.plugins.java.api.semantic.Symbol;
2930
import org.sonar.plugins.java.api.semantic.Type;
3031
import org.sonar.plugins.java.api.tree.BaseTreeVisitor;
@@ -47,11 +48,13 @@ public class RawExceptionCheck extends BaseTreeVisitor implements JavaFileScanne
4748
"java.lang.RuntimeException");
4849

4950
private FluentReporting context;
51+
private JavaVersion javaVersion;
5052
private final Set<Type> exceptionsThrownByMethodInvocations = new HashSet<>();
5153

5254
@Override
5355
public void scanFile(JavaFileScannerContext context) {
5456
this.context = (FluentReporting) context;
57+
this.javaVersion = context.getJavaVersion();
5558
scan(context.getTree());
5659
}
5760

@@ -120,8 +123,8 @@ private static boolean isNotOverridden(MethodTree tree) {
120123
return Boolean.FALSE.equals(tree.isOverriding());
121124
}
122125

123-
private static boolean isNotMainMethod(MethodTree tree) {
124-
return !MethodTreeUtils.isMainMethod(tree);
126+
private boolean isNotMainMethod(MethodTree tree) {
127+
return !MethodTreeUtils.isMainMethod(tree, javaVersion);
125128
}
126129

127130
}

java-checks/src/main/java/org/sonar/java/checks/SystemExitCalledCheck.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public List<Tree.Kind> nodesToVisit() {
4242

4343
@Override
4444
public void visitNode(Tree tree) {
45-
if (tree.is(Tree.Kind.CONSTRUCTOR) || !MethodTreeUtils.isMainMethod((MethodTree) tree)) {
45+
if (tree.is(Tree.Kind.CONSTRUCTOR) || !MethodTreeUtils.isMainMethod((MethodTree) tree, context.getJavaVersion())) {
4646
tree.accept(new InvocationVisitor());
4747
}
4848
}

java-checks/src/main/java/org/sonar/java/checks/ThrowsSeveralCheckedExceptionCheck.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public List<Tree.Kind> nodesToVisit() {
3737
@Override
3838
public void visitNode(Tree tree) {
3939
MethodTree methodTree = (MethodTree) tree;
40-
if (isPublic(methodTree) && !MethodTreeUtils.isMainMethod(methodTree)) {
40+
if (isPublic(methodTree) && !MethodTreeUtils.isMainMethod(methodTree, context.getJavaVersion())) {
4141
List<String> thrownCheckedExceptions = getThrownCheckedExceptions(methodTree);
4242
if (thrownCheckedExceptions.size() > 1 && isNotOverridden(methodTree)) {
4343
reportIssue(methodTree.simpleName(), "Refactor this method to throw at most one checked exception instead of: " + String.join(", ", thrownCheckedExceptions));

java-checks/src/main/java/org/sonar/java/checks/UtilityClassWithPublicConstructorCheck.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public List<Tree.Kind> nodesToVisit() {
6464
@Override
6565
public void visitNode(Tree tree) {
6666
ClassTree classTree = (ClassTree) tree;
67-
if (!ClassPatternsUtils.isUtilityClass(classTree) || ClassPatternsUtils.isPrivateInnerClass(classTree)) {
67+
if (!ClassPatternsUtils.isUtilityClass(classTree, context.getJavaVersion()) || ClassPatternsUtils.isPrivateInnerClass(classTree)) {
6868
return;
6969
}
7070
boolean hasImplicitPublicConstructor = true;

java-checks/src/main/java/org/sonar/java/checks/design/ClassImportCouplingCheck.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public void scanFile(JavaFileScannerContext context) {
5959
@Override
6060
public void visitClass(ClassTree tree) {
6161
// if class is utility or private inner class -> don't report
62-
if (ClassPatternsUtils.isUtilityClass(tree) || ClassPatternsUtils.isPrivateInnerClass(tree)) {
62+
if (ClassPatternsUtils.isUtilityClass(tree, context.getJavaVersion()) || ClassPatternsUtils.isPrivateInnerClass(tree)) {
6363
return;
6464
}
6565

java-checks/src/main/java/org/sonar/java/checks/helpers/ClassPatternsUtils.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.util.List;
2020
import org.sonar.java.model.ModifiersUtils;
21+
import org.sonar.plugins.java.api.JavaVersion;
2122
import org.sonar.plugins.java.api.tree.ClassTree;
2223
import org.sonar.plugins.java.api.tree.MethodTree;
2324
import org.sonar.plugins.java.api.tree.Modifier;
@@ -35,15 +36,16 @@ public static boolean isPrivateInnerClass(ClassTree classTree) {
3536
ModifiersUtils.hasModifier(classTree.modifiers(), Modifier.PRIVATE);
3637
}
3738

38-
public static boolean isUtilityClass(ClassTree classTree) {
39+
/** True if the class contains only static members (with caveats). */
40+
public static boolean isUtilityClass(ClassTree classTree, JavaVersion javaVersion) {
3941
return !anonymousClass(classTree) && hasOnlyStaticMembers(classTree) && !extendsAnotherClassOrImplementsSerializable(classTree)
40-
&& !containsMainMethod(classTree);
42+
&& !containsMainMethod(classTree, javaVersion);
4143
}
4244

43-
private static boolean containsMainMethod(ClassTree classTree) {
45+
private static boolean containsMainMethod(ClassTree classTree, JavaVersion javaVersion) {
4446
return classTree.members().stream()
4547
.filter(member -> member.is(Tree.Kind.METHOD))
46-
.anyMatch(method -> MethodTreeUtils.isMainMethod((MethodTree) method));
48+
.anyMatch(method -> MethodTreeUtils.isMainMethod((MethodTree) method, javaVersion));
4749
}
4850

4951
private static boolean hasOnlyStaticMembers(ClassTree classTree) {

0 commit comments

Comments
 (0)