Skip to content

Commit 33535af

Browse files
committed
Auto merge of #40851 - oli-obk:multisugg, r=jonathandturner
Minimize single span suggestions into a label changes ``` 14 | println!("☃{}", tup[0]); | ^^^^^^ | help: to access tuple elements, use tuple indexing syntax as shown | println!("☃{}", tup.0); ``` into ``` 14 | println!("☃{}", tup[0]); | ^^^^^^ to access tuple elements, use `tup.0` ``` Also makes suggestions explicit in the backend in preparation of adding multiple suggestions to a single diagnostic. Currently that's already possible, but results in a full help message + modified code snippet per suggestion, and has no rate limit (might show 100+ suggestions).
2 parents de4bdd2 + d64af4a commit 33535af

20 files changed

+155
-92
lines changed

src/librustc_errors/diagnostic.rs

+10-14
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
use CodeSuggestion;
1212
use Level;
1313
use RenderSpan;
14-
use RenderSpan::Suggestion;
1514
use std::fmt;
1615
use syntax_pos::{MultiSpan, Span};
1716
use snippet::Style;
@@ -24,6 +23,7 @@ pub struct Diagnostic {
2423
pub code: Option<String>,
2524
pub span: MultiSpan,
2625
pub children: Vec<SubDiagnostic>,
26+
pub suggestion: Option<CodeSuggestion>,
2727
}
2828

2929
/// For example a note attached to an error.
@@ -87,6 +87,7 @@ impl Diagnostic {
8787
code: code,
8888
span: MultiSpan::new(),
8989
children: vec![],
90+
suggestion: None,
9091
}
9192
}
9293

