Skip to content

Commit e054c42

Browse files
Read Spoon compliance level from the project's pom.xml (#352)
## Problem `CommandLineLauncher` sets Spoon's compliance level to 8, so any Java 9+ syntax fails to compile and LiquidJava verifies a broken model with only a generic warning ("Java compilation encountered issues"). E.g. `var n = 5;` is read as a variable of class `testSuite.var` (`Sorts testSuite.var and Int are incompatible`), and `try (r)` (Java 9 resource reference) does not parse, which blocks the second reproducer of #334. ## Change - New `ComplianceLevel` reads the level from the `maven.compiler.release` (or `maven.compiler.source`) property of the nearest `pom.xml` of each verified path, walking up to enclosing poms, using the existing `maven-model` dependency. `1.8` is read as `8`; with several paths the highest level wins. - Defaults to **19** when no pom declares it, and caps at **19**: the highest level Spoon 10.4.2's JDT (3.33) accepts (`20` throws `Unrecognized option : -20`). The cap is silent (the level is printed with `--debug`), since e.g. `liquidjava-example` declares 20 and a warning would show up on every run. - New test `CorrectModernJavaSyntax` (`var`, `try (r)`, switch expression): fails at level 8, passes now. - Unit tests `TestComplianceLevel` (reads the pom, caps). Not read (falls back to 19): the compiler plugin's `<release>`/`<source>` config, parents outside the enclosing directories, Gradle builds. Upgrading to Spoon 11.5 (levels up to 26) is in #363. ## Testing `mvn test`: 369/369 pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent ad083df commit e054c42

4 files changed

Lines changed: 114 additions & 1 deletion

File tree

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package testSuite;
2+
3+
import java.io.StringReader;
4+
5+
import liquidjava.specification.Refinement;
6+
7+
@SuppressWarnings("unused")
8+
public class CorrectModernJavaSyntax {
9+
10+
// local variable type inference (Java 10)
11+
void localVar() {
12+
var n = 5;
13+
@Refinement("_ > 0")
14+
int p = n;
15+
}
16+
17+
// resource reference in try-with-resources (Java 9)
18+
void resourceReference() throws Exception {
19+
StringReader reader = new StringReader("a");
20+
try (reader) {
21+
reader.read();
22+
}
23+
}
24+
25+
// switch expression (Java 14)
26+
int switchExpression(int k) {
27+
return switch (k) {
28+
case 0 -> 1;
29+
default -> 2;
30+
};
31+
}
32+
}

‎liquidjava-verifier/src/main/java/liquidjava/api/CommandLineLauncher.java‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.io.File;
44
import java.util.Arrays;
55

6+
import liquidjava.diagnostics.DebugLog;
67
import liquidjava.diagnostics.Diagnostics;
78
import liquidjava.diagnostics.errors.CustomError;
89
import liquidjava.diagnostics.warnings.CustomWarning;
@@ -70,7 +71,10 @@ public static void launch(String... paths) {
7071
env.setSourceClasspath(
7172
new String[] { new File(Refinement.class.getProtectionDomain().getCodeSource().getLocation().getFile())
7273
.getAbsolutePath() });
73-
env.setComplianceLevel(8);
74+
int complianceLevel = ComplianceLevel.resolve(paths);
75+
if (DebugLog.enabled())
76+
System.out.println("Java compliance level: " + complianceLevel);
77+
env.setComplianceLevel(complianceLevel);
7478

7579
boolean buildSuccess = launcher.getModelBuilder().build();
7680
if (!buildSuccess && (env.getErrorCount() > 0 || env.getWarningCount() > 0)) {
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package liquidjava.api;
2+
3+
import java.io.File;
4+
import java.io.FileReader;
5+
import java.util.Arrays;
6+
import java.util.Optional;
7+
import java.util.Properties;
8+
9+
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
10+
11+
/**
12+
* Java compliance level used by Spoon, read from the {@code maven.compiler.release} or {@code maven.compiler.source}
13+
* property of the nearest {@code pom.xml} of the verified paths
14+
*/
15+
public final class ComplianceLevel {
16+
17+
/** Highest level accepted by Spoon 10.4.2 (JDT 3.33), also used when no pom declares one */
18+
public static final int MAX_SUPPORTED = 19;
19+
20+
public static int resolve(String... paths) {
21+
return Arrays.stream(paths).map(path -> fromPom(new File(path).getAbsoluteFile())).flatMap(Optional::stream)
22+
.max(Integer::compare).map(level -> Math.min(level, MAX_SUPPORTED)).orElse(MAX_SUPPORTED);
23+
}
24+
25+
private static Optional<Integer> fromPom(File path) {
26+
for (File dir = path; dir != null; dir = dir.getParentFile()) {
27+
File pom = new File(dir, "pom.xml");
28+
if (!pom.isFile())
29+
continue;
30+
try (FileReader reader = new FileReader(pom)) {
31+
Properties properties = new MavenXpp3Reader().read(reader).getProperties();
32+
String version = properties.getProperty("maven.compiler.release",
33+
properties.getProperty("maven.compiler.source"));
34+
if (version != null)
35+
return Optional.of(Integer.parseInt(version.trim().replaceFirst("^1\\.", "")));
36+
} catch (Exception ignored) {
37+
// unreadable pom or non-numeric version: keep searching enclosing poms
38+
}
39+
}
40+
return Optional.empty();
41+
}
42+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package liquidjava.api.tests;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
5+
import java.io.IOException;
6+
import java.nio.file.Files;
7+
import java.nio.file.Path;
8+
9+
import org.junit.jupiter.api.Test;
10+
import org.junit.jupiter.api.io.TempDir;
11+
12+
import liquidjava.api.ComplianceLevel;
13+
14+
class TestComplianceLevel {
15+
16+
@TempDir
17+
Path project;
18+
19+
private int resolveWithRelease(String release) throws IOException {
20+
Files.writeString(project.resolve("pom.xml"),
21+
"<project><modelVersion>4.0.0</modelVersion><properties><maven.compiler.release>" + release
22+
+ "</maven.compiler.release></properties></project>");
23+
return ComplianceLevel.resolve(Files.createDirectories(project.resolve("src")).toString());
24+
}
25+
26+
@Test
27+
void readsLevelFromPom() throws IOException {
28+
assertEquals(8, resolveWithRelease("1.8"));
29+
}
30+
31+
@Test
32+
void capsAtMaxSupported() throws IOException {
33+
assertEquals(ComplianceLevel.MAX_SUPPORTED, resolveWithRelease("30"));
34+
}
35+
}

0 commit comments

Comments
 (0)