# `Mutare.Mutators.ReturnValue`
[🔗](https://github.com/foxbenjaminfox/mutare/blob/v0.2.1/lib/mutare/mutators/return_value.ex#L1)

Replaces function return expressions with fixed constants.

It applies to each `def` and `defp` return path. When a clause tail is a `case`/`cond`/`if`/`unless`/`with`/`try`/`receive`, each branch's tail is its own return path. The value of a `try` `after` block is excluded because it is discarded. Each clause of an anonymous function is handled in the same way.

Each eligible return path produces two replacements based on its shape:

| tail shape                                   | empty/zero | sentinel   |
|----------------------------------------------|------------|------------|
| numeric (`a + b`, `x * 2`, `div(a, b)`, `-n`)| `0`        | `1`        |
| string concatenation (`a <> b`)              | `""`       | `"mutare"` |
| list expression (`a ++ b`, `xs -- ys`)       | `[]`       | `[:mutare]`|
| anything else (variable, call, tuple, map,   | `nil`      | `:mutare`  |
| `:ok`/`:error` atom, an opaque-macro result) |            |            |

A replacement equal to the original return is omitted.

Two separate rules keep this family from restating another's work, and they answer different
questions. A *literal* tail belongs to its node-level family outright, whatever the run
enables — see Exclusions. On every tail this family does claim, the transform then compares
*values*: when an enabled node-level family already produces the same scalar replacement at
that return expression, it keeps the node-level mutation and drops this family's duplicate.
So with `AtomLiteral` enabled, `:foo → :mutare` is reported as `atom` while `:foo → nil`
stays a `return_value` mutation; disable `AtomLiteral` and both replacements come back.

## Exclusions

  * Boolean expressions are handled by `Mutare.Mutators.Conditional`.
  * Integer, float, string, list, and boolean literals are handed to their node-level families
    outright — unlike the value comparison above, this holds even when that family is disabled.
    On a literal tail the contrasting pair earns nothing: `nil`/`:mutare` collide with no
    replacement those families emit, so no comparison would catch them, and they are at best
    weaker restatements of one. A `do: false` tail mutated to `nil` is the clearest case — both
    are falsy, so every `refute`-style assertion lets it through, and only a strict comparison
    or a `false` pattern match kills it. Bare atoms remain eligible.
  * `nil` return expressions are not mutated.
  * A **unit-returning** function — one whose every return path, across all its clauses, is literally `:ok` or `nil` — has no return positions at all. Its return carries no data, so a replacement there survives whenever the caller discards the value (the norm for a side-effect helper) and is killed only by a test asserting a static fact. A behaviour callback is exempt — a function that is a callback of one of the module's declared behaviours, or that carries an `@impl` — because its caller is the behaviour's runtime, which may inspect a lone `:ok` as one contract outcome among several (an `Oban.Worker.perform/1`). Anonymous functions are classified the same way. The classification is syntactic, over the clauses visible in the source (a clause a macro generates beside hand-written ones of the same signature is invisible to it), and otherwise errs only toward missing a unit function: a call in tail position (`Logger.info(x)`), a variable bound to `:ok`, or an else-less `with` (its non-matching value is a return path) is not recognised, and one `:ok` path beside a `{:error, _}` path is still mutated — that `:ok` carries the success bit. A tail that never returns (`raise`/`reraise`/`throw`/`exit`, or the `:erlang` primitives) is not a return path, so an `:ok`-or-raise function is unit; the raising tail itself keeps its return mutants.
  * `quote` blocks are compile-time code and are not mutated as a whole.

The `empty` and `sentinel` variants can be selected independently in an ignore directive, for example `# mutare:ignore[return_value:empty]`. This family is enabled by default.

# `return_replacements`

```elixir
@spec return_replacements(Macro.t()) :: [Macro.t()]
```

Returns clean-meta constant replacements for a clause return expression.

Eligible expressions receive an empty or zero value and a non-empty sentinel,
excluding any replacement equal to the original. Returns `[]` for expressions
handled by another value family, boolean expressions, `nil`, and quoted code.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