@@ -202,19 +203,14 @@ impl Diagnostic {
202203

203204
/// Prints out a message with a suggested edit of the code.
204205
///
205-
/// See `diagnostic::RenderSpan::Suggestion` for more information.
206-
pub fn span_suggestion<S: Into<MultiSpan>>(&mut self,
207-
sp: S,
208-
msg: &str,
209-
suggestion: String)
210-
-> &mut Self {
211-
self.sub(Level::Help,
212-
msg,
213-
MultiSpan::new(),
214-
Some(Suggestion(CodeSuggestion {
215-
msp: sp.into(),
216-
substitutes: vec![suggestion],
217-
})));
206+
/// See `diagnostic::CodeSuggestion` for more information.
207+
pub fn span_suggestion(&mut self, sp: Span, msg: &str, suggestion: String) -> &mut Self {
208+
assert!(self.suggestion.is_none());
209+
self.suggestion = Some(CodeSuggestion {
210+
msp: sp.into(),
211+
substitutes: vec![suggestion],
212+
msg: msg.to_owned(),
213+
});
218214
self
219215
}
220216

src/librustc_errors/diagnostic_builder.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -141,11 +141,11 @@ impl<'a> DiagnosticBuilder<'a> {
141141
sp: S,
142142
msg: &str)
143143
-> &mut Self);
144-
forward!(pub fn span_suggestion<S: Into<MultiSpan>>(&mut self,
145-
sp: S,
146-
msg: &str,
147-
suggestion: String)
148-
-> &mut Self);
144+
forward!(pub fn span_suggestion(&mut self,
145+
sp: Span,
146+
msg: &str,
147+
suggestion: String)
148+
-> &mut Self);
149149
forward!(pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self);
150150
forward!(pub fn code(&mut self, s: String) -> &mut Self);
151151

src/librustc_errors/emitter.rs

+23-4
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,27 @@ impl Emitter for EmitterWriter {
3434
fn emit(&mut self, db: &DiagnosticBuilder) {
3535
let mut primary_span = db.span.clone();
3636
let mut children = db.children.clone();
37+
38+
if let Some(sugg) = db.suggestion.clone() {
39+
assert_eq!(sugg.msp.primary_spans().len(), sugg.substitutes.len());
40+
// don't display multispans as labels
41+
if sugg.substitutes.len() == 1 &&
42+
// don't display long messages as labels
43+
sugg.msg.split_whitespace().count() < 10 &&
44+
// don't display multiline suggestions as labels
45+
sugg.substitutes[0].find('\n').is_none() {
46+
let msg = format!("help: {} `{}`", sugg.msg, sugg.substitutes[0]);
47+
primary_span.push_span_label(sugg.msp.primary_spans()[0], msg);
48+
} else {
49+
children.push(SubDiagnostic {
50+
level: Level::Help,
51+
message: Vec::new(),
52+
span: MultiSpan::new(),
53+
render_span: Some(Suggestion(sugg)),
54+
});
55+
}
56+
}
57+
3758
self.fix_multispans_in_std_macros(&mut primary_span, &mut children);
3859
self.emit_messages_default(&db.level,
3960
&db.styled_message(),
@@ -756,7 +777,7 @@ impl EmitterWriter {
756777
/// displayed, keeping the provided highlighting.
757778
fn msg_to_buffer(&self,
758779
buffer: &mut StyledBuffer,
759-
msg: &Vec<(String, Style)>,
780+
msg: &[(String, Style)],
760781
padding: usize,
761782
label: &str,
762783
override_style: Option<Style>) {
@@ -1022,7 +1043,6 @@ impl EmitterWriter {
10221043
fn emit_suggestion_default(&mut self,
10231044
suggestion: &CodeSuggestion,
10241045
level: &Level,
1025-
msg: &Vec<(String, Style)>,
10261046
max_line_num_len: usize)
10271047
-> io::Result<()> {
10281048
use std::borrow::Borrow;
@@ -1034,7 +1054,7 @@ impl EmitterWriter {
10341054
buffer.append(0, &level.to_string(), Style::Level(level.clone()));
10351055
buffer.append(0, ": ", Style::HeaderMsg);
10361056
self.msg_to_buffer(&mut buffer,
1037-
msg,
1057+
&[(suggestion.msg.to_owned(), Style::NoStyle)],
10381058
max_line_num_len,
10391059
"suggestion",
10401060
Some(Style::HeaderMsg));
@@ -1099,7 +1119,6 @@ impl EmitterWriter {
10991119
Some(Suggestion(ref cs)) => {
11001120
match self.emit_suggestion_default(cs,
11011121
&child.level,
1102-
&child.styled_message(),
11031122
max_line_num_len) {
11041123
Err(e) => panic!("failed to emit error: {}", e),
11051124
_ => ()

src/librustc_errors/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ pub enum RenderSpan {
6767
pub struct CodeSuggestion {
6868
pub msp: MultiSpan,
6969
pub substitutes: Vec<String>,
70+
pub msg: String,
7071
}
7172

7273
pub trait CodeMapper {

src/librustc_typeck/check/mod.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -3883,9 +3883,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
38833883
let snip = tcx.sess.codemap().span_to_snippet(base.span);
38843884
if let Ok(snip) = snip {
38853885
err.span_suggestion(expr.span,
3886-
"to access tuple elements, \
3887-
use tuple indexing syntax \
3888-
as shown",
3886+
"to access tuple elements, use",
38893887
format!("{}.{}", snip, i));
38903888
needs_note = false;
38913889
}

src/librustc_typeck/check/op.rs

+9-11
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
197197
// error types are considered "builtin"
198198
if !lhs_ty.references_error() {
199199
if let IsAssign::Yes = is_assign {
200-
struct_span_err!(self.tcx.sess, lhs_expr.span, E0368,
200+
struct_span_err!(self.tcx.sess, expr.span, E0368,
201201
"binary assignment operation `{}=` \
202202
cannot be applied to type `{}`",
203203
op.node.as_str(),
@@ -207,7 +207,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
207207
op.node.as_str(), lhs_ty))
208208
.emit();
209209
} else {
210-
let mut err = struct_span_err!(self.tcx.sess, lhs_expr.span, E0369,
210+
let mut err = struct_span_err!(self.tcx.sess, expr.span, E0369,
211211
"binary operation `{}` cannot be applied to type `{}`",
212212
op.node.as_str(),
213213
lhs_ty);
@@ -245,7 +245,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
245245
if let Some(missing_trait) = missing_trait {
246246
if missing_trait == "std::ops::Add" &&
247247
self.check_str_addition(expr, lhs_expr, lhs_ty,
248-
rhs_expr, rhs_ty, &mut err) {
248+
rhs_ty, &mut err) {
249249
// This has nothing here because it means we did string
250250
// concatenation (e.g. "Hello " + "World!"). This means
251251
// we don't want the note in the else clause to be emitted
@@ -269,7 +269,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
269269
expr: &'gcx hir::Expr,
270270
lhs_expr: &'gcx hir::Expr,
271271
lhs_ty: Ty<'tcx>,
272-
rhs_expr: &'gcx hir::Expr,
273272
rhs_ty: Ty<'tcx>,
274273
mut err: &mut errors::DiagnosticBuilder) -> bool {
275274
// If this function returns true it means a note was printed, so we don't need
@@ -278,17 +277,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
278277
if let TyRef(_, l_ty) = lhs_ty.sty {
279278
if let TyRef(_, r_ty) = rhs_ty.sty {
280279
if l_ty.ty.sty == TyStr && r_ty.ty.sty == TyStr {
281-
err.note("`+` can't be used to concatenate two `&str` strings");
280+
err.span_label(expr.span,
281+
&"`+` can't be used to concatenate two `&str` strings");
282282
let codemap = self.tcx.sess.codemap();
283283
let suggestion =
284-
match (codemap.span_to_snippet(lhs_expr.span),
285-
codemap.span_to_snippet(rhs_expr.span)) {
286-
(Ok(lstring), Ok(rstring)) =>
287-
format!("{}.to_owned() + {}", lstring, rstring),
284+
match codemap.span_to_snippet(lhs_expr.span) {
285+
Ok(lstring) => format!("{}.to_owned()", lstring),
288286
_ => format!("<expression>")
289287
};
290-
err.span_suggestion(expr.span,
291-
&format!("to_owned() can be used to create an owned `String` \
288+
err.span_suggestion(lhs_expr.span,
289+
&format!("`to_owned()` can be used to create an owned `String` \
292290
from a string reference. String concatenation \
293291
appends the string on the right to the string \
294292
on the left and may require reallocation. This \

src/librustc_typeck/diagnostics.rs

+7
Original file line numberDiff line numberDiff line change
@@ -3181,6 +3181,13 @@ x << 2; // ok!
31813181
31823182
It is also possible to overload most operators for your own type by
31833183
implementing traits from `std::ops`.
3184+
3185+
String concatenation appends the string on the right to the string on the
3186+
left and may require reallocation. This requires ownership of the string
3187+
on the left. If something should be added to a string literal, move the
3188+
literal to the heap by allocating it with `to_owned()` like in
3189+
`"Your text".to_owned()`.
3190+
31843191
"##,
31853192

31863193
E0370: r##"

src/libsyntax/json.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@
2222
use codemap::{CodeMap, FilePathMapping};
2323
use syntax_pos::{self, MacroBacktrace, Span, SpanLabel, MultiSpan};
2424
use errors::registry::Registry;
25-
use errors::{DiagnosticBuilder, SubDiagnostic, RenderSpan, CodeSuggestion, CodeMapper};
25+
use errors::{Level, DiagnosticBuilder, SubDiagnostic, RenderSpan, CodeSuggestion, CodeMapper};
2626
use errors::emitter::Emitter;
27+
use errors::snippet::Style;
2728

2829
use std::rc::Rc;
2930
use std::io::{self, Write};
@@ -153,12 +154,21 @@ impl Diagnostic {
153154
fn from_diagnostic_builder(db: &DiagnosticBuilder,
154155
je: &JsonEmitter)
155156
-> Diagnostic {
157+
let sugg = db.suggestion.as_ref().map(|sugg| {
158+
SubDiagnostic {
159+
level: Level::Help,
160+
message: vec![(sugg.msg.clone(), Style::NoStyle)],
161+
span: MultiSpan::new(),
162+
render_span: Some(RenderSpan::Suggestion(sugg.clone())),
163+
}
164+
});
165+
let sugg = sugg.as_ref();
156166
Diagnostic {
157167
message: db.message(),
158168
code: DiagnosticCode::map_opt_string(db.code.clone(), je),
159169
level: db.level.to_str(),
160170
spans: DiagnosticSpan::from_multispan(&db.span, je),
161-
children: db.children.iter().map(|c| {
171+
children: db.children.iter().chain(sugg).map(|c| {
162172
Diagnostic::from_sub_diagnostic(c, je)
163173
}).collect(),
164174
rendered: None,

src/libsyntax/parse/parser.rs

+7-10
Original file line numberDiff line numberDiff line change
@@ -1492,9 +1492,8 @@ impl<'a> Parser<'a> {
14921492
let bounds = self.parse_ty_param_bounds()?;
14931493
let sum_span = ty.span.to(self.prev_span);
14941494

1495-
let mut err = struct_span_err!(self.sess.span_diagnostic, ty.span, E0178,
1495+
let mut err = struct_span_err!(self.sess.span_diagnostic, sum_span, E0178,
14961496
"expected a path on the left-hand side of `+`, not `{}`", pprust::ty_to_string(&ty));
1497-
err.span_label(ty.span, &format!("expected a path"));
14981497

14991498
match ty.node {
15001499
TyKind::Rptr(ref lifetime, ref mut_ty) => {
@@ -1513,9 +1512,11 @@ impl<'a> Parser<'a> {
15131512
err.span_suggestion(sum_span, "try adding parentheses:", sum_with_parens);
15141513
}
15151514
TyKind::Ptr(..) | TyKind::BareFn(..) => {
1516-
help!(&mut err, "perhaps you forgot parentheses?");
1515+
err.span_label(sum_span, &"perhaps you forgot parentheses?");
15171516
}
1518-
_ => {}
1517+
_ => {
1518+
err.span_label(sum_span, &"expected a path");
1519+
},
15191520
}
15201521
err.emit();
15211522
Ok(())
@@ -5131,7 +5132,6 @@ impl<'a> Parser<'a> {
51315132
}
51325133

51335134
if self.check(&token::OpenDelim(token::Paren)) {
5134-
let start_span = self.span;
51355135
// We don't `self.bump()` the `(` yet because this might be a struct definition where
51365136
// `()` or a tuple might be allowed. For example, `struct Struct(pub (), pub (usize));`.
51375137
// Because of this, we only `bump` the `(` if we're assured it is appropriate to do so
@@ -5170,12 +5170,9 @@ impl<'a> Parser<'a> {
51705170
`pub(in path::to::module)`: visible only on the specified path"##;
51715171
let path = self.parse_path(PathStyle::Mod)?;
51725172
let path_span = self.prev_span;
5173-
let help_msg = format!("to make this visible only to module `{}`, add `in` before \
5174-
the path:",
5175-
path);
5173+
let help_msg = format!("make this visible only to module `{}` with `in`:", path);
51765174
self.expect(&token::CloseDelim(token::Paren))?; // `)`
5177-
let sp = start_span.to(self.prev_span);
5178-
let mut err = self.span_fatal_help(sp, &msg, &suggestion);
5175+
let mut err = self.span_fatal_help(path_span, &msg, &suggestion);
51795176
err.span_suggestion(path_span, &help_msg, format!("in {}", path));
51805177
err.emit(); // emit diagnostic, but continue with public visibility
51815178
}

src/test/compile-fail/issue-27842.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ fn main() {
1313
// the case where we show a suggestion
1414
let _ = tup[0];
1515
//~^ ERROR cannot index a value of type
16-
//~| HELP to access tuple elements, use tuple indexing syntax as shown
16+
//~| HELP to access tuple elements, use
1717
//~| SUGGESTION let _ = tup.0
1818

1919
// the case where we show just a general hint

src/test/parse-fail/trait-object-polytrait-priority.rs

-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ trait Trait<'a> {}
1313
fn main() {
1414
let _: &for<'a> Trait<'a> + 'static;
1515
//~^ ERROR expected a path on the left-hand side of `+`, not `& for<'a>Trait<'a>`
16-
//~| NOTE expected a path
1716
//~| HELP try adding parentheses
1817
//~| SUGGESTION &( for<'a>Trait<'a> + 'static)
1918
}

src/test/compile-fail/E0178.rs renamed to src/test/ui/did_you_mean/E0178.rs

-8
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,9 @@ trait Foo {}
1212

1313
struct Bar<'a> {
1414
w: &'a Foo + Copy,
15-
//~^ ERROR E0178
16-
//~| NOTE expected a path
1715
x: &'a Foo + 'a,
18-
//~^ ERROR E0178
19-
//~| NOTE expected a path
2016
y: &'a mut Foo + 'a,
21-
//~^ ERROR E0178
22-
//~| NOTE expected a path
2317
z: fn() -> Foo + 'a,
24-
//~^ ERROR E0178
25-
//~| NOTE expected a path
2618
}
2719

2820
fn main() {

src/test/ui/did_you_mean/E0178.stderr

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
error[E0178]: expected a path on the left-hand side of `+`, not `&'a Foo`
2+
--> $DIR/E0178.rs:14:8
3+
|
4+
14 | w: &'a Foo + Copy,
5+
| ^^^^^^^^^^^^^^ help: try adding parentheses: `&'a (Foo + Copy)`
6+
7+
error[E0178]: expected a path on the left-hand side of `+`, not `&'a Foo`
8+
--> $DIR/E0178.rs:15:8
9+
|
10+
15 | x: &'a Foo + 'a,
11+
| ^^^^^^^^^^^^ help: try adding parentheses: `&'a (Foo + 'a)`
12+
13+
error[E0178]: expected a path on the left-hand side of `+`, not `&'a mut Foo`
14+
--> $DIR/E0178.rs:16:8
15+
|
16+
16 | y: &'a mut Foo + 'a,
17+
| ^^^^^^^^^^^^^^^^ help: try adding parentheses: `&'a mut (Foo + 'a)`
18+
19+
error[E0178]: expected a path on the left-hand side of `+`, not `fn() -> Foo`
20+
--> $DIR/E0178.rs:17:8
21+
|
22+
17 | z: fn() -> Foo + 'a,
23+
| ^^^^^^^^^^^^^^^^ perhaps you forgot parentheses?
24+
25+
error: aborting due to 4 previous errors
26+

src/test/compile-fail/trait-object-reference-without-parens-suggestion.rs renamed to src/test/ui/did_you_mean/trait-object-reference-without-parens-suggestion.rs

-7
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,5 @@
1010

1111
fn main() {
1212
let _: &Copy + 'static;
13-
//~^ ERROR expected a path
14-
//~| HELP try adding parentheses
15-
//~| SUGGESTION let _: &(Copy + 'static);
16-
//~| ERROR the trait `std::marker::Copy` cannot be made into an object
1713
let _: &'static Copy + 'static;
18-
//~^ ERROR expected a path
19-
//~| HELP try adding parentheses
20-
//~| SUGGESTION let _: &'static (Copy + 'static);
2114
}

0 commit comments

Comments
 (0)