Excluded generated sources from finalguard plugin
All checks were successful
CI / build (push) Successful in 5m58s
All checks were successful
CI / build (push) Successful in 5m58s
Also added optional list of exclusion paths
This commit is contained in:
@@ -4,7 +4,7 @@ on:
|
||||
branches: [ master ]
|
||||
jobs:
|
||||
build:
|
||||
runs-on: hostinger
|
||||
runs-on: woryzen
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v4
|
||||
|
||||
@@ -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) {
|
||||
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) {
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,2 @@
|
||||
import java.util.Arrays;
|
||||
|
||||
public record TestCase12<T, U>(double x, double y) {
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
import java.util.Arrays;
|
||||
|
||||
public interface TestCase13 {
|
||||
void foo(double x, double y);
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
import java.util.Arrays;
|
||||
|
||||
public abstract class TestCase14 {
|
||||
abstract public void foo(double x, double y);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.woggioni.gradle.finalguard;
|
||||
|
||||
import org.gradle.api.provider.ListProperty;
|
||||
import org.gradle.api.provider.Property;
|
||||
|
||||
import javax.tools.Diagnostic;
|
||||
@@ -21,4 +22,8 @@ public interface FinalGuardExtension {
|
||||
Property<Diagnostic.Kind> getAbstractMethodParameterLevel();
|
||||
|
||||
Property<Diagnostic.Kind> getCatchParameterLevel();
|
||||
|
||||
Property<Boolean> getSkipGeneratedSources();
|
||||
|
||||
ListProperty<String> getExcludedPrefixes();
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import org.gradle.api.tasks.compile.CompileOptions;
|
||||
import org.gradle.api.tasks.compile.JavaCompile;
|
||||
|
||||
import javax.tools.Diagnostic;
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.Manifest;
|
||||
@@ -21,6 +22,7 @@ import java.util.jar.Manifest;
|
||||
public class FinalGuardPlugin implements Plugin<Project> {
|
||||
private static final String FINALGUARD_PLUGIN_CONFIGURATION = "finalguard_plugin";
|
||||
private static final String JAVAC_PLUGIN_NAME = "net.woggioni.finalguard.FinalGuardPlugin";
|
||||
private static final String EXCLUDE_KEY = "exclude";
|
||||
|
||||
@Override
|
||||
public void apply(final Project project) {
|
||||
@@ -49,10 +51,12 @@ public class FinalGuardPlugin implements Plugin<Project> {
|
||||
});
|
||||
|
||||
final FinalGuardExtension finalGuardExtension = objects.newInstance(FinalGuardExtension.class);
|
||||
extensionContainer.add("finalGuard", finalGuardExtension);
|
||||
finalGuardExtension.getSkipGeneratedSources().convention(true);
|
||||
extensionContainer.add("finalguard", finalGuardExtension);
|
||||
tasks.withType(JavaCompile.class, javaCompileTask -> {
|
||||
javaCompileTask.doFirst(t -> {
|
||||
final CompileOptions options = javaCompileTask.getOptions();
|
||||
options.setAnnotationProcessorPath(options.getAnnotationProcessorPath().plus(javacPluginConfiguration));
|
||||
final StringBuilder xpluginArg = new StringBuilder("-Xplugin:").append(JAVAC_PLUGIN_NAME);
|
||||
appendOption(xpluginArg, "default.level", finalGuardExtension.getDefaultLevel());
|
||||
appendOption(xpluginArg, "local.variable.level", finalGuardExtension.getLocalVariableLevel());
|
||||
@@ -62,14 +66,27 @@ public class FinalGuardPlugin implements Plugin<Project> {
|
||||
appendOption(xpluginArg, "try.param.level", finalGuardExtension.getTryWithResourceLevel());
|
||||
appendOption(xpluginArg, "catch.param.level", finalGuardExtension.getCatchParameterLevel());
|
||||
appendOption(xpluginArg, "lambda.param.level", finalGuardExtension.getLambdaParameterLevel());
|
||||
if (finalGuardExtension.getSkipGeneratedSources().getOrElse(true)) {
|
||||
final File generatedSourceDir = options.getGeneratedSourceOutputDirectory().getAsFile().getOrNull();
|
||||
if (generatedSourceDir != null) {
|
||||
appendOption(xpluginArg, EXCLUDE_KEY, generatedSourceDir.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
for(final String excludedPrefix : finalGuardExtension.getExcludedPrefixes().get()) {
|
||||
appendOption(xpluginArg, EXCLUDE_KEY, excludedPrefix);
|
||||
}
|
||||
options.getCompilerArgs().add(xpluginArg.toString());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private static void appendOption(StringBuilder sb, String key, String value) {
|
||||
sb.append(' ').append(key).append('=').append(value);
|
||||
}
|
||||
|
||||
private static void appendOption(StringBuilder sb, String key, Property<Diagnostic.Kind> property) {
|
||||
if (property.isPresent()) {
|
||||
sb.append(' ').append(key).append('=').append(property.get());
|
||||
appendOption(sb, key, property.get().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
lys.catalog.version=2025.12.27
|
||||
version.myGradlePlugins=2026.02.17
|
||||
version.myGradlePlugins=2026.02.19
|
||||
version.gradle=9.3.1
|
||||
|
||||
gitea.maven.url = https://gitea.woggioni.net/api/packages/woggioni/maven
|
||||
|
||||
@@ -2,6 +2,12 @@ plugins {
|
||||
id 'groovy-gradle-plugin'
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility(JavaVersion.VERSION_1_8.toString())
|
||||
targetCompatibility(JavaVersion.VERSION_1_8.toString())
|
||||
modularity.inferModulePath = false
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
create("JPMSCheckPlugin") {
|
||||
|
||||
Reference in New Issue
Block a user