Package groovy.contracts

# Annotation Interface ThrowsIf

@Documented @Retention(RUNTIME) @Target({CONSTRUCTOR,METHOD}) @Incubating @Repeatable(ThrowsIfConditions.class) public @interface ThrowsIf
Represents a method's exceptional contract: the method throws the given exception when the given condition holds on entry.

     @ThrowsIf(value = { b == 0 }, exception = ArithmeticException)
     int divide(int a, int b) { a.intdiv(b) }

This is the exceptional counterpart of `Requires`: a precondition says the caller must not do this (violating it is the caller's bug), whereas a `@ThrowsIf` arm says this input is handled, by throwing — it is defined behaviour, part of the contract, that callers may rely on (and catch). The condition is a closure over the method's parameters, using the same conventions as `Requires`.

Weaving. With the default `woven = true` the guard-throw is generated at method entry — the general form of the pattern `groovy.transform.NullCheck` provides for the null-check special case — so the annotation is the implementation, not a comment about one:

     if (b == 0) throw new ArithmeticException(...)   // inserted; message derived from the condition

On a constructor whose first statement is an explicit `super(...)` or `this(...)` call, the guard is inserted immediately after that call, which must stay first — a language constraint.

With `woven = false` nothing is generated: the throw already exists. Where it exists is the `direct` member — pure information for readers and tools, with no effect on bytecode. `direct = true` (the default): a hand-written `throw` statement lives in this body — checkable documentation of a guard that is already there. `direct = false`: the exception arises from code this method executes — a call, possibly transitively (a third-party library), or a runtime operation — so there is no throw statement in this body to find (and weaving a wrong specification about invoked code would silently change behaviour, which is why such arms are spelled `woven = false`). For woven code `direct` is implicitly true and the member is ignored — most users never set it; a tool that cannot find the promised throw in the body is the usual prompt to add `direct = false`. The attributes are per-arm because real methods mix modes:

     @ThrowsIf(value = { x == null }, exception = NullPointerException)                 // woven for x
     @ThrowsIf(value = { y == null }, exception = NullPointerException, woven = false)  // body guards y
     Object process(Object x, Object y) { ... }

Semantics. Read as an iff by default: the method throws a matching exception exactly when some arm's condition holds — each arm is a must-throw (condition on entry ⇒ the method throws, not returns), and the arm-set as a whole claims only-when (a matching throw ⇒ some arm's condition held). `exhaustive = false` disclaims the only-when half for the whole arm-set (a one-directional, JML `signals`-style arm): the condition is sufficient for the throw but the set does not claim to list every reason — useful when the full condition is inexpressible (for example, `Integer.parseInt`: `s == null` is a true sufficient condition, while "malformed or out of range" is not reasonably a parameter closure). Note what neither mode claims: exhaustiveness is (at most) over the conditions for the exception types mentioned, never over exception types themselves — declaring an `ArithmeticException` arm says nothing about whether the method can throw anything else. And no claim, however exhaustive, reasons about VM resource conditions: an `OutOfMemoryError` or `StackOverflowError` is outside contract semantics — the checked wrapper passes any `VirtualMachineError` through unjudged, whatever the arm types.

Checking. Weaving implements the contract; `checked = true` additionally verifies it at runtime, in the same assertion style as `Ensures`: on a normal return, no non-woven arm's condition may have held (must-throw), and an escaping exception matching some arm's type must be justified by a matching arm's condition having held on entry (only-when — checked only for `exhaustive` arm-sets). A broken implementation raises `ThrowsIfViolation` — never the declared exception, which is defined behaviour delivered at entry; a justified throw always propagates untouched. `checked` is set-level: if any arm is `checked`, the whole arm-set is checked — every non-woven arm is must-throw checked, and every arm serves as an only-when justifier (a sibling cannot silently opt out). A pleasant corollary: `woven = false, direct = false` plus `checked = true` runtime-validates a claim about invoked third-party code — a wrong specification is exposed, not silently believed.

