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

NLL: rewrites should include actual source input, not solely the hypothesized rewrites #52877

Closed
pnkfelix opened this issue Jul 30, 2018 · 3 comments · Fixed by #52907
Closed
Labels
A-NLL Area: Non-lexical lifetimes (NLL) NLL-diagnostics Working towards the "diagnostic parity" goal

Comments

@pnkfelix
Copy link
Member

While working on #52663, I noticed something I had not seen before: the .nll.stderr files for borrowck-move-out-of-vec-tail.nll.stderr and borrowck-vec-pattern-nesting.nll.stderr have an odd characteristic: they print out highlighted spans that do not actually correspond to the actual input source code, but rather correspond to the hypothetical rewritten code that we are asking the user to type in.


Let me explain with actual pointers to lines in the files:

This is what the .nll.stderr files say we expect to see from the compiler (and what the compiler is actually printing under NLL mode, modulo details like the specific line numbers in the left-hand column):

help: to prevent move, use ref or ref mut
|
LL | &[Foo { string: ref a },
| ^^^^^
help: to prevent move, use ref or ref mut
|
LL | Foo { string: ref b }] => {
| ^^^^^

help: to prevent move, use ref or ref mut
|
LL | &mut [ref _a, _b, _c] => {} //~ ERROR cannot move out
| ^^^^^^
help: to prevent move, use ref or ref mut
|
LL | &mut [_a, ref _b, _c] => {} //~ ERROR cannot move out
| ^^^^^^
help: to prevent move, use ref or ref mut
|
LL | &mut [_a, _b, ref _c] => {} //~ ERROR cannot move out
| ^^^^^^

In the .nll.stderr files that I linked above, the highlighted spans correspond to these lines:

match tail {
&[Foo { string: a },
//~^ ERROR cannot move out of type `[Foo]`
//~| cannot move out
//~| to prevent move
Foo { string: b }] => {

match vec {
&mut [_a, _b, _c] => {} //~ ERROR cannot move out
//~| cannot move out

(Do you see the problem? If not, read on...)


The problem I have with this is that when I see output from rustc like:

here be some code
     ^^

I expect to actually find the actual text here is some code in my source file. If the diagnostic wants to include a suggestion that we replace "be" with "is", then I expect that to appear as help text that says something like "write is instead of be" or even just "write is here" (with the "be" highlighted, as above)

In short, the user experience (at least for me) from the current NLL is a bit frustrating, because when I see the error message, it seems like it is saying "insert a ref here", but then from my point of view it is printing out the source code and my reaction is "there already is a ref there" (because I'm staring at the print-out in the user terminal and not the source code in my text editor).


The fix for this should be simple: identify the cases that are creating these strange diagnostics, and replace them with something more traditional. Note in particular that in both the cases above, the AST-borrowck generated error is more normal:

LL | &[Foo { string: a },
| ^ - hint: to prevent move, use `ref a` or `ref mut a`
| __________________|
| |
LL | | //~^ ERROR cannot move out of type `[Foo]`
LL | | //~| cannot move out
LL | | //~| to prevent move
LL | | Foo { string: b }] => {
| |_________________________________-__^ cannot move out of here
| |
| ...and here (use `ref b` or `ref mut b`)

LL | &mut [_a, _b, _c] => {} //~ ERROR cannot move out
| ^--^^--^^--^
| || | |
| || | ...and here (use `ref _c` or `ref mut _c`)
| || ...and here (use `ref _b` or `ref mut _b`)
| |hint: to prevent move, use `ref _a` or `ref mut _a`

However, if someone can point me at at an established precedent for this pattern (lets say at least three examples) in the current rustc, then I would also be willing to accept that this is something we already do and that I am just an old fogie who is unaware of how the UX tide is shifting.

@pnkfelix pnkfelix added A-NLL Area: Non-lexical lifetimes (NLL) NLL-diagnostics Working towards the "diagnostic parity" goal WG-compiler-nll labels Jul 30, 2018
@pnkfelix
Copy link
Member Author

pnkfelix commented Jul 31, 2018

One thing that bothers me about this ticket: the rustc code generating the diagnostic in question, AFAICT, does not appear to be doing anything out of the ordinary:

Some(name) => {
err.span_suggestion(
binding_span,
"to prevent move, use ref or ref mut",
format!("{} {:?}", ref_kind, name),
);
}

But if that code isn't doing anything weird to try to construct this diagnostic, then is this in fact a kind of diagnostic that we expect to see in practice?

(Note: I am not saying that I dislike printing out lines of hypothetical code. My whole problem here is just about the row of "^^^^^^" being printed underneath the hypothesized lines, because I interpret those as highlighting existing code. If those were not there, I probably would never have noticed this in the first place)

@pnkfelix
Copy link
Member Author

Note also that in comparing with the AST-borrowck code:

fn note_move_destination(mut err: DiagnosticBuilder,
move_to_span: syntax_pos::Span,
pat_name: ast::Name,
is_first_note: bool) -> DiagnosticBuilder {
if is_first_note {
err.span_label(
move_to_span,
format!("hint: to prevent move, use `ref {0}` or `ref mut {0}`",
pat_name));
err
} else {
err.span_label(move_to_span,
format!("...and here (use `ref {0}` or `ref mut {0}`)",
pat_name));
err
}
}

it should indeed be really easy to try to recreate the same combined set of diagnostics here that AST-borrowck is able to produce. if we want that (e.g. by switching to span_label instead of span_diagnostic...)

@pnkfelix
Copy link
Member Author

Okay, after quite a bit of review, I tracked down code in the compiler that decides whether to emit the ^^^^^ for a suggestion:

// Only show underline if the suggestion spans a single line and doesn't cover the
// entirety of the code output. If you have multiple replacements in the same line
// of code, show the underline.
let show_underline = !(parts.len() == 1
&& parts[0].snippet.trim() == complete.trim())
&& complete.lines().count() == 1;

And after I found that line, I was able to find its associated PR, and thus find an example of a test that is making use of this ... "feature" ...:

LL | fn bar(&self, _: &impl Debug) { }
| ^^^^^^^^^^ expected generic parameter, found `impl Trait`
help: try changing the `impl Trait` argument to a generic parameter
|
LL | fn bar<U: Debug>(&self, _: &U) { }
| ^^^^^^^^^^ ^

However, that example has a crucial difference. Yes, it prints a rewrite of the code (and highlights the rewritten portion). But, it only does so after it has printed an error that highlights the problem in the original source. And that seems like one potential way to resolve this problem in our case.

@pnkfelix pnkfelix changed the title NLL: highlighted spans should correspond to actual source input, not hypothesized rewrites NLL: highlighted spans print actual source input, not solely the hypothesized rewrites Jul 31, 2018
@pnkfelix pnkfelix changed the title NLL: highlighted spans print actual source input, not solely the hypothesized rewrites NLL: highlighted spans should print actual source input, not solely the hypothesized rewrites Jul 31, 2018
@pnkfelix pnkfelix changed the title NLL: highlighted spans should print actual source input, not solely the hypothesized rewrites NLL: highlighted spans should include actual source input, not solely the hypothesized rewrites Jul 31, 2018
@pnkfelix pnkfelix changed the title NLL: highlighted spans should include actual source input, not solely the hypothesized rewrites NLL: rewrites should include actual source input, not solely the hypothesized rewrites Jul 31, 2018
Mark-Simulacrum added a commit to Mark-Simulacrum/rust that referenced this issue Aug 1, 2018
…e-should-precede-suggestions, r=petrochenkov

NLL: On "cannot move out of type" error, print original before rewrite

NLL: On "cannot move out of type" error, print original source before rewrite.

 * Arguably this change is sometimes injecting noise into the output  (namely in the cases where the suggested rewrite is inline with the   suggestion and we end up highlighting the original source code).   I would not be opposed to something more aggressive/dynamic, like   revising the suggestion code to automatically print the original  source when necessary (e.g. when the error does not have a span   that includes the span of the suggestion).

 * Also, as another note on this change: The doc comment for `Diagnostic::span_suggestion`  says:
```rust
    /// The message
    ///
    /// * should not end in any punctuation (a `:` is added automatically)
    /// * should not be a question
    /// * should not contain any parts like "the following", "as shown"
```
  *  but the `:` is *not* added when the emitted line appears  out-of-line relative to the suggestion. I find that to be an  unfortunate UI experience.

----

As a drive-by fix, also changed code to combine multiple suggestions for a pattern into a single multipart suggestion (which vastly improves user experience IMO).

----

Includes the updates to expected NLL diagnostics.

Fix rust-lang#52877
pietroalbini added a commit to pietroalbini/rust that referenced this issue Aug 1, 2018
…e-should-precede-suggestions, r=petrochenkov

NLL: On "cannot move out of type" error, print original before rewrite

NLL: On "cannot move out of type" error, print original source before rewrite.

 * Arguably this change is sometimes injecting noise into the output  (namely in the cases where the suggested rewrite is inline with the   suggestion and we end up highlighting the original source code).   I would not be opposed to something more aggressive/dynamic, like   revising the suggestion code to automatically print the original  source when necessary (e.g. when the error does not have a span   that includes the span of the suggestion).

 * Also, as another note on this change: The doc comment for `Diagnostic::span_suggestion`  says:
```rust
    /// The message
    ///
    /// * should not end in any punctuation (a `:` is added automatically)
    /// * should not be a question
    /// * should not contain any parts like "the following", "as shown"
```
  *  but the `:` is *not* added when the emitted line appears  out-of-line relative to the suggestion. I find that to be an  unfortunate UI experience.

----

As a drive-by fix, also changed code to combine multiple suggestions for a pattern into a single multipart suggestion (which vastly improves user experience IMO).

----

Includes the updates to expected NLL diagnostics.

Fix rust-lang#52877
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-NLL Area: Non-lexical lifetimes (NLL) NLL-diagnostics Working towards the "diagnostic parity" goal
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant