JuliaLang / JuliaLang/JuliaSyntax.jl

Diagnostics integration with Base

Open
#276 0 comments 0 reactions 0 assignees View on GitHub
design error messages
Dominant language
Julia
Stars
293
Forks
50
PR merge metrics
No merged PRs in 30d

Description

Copying a comment from https://github.com/JuliaLang/julia/issues/36547#issuecomment-1454326641 to avoid forgetting it, here's some thoughts about how we might integrate diagnostics such as warnings better with Base.

### Diagnostics design sketch

Ok. How about the following sketch for how diagnostics could flow through the parser and runtime:

1. Change `Core._parse` API such that, instead of returning `svec(ex, last_offset)`, it returns a `svec(ex, last_offset, diagnostics)`. Note that in general, compiler diagnostics do not need to align with the tree structure returned in `ex`. So having them in a separate list is more flexible. See also the `Expr(:diagnostics)` design alternative below
2. Figure out the API for the `diagnostics` data structure. Probably `show` and some kind of other summary function (`Meta.diagnostic_summary`? - see discussion below)
3. Modify the `Meta.parse()` machinery for the new `Core._parse`, in some way that it can (optionally I guess, for compatibility?) return diagnostics to the caller. In `JuliaSyntax.parse()` we have `ignore_warnings`
4. Change `jlfrontend.scm` and `julia-parser.scm` to use a dynamically scoped list to collect warnings during parsing, as mentioned in a previous comment. Change the runtime C function `fl_parse` to deal with the diagnostics list returned.
5. Change the use of `Meta.parse` within `Base` and the runtime to request diagnostics and report them in some way. For the use in `loading.jl` in `include/include_string`, presumably they would go via the logging system, as we're already running Julia code there. Same for the use of `parseall` in `client.jl` and `parse_input_line` in the REPL. (side note - `parse_input_line()` predates `parseall()` but in principle should just be replaced with `parseall()`)
6. Users may still call into the C runtime functions `jl_eval_string` / `jl_parse_string` / `jl_parse_all` from arbitrary C code. We could report warnings arising from those to `stderr`? Or just ignore them? It seems like an edge case we could ignore.

#### Design alternative with `Expr(:diagnostics)`

A possible alternative to returning the diagnostics separately from `Core._parse` is to encode them into the expression tree, somewhat similarly to how `Expr(:error)` is done. However, I think it best to encode them in the root of the tree as `Expr(:diagnostics, ex, diagnostics)`, as they're not part of the tree structure per se. This is different from how `Expr(:error)` currently gets tacked onto the end of the `Expr(:toplevel)` args list.

Then report any diagnostics which exist at `eval()` time. This might be neater and more easy to make compatible than threading a separate diagnostics data structure around everywhere - implementing will tell. The advantages of this approach is that it could allow us to have warnings on in `Meta.parse()` by default. And also that there's a chance that they could survive flowing through user code (including macros) and back into `eval()`. (But tbh I'm not sure these features are entirely necessary... needs thought.)

### Source Diagnostics data structures / API

One straightforward concrete option would be a `Vector` of `(level, first_byte, last_byte, message)` where `level` can be `:error`, `:warn` (or maybe `:info`) - `first_byte:last_byte`, refers to a source range of the input text, and `message` is a string. This is basically just the `JuliaSyntax.Diagnostic` data structure.

A challenge here is I don't think `JuliaSyntax.Diagnostic` is "finished" and I'd like flexibility to expand it in the future. For example, adding
* Sub-ranges for highlighting and additional message structure
* Continue to work on pretty printing. Including
- Printing for mime types other than text
- Formatting multiple diagnostics on a single line in a way which only prints the source line once.

So a better option than requiring a particular data structure for diagnostics is probably to rely on generic functions - minimal, just enough for the runtime to query the diagnostics data structure as necessary. `show(io, diagnostics)` being an obvious one for pretty printing. But perhaps also an analogue of `JuliaSyntax.any_error()` to summarize the diagnostics. Maybe it could be `Meta.diagnostic_summary` or some such so we can distinguish between errors and warnings. (Not sure if it needs to be in `Core`? Needs investigation.)

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.