# `Feel.Analysis.Declarations`
[🔗](https://github.com/FeeJai/BDM/blob/feel-v0.1.0/apps/feel/lib/feel/analysis/declarations.ex#L1)

What the declarations of a scope are used for: the one option `declarations:` of `Feel.prepare/2` (D-138).

A scope declares the type of a name and, for some names, its allowed values (`Feel.Scope`). Two things can be done with
a declaration, and they are separate (D-138). The *run* can look at a bound value that does not fit and do something about
it. The *analysis* can use the allowed values to narrow the type of the name: `age` declared with `[0..150]` is
`number in [0..150]`, so `age + 1` is `number in [1..151]`, which lets the analysis prove things about it and the
optimizer take out checks that the types make useless. The declared type of a name is used whatever is chosen; it is
the narrowing by the allowed values that is chosen.

Narrowing is sound when the run makes the values what the types say, and it is the caller's word when it does not: a
value that is not allowed after all makes the types wrong, and what the optimizer took out because of them. So the two
are not chosen apart. The option is a *stance* on the declarations, one of six:

| Stance | A bound value that does not fit | The allowed values | `age + 1`, `age` in `[0..150]` |
| ------ | ------------------------------- | ------------------ | ------------------------------ |
| `:enforce` (default) | is converted, or is `null` with an error | narrow the types | `number in [1..151] \| Null` |
| `:enforce_wide` | the same | do not narrow | `number \| Null` |
| `:trust` | is not looked at: the host guarantees the values | narrow the types | `number in [1..151] \| Null` |
| `:audit` | stays, and is reported as an error | narrow the types | `number in [1..151] \| Null` |
| `:report` | stays, and is reported: as an error for a declared type, as a warning for allowed values | do not narrow | `number \| Null` |
| `:ignore` | is not looked at | do not narrow | `number \| Null` |

`:enforce` is what makes the types true, so it is the default. `:trust` is for a host that has looked at the values
already: the declarations are used and never checked, and nothing is put around the expression. `:audit` trusts in the
same way and says when it was wrong, as an error, which ends a strict run exactly where the types may be false; the
value stays, since an error that came with a `null` would be a different run. `:report` and `:ignore` rely on no allowed
value. The declared type is relied on in every stance, as a scope says it is the type of what the names are bound to; the
stances that look at the values (all but `:trust` and `:ignore`) differ in what they do about one that does not fit.

A name declared not null (`number!`, D-48) has no `null` in its type only under `:enforce` and `:enforce_wide`, where the
run ends if it would be `null`.

## The tiers

Narrowing makes sets of values, which `analysis: :core` has none of (`Feel.Analysis.Refinement`, D-133). So there
`:enforce` narrows nothing, as `:enforce_wide` does not (a run gives the same values and diagnostics), and `:trust` and
`:audit`, which exist to narrow, raise `ArgumentError`.

## What the analyzer is given

`settings!/2` gives the analyzer the pair it works with, `{verify, narrow}`, from the stance and the tier. `verify` is
what the run does (`:enforce`, `:report` or `false`) and `narrow` whether the allowed values narrow the types. They
decide the nodes around the expression (`Feel.Analysis.Analyzer`), the severity of a report, and what the optimizer may
rely on, and are not options.

# `stance`

```elixir
@type stance() :: :enforce | :enforce_wide | :trust | :audit | :report | :ignore
```

A stance on the declarations of a scope: `:enforce` (the default), `:enforce_wide`, `:trust`, `:audit`, `:report` or
`:ignore`. The table is in the documentation of `Feel.Analysis.Declarations`.

# `verify`

```elixir
@type verify() :: :enforce | :report | false
```

What the run does with a bound value that does not fit its declaration.

# `reject_removed!`

```elixir
@spec reject_removed!(keyword()) :: :ok
```

Raises `ArgumentError` if the options have `:verify` or `:narrow`, the two options that `:declarations` replaced (D-138),
and names the stance to use for what was passed.

    iex> Feel.Analysis.Declarations.reject_removed!(declarations: :trust)
    :ok
    iex> Feel.Analysis.Declarations.reject_removed!(verify: false, narrow: true)
    ** (ArgumentError) the options :verify and :narrow are replaced by :declarations, which takes :enforce, :enforce_wide, :trust, :audit, :report or :ignore (see Feel.prepare/2): [verify: false, narrow: true] is declarations: :trust

# `settings!`

```elixir
@spec settings!(term(), Feel.Analysis.Refinement.tier()) :: {verify(), boolean()}
```

What the run does with a value and whether the allowed values narrow the types, for a stance in a tier of the analysis.

`nil` is the default stance, `:enforce`. Raises `ArgumentError` for anything that is not a stance, and for `:trust` and
`:audit` in the tier `:core`, which has no sets of values to narrow by.

    iex> Feel.Analysis.Declarations.settings!(:enforce, :full)
    {:enforce, true}
    iex> Feel.Analysis.Declarations.settings!(:enforce, :core)
    {:enforce, false}
    iex> Feel.Analysis.Declarations.settings!(:trust, :full)
    {false, true}
    iex> Feel.Analysis.Declarations.settings!(:audit, :full)
    {:report, true}
    iex> Feel.Analysis.Declarations.settings!(:ignore, :core)
    {false, false}

# `stances`

```elixir
@spec stances() :: [stance(), ...]
```

The stances, the default first.

---

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