diff --git a/liquidjava-example/src/main/java/testSuite/CorrectModernJavaSyntax.java b/liquidjava-example/src/main/java/testSuite/CorrectModernJavaSyntax.java new file mode 100644 index 00000000..5c310fab --- /dev/null +++ b/liquidjava-example/src/main/java/testSuite/CorrectModernJavaSyntax.java @@ -0,0 +1,32 @@ +package testSuite; + +import java.io.StringReader; + +import liquidjava.specification.Refinement; + +@SuppressWarnings("unused") +public class CorrectModernJavaSyntax { + + // local variable type inference (Java 10) + void localVar() { + var n = 5; + @Refinement("_ > 0") + int p = n; + } + + // resource reference in try-with-resources (Java 9) + void resourceReference() throws Exception { + StringReader reader = new StringReader("a"); + try (reader) { + reader.read(); + } + } + + // switch expression (Java 14) + int switchExpression(int k) { + return switch (k) { + case 0 -> 1; + default -> 2; + }; + } +} diff --git a/liquidjava-verifier/src/main/java/liquidjava/api/CommandLineLauncher.java b/liquidjava-verifier/src/main/java/liquidjava/api/CommandLineLauncher.java index 621e8c2b..f10d9abe 100644 --- a/liquidjava-verifier/src/main/java/liquidjava/api/CommandLineLauncher.java +++ b/liquidjava-verifier/src/main/java/liquidjava/api/CommandLineLauncher.java @@ -3,6 +3,7 @@ import java.io.File; import java.util.Arrays; +import liquidjava.diagnostics.DebugLog; import liquidjava.diagnostics.Diagnostics; import liquidjava.diagnostics.errors.CustomError; import liquidjava.diagnostics.warnings.CustomWarning; @@ -70,7 +71,10 @@ public static void launch(String... paths) { env.setSourceClasspath( new String[] { new File(Refinement.class.getProtectionDomain().getCodeSource().getLocation().getFile()) .getAbsolutePath() }); - env.setComplianceLevel(8); + int complianceLevel = ComplianceLevel.resolve(paths); + if (DebugLog.enabled()) + System.out.println("Java compliance level: " + complianceLevel); + env.setComplianceLevel(complianceLevel); boolean buildSuccess = launcher.getModelBuilder().build(); if (!buildSuccess && (env.getErrorCount() > 0 || env.getWarningCount() > 0)) { diff --git a/liquidjava-verifier/src/main/java/liquidjava/api/ComplianceLevel.java b/liquidjava-verifier/src/main/java/liquidjava/api/ComplianceLevel.java new file mode 100644 index 00000000..90182424 --- /dev/null +++ b/liquidjava-verifier/src/main/java/liquidjava/api/ComplianceLevel.java @@ -0,0 +1,42 @@ +package liquidjava.api; + +import java.io.File; +import java.io.FileReader; +import java.util.Arrays; +import java.util.Optional; +import java.util.Properties; + +import org.apache.maven.model.io.xpp3.MavenXpp3Reader; + +/** + * Java compliance level used by Spoon, read from the {@code maven.compiler.release} or {@code maven.compiler.source} + * property of the nearest {@code pom.xml} of the verified paths + */ +public final class ComplianceLevel { + + /** Highest level accepted by Spoon 10.4.2 (JDT 3.33), also used when no pom declares one */ + public static final int MAX_SUPPORTED = 19; + + public static int resolve(String... paths) { + return Arrays.stream(paths).map(path -> fromPom(new File(path).getAbsoluteFile())).flatMap(Optional::stream) + .max(Integer::compare).map(level -> Math.min(level, MAX_SUPPORTED)).orElse(MAX_SUPPORTED); + } + + private static Optional fromPom(File path) { + for (File dir = path; dir != null; dir = dir.getParentFile()) { + File pom = new File(dir, "pom.xml"); + if (!pom.isFile()) + continue; + try (FileReader reader = new FileReader(pom)) { + Properties properties = new MavenXpp3Reader().read(reader).getProperties(); + String version = properties.getProperty("maven.compiler.release", + properties.getProperty("maven.compiler.source")); + if (version != null) + return Optional.of(Integer.parseInt(version.trim().replaceFirst("^1\\.", ""))); + } catch (Exception ignored) { + // unreadable pom or non-numeric version: keep searching enclosing poms + } + } + return Optional.empty(); + } +} diff --git a/liquidjava-verifier/src/test/java/liquidjava/api/tests/TestComplianceLevel.java b/liquidjava-verifier/src/test/java/liquidjava/api/tests/TestComplianceLevel.java new file mode 100644 index 00000000..d99feb16 --- /dev/null +++ b/liquidjava-verifier/src/test/java/liquidjava/api/tests/TestComplianceLevel.java @@ -0,0 +1,35 @@ +package liquidjava.api.tests; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import liquidjava.api.ComplianceLevel; + +class TestComplianceLevel { + + @TempDir + Path project; + + private int resolveWithRelease(String release) throws IOException { + Files.writeString(project.resolve("pom.xml"), + "4.0.0" + release + + ""); + return ComplianceLevel.resolve(Files.createDirectories(project.resolve("src")).toString()); + } + + @Test + void readsLevelFromPom() throws IOException { + assertEquals(8, resolveWithRelease("1.8")); + } + + @Test + void capsAtMaxSupported() throws IOException { + assertEquals(ComplianceLevel.MAX_SUPPORTED, resolveWithRelease("30")); + } +}