Feel.Diagnostic (feel v0.1.0)

Copy Markdown View Source

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.

Summary

Types

How serious a diagnostic is.

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

t()

Functions

Returns true for a diagnostic with :error severity.

Builds a diagnostic.

Types

severity()

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

How serious a diagnostic is.

span()

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

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

t()

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

Functions

error?(diagnostic)

@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(code, message, options \\ [])

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

Builds a diagnostic.

Options

  • :severity - :error (default), :warning, or :info.
  • :span - see 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}}