Describe the bug
In this label, First _ and Second _ are two alternative patterns:
case First _, Second _ when enabled -> 1;
At run time, First _ matches a First value and Second _ matches a Second value. The expression after when is a separate boolean guard. Spoon must therefore build two case-pattern nodes and one guard.
ECJ stores both patterns inside one EitherOrMultiPattern node, then wraps that node and the enabled condition in a GuardedPattern.
For this label, the correct alternative count is two. Spoon uses that count while it receives ECJ's child nodes: the first two pattern children belong to the comma-separated case label, and the following expression is the guard.
Spoon inspected EitherOrMultiPattern only when that node was the direct case expression. The surrounding GuardedPattern hid the count, so Spoon expected one case pattern instead of two. It attached First _ correctly, then mistook the CtTypePattern for Second _ for the guard. Spoon rejected that parent because a CtTypePattern belongs under a CtCasePattern, not directly under a CtCase guard.
A compatible fix must unwrap the ECJ guard only while counting alternatives. Each alternative must receive its own CtCasePattern, and the real guard must stay attached to the CtCase.
Source code you are trying to analyze/transform
Reduced form:
sealed interface Value permits First, Second {}
record First(int value) implements Value {}
record Second(int value) implements Value {}
class GuardedPatterns {
int read(Value value, boolean enabled) {
return switch (value) {
case First _, Second _ when enabled -> 1;
default -> 0;
};
}
}
The issue was found in Unnamed.java from openjdk/jdk@be40b6bcdab37368ea3c769e575b36e290d0c6a1.
Source code for your Spoon processing
Launcher launcher = new Launcher();
launcher.getEnvironment().setComplianceLevel(25);
launcher.addInputResource("Unnamed.java");
launcher.buildModel();
Actual output
spoon.SpoonException: type pattern can only be used in an instanceof binary
operator or a case pattern (was class spoon.support.reflect.code.CtCaseImpl)
at CtTypePatternImpl.setParent(...)
at CtCaseImpl.setGuard(...)
at ParentExiter.visitCtCase(...)
Expected output
Spoon builds this hierarchy:
- one
CtCasePattern containing the CtTypePattern for First _;
- one
CtCasePattern containing the CtTypePattern for Second _;
- the expression
enabled as the CtCase guard.
Spoon Version
Reproduced on Spoon master at 06b187819380700384762416d15db2cc4f112175.
JVM Version
OpenJDK 25.0.3
What operating system are you using?
Linux x86-64
Describe the bug
In this label,
First _andSecond _are two alternative patterns:At run time,
First _matches aFirstvalue andSecond _matches aSecondvalue. The expression afterwhenis a separate boolean guard. Spoon must therefore build two case-pattern nodes and one guard.ECJ stores both patterns inside one
EitherOrMultiPatternnode, then wraps that node and theenabledcondition in aGuardedPattern.For this label, the correct alternative count is two. Spoon uses that count while it receives ECJ's child nodes: the first two pattern children belong to the comma-separated case label, and the following expression is the guard.
Spoon inspected
EitherOrMultiPatternonly when that node was the direct case expression. The surroundingGuardedPatternhid the count, so Spoon expected one case pattern instead of two. It attachedFirst _correctly, then mistook theCtTypePatternforSecond _for the guard. Spoon rejected that parent because aCtTypePatternbelongs under aCtCasePattern, not directly under aCtCaseguard.A compatible fix must unwrap the ECJ guard only while counting alternatives. Each alternative must receive its own
CtCasePattern, and the real guard must stay attached to theCtCase.Source code you are trying to analyze/transform
Reduced form:
The issue was found in Unnamed.java from
openjdk/jdk@be40b6bcdab37368ea3c769e575b36e290d0c6a1.Source code for your Spoon processing
Actual output
Expected output
Spoon builds this hierarchy:
CtCasePatterncontaining theCtTypePatternforFirst _;CtCasePatterncontaining theCtTypePatternforSecond _;enabledas theCtCaseguard.Spoon Version
Reproduced on Spoon
masterat06b187819380700384762416d15db2cc4f112175.JVM Version
OpenJDK 25.0.3
What operating system are you using?
Linux x86-64