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

Show multiline spans in full if short enough #37369

Merged
merged 2 commits into from
Nov 29, 2016

Conversation

estebank
Copy link
Contributor

@estebank estebank commented Oct 24, 2016

When dealing with multiline spans that span few lines, show the complete span instead of restricting to the first character of the first line.

For example, instead of:

% ./rustc file2.rs
error[E0277]: the trait bound `{integer}: std::ops::Add<()>` is not satisfied
  --> file2.rs:13:9
   |
13 |    foo(1 + bar(x,
   |        ^ trait `{integer}: std::ops::Add<()>` not satisfied
   |

show

% ./rustc file2.rs
error[E0277]: the trait bound `{integer}: std::ops::Add<()>` is not satisfied
  --> file2.rs:13:9
   |
13 |      foo(1 + bar(x,
   |  ________^ starting here...
14 | |            y),
   | |_____________^ ...ending here: trait `{integer}: std::ops::Add<()>` not satisfied
   |

The proposal in internals outlines the reasoning behind this.

@rust-highfive
Copy link
Collaborator

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @arielb1 (or someone else) soon.

If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes.

Please see the contribution instructions for more information.

// If the span is multi-line, simplify down to the span of one character
if lo.line != hi.line {
// If the span is long multi-line, simplify down to the span of one character
let max_multiline_span_length = 20;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will need to be much lower. I propose a value between 5 and 10.

@fabric-and-ink
Copy link
Contributor

fabric-and-ink commented Oct 24, 2016

Hi there! I think this is nice work but I am unsure if this is the right way. I would suggest to use the box-drawing characters for the lines. This would result in a cleaner look. Like this

error[E0277]: the trait bound `{integer}: std::ops::Add<()>` is not satisfied
  --> file2.rs:13:9
   |
13 |      foo(1 + bar(x,
   | ┌────────┘ starting here...
14 | │            y),
   | └─────────────┘ ...ending here: trait `{integer}: std::ops::Add<()>` not satisfied
   |

@estebank
Copy link
Contributor Author

@dawirstejeck as per @nikomatsakis in the original PR:

I am avoiding unicode because many terminals can't handle it, so in the past we've tried to stick to ASCII.

I think it'd be neat if there were a way to probe for unicode awareness and use ascii safe characters or unicode accordingly, but that's beyond the scope of this PR :)

@estebank estebank force-pushed the multiline-span branch 4 times, most recently from 6f80da2 to 41c9950 Compare October 25, 2016 22:05
eddyb added a commit to eddyb/rust that referenced this pull request Nov 9, 2016
…-back, r=nikomatsakis

Include type of missing trait methods in error

Provide either a span pointing to the original definition of missing
trait items, or a message with the inferred definitions.

Fixes rust-lang#24626. Follow up to PR rust-lang#36371.

If PR rust-lang#37369 lands, missing trait items that present a multiline span will be able to show the entirety of the item definition on the error itself, instead of just the first line.
@alexcrichton
Copy link
Member

r? @nikomatsakis

Carrying over reviewer from the original PR. Looks like the conclusion there was to close in favor of an internals thread and open up a new one upon reaching consensus.

With my reading of the internals thread it looks like we haven't quite reached consensus yet, but @nikomatsakis seems to have been following more closely than I and may know more! @jonathandturner you were also interested in the first PR so you're likely to be interested in this one :)

@alexcrichton alexcrichton added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Nov 10, 2016
@brson
Copy link
Contributor

brson commented Nov 11, 2016

Neat feature!

Copy link
Contributor

@nikomatsakis nikomatsakis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, so I see that the #[test] infrastructure for testing snippet highlighting has been removed and move to UI tests. That's a bit unfortunate here, as it would have been really easy to make a bunch of tests for edge cases. I'm trying to think now if there is an easy way to reproduce it. Perhaps just adding back the #[test] stuff, but as @jonathandturner pointed out, it IS kind of a pain.

Let me poke around a bit. I'd love to land this PR and don't want to block it on extensive work to build up a nice unit-testing infrastructure -- on the other hand, I'd feel a lot more comfortable if we could just write down tests for various edge cases (even if they don't occur in rustc...yet). The code seems pretty clean but oveall this formatting logic is easier to verify as a block-box, frankly.


// Find overlapping multiline annotations, put them at different depths
multiline_annotations.sort_by(|a, b| a.1.start_col.cmp(&b.1.start_col));
for item in multiline_annotations.clone() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code confuses me a bit -- why doesn't it have to consider lines at all?

What if the input was like

X0
...
X1
Y0
...
Y1

With two multi-line spans stretching from X0..X1 and Y0..Y1... wouldn't that cause it to indent the Y case even though they do not in fact overlap?

I think I would expect this indentation to be done on the basis not of start_col and end_col but rather a (line, col) pair, or something like that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only the line needs to be taken into account. I probably just made a mistake here that, as I didn't have any overlapping example to test against, I didn't catch.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.


let mut max_depth = 0; // max overlapping multiline spans
for (file, ann) in multiline_annotations {
if let AnnotationType::Multiline {line_start, line_end, depth} = ann.annotation_type {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all the annotations in this vector should be multiline, right? If so, can you add else { panic!("non-multiline annotation inmultiline_annotations!"); } to make this clearer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added.

@nikomatsakis
Copy link
Contributor

Bah, I didn't get time to finish implementing that test harness. =(

The basic idea was just going to be adding some #[test] functions to librustc_errors which would gin up a file map etc.

@nikomatsakis
Copy link
Contributor

OK, so, I'm sorry I've been letting the perfect be the enemy of the good here. Let's land this PR and hack on a test harness separately. I've been toying with some stuff locally but the setup is kind of a pain so I don't want to block things on it.

@bors r+

@bors
Copy link
Contributor

bors commented Nov 15, 2016

📌 Commit 41c9950 has been approved by nikomatsakis

@nikomatsakis
Copy link
Contributor

@bors r-

Actually, I'd still like to know the answer to this question and have this nit addressed.

@estebank, you around?

@estebank
Copy link
Contributor Author

@nikomatsakis sure thing. I'll update the PR this afternoon.

@estebank
Copy link
Contributor Author

estebank commented Nov 15, 2016

I've been toying with some stuff locally but the setup is kind of a pain so I don't want to block things on it.

If you publish branch I can use as a base to help, please let me know. Also, #12182 covers exactly that work, I believe.

@nikomatsakis
Copy link
Contributor

@estebank ok I got something working in this branch:

https://github.com/nikomatsakis/rust/tree/multiline-span

In particular, you can just cherry-pick this commit:

nikomatsakis@0166ce0

The tests are all calls to this test_harness() function. You give it the input along with some spans and labels, specified as a string to search for (it takes the Nth match). Then you put in the expected output. You can see that the nested cases look a bit odd.

To run the tests, do make check-stage0-syntax (presuming you are using makefiles; I'm not sure of the x.py way to do this). The only annoying thing is that building libsyntax does take a while: on the other hand, you don't have to build the rest of the compiler if you make changes.

@estebank
Copy link
Contributor Author

estebank commented Nov 15, 2016

In particular, you can just cherry-pick this commit

Awesome!

Then you put in the expected output. You can see that the nested cases look a bit odd.

It actually looks exactly how I expected it to look, as in, failing in the exact ways I expected it to be failing :)

--> test.rs:3:3
  |
3 |      X0 Y0
  |  _______- starting here...
  |      |
  |      starting here...
4 | ||   X1 Y1
  | |________- ...ending here: `Y` is a good letter too
  |       |
  |       ...ending here: `X` is a good letter

I would like to arrive with you to what the appropriate output for this case would be, where the multispans start (or end) both on the same line. I'm thinking something along the lines of:

--> test.rs:3:3
  |
3 |      X0 Y0
  |  _______- starting here...
  | | ___|
  | ||   starting here...
4 | ||   X1 Y1
  | |+_______- ...ending here: `Y` is a good letter too
  |  |____|
  |       ...ending here: `X` is a good letter

or

--> test.rs:3:3
  |
3 |      X0 Y0
  |  ____-__- starting here...
  | ||   |
  | ||   starting here...
4 | ||   X1 Y1
  | |⊥____-__- ...ending here: `Y` is a good letter too
  |       |
  |       ...ending here: `X` is a good letter

Both cases seem a bit messy, to honest.

This might be not as accurate but more composable:

--> test.rs:3:3
  |
3 |      X0 Y0
  | ++   -  - starting here...
  | ||   |
  | ||   starting here...
4 | ||   X1 Y1
  | ++    -  - ...ending here: `Y` is a good letter too
  |       |
  |       ...ending here: `X` is a good letter

Which for the PR's example would look like:

  --> file2.rs:13:9
   |
13 |      foo(1 + bar(x,
   | +        ^ starting here...
14 | |            y),
   | +             ^ ...ending here: trait `{integer}: std::ops::Add<()>` not satisfied
   |

@nikomatsakis
Copy link
Contributor

nikomatsakis commented Nov 15, 2016

@estebank

Both cases seem a bit messy, to honest.

Yes, but this is an extreme case. I mostly want us to do "something sensible". I wonder if we can provide a bit of extra info, though, to help people disambiguate, like a number. Something like this (riffing on my preferred variant):

--> test.rs:3:3
  |
3 |      X0 Y0
  |  _______- starting here... [1]
  | | ___|
  | ||   starting here... [2]
4 | ||   X1 Y1
  | ||_______- ...ending here [2]: `Y` is a good letter too
  |  |____|
  |       ...ending here [1]: `X` is a good letter

Maybe we only show these if things overlap?

@nikomatsakis
Copy link
Contributor

Oh, and I removed the + in favor of just drawing a |, which I think reads a bit better.

@estebank estebank force-pushed the multiline-span branch 3 times, most recently from 2dc9cfc to 935c0df Compare November 22, 2016 19:53
When dealing with multiline spans that span few lines, show the complete
span instead of restricting to the first character of the first line.

For example, instead of:

```
% ./rustc foo.rs
error[E0277]: the trait bound `{integer}: std::ops::Add<()>` is not satisfied
  --> foo.rs:13:9
   |
13 |    foo(1 + bar(x,
   |        ^ trait `{integer}: std::ops::Add<()>` not satisfied
   |
```

show

```
% ./rustc foo.rs
error[E0277]: the trait bound `{integer}: std::ops::Add<()>` is not satisfied
  --> foo.rs:13:9
   |
13 |      foo(1 + bar(x,
   |  ________^ starting here...
14 | |            y),
   | |_____________^ ...ending here: trait `{integer}: std::ops::Add<()>` not satisfied
   |
```
Copy link
Contributor

@nikomatsakis nikomatsakis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left one suggestion. One other thing I'd like: can you give a test with two disjoint ranges (touching distinct lines)?

I confess I didn't read the actual code in detail, but your comments gave me confidence that I could read it if ever I had to. =) The tests seem quite good though.

Basically r=me modulo the suggested refactoring and test (and feel free to tell me if you think my suggestion is not a good one).

}

// Find overlapping multiline annotations, put them at different depths
multiline_annotations.sort_by(|a, b| {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this code might be cleaner if you made a struct for MultilineAnnotation, and then adjusted the enum to wrap the struct. Something like:

enum Annotation {
    ...
    Multiline(MultilineAnnotation),
    ...
}

struct MultilineAnnotation {
        depth: usize,
        line_start: usize,
        line_end: usize,
}

in that case, the type of multiline_annotations can be Vec<MultilineAnnotation>.

// on a different line as the underline.
//
// After this we will have:
//
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️ loving the comments! ❤️

@estebank
Copy link
Contributor Author

Left one suggestion. One other thing I'd like: can you give a test with two disjoint ranges (touching distinct lines)?

Added (last two tests).

this code might be cleaner if you made a struct for MultilineAnnotation, and then adjusted the enum to wrap the struct. Something like:

There's some duplication of fields now for this to work, but I don't think it'll make the code any harder to follow.

feel free to tell me if you think my suggestion is not a good one

Other than the issue mentioned above, the suggestion makes sense.

@estebank estebank force-pushed the multiline-span branch 2 times, most recently from 1ef8ede to d9c370e Compare November 24, 2016 02:49
@nikomatsakis
Copy link
Contributor

@bors r+

Very nice.

@bors
Copy link
Contributor

bors commented Nov 29, 2016

📌 Commit b7982bb has been approved by nikomatsakis

@bors
Copy link
Contributor

bors commented Nov 29, 2016

⌛ Testing commit b7982bb with merge b30022a...

bors added a commit that referenced this pull request Nov 29, 2016
Show multiline spans in full if short enough

When dealing with multiline spans that span few lines, show the complete span instead of restricting to the first character of the first line.

For example, instead of:

```
% ./rustc file2.rs
error[E0277]: the trait bound `{integer}: std::ops::Add<()>` is not satisfied
  --> file2.rs:13:9
   |
13 |    foo(1 + bar(x,
   |        ^ trait `{integer}: std::ops::Add<()>` not satisfied
   |
```

show

```
% ./rustc file2.rs
error[E0277]: the trait bound `{integer}: std::ops::Add<()>` is not satisfied
  --> file2.rs:13:9
   |
13 |      foo(1 + bar(x,
   |  ________^ starting here...
14 | |            y),
   | |_____________^ ...ending here: trait `{integer}: std::ops::Add<()>` not satisfied
   |
```

The [proposal in internals](https://internals.rust-lang.org/t/proposal-for-multiline-span-comments/4242/6) outlines the reasoning behind this.
@bors bors merged commit b7982bb into rust-lang:master Nov 29, 2016
estebank added a commit to estebank/rust that referenced this pull request Dec 15, 2016
After the fix of rust-lang#37453 in PR rust-lang#37369, instead of pointing at only the
cast type, point at the full cast span when a cast needs a dereference:

```
error: casting `&{float}` as `f32` is invalid
  --> ../../../src/test/ui/mismatched_types/cast-rfc0401.rs:81:30
   |
81 |     vec![0.0].iter().map(|s| s as f32).collect::<Vec<f32>>();
   |                              ^^^^^^^^ cannot cast `&{float}` as `f32`
   |
help: did you mean `*s`?
  --> ../../../src/test/ui/mismatched_types/cast-rfc0401.rs:81:30
   |
81 |     vec![0.0].iter().map(|s| s as f32).collect::<Vec<f32>>();
   |                              ^
```

instead of

```
error: casting `&{float}` as `f32` is invalid
  --> ../../../src/test/ui/mismatched_types/cast-rfc0401.rs:81:35
   |
81 |     vec![0.0].iter().map(|s| s as f32).collect::<Vec<f32>>();
   |                              -    ^^^
   |                              |
   |                              |
   |                              did you mean `*s`?
   |                              cannot cast `&{float}` as `f32`
```
bors added a commit that referenced this pull request Dec 20, 2016
When cast needs a dereference point at full cast

After the fix of #37453 in PR #37369, instead of pointing at only the cast type, point at the full cast span when a cast needs a dereference, as well as assign the error label to the correct span for proper coloring:

<img width="471" alt="error span pointing at the entire cast" src="https://cloud.githubusercontent.com/assets/1606434/21024245/8797fc2e-bd38-11e6-82c1-66c281c656c1.png">

instead of

<img width="471" alt="error span pointing at the type of the cast" src="https://cloud.githubusercontent.com/assets/1606434/21023777/d4814aa6-bd36-11e6-9fc3-b2a0ea5ee15d.png">

Move `compile-fail` test to `ui` test.
alexcrichton added a commit to alexcrichton/rust that referenced this pull request Dec 20, 2016
…atsakis

When cast needs a dereference point at full cast

After the fix of rust-lang#37453 in PR rust-lang#37369, instead of pointing at only the cast type, point at the full cast span when a cast needs a dereference, as well as assign the error label to the correct span for proper coloring:

<img width="471" alt="error span pointing at the entire cast" src="https://cloud.githubusercontent.com/assets/1606434/21024245/8797fc2e-bd38-11e6-82c1-66c281c656c1.png">

instead of

<img width="471" alt="error span pointing at the type of the cast" src="https://cloud.githubusercontent.com/assets/1606434/21023777/d4814aa6-bd36-11e6-9fc3-b2a0ea5ee15d.png">

Move `compile-fail` test to `ui` test.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants