2021-08-15 21:52:52 +03:00
|
|
|
# Error reporting made easy
|
|
|
|
|
|
|
|
Diagnose is a small library used to report compiler/interpreter errors in a beautiful yet readable way.
|
2022-04-21 20:19:16 +03:00
|
|
|
It was in the beginning heavily inspired by [ariadne](https://github.com/zesterer/ariadne), but ended up quickly becoming its own thing.
|
2021-08-15 21:52:52 +03:00
|
|
|
|
|
|
|
As a great example, here's the output of the last test:
|
|
|
|
|
|
|
|
![first example](./assets/real-world-example-unicode.png)
|
|
|
|
|
|
|
|
If you do not like unicode characters, or choose to target platforms which cannot output them natively;
|
|
|
|
you may alternatively print the whole diagnostic with ASCII characters, like this:
|
|
|
|
|
|
|
|
![second example](./assets/real-world-example-ascii.png)
|
|
|
|
|
2022-04-21 20:19:16 +03:00
|
|
|
Colors are also optional, and you may choose not to print them.
|
2021-08-15 21:52:52 +03:00
|
|
|
|
2022-04-21 20:19:16 +03:00
|
|
|
## Features
|
|
|
|
|
|
|
|
- Show diagnostics with/without 8-bit colors, with/without Unicode characters
|
|
|
|
- Inline and multiline markers are nicely displayed
|
|
|
|
- The order of markers matters!
|
|
|
|
If there are multiple markers on the same line, they are ordered according to how they were put in each report
|
|
|
|
- Reports spanning across multiple files are handled as well
|
|
|
|
- Generic over the type of message which can be displayed, meaning that you can output custom data types as well as they can be pretty-printed
|
|
|
|
- Diagnostics can be exported to JSON, if you don't quite like the rendering as it is, or if you need to transmit them to e.g. a website
|
|
|
|
- Plug and play (mega)parsec integration and it magically works with your parsers!
|
2022-04-22 22:45:14 +03:00
|
|
|
- Support for optional custom error codes, if you want to go the Rust way
|
2022-04-27 01:28:02 +03:00
|
|
|
- Variable width Unicode characters are handled in a crossplatform manner
|
|
|
|
- TAB characters have custom sizes specified when printing a diagnostic, so that *you* decide the width of a TAB, not your terminal emulator!
|
2022-05-03 13:54:25 +03:00
|
|
|
- Colors can be tweaked thanks to the ability to export diagnostics as `Doc`uments
|
2022-04-21 20:19:16 +03:00
|
|
|
|
|
|
|
## Usage
|
|
|
|
|
|
|
|
You only need to `import Error.Diagnose`, and everything should be ready to go.
|
|
|
|
You don't even need to `import Prettyprinter`, as it is already provided to you by `Error.Diagnose`!
|
|
|
|
|
|
|
|
--------
|
|
|
|
|
|
|
|
A diagnostic can be viewed as a collection of reports, spanning on files.
|
|
|
|
This is what the `Diagnostic` type embodies.
|
|
|
|
|
2023-05-22 08:01:09 +03:00
|
|
|
It is an instance of `Monoid`, which can be used to construct an empty
|
|
|
|
diagnostic (contains no reports, and has no files).
|
2022-04-21 20:19:16 +03:00
|
|
|
|
|
|
|
The second step is to add some reports.
|
|
|
|
There are two kinds of reports:
|
2022-08-14 16:12:23 +03:00
|
|
|
- Error reports, created through `Err`
|
|
|
|
- Warning reports, created by using `Warn`
|
2022-04-21 20:19:16 +03:00
|
|
|
|
|
|
|
Both of these fonctions have the following type:
|
|
|
|
```haskell
|
2022-04-22 22:45:14 +03:00
|
|
|
-- | An optional error code, shown right after @error@ or @warning@ in the square brackets
|
|
|
|
Maybe msg ->
|
|
|
|
-- | The main message, which is output at the top right after @[error]@ or @[warning]@
|
|
|
|
msg ->
|
|
|
|
-- | A list of markers, along with the positions they span on
|
|
|
|
[(Position, Marker msg)] ->
|
|
|
|
-- | Some hints to be output at the bottom of the report
|
2022-06-26 15:20:21 +03:00
|
|
|
[Note msg] ->
|
2022-04-22 22:45:14 +03:00
|
|
|
-- | The created report
|
|
|
|
Report msg
|
2022-04-21 20:19:16 +03:00
|
|
|
```
|
|
|
|
|
|
|
|
Each report contains markers, which are what underlines the code in the screenshots above.
|
|
|
|
They come in three flavors:
|
|
|
|
- A `This` marker indicates the main reason of the error.
|
|
|
|
It is highlighted in red (for errors) or yellow (for warnings).
|
|
|
|
Ideally, there is only one per report, but this isn't strictly required.
|
|
|
|
- A `Where` marker adds additional context to the error by adding highlighted code to the error.
|
|
|
|
This can be used to remind used that a variable was found of a given type earlier, or even where a previous declaration was found in another file.
|
|
|
|
This is output in blue by default.
|
|
|
|
- A `Maybe` marker is probably the rarest one.
|
|
|
|
It is basically a way of suggesting fixes (as when GCC tells you that you probably mistyped a variable name).
|
|
|
|
These markers are highlighted in green.
|
|
|
|
|
|
|
|
The `Position` datatype is however required to be used with this library.
|
|
|
|
If you use another way of keeping track of position information, you will need to convert them to the `Position` datatype.
|
|
|
|
|
|
|
|
Once your reports are created, you will need to add them inside the diagnostic using `addReport`.
|
|
|
|
You will also need to put your files into the diagnostic with `addFile`, else lines won't be printed and you will get `<no-line>` in your reports.
|
|
|
|
|
2022-05-03 18:32:24 +03:00
|
|
|
After all of this is done, you may choose to either:
|
|
|
|
- print the diagnostic onto a file `Handle` (most likely `stdout` or `stderr`) using `printDiagnostic`;
|
|
|
|
- create a `Doc`ument which can be further altered using `prettyDiagnostic`;
|
|
|
|
- or export it to JSON with `diagnosticToJson` or the `ToJSON` class of Aeson (the output format is documented under the `diagnosticToJson` function).
|
2022-04-21 20:19:16 +03:00
|
|
|
|
2022-04-21 20:35:53 +03:00
|
|
|
## Example
|
|
|
|
|
|
|
|
Here is how the above screenshot was generated:
|
|
|
|
```haskell
|
|
|
|
let beautifulExample =
|
|
|
|
err
|
2022-04-22 22:45:14 +03:00
|
|
|
Nothing
|
2022-04-21 20:35:53 +03:00
|
|
|
"Could not deduce constraint 'Num(a)' from the current context"
|
|
|
|
[ (Position (1, 25) (2, 6) "somefile.zc", This "While applying function '+'"),
|
|
|
|
(Position (1, 11) (1, 16) "somefile.zc", Where "'x' is supposed to have type 'a'"),
|
|
|
|
(Position (1, 8) (1, 9) "somefile.zc", Where "type 'a' is bound here without constraints")
|
|
|
|
]
|
|
|
|
["Adding 'Num(a)' to the list of constraints may solve this problem."]
|
2022-06-26 15:20:21 +03:00
|
|
|
-- ^^^^ This is a 'Note' not a 'Hint', as specified by its 'IsString' instance
|
2022-04-21 20:35:53 +03:00
|
|
|
|
2022-08-14 16:12:23 +03:00
|
|
|
-- Create the diagnostic
|
2023-05-22 08:01:09 +03:00
|
|
|
let diagnostic = addFile mempty "somefile.zc" "let id<a>(x : a) : a := x\n + 1"
|
2022-04-21 20:35:53 +03:00
|
|
|
let diagnostic' = addReport diagnostic beautifulExample
|
|
|
|
|
2023-05-30 16:19:47 +03:00
|
|
|
-- Print with unicode characters, and the default (colorful) style
|
|
|
|
printDiagnostic stdout WithUnicode 4 defaultStyle diagnostic'
|
2022-04-21 20:35:53 +03:00
|
|
|
```
|
|
|
|
|
2022-05-03 18:32:24 +03:00
|
|
|
More examples are given in the [`test/rendering`](./test/rendering) folder (execute `stack test` to see the output).
|
2022-04-21 20:35:53 +03:00
|
|
|
|
2022-04-22 13:33:39 +03:00
|
|
|
## TODO list
|
|
|
|
|
2022-04-27 01:26:12 +03:00
|
|
|
<< empty, for now >>
|
2022-04-22 13:33:39 +03:00
|
|
|
|
2022-04-21 20:19:16 +03:00
|
|
|
## License
|
|
|
|
|
|
|
|
This work is licensed under the BSD-3 clause license.
|
|
|
|
|
2022-05-03 18:32:24 +03:00
|
|
|
Copyright (c) 2021-2022 Mesabloo, all rights reserved.
|