Mutare.Mutator.Mutation (mutare v0.1.0)

Copy Markdown View Source

A replacement AST node with optional report metadata.

Mutators normally return a bare replacement node. Return a Mutation when the replacement also needs either of these fields:

  • note — text shown with a surviving mutant. It does not suppress the mutant or change its score.
  • variant — one or more labels used by # mutare:ignore[family:label]. A mutator may attach the label here or derive it with Mutare.Mutator.variant/2.
  • producer — the Mutare.Mutator.Spec this mutation is recorded under instead of the mutator that returned it. Set it only when relaying a mutation another family reasoned about — the selector-host sub-contract case, where Mutare.Mutator.MacroHost.host/2 returns interior mutants collected from core's families via Mutare.Analyze.expression_mutations/3: the site (and its # mutare:ignore vocabulary) then belongs to the producing family, not the host. nil (the default) records the mutation under the returning mutator, exactly as before.
  • attribution — decouples where the mutant is reported from what is spliced into the metamutant. Set it when :node is a whole-node rewrite whose textual footprint is one inner clause — a mutate/2 that rebuilds and returns an entire from(...) query but only changed its order_by:. Without it the site's line/column and before/after diff are pinned to the offered node (the whole from), so a multi-line rewrite reports every mutant at the from line and # mutare:ignore (line-keyed) cannot target the clause. Build one with at/2 (a replacement clause) or at_drop/1 (a removed clause); the metamutant is still built by splicing :node, attribution only moves the report. nil (the default) attributes to the offered node, exactly as before.

Mutation values are accepted by Mutare.Mutator.mutate/1, Mutare.Mutator.mutate/2, and Mutare.Mutator.MacroHost.host/2. Use this struct rather than a plain map, because a map is also a valid replacement AST node.

Summary

Types

t()

A produced mutation's variant label(s): nil, one label, or a list (see Mutare.Mutator.variant/2).

Functions

Builds a report-location override that shows original replaced by mutated.

Builds a report-location override that shows original removed.

Builds a mutation for replacement node.

Builds a mutation with one or more ignore-variant labels.

Types

t()

@type t() :: %Mutare.Mutator.Mutation{
  attribution: Mutare.Mutator.Mutation.Attribution.t() | nil,
  node: Macro.t(),
  note: String.t() | nil,
  producer: Mutare.Mutator.Spec.t() | nil,
  variant: variant()
}

variant()

@type variant() :: nil | String.t() | atom() | [String.t() | atom()]

A produced mutation's variant label(s): nil, one label, or a list (see Mutare.Mutator.variant/2).

Functions

at(original, mutated)

Builds a report-location override that shows original replaced by mutated.

Pass the result as a mutation's :attribution (see the moduledoc). original and mutated are the clause before and after the change — the site is located at original's range and its diff renders original → mutated, even though the mutation's :node splices a larger rewrite into the metamutant.

Examples

iex> alias Mutare.Mutator.Mutation
iex> %Mutation.Attribution{mutated: :desc} = Mutation.at(:asc, :desc)

at_drop(original)

Builds a report-location override that shows original removed.

Pass the result as a mutation's :attribution (see the moduledoc). The site is located at original's range and recorded as a deletion (a delete-style diff over the clause), even though the mutation's :node splices the rewritten, clause-less node into the metamutant.

Examples

iex> alias Mutare.Mutator.Mutation
iex> Mutation.at_drop(:some_clause).mutated
:drop

new(node, note_or_opts \\ nil)

@spec new(Macro.t(), String.t() | nil | keyword()) :: t()

Builds a mutation for replacement node.

The second argument may be a note string or a keyword list containing :note, :variant, :producer, and :attribution:

Mutation.new(mutated)
Mutation.new(mutated, "kill needs boundary data")
Mutation.new(mutated, note: "kill needs boundary data", variant: "zero")
Mutation.new(mutated, variant: "zero")
Mutation.new(mutated, producer: literal_spec)
Mutation.new(rebuilt_from, attribution: Mutation.at(order_by, flipped_order_by))

note must be a string or nil. variant may be one label, a list of labels, or nil. producer must be a Mutare.Mutator.Spec or nil (see the moduledoc). attribution must be a Mutare.Mutator.Mutation.Attribution (from at/2/at_drop/1) or nil. Unknown options raise ArgumentError.

Examples

iex> alias Mutare.Mutator.Mutation
iex> mutation = Mutation.new(:replacement, note: "needs a boundary test", variant: "zero")
iex> {mutation.node, mutation.note, mutation.variant}
{:replacement, "needs a boundary test", "zero"}

iex> alias Mutare.Mutator.Mutation
iex> Mutation.new(:replacement, "shown for survivors").note
"shown for survivors"

tagged(node, variant)

@spec tagged(Macro.t(), variant()) :: t()

Builds a mutation with one or more ignore-variant labels.

This is equivalent to new(node, variant: variant).

Examples

iex> alias Mutare.Mutator.Mutation
iex> Mutation.tagged(:replacement, ["pred", "zero"]).variant
["pred", "zero"]