The annotation is runtime-retained and repeatable, so the exceptional contract is structured metadata available to documentation generators, static analysers, verification tools, and AI coding agents — none of which can reliably consume javadoc prose or discover guarded throw sites by body traversal. Only the must-throw guard of a woven arm and the `checked` wrapper affect generated code; `exception`, `exhaustive` and `direct` are otherwise consumed by tools.

Since:
  6.0.0
See Also:
  - `Requires`
  - `ThrowsIfConditions`

- ## Required Element Summary

  Required Elements
  Modifier and Type
  Required Element
  Description
  `Class`
  `value`
  Returns the closure class that evaluates the throw condition, a boolean expression over the method's parameters.

- ## Optional Element Summary

  Optional Elements
  Modifier and Type
  Optional Element
  Description
  `boolean`
  `checked`
  `true`: verify the contract at runtime in the assertion style of `Ensures` — a normal return with a non-woven arm's condition held, or an unjustified escaping throw of a matching type (`exhaustive` arm-sets only), raises a `ThrowsIfViolation`.
  `boolean`
  `direct`
  Information for readers and tools, with no effect on bytecode; ignored (implicitly `true`) for woven arms.
  `Class<? extends Throwable>`
  `exception`
  The exception type thrown when the condition holds.
  `boolean`
  `exhaustive`
  No effect on generated code by itself.
  `boolean`
  `woven`
  `true` (default): generate the guard-throw at method entry.

- ## Element Details

  - ### value

    Class value
    Returns the closure class that evaluates the throw condition, a boolean expression over the method's parameters.
    Returns:
      the generated closure class backing the condition

- - ### exception

    Class<? extends Throwable> exception
    The exception type thrown when the condition holds. A woven arm generates `new <exception>(String)`, so the type should provide a `(String)` constructor (all the standard JDK exceptions do).
    Returns:
      the exception type
    Default:
      java.lang.Throwable.class

  - ### woven

    boolean woven
    `true` (default): generate the guard-throw at method entry. `false`: the throw already exists (see `direct` for where) — nothing is generated.
    Returns:
      whether the guard is generated
    Default:
      true

  - ### direct

    boolean direct
    Information for readers and tools, with no effect on bytecode; ignored (implicitly `true`) for woven arms. `true` (default): a hand-written `throw` statement lives in this body. `false`: the exception arises from code this method executes — a call, possibly transitively, or a runtime operation — so there is no throw statement in this body to find. Most users never set this; a verification or analysis tool unable to find the promised throw in the body is the usual prompt.
    Returns:
      whether the body itself contains the throw
    Default:
      true

  - ### exhaustive

    boolean exhaustive
    No effect on generated code by itself. Under `checked = true` it gates the escaping-throw check: `true` (default) claims the listed conditions are the only reasons a matching exception is thrown, so an escaping match with no condition held is a `ThrowsIfViolation`; `false` says the method may throw the same exception for other, unlisted reasons — escaping throws are never judged (and verification tools likewise skip the only-when direction). One `false` arm disclaims the check for the whole arm-set. In specification terms: `true` is an iff, `false` a one-directional (JML `signals`-style) sufficient condition — the honest choice when the full condition is unstatable (`Integer.parseInt`: `s == null` is a true sufficient half; "malformed or out of range" is not a parameter closure).
    Returns:
      whether the arm-set claims to list every reason a matching exception is thrown
    Default:
      true

  - ### checked

    boolean checked
    `true`: verify the contract at runtime in the assertion style of `Ensures` — a normal return with a non-woven arm's condition held, or an unjustified escaping throw of a matching type (`exhaustive` arm-sets only), raises a `ThrowsIfViolation`. `false` (default): no runtime checking beyond any `WOVEN` guard.
    Returns:
      whether the contract is runtime-checked
    Default:
      false