Excluded generated sources from finalguard plugin
All checks were successful
CI / build (push) Successful in 5m40s

Also added optional list of exclusion paths
This commit is contained in:
2026-02-19 19:41:05 +08:00
parent c5dcee554e
commit 7817aedd85
9 changed files with 83 additions and 34 deletions

View File

@@ -1,35 +1,18 @@
package net.woggioni.finalguard;
import com.sun.source.tree.AssignmentTree;
import com.sun.source.tree.BlockTree;
import com.sun.source.tree.CatchTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.CompoundAssignmentTree;
import com.sun.source.tree.EnhancedForLoopTree;
import com.sun.source.tree.ForLoopTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.LambdaExpressionTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.TryTree;
import com.sun.source.tree.UnaryTree;
import com.sun.source.tree.VariableTree;
import com.sun.source.util.JavacTask;
import com.sun.source.util.Plugin;
import com.sun.source.util.TaskEvent;
import com.sun.source.util.TaskListener;
import com.sun.source.util.TreePath;
import com.sun.source.util.TreePathScanner;
import com.sun.source.util.Trees;
import com.sun.source.tree.*;
import com.sun.source.util.*;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.tools.Diagnostic;
import java.util.*;
import java.util.stream.Collectors;
public class FinalGuardPlugin implements Plugin {
public static final String DEFAULT_LEVEL_KEY = "default.level";
public static final String EXCLUDE_KEY = "exclude";
public static final String IGNORE_ABSTRACT_METHOD_PARAMS_KEY = "net.woggioni.finalguard.ignore.abstract.method.params";
enum VariableType {
LOCAL_VAR("local.variable.level"),
@@ -83,15 +66,22 @@ public class FinalGuardPlugin implements Plugin {
private static final class Configuration {
private final Map<VariableType, Diagnostic.Kind> levels;
private final List<String> excludedPaths;
public Configuration(final String... args) {
final Map<String, String> props = new HashMap<>();
final List<String> excluded = new ArrayList<>();
for (final String arg : args) {
final String[] parts = arg.split("=", 2);
if (parts.length == 2) {
props.put(parts[0], parts[1]);
if (EXCLUDE_KEY.equals(parts[0])) {
excluded.add(parts[1]);
} else {
props.put(parts[0], parts[1]);
}
}
}
this.excludedPaths = Collections.unmodifiableList(excluded);
final Diagnostic.Kind defaultLevel =
Optional.ofNullable(props.get(DEFAULT_LEVEL_KEY)).map(Diagnostic.Kind::valueOf).orElse(null);
this.levels = Arrays.stream(VariableType.values()).map(vt -> {
@@ -103,6 +93,15 @@ public class FinalGuardPlugin implements Plugin {
}
}).filter(Objects::nonNull).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
public boolean isExcluded(final String sourcePath) {
for (final String excludedPath : excludedPaths) {
if (sourcePath.startsWith(excludedPath)) {
return true;
}
}
return false;
}
}
private static boolean isJava17OrHigher() {
@@ -134,6 +133,10 @@ public class FinalGuardPlugin implements Plugin {
}
private void analyzeFinalVariables(CompilationUnitTree compilationUnit, JavacTask task, Element typeElement, Configuration configuration) {
final String sourcePath = compilationUnit.getSourceFile().toUri().getPath();
if (sourcePath != null && configuration.isExcluded(sourcePath)) {
return;
}
FinalVariableAnalyzer analyzer = new FinalVariableAnalyzer(compilationUnit, task, configuration);
TreePath path = Trees.instance(task).getPath(typeElement);
if (path != null) {

View File

@@ -1,6 +1,7 @@
package net.woggioni.finalguard;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
@@ -81,15 +82,23 @@ public class PluginTest {
}
private Optional<Iterable<Diagnostic<? extends JavaFileObject>>> compile(Iterable<URI> sources) {
return compile(sources, "");
}
private Optional<Iterable<Diagnostic<? extends JavaFileObject>>> compile(Iterable<URI> sources, String extraPluginArgs) {
StringWriter output = new StringWriter();
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
FileManager fileManager =
new FileManager(compiler.getStandardFileManager(null, null, null));
List<JavaFileObject> compilationUnits = StreamSupport.stream(sources.spliterator(), false)
.map(SourceFile::new).collect(Collectors.toList());
String pluginArg = "-Xplugin:" + FinalGuardPlugin.class.getName() + " " + FinalGuardPlugin.DEFAULT_LEVEL_KEY + "=ERROR";
if (!extraPluginArgs.isEmpty()) {
pluginArg += " " + extraPluginArgs;
}
List<String> arguments = Arrays.asList(
"-classpath", System.getProperty("test.compilation.classpath"),
"-Xplugin:" + FinalGuardPlugin.class.getName() + " " + FinalGuardPlugin.DEFAULT_LEVEL_KEY + "=ERROR"
pluginArg
);
final ArrayList<Diagnostic<? extends JavaFileObject>> compilerMessages = new ArrayList<>();
JavaCompiler.CompilationTask task = compiler.getTask(
@@ -184,4 +193,25 @@ public class PluginTest {
Assertions.assertTrue(compilationErrors.isEmpty(), "Unexpected compilation errors found in the output");
}
}
@Test
public void testExcludedSourceIsSkipped() throws Exception {
// TestCase3 normally produces an error (local var 'n' not final).
// When we exclude the directory containing TestCase3, it should compile without errors.
ClassLoader cl = getClass().getClassLoader();
URI sourceUri = cl.getResource("net/woggioni/finalguard/test/TestCase3.java").toURI();
String sourcePath = new File(sourceUri).getParent();
// Compile WITH exclude — should succeed (no errors)
Optional<Iterable<Diagnostic<? extends JavaFileObject>>> resultWithExclude =
compile(Collections.singletonList(sourceUri), FinalGuardPlugin.EXCLUDE_KEY + "=" + sourcePath);
Assertions.assertFalse(resultWithExclude.isPresent(),
"Compilation should succeed when source directory is excluded");
// Compile WITHOUT exclude — should fail (TestCase3 has errors)
Optional<Iterable<Diagnostic<? extends JavaFileObject>>> resultWithoutExclude =
compile(Collections.singletonList(sourceUri));
Assertions.assertTrue(resultWithoutExclude.isPresent(),
"Compilation should fail when source directory is NOT excluded");
}
}

View File

@@ -1,4 +1,2 @@
import java.util.Arrays;
public record TestCase12<T, U>(double x, double y) {
}

View File

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

View File

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