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

A report about a problem in a FEEL expression.

DMN requires an implementation to report diagnostic information whenever a
semantic error makes a result `null` (section 7.3.8). Diagnostics are also
produced when an expression is prepared, for problems found without running it.

## Fields

  * `:severity` - `:error` for a problem that makes a result `null` or an
    expression unusable, `:warning` for a suspicious construct, and `:info` for
    something that was done on a value's behalf and is worth knowing, such as an
    implicit conversion (D-46). An `:info` is never a problem: it does not end a
    strict run, and it does not make a result `null`.
  * `:code` - an atom identifying the kind of problem, such as
    `:type_mismatch`. The codes used or planned so far are
    `:syntax_error`, `:unterminated_string`, `:unterminated_comment`,
    `:invalid_escape`, `:unexpected_character`, `:number_out_of_range`,
    `:invalid_encoding`, `:source_too_large`, `:nesting_too_deep`, `:name_too_long`, `:too_many_facts`, `:too_large_to_compile`, `:unknown_name`,
    `:unknown_function`, `:not_a_function`, `:arity_mismatch`,
    `:type_mismatch`, `:undefined_operation`, `:division_by_zero`,
    `:overflow`, `:argument_out_of_domain`, `:duplicate_key`,
    `:cyclic_reference`, `:invalid_type_expression`, `:invalid_property`,
    `:date_out_of_range`, `:limit_exceeded`, and `:not_implemented` (a construct the
    analyzer does not handle yet), `:invalid_allowed_values` and `:value_not_allowed`
    (D-138), `:null_not_allowed` (a name declared `!` has no value, or `null`, D-48; it is the
    diagnostic of an `:input` error), `:native_function_failed` (a function of the host raised
    when it was called, D-82), `:implicit_conversion` (D-46), `:outside_s_feel` (a construct that is not in
    S-FEEL, D-49), and `:offset_mismatch` (a subtraction of a time, or a date and time, that has an
    offset or a zone from one that has none, D-54), and `:decided_condition` (the condition of an `if`
    reads a name and can still be only one thing, so a branch never runs, D-194), `:unreachable_branch` (a branch that the type of the
    condition leaves, in which a read finds that the facts of the conditions leave its name no value, so it never runs, D-173),
    and `:operand_near_zero`, `:operand_too_large`, `:operand_unbounded` and `:null_possible` (infos: an
    operation on operands that are not `null` can be `null` for some of their values, and the `details` have the reasons, each
    a pair of values of the operands for which the run gives `null` and the cause, D-161; the first three are the ones that
    tighter allowed values rule out, D-161). An `:overflow` is also a warning of `prepare/2`, for an operation on constants (D-161). The list is
    not final.
  * `:message` - a human-readable description.
  * `:span` - `{from, to}`, the byte range of the source text the diagnostic
    refers to (`from` inclusive, `to` exclusive), or `nil` when it has no
    single location.
  * `:hint` - a suggestion, such as `~s(did you mean "people"?)`, or `nil`.
  * `:details` - extra facts, such as the types that mismatched.
  * `:origin` - which text `:span` is a range of, when a run is of more than one. `nil` is the text of the expression that
    was run, unless the host gave the run an origin (`Feel.run/3`, `:origin`, D-201). A function written in an expression keeps
    the origin of the run it is made in, wherever it is called, and what is reported in its body while it runs has it, so
    that the host can find the span in the text it came from and not in the caller's. A host that binds a function value
    made by a run that had none can give it one (`Feel.Value.Function.with_origin/2`). The term is the host's.

# `severity`

```elixir
@type severity() :: :error | :warning | :info
```

How serious a diagnostic is.

# `span`

```elixir
@type span() :: {from :: non_neg_integer(), to :: non_neg_integer()}
```

A byte range in the source text: `from` inclusive, `to` exclusive.

# `t`

```elixir
@type t() :: %Feel.Diagnostic{
  code: atom(),
  details: %{optional(atom()) =&gt; term()},
  hint: String.t() | nil,
  message: String.t(),
  origin: term(),
  severity: severity(),
  span: span() | nil
}
```

# `error?`

```elixir
@spec error?(t()) :: boolean()
```

Returns `true` for a diagnostic with `:error` severity.

    iex> Feel.Diagnostic.error?(Feel.Diagnostic.new(:type_mismatch, "expected a number"))
    true
    iex> Feel.Diagnostic.error?(Feel.Diagnostic.new(:unknown_name, "x", severity: :warning))
    false
    iex> Feel.Diagnostic.error?(Feel.Diagnostic.new(:implicit_conversion, "x", severity: :info))
    false

# `new`

```elixir
@spec new(atom(), String.t(), keyword()) :: t()
```

Builds a diagnostic.

## Options

  * `:severity` - `:error` (default), `:warning`, or `:info`.
  * `:span` - see `t:span/0`.
  * `:hint` - a suggestion.
  * `:details` - a map of extra facts.

Raises `ArgumentError` for any other option.

    iex> diagnostic = Feel.Diagnostic.new(:unknown_name, ~s(unknown name "peeple"), span: {0, 6})
    iex> {diagnostic.severity, diagnostic.code, diagnostic.span}
    {:error, :unknown_name, {0, 6}}

---

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