Skip to content

Commit 43023fc

Browse files
Upgrade Spoon to 11.5.0 and default the compliance level to 21 (#368)
Redoes #363, which was merged into `fix/compliance-level` after that branch had already been merged into `main` (#352), so `main` is still on Spoon 10.4.2. ## Change - `version.spoon`: 10.4.2 → **11.5.0** (JDT 3.46). Its class files target Java 17, so it runs on our Java 20 build. - `ComplianceLevel`: the cap goes from 19 to **26** (highest level JDT 3.46 accepts; `27` throws), and a separate `DEFAULT` of **21** is used when no pom declares a Java version (it used to be the cap). - `RefinementTypeChecker#visitCtTryWithResource` (from #358): in Spoon 11, `CtResource` is no longer a `CtVariable`. A resource is now either a `CtLocalVariable` (`try (R r = ...)`) or a `CtVariableRead` (Java 9 `try (r)`). Spoon 10 modelled `try (r)` as an implicit copy of `r`'s declaration, repeated per earlier same-named local. That workaround (skip implicit copies, dedupe by name, header position) is gone: every resource is scanned and closed once, and the implicit `close()` is built from the declaration's reference or a clone of the read, positioned at the resource. ## Downstream `vscode-liquidjava/server/pom.xml` declares `spoon-core` 10.4.2 directly, which overrides the verifier's version. Bump it to 11.5.0 together with the verifier release that includes this. ## Testing `mvn test`: 379/379 pass, including `try_with_resources_correct` / `try_with_resources_error` (both resource forms) and `CorrectModernJavaSyntax`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 9045ecd commit 43023fc

3 files changed

Lines changed: 20 additions & 41 deletions

File tree

‎liquidjava-verifier/pom.xml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@
257257
<version.junit>5.10.0</version.junit>
258258
<version.memcompiler>1.3.0</version.memcompiler>
259259
<version.commons-text>1.12.0</version.commons-text>
260-
<version.spoon>10.4.2</version.spoon>
260+
<version.spoon>11.5.0</version.spoon>
261261
<version.z3>4.8.17</version.z3>
262262
<!-- plugin versions -->
263263
<pVersion.compiler>3.8.0</pVersion.compiler>

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,15 @@
1414
*/
1515
public final class ComplianceLevel {
1616

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;
17+
/** Highest level accepted by Spoon 11.5.0 (JDT 3.46) */
18+
public static final int MAX_SUPPORTED = 26;
19+
20+
/** Level used when no pom declares one */
21+
public static final int DEFAULT = 21;
1922

2023
public static int resolve(String... paths) {
2124
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);
25+
.max(Integer::compare).map(level -> Math.min(level, MAX_SUPPORTED)).orElse(DEFAULT);
2326
}
2427

2528
private static Optional<Integer> fromPom(File path) {

‎liquidjava-verifier/src/main/java/liquidjava/processor/refinement_checker/RefinementTypeChecker.java‎

Lines changed: 13 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,8 @@
33
import java.lang.annotation.Annotation;
44
import java.util.ArrayList;
55
import java.util.Arrays;
6-
import java.util.Collections;
7-
import java.util.LinkedHashMap;
86
import java.util.LinkedHashSet;
97
import java.util.List;
10-
import java.util.Map;
118
import java.util.Optional;
129
import java.util.Set;
1310

@@ -64,8 +61,6 @@
6461
import spoon.reflect.code.CtVariableRead;
6562
import spoon.reflect.code.CtVariableWrite;
6663
import spoon.reflect.code.CtWhile;
67-
import spoon.reflect.cu.CompilationUnit;
68-
import spoon.reflect.cu.SourcePosition;
6964
import spoon.reflect.declaration.*;
7065
import spoon.reflect.factory.Factory;
7166
import spoon.reflect.reference.CtExecutableReference;
@@ -535,51 +530,32 @@ private boolean canCompleteNormally(CtStatement statement) {
535530

536531
@Override
537532
public void visitCtTryWithResource(CtTryWithResource tryWithResource) {
538-
// A resource reference (Java 9 `try (r)`) is modelled by Spoon as an implicit copy of r's declaration,
539-
// initializer included, and repeated once per earlier local with the same name, so it is not scanned (that
540-
// would re-run the initializer) and is only closed once
541-
Map<String, CtResource<?>> resources = new LinkedHashMap<>();
542-
for (CtResource<?> resource : tryWithResource.getResources()) {
543-
if (!resource.isImplicit())
544-
scan(resource);
545-
resources.put(resource.getSimpleName(), resource);
546-
}
533+
// a resource is either a declaration (`try (R r = ...)`) or a reference to an existing variable (Java 9 `try
534+
// (r)`)
535+
List<CtResource<?>> resources = tryWithResource.getResources();
536+
scan(resources);
547537
scan(tryWithResource.getBody());
548538

549539
// the resources are closed when the body ends, in reverse order, before any catch or finally block runs
550-
List<CtResource<?>> toClose = new ArrayList<>(resources.values());
551-
Collections.reverse(toClose);
552-
for (CtResource<?> resource : toClose) {
553-
SourcePosition position = resource.isImplicit() ? getHeaderPosition(tryWithResource)
554-
: resource.getPosition();
555-
scan(createImplicitClose(resource, tryWithResource, position));
556-
}
540+
for (int i = resources.size() - 1; i >= 0; i--)
541+
scan(createImplicitClose(resources.get(i), tryWithResource));
557542
scan(tryWithResource.getCatchers());
558543
scan(tryWithResource.getFinalizer());
559544
}
560545

561-
/** Position of {@code try (...)}, without the blocks */
562-
private SourcePosition getHeaderPosition(CtTryWithResource tryWithResource) {
563-
SourcePosition position = tryWithResource.getPosition();
564-
CompilationUnit cu = position.getCompilationUnit();
565-
int end = cu.getOriginalSourceCode().lastIndexOf(')', tryWithResource.getBody().getPosition().getSourceStart());
566-
if (end < position.getSourceStart())
567-
return position;
568-
return factory.Core().createSourcePosition(cu, position.getSourceStart(), end, cu.getLineSeparatorPositions());
569-
}
570-
571546
/** Builds the {@code resource.close()} that Java inserts at the end of a try-with-resources block */
572-
private CtInvocation<?> createImplicitClose(CtResource<?> resource, CtTryWithResource tryWithResource,
573-
SourcePosition position) {
574-
CtTypeReference<?> type = resource.getType();
547+
private CtInvocation<?> createImplicitClose(CtResource<?> resource, CtTryWithResource tryWithResource) {
548+
CtExpression<?> target = resource instanceof CtLocalVariable<?> variable
549+
? factory.Code().createVariableRead(variable.getReference(), false)
550+
: ((CtVariableRead<?>) resource).clone();
551+
CtTypeReference<?> type = target.getType();
575552
CtExecutableReference<?> close = type.getAllExecutables().stream()
576553
.filter(e -> e.getSimpleName().equals("close") && e.getParameters().isEmpty()).findFirst()
577554
.orElseGet(() -> factory.Executable().createReference(type, factory.Type().VOID_PRIMITIVE, "close"));
578-
CtExpression<?> target = factory.Code().createVariableRead(resource.getReference(), false);
579555
CtInvocation<?> invocation = factory.Code().createInvocation(target, close);
580556
invocation.setParent(tryWithResource);
581-
invocation.setPosition(position);
582-
target.setPosition(position);
557+
invocation.setPosition(resource.getPosition());
558+
target.setPosition(resource.getPosition());
583559
return invocation;
584560
}
585561

0 commit comments

Comments
 (0)