nix-error-proposal icon indicating copy to clipboard operation
nix-error-proposal copied to clipboard

Error format data structure

Open roberth opened this issue 5 years ago • 1 comments

Scope

I'm proposing to pay extra attention to the use of (semi)structured data for error messages. Considering that the plan is to update many if not all error messages, this seems like the right time to make sure those errors are usable by programs too, without duplicating effort.

This rest of this issue sketches out a broad scope, but for this to be effective, the most important action is to rewrite the exceptions into a well-designed C++ data structure that preserves the underlying structure until it is formatted by a generic error formatter.

Structured data for integrations

Errors are currently stringly typed. This poses a challenge for tooling that needs to use the messages, such as IDE integrations. For example, Nix has many exceptions like this that essentially throws away the source position by converting it to text.

throw EvalError(format("cannot import '%1%', since path '%2%' is not valid, at %3%")
        % path % e.path % pos);

A related problem exists on the Nix language side. Through primops like builtins.throw, Nix only exposes stringly typed error messages. This has been stretched to include terminal color codes,

Structured data for emitters

The Nix language currently allows the programmer to emit custom errors, warnings and stack trace items, but they only allow text. This has been stretched to include terminal color codes to create an appearance of consistency, but to programs it is still text. In their current form, the builtins require a reimplementation of the error formatting.

Single data structure?

Both of these use cases can be solved with the right data structures. I expect these to be the same if not very similar. The format for integrations should be serializable as JSON and Nix language values are very similar to JSON. One possible difference is that the builtins may need to take in some hints where to gather extra information, because not all information is exposed to Nix programs, to avoid impurities.

Example Nix exception

builtins.throw {
  range = m.file;
  errorName = "Option does not exist.";
  toolName = "module system"; # LSP calls this `source`
  message = [
    "The option "
    { highlight = "wrong"; text = showOption (prefix ++ [name]); }
    " does not exist. You can only define values for options that have been declared. See the"
    { text = "manual"; link = "https://nixos.org/nixos/manual/index.html#sec-option-definitions"; }
    " for more."
  ];
};

The Language Server Protocol Diagnostic may be a good source of inspiration for the format, to keep the vocabulary similar and the conversions simple. However, some deviation is for the better. For example string constants are easier to work with than magic integers from a spec.

roberth avatar Mar 18 '20 11:03 roberth

Good points! I think LSP support for nix would be amazing, and I agree that we have an opportunity to get closer to that goal with this error project. I'll keep that in mind as I get into the implementation details.

ideally we'll use the same data structures throughout the nix code base, as well as having common error handing code - rendering and otherwise processing the errors. That will make wiring up LSP or other facilities easier.

bburdette avatar Mar 18 '20 22:03 bburdette