# `Mutare.Ignore`
[🔗](https://github.com/foxbenjaminfox/mutare/blob/v0.2.1/lib/mutare/ignore.ex#L1)

Suppresses selected mutants with a source comment. Ignored mutants remain in the report but are excluded from the mutation score. Their generated code is omitted from the metamutant, reducing compiler input and preventing their replacements from poisoning the build. They retain their IDs, reasons, and positions within `:max_mutants`.

A trailing directive applies to its own line. A standalone directive applies to the next line of code, reading through any comment lines in between — so it works at either end of an explanatory comment block:

    expression() # mutare:ignore
    # mutare:ignore
    expression()
    # mutare:ignore[integer] checked by the boundary test;
    # any non-empty replacement is behaviorally equivalent
    expression()

A blank line ends the comment block: the directive then targets the blank line and suppresses nothing (warned — see below).

A plain `# mutare:ignore` covers exactly **one** line of code (see "Scoped directives" below for regions and whole files). For a multi-line expression, place it directly above the line that carries the mutated code — above the `|>` step containing it, not the pipe's first line:

    digested_files
    # mutare:ignore[atom] delivery order is discarded
    |> Task.async_stream(&write/1, ordered: false)
    |> Stream.run()

A directive misplaced onto an earlier line of the same expression suppresses nothing; the resulting warning points at the line that has the matching mutants.

A directive may include a family filter, a reason, or both:

    # mutare:ignore                              suppress every mutant on the line
    # mutare:ignore equivalent under int math    suppress all; record the reason
    # mutare:ignore[arithmetic]                  suppress only arithmetic mutants
    # mutare:ignore[arithmetic, relational]      suppress two families
    # mutare:ignore[relational:>]                suppress only the `i > j` swap
    # mutare:ignore[integer] off-by-one is fine  a filter and a reason together

Filter entries are mutator family names from `Mutare.Mutators.families/0` or a custom mutator's `c:Mutare.Mutator.name/0`. Without a filter, the directive suppresses every mutant on the line.

## Scoped directives: regions and whole files

Annotating a large low-value span line by line — a literal lookup table, a generated module — doesn't scale, so two scoped verbs suppress in bulk. Both take the same optional `[...]` filter and reason as `# mutare:ignore`:

    # mutare:ignore-start spot-checked; exhaustively covered by the round-trip test
    def encode(?A), do: ?B
    def encode(?B), do: ?C
    def encode(?C), do: ?D
    # mutare:ignore-end

An `# mutare:ignore-start` opens a region and the next `# mutare:ignore-end` closes it; every mutant on the delimiter comments' lines and between them is suppressed (the span is **inclusive** of both delimiter lines, so a trailing `-start` or `-end` on a code line covers that line's mutants too). Text after `# mutare:ignore-end` is plain prose — the filter and reason belong on the `-start`.

`# mutare:ignore-file`, anywhere in a file (conventionally at the top), suppresses every matching mutant in that file:

    # mutare:ignore-file generated by `mix gen.unicode_tables` — do not hand-edit

A broken region pairing is a hard `Mutare.Ignore.SpecError`, not a warning: an `-end` with no open `-start`, a second `-start` inside an open region (regions don't nest), or a `-start` never closed each abort the run — the mistake is provable, and both lenient readings fail the wrong way (silently suppressing to end-of-file, or silently suppressing nothing).

When several directives match one mutant, the most specific filter wins (`[family:label]` over `[family]` over unfiltered); on a tie, the narrowest scope (line over region over file), so the recorded reason is the most locally-written one. Scoped directives are held to the same ineffectiveness bar as line directives: an `-file` or region that suppresses nothing is warned (and aborts under `--strict-ignores`).

Use `family:label` to select one variant from a family. For example, `[relational:<=]` suppresses the `<=` replacement but not the other relational replacements. Labels are declared by each family and matched case-insensitively. If a mutant has several labels, any matching label suppresses it. Run `mix mutare --list-mutators` for the complete built-in list.

Text after the keyword or filter is stored as the ignore reason and shown in the report.

## A recognizable equivalent mutant: the re-stated delegate guard

A pattern worth knowing when triaging `guard_drop`/`pattern_guard` survivors in wrapper-heavy code — a thin wrapper that re-states the guard of the function it delegates to:

    def sign(data, salt) when is_binary(salt),
      do: Plug.Crypto.sign(data, salt)   # Plug.Crypto.sign/2 has the identical guard

Dropping the wrapper's guard is provably unobservable: a bad input still raises the same `FunctionClauseError`, one stack frame deeper, inside the delegate. Verify against the delegate's source (the guards must really be equivalent), then ignore with a reason naming it:

    # mutare:ignore[guard_drop] Plug.Crypto.sign/2 re-checks is_binary(salt)
    def sign(data, salt) when is_binary(salt), do: Plug.Crypto.sign(data, salt)

This shape recurs constantly in real codebases (any module wrapping a well-guarded library); recognizing it saves convincing yourself a survivor is "just uncovered" when it is actually unreachable.

## When a directive errors or does nothing

An unknown label for a built-in family, including a disabled one, or an active custom family is an error. Unknown families, bare-family typos, empty filters, and malformed filters match nothing. Any directive that suppresses no mutant produces a warning; `--strict-ignores` turns that warning into a non-zero exit.

The `mutare:` comment namespace is reserved. A comment that starts with `# mutare:` but does not spell a recognized directive — a typo (`# mutare:ingore`), a stray space after the colon (`# mutare: ignore`), or a directive from a future Mutare version — is never silently inert: it produces the same scan-time warning, and `--strict-ignores` the same non-zero exit.

Only source comments are parsed. Text such as `"# mutare:ignore"` inside a string has no effect.

---

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