Skip to content

Minimize single span suggestions into a label #40851

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

Merged
merged 7 commits into from
May 2, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 10 additions & 14 deletions src/librustc_errors/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
use CodeSuggestion;
use Level;
use RenderSpan;
use RenderSpan::Suggestion;
Copy link
Member

Choose a reason for hiding this comment

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

Can we remove RenderSpan::Suggestion now or is it still used elsewhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I left it in for keeping the diff small. I convert to it in the emitter and json impls. Wanted to get feedback on a readable diff first

use std::fmt;
use syntax_pos::{MultiSpan, Span};
use snippet::Style;
Expand All @@ -24,6 +23,7 @@ pub struct Diagnostic {
pub code: Option<String>,
pub span: MultiSpan,
pub children: Vec<SubDiagnostic>,
pub suggestion: Option<CodeSuggestion>,
}

/// For example a note attached to an error.
Expand Down Expand Up @@ -87,6 +87,7 @@ impl Diagnostic {
code: code,
span: MultiSpan::new(),
children: vec![],
suggestion: None,
}
}

Expand Down Expand Up @@ -202,19 +203,14 @@ impl Diagnostic {

/// Prints out a message with a suggested edit of the code.
///
/// See `diagnostic::RenderSpan::Suggestion` for more information.
pub fn span_suggestion<S: Into<MultiSpan>>(&mut self,
sp: S,
msg: &str,
suggestion: String)
-> &mut Self {
self.sub(Level::Help,
msg,
MultiSpan::new(),
Some(Suggestion(CodeSuggestion {
msp: sp.into(),
substitutes: vec![suggestion],
})));
/// See `diagnostic::CodeSuggestion` for more information.
pub fn span_suggestion(&mut self, sp: Span, msg: &str, suggestion: String) -> &mut Self {
Copy link
Member

Choose a reason for hiding this comment

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

I think it is worth keeping the Into<MultiSpan> stuff for consistency with the other methods, unless there is a specific reason to remove it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The reason is that using a multispan will panic in the original emitter code

assert!(self.suggestion.is_none());
Copy link
Member

Choose a reason for hiding this comment

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

It surprises me that we never have multiple suggestions, but I guess part of your plan is to do this properly in the long run?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes

self.suggestion = Some(CodeSuggestion {
msp: sp.into(),
substitutes: vec![suggestion],
msg: msg.to_owned(),
});
self
}

Expand Down
10 changes: 5 additions & 5 deletions src/librustc_errors/diagnostic_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,11 +141,11 @@ impl<'a> DiagnosticBuilder<'a> {
sp: S,
msg: &str)
-> &mut Self);
forward!(pub fn span_suggestion<S: Into<MultiSpan>>(&mut self,
sp: S,
msg: &str,
suggestion: String)
-> &mut Self);
forward!(pub fn span_suggestion(&mut self,
sp: Span,
msg: &str,
suggestion: String)
-> &mut Self);
forward!(pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self);
forward!(pub fn code(&mut self, s: String) -> &mut Self);

Expand Down
27 changes: 23 additions & 4 deletions src/librustc_errors/emitter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,27 @@ impl Emitter for EmitterWriter {
fn emit(&mut self, db: &DiagnosticBuilder) {
let mut primary_span = db.span.clone();
let mut children = db.children.clone();

if let Some(sugg) = db.suggestion.clone() {
assert_eq!(sugg.msp.primary_spans().len(), sugg.substitutes.len());
// don't display multispans as labels
if sugg.substitutes.len() == 1 &&
// don't display long messages as labels
sugg.msg.split_whitespace().count() < 10 &&
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The rules about message length ate defined here. Currently only single line messages with fewer than 10 words are accepted

// don't display multiline suggestions as labels
sugg.substitutes[0].find('\n').is_none() {
let msg = format!("help: {} `{}`", sugg.msg, sugg.substitutes[0]);
primary_span.push_span_label(sugg.msp.primary_spans()[0], msg);
} else {
children.push(SubDiagnostic {
level: Level::Help,
message: Vec::new(),
span: MultiSpan::new(),
render_span: Some(Suggestion(sugg)),
});
}
}

self.fix_multispans_in_std_macros(&mut primary_span, &mut children);
self.emit_messages_default(&db.level,
&db.styled_message(),
Expand Down Expand Up @@ -756,7 +777,7 @@ impl EmitterWriter {
/// displayed, keeping the provided highlighting.
fn msg_to_buffer(&self,
buffer: &mut StyledBuffer,
msg: &Vec<(String, Style)>,
msg: &[(String, Style)],
padding: usize,
label: &str,
override_style: Option<Style>) {
Expand Down Expand Up @@ -1022,7 +1043,6 @@ impl EmitterWriter {
fn emit_suggestion_default(&mut self,
suggestion: &CodeSuggestion,
level: &Level,
msg: &Vec<(String, Style)>,
max_line_num_len: usize)
-> io::Result<()> {
use std::borrow::Borrow;
Expand All @@ -1034,7 +1054,7 @@ impl EmitterWriter {
buffer.append(0, &level.to_string(), Style::Level(level.clone()));
buffer.append(0, ": ", Style::HeaderMsg);
self.msg_to_buffer(&mut buffer,
msg,
&[(suggestion.msg.to_owned(), Style::NoStyle)],
max_line_num_len,
"suggestion",
Some(Style::HeaderMsg));
Expand Down Expand Up @@ -1099,7 +1119,6 @@ impl EmitterWriter {
Some(Suggestion(ref cs)) => {
match self.emit_suggestion_default(cs,
&child.level,
&child.styled_message(),
max_line_num_len) {
Err(e) => panic!("failed to emit error: {}", e),
_ => ()
Expand Down
1 change: 1 addition & 0 deletions src/librustc_errors/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ pub enum RenderSpan {
pub struct CodeSuggestion {
pub msp: MultiSpan,
pub substitutes: Vec<String>,
pub msg: String,
}

pub trait CodeMapper {
Expand Down
4 changes: 1 addition & 3 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3804,9 +3804,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let snip = tcx.sess.codemap().span_to_snippet(base.span);
if let Ok(snip) = snip {
err.span_suggestion(expr.span,
"to access tuple elements, \
use tuple indexing syntax \
as shown",
"to access tuple elements, use",
format!("{}.{}", snip, i));
needs_note = false;
}
Expand Down
20 changes: 9 additions & 11 deletions src/librustc_typeck/check/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// error types are considered "builtin"
if !lhs_ty.references_error() {
if let IsAssign::Yes = is_assign {
struct_span_err!(self.tcx.sess, lhs_expr.span, E0368,
struct_span_err!(self.tcx.sess, expr.span, E0368,
"binary assignment operation `{}=` \
cannot be applied to type `{}`",
op.node.as_str(),
Expand All @@ -207,7 +207,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
op.node.as_str(), lhs_ty))
.emit();
} else {
let mut err = struct_span_err!(self.tcx.sess, lhs_expr.span, E0369,
let mut err = struct_span_err!(self.tcx.sess, expr.span, E0369,
"binary operation `{}` cannot be applied to type `{}`",
op.node.as_str(),
lhs_ty);
Expand Down Expand Up @@ -245,7 +245,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
if let Some(missing_trait) = missing_trait {
if missing_trait == "std::ops::Add" &&
self.check_str_addition(expr, lhs_expr, lhs_ty,
rhs_expr, rhs_ty, &mut err) {
rhs_ty, &mut err) {
// This has nothing here because it means we did string
// concatenation (e.g. "Hello " + "World!"). This means
// we don't want the note in the else clause to be emitted
Expand All @@ -269,7 +269,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
expr: &'gcx hir::Expr,
lhs_expr: &'gcx hir::Expr,
lhs_ty: Ty<'tcx>,
rhs_expr: &'gcx hir::Expr,
rhs_ty: Ty<'tcx>,
mut err: &mut errors::DiagnosticBuilder) -> bool {
// If this function returns true it means a note was printed, so we don't need
Expand All @@ -278,17 +277,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
if let TyRef(_, l_ty) = lhs_ty.sty {
if let TyRef(_, r_ty) = rhs_ty.sty {
if l_ty.ty.sty == TyStr && r_ty.ty.sty == TyStr {
err.note("`+` can't be used to concatenate two `&str` strings");
err.span_label(expr.span,
&"`+` can't be used to concatenate two `&str` strings");
let codemap = self.tcx.sess.codemap();
let suggestion =
match (codemap.span_to_snippet(lhs_expr.span),
codemap.span_to_snippet(rhs_expr.span)) {
(Ok(lstring), Ok(rstring)) =>
format!("{}.to_owned() + {}", lstring, rstring),
match codemap.span_to_snippet(lhs_expr.span) {
Ok(lstring) => format!("{}.to_owned()", lstring),
_ => format!("<expression>")
};
err.span_suggestion(expr.span,
&format!("to_owned() can be used to create an owned `String` \
err.span_suggestion(lhs_expr.span,
&format!("`to_owned()` can be used to create an owned `String` \
from a string reference. String concatenation \
appends the string on the right to the string \
on the left and may require reallocation. This \
Expand Down
7 changes: 7 additions & 0 deletions src/librustc_typeck/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3181,6 +3181,13 @@ x << 2; // ok!

It is also possible to overload most operators for your own type by
implementing traits from `std::ops`.

String concatenation appends the string on the right to the string on the
left and may require reallocation. This requires ownership of the string
on the left. If something should be added to a string literal, move the
literal to the heap by allocating it with `to_owned()` like in
`"Your text".to_owned()`.

"##,

E0370: r##"
Expand Down
14 changes: 12 additions & 2 deletions src/libsyntax/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@
use codemap::CodeMap;
use syntax_pos::{self, MacroBacktrace, Span, SpanLabel, MultiSpan};
use errors::registry::Registry;
use errors::{DiagnosticBuilder, SubDiagnostic, RenderSpan, CodeSuggestion, CodeMapper};
use errors::{Level, DiagnosticBuilder, SubDiagnostic, RenderSpan, CodeSuggestion, CodeMapper};
use errors::emitter::Emitter;
use errors::snippet::Style;

use std::rc::Rc;
use std::io::{self, Write};
Expand Down Expand Up @@ -152,12 +153,21 @@ impl Diagnostic {
fn from_diagnostic_builder(db: &DiagnosticBuilder,
je: &JsonEmitter)
-> Diagnostic {
let sugg = db.suggestion.as_ref().map(|sugg| {
SubDiagnostic {
level: Level::Help,
message: vec![(sugg.msg.clone(), Style::NoStyle)],
span: MultiSpan::new(),
render_span: Some(RenderSpan::Suggestion(sugg.clone())),
}
});
let sugg = sugg.as_ref();
Diagnostic {
message: db.message(),
code: DiagnosticCode::map_opt_string(db.code.clone(), je),
level: db.level.to_str(),
spans: DiagnosticSpan::from_multispan(&db.span, je),
children: db.children.iter().map(|c| {
children: db.children.iter().chain(sugg).map(|c| {
Diagnostic::from_sub_diagnostic(c, je)
}).collect(),
rendered: None,
Expand Down
17 changes: 7 additions & 10 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1490,9 +1490,8 @@ impl<'a> Parser<'a> {
let bounds = self.parse_ty_param_bounds()?;
let sum_span = ty.span.to(self.prev_span);

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

match ty.node {
TyKind::Rptr(ref lifetime, ref mut_ty) => {
Expand All @@ -1511,9 +1510,11 @@ impl<'a> Parser<'a> {
err.span_suggestion(sum_span, "try adding parentheses:", sum_with_parens);
}
TyKind::Ptr(..) | TyKind::BareFn(..) => {
help!(&mut err, "perhaps you forgot parentheses?");
err.span_label(sum_span, &"perhaps you forgot parentheses?");
}
_ => {}
_ => {
err.span_label(sum_span, &"expected a path");
},
}
err.emit();
Ok(())
Expand Down Expand Up @@ -5122,7 +5123,6 @@ impl<'a> Parser<'a> {
}

if self.check(&token::OpenDelim(token::Paren)) {
let start_span = self.span;
// We don't `self.bump()` the `(` yet because this might be a struct definition where
// `()` or a tuple might be allowed. For example, `struct Struct(pub (), pub (usize));`.
// Because of this, we only `bump` the `(` if we're assured it is appropriate to do so
Expand Down Expand Up @@ -5161,12 +5161,9 @@ impl<'a> Parser<'a> {
`pub(in path::to::module)`: visible only on the specified path"##;
let path = self.parse_path(PathStyle::Mod)?;
let path_span = self.prev_span;
let help_msg = format!("to make this visible only to module `{}`, add `in` before \
the path:",
path);
let help_msg = format!("make this visible only to module `{}` with `in`:", path);
self.expect(&token::CloseDelim(token::Paren))?; // `)`
let sp = start_span.to(self.prev_span);
let mut err = self.span_fatal_help(sp, &msg, &suggestion);
let mut err = self.span_fatal_help(path_span, &msg, &suggestion);
err.span_suggestion(path_span, &help_msg, format!("in {}", path));
err.emit(); // emit diagnostic, but continue with public visibility
}
Expand Down
2 changes: 1 addition & 1 deletion src/test/compile-fail/issue-27842.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ fn main() {
// the case where we show a suggestion
let _ = tup[0];
//~^ ERROR cannot index a value of type
//~| HELP to access tuple elements, use tuple indexing syntax as shown
//~| HELP to access tuple elements, use
//~| SUGGESTION let _ = tup.0

// the case where we show just a general hint
Expand Down
1 change: 0 additions & 1 deletion src/test/parse-fail/trait-object-polytrait-priority.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ trait Trait<'a> {}
fn main() {
let _: &for<'a> Trait<'a> + 'static;
//~^ ERROR expected a path on the left-hand side of `+`, not `& for<'a>Trait<'a>`
//~| NOTE expected a path
//~| HELP try adding parentheses
//~| SUGGESTION &( for<'a>Trait<'a> + 'static)
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,9 @@ trait Foo {}

struct Bar<'a> {
w: &'a Foo + Copy,
//~^ ERROR E0178
//~| NOTE expected a path
x: &'a Foo + 'a,
//~^ ERROR E0178
//~| NOTE expected a path
y: &'a mut Foo + 'a,
//~^ ERROR E0178
//~| NOTE expected a path
z: fn() -> Foo + 'a,
//~^ ERROR E0178
//~| NOTE expected a path
}

fn main() {
Expand Down
26 changes: 26 additions & 0 deletions src/test/ui/did_you_mean/E0178.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
error[E0178]: expected a path on the left-hand side of `+`, not `&'a Foo`
--> $DIR/E0178.rs:14:8
|
14 | w: &'a Foo + Copy,
| ^^^^^^^^^^^^^^ help: try adding parentheses: `&'a (Foo + Copy)`

error[E0178]: expected a path on the left-hand side of `+`, not `&'a Foo`
--> $DIR/E0178.rs:15:8
|
15 | x: &'a Foo + 'a,
| ^^^^^^^^^^^^ help: try adding parentheses: `&'a (Foo + 'a)`

error[E0178]: expected a path on the left-hand side of `+`, not `&'a mut Foo`
--> $DIR/E0178.rs:16:8
|
16 | y: &'a mut Foo + 'a,
| ^^^^^^^^^^^^^^^^ help: try adding parentheses: `&'a mut (Foo + 'a)`

error[E0178]: expected a path on the left-hand side of `+`, not `fn() -> Foo`
--> $DIR/E0178.rs:17:8
|
17 | z: fn() -> Foo + 'a,
| ^^^^^^^^^^^^^^^^ perhaps you forgot parentheses?

error: aborting due to 4 previous errors

Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,5 @@

fn main() {
let _: &Copy + 'static;
//~^ ERROR expected a path
//~| HELP try adding parentheses
//~| SUGGESTION let _: &(Copy + 'static);
//~| ERROR the trait `std::marker::Copy` cannot be made into an object
let _: &'static Copy + 'static;
//~^ ERROR expected a path
//~| HELP try adding parentheses
//~| SUGGESTION let _: &'static (Copy + 'static);
}
Loading