Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make diagnostics emitting independent of the happy code path #61132

Closed
oli-obk opened this issue May 24, 2019 · 18 comments · Fixed by #75138
Closed

Make diagnostics emitting independent of the happy code path #61132

oli-obk opened this issue May 24, 2019 · 18 comments · Fixed by #75138
Assignees
Labels
A-diagnostics Area: Messages for errors, warnings, and lints C-cleanup Category: PRs that clean code up or issues documenting cleanup. E-medium Call for participation: Medium difficulty. Experience needed to fix: Intermediate. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@oli-obk
Copy link
Contributor

oli-obk commented May 24, 2019

In our first @rust-lang/wg-diagnostics meeting on zulip we figured that a first start would be to create a new trait

trait AsError {
    /// ParseSess, Session or `TyCtxt`
    type Session;
    fn to_error(self, session: Self::Session) -> DiagnosticBuilder;
    fn emit(self) {
        self.to_error().emit()
    }
}

that is implemented for structs containing all relevant info for a diagnostic, e.g.

struct TypeError {
    expected: Ty,
    found: Ty,
    span: Span,
}

and then the code that currently constructs the diagnostic and emits it would just become

TypeError {
    expected,
    found,
    span: Span,
}.emit();

This issue has been assigned to @jumbatm via this comment.

@oli-obk oli-obk added C-cleanup Category: PRs that clean code up or issues documenting cleanup. A-diagnostics Area: Messages for errors, warnings, and lints E-medium Call for participation: Medium difficulty. Experience needed to fix: Intermediate. labels May 24, 2019
@Manishearth
Copy link
Member

The vast majority of these could probably be handled by a custom derive:

#[derive(AsError)]
#[error = "Cannot move {ty} out of borrow", span]
#[note = "First borrwed here", other_span]
#[code = E12345]
struct MoveOutOfBorrowError {
    ty: Ty,
    span: Span,
    other_span: Span,
}

(exact syntax is bikesheddable, of course)

@estebank
Copy link
Contributor

At first I thought about how the derive macro would be constraining for some of the more complex errors, but looking at it I can see how it'd be more than appropriate for the great majority of errors we emit:

#[derive(AsError)]
#[code = E12345]
struct MoveOutOfBorrowError {
    ty: Ty,
    #[error = "cannot move {ty} out of borrow"]
    #[label = "cannot move out of borrow"]
    span: Span,
    #[label = "`{ty}` first borrowed here"]
    other_span: Span,
    #[suggestion(msg = "consider cloning here", code = "{opt_sugg}.clone()")]
    opt_sugg: Option<Span>,
}

@Manishearth
Copy link
Member

Oh, tagging spans with this stuff is genius.

@Manishearth
Copy link
Member

Manishearth commented Jun 10, 2019

Hmm, it needs a little tweak:

#[derive(AsError)]
#[code = E12345]
struct MoveOutOfBorrowError {
    ty: Ty,
    #[error = "cannot move {ty} out of borrow"]
    #[label = "cannot move out of borrow"]
    span: Span,
    #[label = "`{ty}` first borrowed here"]
    other_span: Span,
    #[suggestion(msg = "consider cloning here", code = "{1}.clone()")]
    opt_sugg: Option<(Span, Ident)>,
}

@Manishearth
Copy link
Member

So here we'd have to special case Ty, Span, and Option. Not too bad.

@Electron-libre
Copy link
Contributor

This sounds like a great improvment in typeck maintainability.
(And probably a lot of work too)
It could be a good opportunity for me to pratice custom derives.

I think I can do this, It will just take time and trials.

What do you think ?

@Manishearth
Copy link
Member

Go for it, I think. cc @estebank

The derive will basically have to specially notice Tys and Spans and some other stuff, though. Getting the suggestions working is tricky too.

@estebank
Copy link
Contributor

I think we can iterate and disregard the more complex parts of the feature (IE, implement them manually in the code for now) and later pull them into the proc macro.

@Mark-Simulacrum
Copy link
Member

@Electron-libre Are you still working on this? If so please @rustbot claim this issue, thanks!

@Electron-libre
Copy link
Contributor

