added special handling for abstract method parameters in finalguard plugin
All checks were successful
CI / build (push) Successful in 3m31s

This commit is contained in:
2025-11-28 21:07:21 +08:00
parent a6e3d692a0
commit b6c4cca2e7
7 changed files with 53 additions and 21 deletions

View File

@@ -21,9 +21,9 @@ import com.sun.source.util.TaskListener;
import com.sun.source.util.TreePath; import com.sun.source.util.TreePath;
import com.sun.source.util.TreePathScanner; import com.sun.source.util.TreePathScanner;
import com.sun.source.util.Trees; import com.sun.source.util.Trees;
import javax.lang.model.element.Element; import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier; import javax.lang.model.element.Modifier;
import javax.lang.model.element.ExecutableElement;
import javax.tools.Diagnostic; import javax.tools.Diagnostic;
import java.util.AbstractMap; import java.util.AbstractMap;
import java.util.Arrays; import java.util.Arrays;
@@ -36,8 +36,8 @@ import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class FinalGuardPlugin implements Plugin { public class FinalGuardPlugin implements Plugin {
public static final String DIAGNOSTIC_LEVEL_KEY = "net.woggioni.finalguard.diagnostic.level"; public static final String DIAGNOSTIC_LEVEL_KEY = "net.woggioni.finalguard.diagnostic.level";
public static final String IGNORE_ABSTRACT_METHOD_PARAMS_KEY = "net.woggioni.finalguard.ignore.abstract.method.params";
enum VariableType { enum VariableType {
LOCAL_VAR("net.woggioni.finalguard.diagnostic.local.variable.level"), LOCAL_VAR("net.woggioni.finalguard.diagnostic.local.variable.level"),
@@ -45,7 +45,8 @@ public class FinalGuardPlugin implements Plugin {
LOOP_PARAM("net.woggioni.finalguard.diagnostic.for.param.level"), LOOP_PARAM("net.woggioni.finalguard.diagnostic.for.param.level"),
TRY_WITH_PARAM("net.woggioni.finalguard.diagnostic.try.param.level"), TRY_WITH_PARAM("net.woggioni.finalguard.diagnostic.try.param.level"),
CATCH_PARAM("net.woggioni.finalguard.diagnostic.catch.param.level"), CATCH_PARAM("net.woggioni.finalguard.diagnostic.catch.param.level"),
LAMBDA_PARAM("net.woggioni.finalguard.diagnostic.lambda.param.level"); LAMBDA_PARAM("net.woggioni.finalguard.diagnostic.lambda.param.level"),
ABSTRACT_METHOD_PARAM("net.woggioni.finalguard.diagnostic.abstract.method.param.level");
private final String propertyKey; private final String propertyKey;
@@ -71,6 +72,8 @@ public class FinalGuardPlugin implements Plugin {
return "Catch parameter '" + variableName + "' is never reassigned, so it should be declared final"; return "Catch parameter '" + variableName + "' is never reassigned, so it should be declared final";
case LAMBDA_PARAM: case LAMBDA_PARAM:
return "Lambda parameter '" + variableName + "' is never reassigned, so it should be declared final"; return "Lambda parameter '" + variableName + "' is never reassigned, so it should be declared final";
case ABSTRACT_METHOD_PARAM:
return "Abstract method parameter '" + variableName + "' is never reassigned, so it should be declared final";
default: default:
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@@ -80,7 +83,6 @@ public class FinalGuardPlugin implements Plugin {
private static class VariableInfo { private static class VariableInfo {
final VariableTree variableTree; final VariableTree variableTree;
final VariableType variableType; final VariableType variableType;
VariableInfo(VariableTree variableTree, VariableType variableType) { VariableInfo(VariableTree variableTree, VariableType variableType) {
this.variableTree = variableTree; this.variableTree = variableTree;
this.variableType = variableType; this.variableType = variableType;
@@ -109,7 +111,6 @@ public class FinalGuardPlugin implements Plugin {
} }
private static final Configuration configuration = new Configuration(); private static final Configuration configuration = new Configuration();
private static final boolean isJava17OrHigher = isJava17OrHigher(); private static final boolean isJava17OrHigher = isJava17OrHigher();
@Override @Override
@@ -156,9 +157,7 @@ public class FinalGuardPlugin implements Plugin {
public Void visitMethod(MethodTree node, Void p) { public Void visitMethod(MethodTree node, Void p) {
variableInfoMap.clear(); variableInfoMap.clear();
reassignedVariables.clear(); reassignedVariables.clear();
super.visitMethod(node, p); super.visitMethod(node, p);
// Check for variables that could be final // Check for variables that could be final
checkForFinalCandidates(); checkForFinalCandidates();
return null; return null;
@@ -171,6 +170,7 @@ public class FinalGuardPlugin implements Plugin {
final TreePath parentPath = currentPath.getParentPath(); final TreePath parentPath = currentPath.getParentPath();
final Tree parent = parentPath.getLeaf(); final Tree parent = parentPath.getLeaf();
final VariableType type; final VariableType type;
if (parent instanceof LambdaExpressionTree) { if (parent instanceof LambdaExpressionTree) {
type = VariableType.LAMBDA_PARAM; type = VariableType.LAMBDA_PARAM;
} else if (parent instanceof ForLoopTree || parent instanceof EnhancedForLoopTree) { } else if (parent instanceof ForLoopTree || parent instanceof EnhancedForLoopTree) {
@@ -180,11 +180,15 @@ public class FinalGuardPlugin implements Plugin {
} else if (parent instanceof TryTree) { } else if (parent instanceof TryTree) {
type = VariableType.TRY_WITH_PARAM; type = VariableType.TRY_WITH_PARAM;
} else if (parent instanceof MethodTree) { } else if (parent instanceof MethodTree) {
type = VariableType.METHOD_PARAM; if (isAbstractMethodParameter(node, (MethodTree) parent)) {
if(isJava17OrHigher && ((MethodTree) parent).getName().contentEquals("<init>")) { type = VariableType.ABSTRACT_METHOD_PARAM;
final TreePath grandParentPath = parentPath.getParentPath(); } else {
if(grandParentPath.getLeaf().getKind() == Tree.Kind.RECORD) { type = VariableType.METHOD_PARAM;
return super.visitVariable(node, p); if (isJava17OrHigher && ((MethodTree) parent).getName().contentEquals("<init>")) {
final TreePath grandParentPath = parentPath.getParentPath();
if (grandParentPath.getLeaf().getKind() == Tree.Kind.RECORD) {
return super.visitVariable(node, p);
}
} }
} }
} else if (parent instanceof BlockTree) { } else if (parent instanceof BlockTree) {
@@ -192,10 +196,21 @@ public class FinalGuardPlugin implements Plugin {
} else { } else {
type = VariableType.LOCAL_VAR; type = VariableType.LOCAL_VAR;
} }
variableInfoMap.put(varName, new VariableInfo(node, type)); variableInfoMap.put(varName, new VariableInfo(node, type));
return super.visitVariable(node, p); return super.visitVariable(node, p);
} }
private boolean isAbstractMethodParameter(VariableTree variableTree, MethodTree methodTree) {
// Get the element for the method
Element methodElement = trees.getElement(getCurrentPath().getParentPath());
if (methodElement instanceof ExecutableElement) {
ExecutableElement executableElement = (ExecutableElement) methodElement;
return executableElement.getModifiers().contains(Modifier.ABSTRACT);
}
return false;
}
@Override @Override
public Void visitAssignment(AssignmentTree node, Void p) { public Void visitAssignment(AssignmentTree node, Void p) {
if (node.getVariable() instanceof IdentifierTree) { if (node.getVariable() instanceof IdentifierTree) {
@@ -212,8 +227,7 @@ public class FinalGuardPlugin implements Plugin {
node.getKind() == Tree.Kind.POSTFIX_INCREMENT || node.getKind() == Tree.Kind.POSTFIX_INCREMENT ||
node.getKind() == Tree.Kind.POSTFIX_DECREMENT) && node.getKind() == Tree.Kind.POSTFIX_DECREMENT) &&
node.getExpression() instanceof IdentifierTree) { node.getExpression() instanceof IdentifierTree) {
IdentifierTree ident = (IdentifierTree) node.getExpression();
final IdentifierTree ident = (IdentifierTree) node.getExpression();
reassignedVariables.add(ident.getName().toString()); reassignedVariables.add(ident.getName().toString());
} }
return super.visitUnary(node, p); return super.visitUnary(node, p);
@@ -232,23 +246,19 @@ public class FinalGuardPlugin implements Plugin {
for (final Map.Entry<String, VariableInfo> entry : variableInfoMap.entrySet()) { for (final Map.Entry<String, VariableInfo> entry : variableInfoMap.entrySet()) {
final String varName = entry.getKey(); final String varName = entry.getKey();
final VariableInfo info = entry.getValue(); final VariableInfo info = entry.getValue();
Diagnostic.Kind level = configuration.levels.get(info.variableType); Diagnostic.Kind level = configuration.levels.get(info.variableType);
// Skip if level is not configured // Skip if level is not configured
if (level == null) { if (level == null) {
continue; continue;
} }
// Skip if already final // Skip if already final
if (isFinal(info.variableTree)) { if (isFinal(info.variableTree)) {
continue; continue;
} }
// Skip if reassigned // Skip if reassigned
if (reassignedVariables.contains(varName)) { if (reassignedVariables.contains(varName)) {
continue; continue;
} }
trees.printMessage(level, trees.printMessage(level,
info.variableType.getMessage(varName), info.variableType.getMessage(varName),
info.variableTree, info.variableTree,

View File

@@ -33,6 +33,7 @@ import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import java.util.stream.StreamSupport; import java.util.stream.StreamSupport;
import static net.woggioni.finalguard.FinalGuardPlugin.VariableType.ABSTRACT_METHOD_PARAM;
import static net.woggioni.finalguard.FinalGuardPlugin.VariableType.CATCH_PARAM; import static net.woggioni.finalguard.FinalGuardPlugin.VariableType.CATCH_PARAM;
import static net.woggioni.finalguard.FinalGuardPlugin.VariableType.LAMBDA_PARAM; import static net.woggioni.finalguard.FinalGuardPlugin.VariableType.LAMBDA_PARAM;
import static net.woggioni.finalguard.FinalGuardPlugin.VariableType.LOCAL_VAR; import static net.woggioni.finalguard.FinalGuardPlugin.VariableType.LOCAL_VAR;
@@ -161,12 +162,14 @@ public class PluginTest {
Arguments.of(prefix + "TestCase9.java", Arrays.asList(LAMBDA_PARAM.getMessage("s"))), Arguments.of(prefix + "TestCase9.java", Arrays.asList(LAMBDA_PARAM.getMessage("s"))),
Arguments.of(prefix + "TestCase10.java", Arrays.asList(METHOD_PARAM.getMessage("n"))), Arguments.of(prefix + "TestCase10.java", Arrays.asList(METHOD_PARAM.getMessage("n"))),
Arguments.of(prefix + "TestCase11.java", Arrays.asList( Arguments.of(prefix + "TestCase11.java", Arrays.asList(
METHOD_PARAM.getMessage("n"), ABSTRACT_METHOD_PARAM.getMessage("n"),
LOCAL_VAR.getMessage("result"), LOCAL_VAR.getMessage("result"),
LOCAL_VAR.getMessage("size"), LOCAL_VAR.getMessage("size"),
METHOD_PARAM.getMessage("t1s") METHOD_PARAM.getMessage("t1s")
)), )),
Arguments.of(prefix + "TestCase12.java", Collections.emptyList()) Arguments.of(prefix + "TestCase12.java", Collections.emptyList()),
Arguments.of(prefix + "TestCase13.java", Arrays.asList(ABSTRACT_METHOD_PARAM.getMessage("x"), ABSTRACT_METHOD_PARAM.getMessage("y"))),
Arguments.of(prefix + "TestCase14.java", Arrays.asList(ABSTRACT_METHOD_PARAM.getMessage("x"), ABSTRACT_METHOD_PARAM.getMessage("y")))
); );
} }
} }

View File

@@ -0,0 +1,5 @@
import java.util.Arrays;
public interface TestCase13 {
void foo(double x, double y);
}

View File

@@ -0,0 +1,5 @@
import java.util.Arrays;
public abstract class TestCase14 {
abstract public void foo(double x, double y);
}

View File

@@ -18,5 +18,7 @@ public interface FinalGuardExtension {
Property<Diagnostic.Kind> getMethodParameterLevel(); Property<Diagnostic.Kind> getMethodParameterLevel();
Property<Diagnostic.Kind> getAbstractMethodParameterLevel();
Property<Diagnostic.Kind> getCatchParameterLevel(); Property<Diagnostic.Kind> getCatchParameterLevel();
} }

View File

@@ -81,6 +81,13 @@ public class FinalGuardPlugin implements Plugin<Project> {
setProperty.accept("method.param.level", diagnosticKind); setProperty.accept("method.param.level", diagnosticKind);
} }
} }
{
final Property<Diagnostic.Kind> abstractMethodParameterLevel = finalGuardExtension.getAbstractMethodParameterLevel();
if (abstractMethodParameterLevel.isPresent()) {
final Diagnostic.Kind diagnosticKind = abstractMethodParameterLevel.get();
setProperty.accept("abstract.method.param.level", diagnosticKind);
}
}
{ {
final Property<Diagnostic.Kind> forLoopParameterLevel = finalGuardExtension.getForLoopParameterLevel(); final Property<Diagnostic.Kind> forLoopParameterLevel = finalGuardExtension.getForLoopParameterLevel();
if (forLoopParameterLevel.isPresent()) { if (forLoopParameterLevel.isPresent()) {

View File

@@ -1,5 +1,5 @@
lys.catalog.version=2025.11.18 lys.catalog.version=2025.11.18
version.myGradlePlugins=2025.11.28 version.myGradlePlugins=2025.11.29
version.gradle=8.12 version.gradle=8.12
gitea.maven.url = https://gitea.woggioni.net/api/packages/woggioni/maven gitea.maven.url = https://gitea.woggioni.net/api/packages/woggioni/maven