public class ThrowsIfASTTransformation
extends Object
implements ASTTransformation
Handles a method's ThrowsIf arm-set. Processed once per method (the
transformation fires per annotation, so repeats are deduplicated via node
metadata) because the semantics are per-set: guards are generated in
declaration order, and the checked wrapper judges an escaping throw
against every arm.
For each WOVEN-origin arm the guard-throw is generated at method
entry, so that conceptually:
@ThrowsIf(value = { b == 0 }, exception = ArithmeticException)
int divide(int a, int b) { a.intdiv(b) }
compiles as:
int divide(int a, int b) {
if (b == 0) throw new ArithmeticException('@ThrowsIf: b == 0')
a.intdiv(b)
}
— the general form of the pattern groovy.transform.NullCheck provides
for the null-check special case. Arms with woven = false generate no
guard — the throw already exists, in the body (direct = true, the
default) or in invoked code (direct = false); identical bytecode, the
direct distinction is information for tools and is not read here.
When any arm is checked, the conditions are additionally snapshot on
entry and the body is wrapped so that, conceptually:
boolean $c0 = <cond0>; ... // entry snapshot, every arm
if ($c0) throw new E0(...) // woven arms, in declaration order
Throwable $t = null
try { <original body> }
catch (Throwable caught) {
$t = caught
throw ThrowsIfSupport.onlyWhen(caught, [$c0, ...], [E0, ...], [texts], allExhaustive)
} finally {
if ($t == null) ThrowsIfSupport.mustThrow([unwoven $ci ...], [texts]) // normal return only
}
A justified throw propagates untouched (defined behaviour); a broken
implementation raises ThrowsIfViolation,
never the declared exception. Non-woven arms with no checked flag
anywhere generate nothing — runtime-retained metadata for tools.