Skip to content

Commit d2ebb12

Browse files
committed
Auto merge of #42904 - estebank:number-suggestions, r=nikomatsakis
Make suggestion include the line number When there're more than one suggestions in the same diagnostic, they are displayed in their own block, instead of inline. In order to reduce confusion, those blocks now display the line number. New output: ``` error[E0308]: mismatched types --> ../../src/test/ui/block-result/unexpected-return-on-unit.rs:19:5 | 19 | foo() | ^^^^^ expected (), found usize | = note: expected type `()` found type `usize` help: did you mean to add a semicolon here? | 19 | foo(); | ^ help: possibly return type missing here? | 18 | fn bar() -> usize { | ^^^^^^^^ error: aborting due to previous error(s) ``` Fix #39152.
2 parents 696412d + 697c85a commit d2ebb12

18 files changed

+181
-60
lines changed

src/librustc_errors/emitter.rs

+42-14
Original file line numberDiff line numberDiff line change
@@ -1060,44 +1060,72 @@ impl EmitterWriter {
10601060
-> io::Result<()> {
10611061
use std::borrow::Borrow;
10621062

1063-
let primary_span = suggestion.substitution_spans().next().unwrap();
1063+
let primary_sub = &suggestion.substitution_parts[0];
10641064
if let Some(ref cm) = self.cm {
10651065
let mut buffer = StyledBuffer::new();
10661066

1067-
let lines = cm.span_to_lines(primary_span).unwrap();
1067+
let lines = cm.span_to_lines(primary_sub.span).unwrap();
10681068

10691069
assert!(!lines.lines.is_empty());
10701070

10711071
buffer.append(0, &level.to_string(), Style::Level(level.clone()));
10721072
buffer.append(0, ": ", Style::HeaderMsg);
10731073
self.msg_to_buffer(&mut buffer,
1074-
&[(suggestion.msg.to_owned(), Style::NoStyle)],
1075-
max_line_num_len,
1076-
"suggestion",
1077-
Some(Style::HeaderMsg));
1074+
&[(suggestion.msg.to_owned(), Style::NoStyle)],
1075+
max_line_num_len,
1076+
"suggestion",
1077+
Some(Style::HeaderMsg));
10781078

10791079
let suggestions = suggestion.splice_lines(cm.borrow());
1080-
let mut row_num = 1;
1081-
for complete in suggestions.iter().take(MAX_SUGGESTIONS) {
1082-
1083-
// print the suggestion without any line numbers, but leave
1084-
// space for them. This helps with lining up with previous
1085-
// snippets from the actual error being reported.
1080+
let span_start_pos = cm.lookup_char_pos(primary_sub.span.lo);
1081+
let line_start = span_start_pos.line;
1082+
draw_col_separator_no_space(&mut buffer, 1, max_line_num_len + 1);
1083+
let mut row_num = 2;
1084+
for (&(ref complete, show_underline), ref sub) in suggestions
1085+
.iter().zip(primary_sub.substitutions.iter()).take(MAX_SUGGESTIONS)
1086+
{
1087+
let mut line_pos = 0;
1088+
// Only show underline if there's a single suggestion and it is a single line
10861089
let mut lines = complete.lines();
10871090
for line in lines.by_ref().take(MAX_HIGHLIGHT_LINES) {
1091+
// Print the span column to avoid confusion
1092+
buffer.puts(row_num,
1093+
0,
1094+
&((line_start + line_pos).to_string()),
1095+
Style::LineNumber);
1096+
// print the suggestion
10881097
draw_col_separator(&mut buffer, row_num, max_line_num_len + 1);
10891098
buffer.append(row_num, line, Style::NoStyle);
1099+
line_pos += 1;
10901100
row_num += 1;
1101+
// Only show an underline in the suggestions if the suggestion is not the
1102+
// entirety of the code being shown and the displayed code is not multiline.
1103+
if show_underline {
1104+
draw_col_separator(&mut buffer, row_num, max_line_num_len + 1);
1105+
let sub_len = sub.trim_right().len();
1106+
let underline_start = span_start_pos.col.0;
1107+
let underline_end = span_start_pos.col.0 + sub_len;
1108+
for p in underline_start..underline_end {
1109+
buffer.putc(row_num,
1110+
max_line_num_len + 3 + p,
1111+
'^',
1112+
Style::UnderlinePrimary);
1113+
}
1114+
row_num += 1;
1115+
}
10911116
}
10921117

10931118
// if we elided some lines, add an ellipsis
10941119
if let Some(_) = lines.next() {
1095-
buffer.append(row_num, "...", Style::NoStyle);
1120+
buffer.puts(row_num, max_line_num_len - 1, "...", Style::LineNumber);
1121+
} else if !show_underline {
1122+
draw_col_separator_no_space(&mut buffer, row_num, max_line_num_len + 1);
1123+
row_num += 1;
10961124
}
10971125
}
10981126
if suggestions.len() > MAX_SUGGESTIONS {
10991127
let msg = format!("and {} other candidates", suggestions.len() - MAX_SUGGESTIONS);
1100-
buffer.append(row_num, &msg, Style::NoStyle);
1128+
buffer.puts(row_num, 0, &msg, Style::NoStyle);
11011129
}
11021130
emit_to_destination(&buffer.render(), level, &mut self.dst)?;
11031131
}

src/librustc_errors/lib.rs

+17-6
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,8 @@ impl CodeSuggestion {
114114
self.substitution_parts.iter().map(|sub| sub.span)
115115
}
116116

117-
/// Returns the assembled code suggestions.
118-
pub fn splice_lines(&self, cm: &CodeMapper) -> Vec<String> {
117+
/// Returns the assembled code suggestions and wether they should be shown with an underline.
118+
pub fn splice_lines(&self, cm: &CodeMapper) -> Vec<(String, bool)> {
119119
use syntax_pos::{CharPos, Loc, Pos};
120120

121121
fn push_trailing(buf: &mut String,
@@ -138,7 +138,7 @@ impl CodeSuggestion {
138138
}
139139

140140
if self.substitution_parts.is_empty() {
141-
return vec![String::new()];
141+
return vec![(String::new(), false)];
142142
}
143143

144144
let mut primary_spans: Vec<_> = self.substitution_parts
@@ -175,14 +175,25 @@ impl CodeSuggestion {
175175
prev_hi.col = CharPos::from_usize(0);
176176

177177
let mut prev_line = fm.get_line(lines.lines[0].line_index);
178-
let mut bufs = vec![String::new(); self.substitutions()];
178+
let mut bufs = vec![(String::new(), false); self.substitutions()];
179179

180180
for (sp, substitutes) in primary_spans {
181181
let cur_lo = cm.lookup_char_pos(sp.lo);
182-
for (buf, substitute) in bufs.iter_mut().zip(substitutes) {
182+
for (&mut (ref mut buf, ref mut underline), substitute) in bufs.iter_mut()
183+
.zip(substitutes) {
183184
if prev_hi.line == cur_lo.line {
184185
push_trailing(buf, prev_line.as_ref(), &prev_hi, Some(&cur_lo));
186+
187+
// Only show an underline in the suggestions if the suggestion is not the
188+
// entirety of the code being shown and the displayed code is not multiline.
189+
if prev_line.as_ref().unwrap().trim().len() > 0
190+
&& !substitute.ends_with('\n')
191+
&& substitute.lines().count() == 1
192+
{
193+
*underline = true;
194+
}
185195
} else {
196+
*underline = false;
186197
push_trailing(buf, prev_line.as_ref(), &prev_hi, None);
187198
// push lines between the previous and current span (if any)
188199
for idx in prev_hi.line..(cur_lo.line - 1) {
@@ -200,7 +211,7 @@ impl CodeSuggestion {
200211
prev_hi = cm.lookup_char_pos(sp.hi);
201212
prev_line = fm.get_line(prev_hi.line - 1);
202213
}
203-
for buf in &mut bufs {
214+
for &mut (ref mut buf, _) in &mut bufs {
204215
// if the replacement already ends with a newline, don't print the next line
205216
if !buf.ends_with('\n') {
206217
push_trailing(buf, prev_line.as_ref(), &prev_hi, None);

src/libsyntax/json.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ impl DiagnosticCode {
359359

360360
impl JsonEmitter {
361361
fn render(&self, suggestion: &CodeSuggestion) -> Vec<String> {
362-
suggestion.splice_lines(&*self.cm)
362+
suggestion.splice_lines(&*self.cm).iter().map(|line| line.0.to_owned()).collect()
363363
}
364364
}
365365

src/libsyntax/parse/parser.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -2920,10 +2920,6 @@ impl<'a> Parser<'a> {
29202920
err.cancel();
29212921
let codemap = self.sess.codemap();
29222922
let suggestion_span = lhs_span.to(self.prev_span);
2923-
let suggestion = match codemap.span_to_snippet(suggestion_span) {
2924-
Ok(lstring) => format!("({})", lstring),
2925-
_ => format!("(<expression> as <type>)")
2926-
};
29272923
let warn_message = match codemap.span_to_snippet(self.prev_span) {
29282924
Ok(lstring) => format!("`{}`", lstring),
29292925
_ => "a type".to_string(),
@@ -2934,6 +2930,10 @@ impl<'a> Parser<'a> {
29342930
let mut err = self.sess.span_diagnostic.struct_span_err(sp, &msg);
29352931
err.span_label(sp, "interpreted as generic argument");
29362932
err.span_label(self.span, "not interpreted as comparison");
2933+
let suggestion = match codemap.span_to_snippet(suggestion_span) {
2934+
Ok(lstring) => format!("({})", lstring),
2935+
_ => format!("(<expression> as <type>)")
2936+
};
29372937
err.span_suggestion(suggestion_span,
29382938
"if you want to compare the casted value then write:",
29392939
suggestion);

src/test/ui/block-result/unexpected-return-on-unit.stderr

+6-2
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,13 @@ error[E0308]: mismatched types
77
= note: expected type `()`
88
found type `usize`
99
help: did you mean to add a semicolon here?
10-
| foo();
10+
|
11+
19 | foo();
12+
| ^
1113
help: possibly return type missing here?
12-
| fn bar() -> usize {
14+
|
15+
18 | fn bar() -> usize {
16+
| ^^^^^^^^
1317

1418
error: aborting due to previous error
1519

src/test/ui/issue-22644.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,18 @@ fn main() {
1414

1515
println!("{}", a as usize > b);
1616
println!("{}", a as usize < b);
17-
println!("{}", a as usize < 4);
17+
println!("{}", a
18+
as
19+
usize
20+
<
21+
4);
22+
println!("{}", a
23+
24+
25+
as
26+
27+
28+
usize
29+
<
30+
5);
1831
}

src/test/ui/issue-22644.stderr

+32-8
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,42 @@ error: `<` is interpreted as a start of generic arguments for `usize`, not a com
77
| not interpreted as comparison
88
|
99
help: if you want to compare the casted value then write:
10-
| println!("{}", (a as usize) < b);
10+
|
11+
16 | println!("{}", (a as usize) < b);
12+
| ^^^^^^^^^^^^
1113

1214
error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison
13-
--> $DIR/issue-22644.rs:17:33
15+
--> $DIR/issue-22644.rs:21:20
1416
|
15-
17 | println!("{}", a as usize < 4);
16-
| - ^ interpreted as generic argument
17-
| |
18-
| not interpreted as comparison
17+
20 | <
18+
| - not interpreted as comparison
19+
21 | 4);
20+
| ^ interpreted as generic argument
21+
|
22+
help: if you want to compare the casted value then write:
23+
|
24+
17 | println!("{}", (a
25+
18 | as
26+
19 | usize)
27+
|
28+
29+
error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison
30+
--> $DIR/issue-22644.rs:30:20
31+
|
32+
29 | <
33+
| - not interpreted as comparison
34+
30 | 5);
35+
| ^ interpreted as generic argument
1936
|
2037
help: if you want to compare the casted value then write:
21-
| println!("{}", (a as usize) < 4);
38+
|
39+
22 | println!("{}", (a
40+
23 |
41+
24 |
42+
25 | as
43+
26 |
44+
27 |
45+
...
2246

23-
error: aborting due to 2 previous errors
47+
error: aborting due to 3 previous errors
2448

src/test/ui/resolve/enums-are-namespaced-xc.stderr

+9-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ error[E0425]: cannot find value `A` in module `namespaced_enums`
55
| ^ not found in `namespaced_enums`
66
|
77
help: possible candidate is found in another module, you can import it into scope
8-
| use namespaced_enums::Foo::A;
8+
|
9+
12 | use namespaced_enums::Foo::A;
10+
|
911

1012
error[E0425]: cannot find function `B` in module `namespaced_enums`
1113
--> $DIR/enums-are-namespaced-xc.rs:18:31
@@ -14,7 +16,9 @@ error[E0425]: cannot find function `B` in module `namespaced_enums`
1416
| ^ not found in `namespaced_enums`
1517
|
1618
help: possible candidate is found in another module, you can import it into scope
17-
| use namespaced_enums::Foo::B;
19+
|
20+
12 | use namespaced_enums::Foo::B;
21+
|
1822

1923
error[E0422]: cannot find struct, variant or union type `C` in module `namespaced_enums`
2024
--> $DIR/enums-are-namespaced-xc.rs:21:31
@@ -23,7 +27,9 @@ error[E0422]: cannot find struct, variant or union type `C` in module `namespace
2327
| ^ not found in `namespaced_enums`
2428
|
2529
help: possible candidate is found in another module, you can import it into scope
26-
| use namespaced_enums::Foo::C;
30+
|
31+
12 | use namespaced_enums::Foo::C;
32+
|
2733

2834
error: aborting due to 3 previous errors
2935

src/test/ui/resolve/issue-16058.stderr

+7-3
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@ error[E0574]: expected struct, variant or union type, found enum `Result`
55
| ^^^^^^ not a struct, variant or union type
66
|
77
help: possible better candidates are found in other modules, you can import them into scope
8-
| use std::fmt::Result;
9-
| use std::io::Result;
10-
| use std::thread::Result;
8+
|
9+
12 | use std::fmt::Result;
10+
|
11+
12 | use std::io::Result;
12+
|
13+
12 | use std::thread::Result;
14+
|
1115

1216
error: aborting due to previous error
1317

src/test/ui/resolve/issue-17518.stderr

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ error[E0422]: cannot find struct, variant or union type `E` in this scope
55
| ^ not found in this scope
66
|
77
help: possible candidate is found in another module, you can import it into scope
8-
| use SomeEnum::E;
8+
|
9+
11 | use SomeEnum::E;
10+
|
911

1012
error: aborting due to previous error
1113

src/test/ui/resolve/issue-21221-1.stderr

+19-8
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@ error[E0405]: cannot find trait `Mul` in this scope
55
| ^^^ not found in this scope
66
|
77
help: possible candidates are found in other modules, you can import them into scope
8-
| use mul1::Mul;
9-
| use mul2::Mul;
10-
| use std::ops::Mul;
8+
|
9+
11 | use mul1::Mul;
10+
|
11+
11 | use mul2::Mul;
12+
|
13+
11 | use std::ops::Mul;
14+
|
1115

1216
error[E0412]: cannot find type `Mul` in this scope
1317
--> $DIR/issue-21221-1.rs:72:16
@@ -16,10 +20,15 @@ error[E0412]: cannot find type `Mul` in this scope
1620
| ^^^ not found in this scope
1721
|
1822
help: possible candidates are found in other modules, you can import them into scope
19-
| use mul1::Mul;
20-
| use mul2::Mul;
21-
| use mul3::Mul;
22-
| use mul4::Mul;
23+
|
24+
11 | use mul1::Mul;
25+
|
26+
11 | use mul2::Mul;
27+
|
28+
11 | use mul3::Mul;
29+
|
30+
11 | use mul4::Mul;
31+
|
2332
and 2 other candidates
2433

2534
error[E0405]: cannot find trait `ThisTraitReallyDoesntExistInAnyModuleReally` in this scope
@@ -35,7 +44,9 @@ error[E0405]: cannot find trait `Div` in this scope
3544
| ^^^ not found in this scope
3645
|
3746
help: possible candidate is found in another module, you can import it into scope
38-
| use std::ops::Div;
47+
|
48+
11 | use std::ops::Div;
49+
|
3950

4051
error: cannot continue compilation due to previous error
4152

src/test/ui/resolve/issue-21221-2.stderr

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ error[E0405]: cannot find trait `T` in this scope
55
| ^ not found in this scope
66
|
77
help: possible candidate is found in another module, you can import it into scope
8-
| use foo::bar::T;
8+
|
9+
11 | use foo::bar::T;
10+
|
911

1012
error[E0601]: main function not found
1113

src/test/ui/resolve/issue-21221-3.stderr

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ error[E0405]: cannot find trait `OuterTrait` in this scope
55
| ^^^^^^^^^^ not found in this scope
66
|
77
help: possible candidate is found in another module, you can import it into scope
8-
| use issue_21221_3::outer::OuterTrait;
8+
|
9+
16 | use issue_21221_3::outer::OuterTrait;
10+
|
911

1012
error: cannot continue compilation due to previous error
1113

0 commit comments

Comments
 (0)