No sorry, my son is born ( 😍 ) few days after my last comment and then I forgot about this issue.
I will not invest time in OSS contributions the next months.
Someone else should pick this issue.

@Manishearth
Copy link
Member

I and @yaahc may end up working on this

@Manishearth
Copy link
Member

Starting to think about the design. Would this be implemented as a normal derive proc macro (using a syn dependency) or something more like the builtin libsyntax derives? I'm not sure if rustc having a syn dependency is a good idea.

If it's going to be a libsyntax thing, what x.py commands would y'all recommend for testing it for minimal cycle time? Ideally only libsyntax needs to be built to play with this. It's been a while since i poked at this stuff and the build system has changed.

cc @estebank

@bjorn3
Copy link
Member

bjorn3 commented Nov 7, 2019

I think it should be in rustc_macros like other rustc internal derives.

@jonas-schievink jonas-schievink added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Dec 5, 2019
@jumbatm
Copy link
Contributor

jumbatm commented Feb 12, 2020

@Manishearth @yaahc Did you end up / are you still intending on working on this? It's unassigned and I'd like to take this one, but I just want to make sure I'm not treading on any toes, first.

@Manishearth
Copy link
Member

Go for it, I want to eventually work on this but if someone else gets there first that's fine

@jumbatm
Copy link
Contributor

jumbatm commented Feb 14, 2020

Alright, cheers.

@rustbot claim

@JohnTitor
Copy link
Member

Triage: I'm going to release assignment due to inactivity.
@jumbatm If you're still interested in this, feel free to re-claim.
@rustbot release-assignment

@rustbot rustbot removed their assignment Jul 24, 2020
@jumbatm
Copy link
Contributor

jumbatm commented Jul 24, 2020

I'm still actively working on this. Sorry I've been taking so long -- I'll hurry up and get a PR in soon.

@rustbot claim

@rustbot rustbot self-assigned this Jul 24, 2020
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this issue Sep 7, 2020
…r=oli-obk

Add derive macro for specifying diagnostics using attributes.

Introduces `#[derive(SessionDiagnostic)]`, a derive macro for specifying structs that can be converted to Diagnostics using directions given by attributes on the struct and its fields. Currently, the following attributes have been implemented:
- `#[code = "..."]` -- this sets the Diagnostic's error code, and must be provided on the struct iself (ie, not on a field). Equivalent to calling `code`.
- `#[message = "..."]` -- this sets the Diagnostic's primary error message.
- `#[label = "..."]` -- this must be applied to fields of type `Span`, and is equivalent to `span_label`
- `#[suggestion(..)]` -- this allows a suggestion message to be supplied. This attribute must be applied to a field of type `Span` or `(Span, Applicability)`, and is equivalent to calling `span_suggestion`. Valid arguments are:
    - `message = "..."` -- this sets the suggestion message.
    - (Optional) `code = "..."` -- this suggests code for the suggestion. Defaults to empty.

`suggestion`also  comes with other variants: `#[suggestion_short(..)]`, `#[suggestion_hidden(..)]` and `#[suggestion_verbose(..)]` which all take the same keys.

Within the strings passed to each attribute, fields can be referenced without needing to be passed explicitly into the format string -- eg, `#[error = "{ident} already declared"] ` will set the error message to `format!("{} already declared", &self.ident)`. Any fields on the struct can be referenced in this way.

Additionally, for any of these attributes, Option fields can be used to only optionally apply the decoration -- for example:

```rust
#[derive(SessionDiagnostic)]
#[code = "E0123"]
struct SomeKindOfError {
    ...
    #[suggestion(message = "informative error message")]
    opt_sugg: Option<(Span, Applicability)>
    ...
}
```
will not emit a suggestion if `opt_sugg` is `None`.

We plan on iterating on this macro further; this PR is a start.

Closes rust-lang#61132.

r? @oli-obk
@bors bors closed this as completed in 71569e4 Sep 8, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-diagnostics Area: Messages for errors, warnings, and lints C-cleanup Category: PRs that clean code up or issues documenting cleanup. E-medium Call for participation: Medium difficulty. Experience needed to fix: Intermediate. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

10 participants