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

Distinguish guesses from suggestions #39458

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 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
12 changes: 6 additions & 6 deletions src/librustc_borrowck/borrowck/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1008,12 +1008,12 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
.span_label(err.span,
&format!("may outlive borrowed value {}",
cmt_path_or_string))
.span_suggestion(err.span,
&format!("to force the closure to take ownership of {} \
(and any other referenced variables), \
use the `move` keyword, as shown:",
cmt_path_or_string),
suggestion)
.guess(err.span,
&format!("to force the closure to take ownership of {} \
(and any other referenced variables), \
use the `move` keyword, as shown:",
cmt_path_or_string),
suggestion)
.emit();
}

Expand Down
32 changes: 25 additions & 7 deletions src/librustc_errors/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
use CodeSuggestion;
use Level;
use RenderSpan;
use RenderSpan::Suggestion;
use std::fmt;
use RenderSpan::{Suggestion, Guesses};
use std::{fmt, iter};
use syntax_pos::{MultiSpan, Span};
use snippet::Style;

Expand Down Expand Up @@ -155,11 +155,7 @@ 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 {
pub fn span_suggestion(&mut self, sp: Span, msg: &str, suggestion: String) -> &mut Self {
self.sub(Level::Help,
msg,
MultiSpan::new(),
Expand All @@ -170,6 +166,28 @@ impl Diagnostic {
self
}

/// Prints out a message with one or multiple suggested edits of the
/// code, which may break the code, require manual intervention
/// or be plain out wrong, but might possibly be the correct solution.
///
/// See `diagnostic::RenderSpan::Guesses` for more information.
pub fn guesses<I>(&mut self, sp: Span, msg: &str, guesses: I) -> &mut Self
where I: IntoIterator<Item = String>
{
self.sub(Level::Help,
msg,
MultiSpan::new(),
Some(Guesses(guesses.into_iter().map(|guess| CodeSuggestion {
msp: sp.into(),
substitutes: vec![guess],
}).collect())));
self
}

pub fn guess(&mut self, sp: Span, msg: &str, guess: 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 thinks span_guess is a better name, because all other functions taking a span have span in the name. ( for exanple span_suggestion )

self.guesses(sp, msg, iter::once(guess))
}

pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
self.span = sp.into();
self
Expand Down
18 changes: 13 additions & 5 deletions src/librustc_errors/diagnostic_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,20 +139,28 @@ 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);
forward!(pub fn guess(&mut self, sp: Span, msg: &str, guess: String) -> &mut Self);

/// Convenience function for internal use, clients should use one of the
/// struct_* methods on Handler.
pub fn new(handler: &'a Handler, level: Level, message: &str) -> DiagnosticBuilder<'a> {
DiagnosticBuilder::new_with_code(handler, level, None, message)
}

pub fn guesses<I>(&mut self, sp: Span, msg: &str, guesses: I) -> &mut Self
where I: IntoIterator<Item = String>
{
self.diagnostic.guesses(sp, msg, guesses);
self
}

/// Convenience function for internal use, clients should use one of the
/// struct_* methods on Handler.
pub fn new_with_code(handler: &'a Handler,
Expand Down
9 changes: 9 additions & 0 deletions src/librustc_errors/emitter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1025,6 +1025,15 @@ impl EmitterWriter {
_ => ()
}
},
Some(Guesses(ref guesses)) => for cs in guesses {
match self.emit_suggestion_default(cs,
&child.level,
&child.styled_message(),
max_line_num_len) {
Err(e) => panic!("failed to emit error: {}", e),
_ => ()
}
},
None => {
match self.emit_message_default(&child.span,
&child.styled_message(),
Expand Down
7 changes: 7 additions & 0 deletions src/librustc_errors/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ pub enum RenderSpan {
/// of hypothetical source code, where each `String` is spliced
/// into the lines in place of the code covered by each span.
Suggestion(CodeSuggestion),

/// Guesses work just like suggestions, but there can be one or
/// multiple guesses that aren't guaranteed to be correct.
/// This allows updating `did you mean` style error messages
/// to automatically applicable suggestions, but notifying the
/// user that care must be taken when doing so.
Guesses(Vec<CodeSuggestion>),
}

#[derive(Clone, Debug, PartialEq)]
Expand Down
31 changes: 18 additions & 13 deletions src/librustc_resolve/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2167,18 +2167,20 @@ impl<'a> Resolver<'a> {
let self_is_available = this.self_value_is_available(path[0].ctxt);
match candidate {
AssocSuggestion::Field => {
err.span_label(span, &format!("did you mean `self.{}`?", path_str));
err.guess(span, "did you intend to access a struct field?",
format!("self.{}`?", path_str));
if !self_is_available {
err.span_label(span, &format!("`self` value is only available in \
methods with `self` parameter"));
}
}
AssocSuggestion::MethodWithSelf if self_is_available => {
err.span_label(span, &format!("did you mean `self.{}(...)`?",
path_str));
err.guess(span, "did you intend to call a method of the same name?",
format!("self.{}()", path_str));
}
AssocSuggestion::MethodWithSelf | AssocSuggestion::AssocItem => {
err.span_label(span, &format!("did you mean `Self::{}`?", path_str));
err.guess(span, "did you mean an associated item of the same name?",
format!("Self::{}", path_str));
}
}
return err;
Expand All @@ -2189,22 +2191,25 @@ impl<'a> Resolver<'a> {
if let Some(def) = def {
match (def, source) {
(Def::Macro(..), _) => {
err.span_label(span, &format!("did you mean `{}!(...)`?", path_str));
err.guess(span, "did you intend to invoke a macro of the same name?",
format!("{}!(...)", path_str));
return err;
}
(Def::TyAlias(..), PathSource::Trait) => {
err.span_label(span, &format!("type aliases cannot be used for traits"));
err.span_label(span, &"type aliases cannot be used for traits");
return err;
}
(Def::Mod(..), PathSource::Expr(Some(parent))) => match *parent {
ExprKind::Field(_, ident) => {
err.span_label(span, &format!("did you mean `{}::{}`?",
path_str, ident.node));
err.guess(span,
"did you mean",
format!("{}::{}", path_str, ident.node));
return err;
}
ExprKind::MethodCall(ident, ..) => {
err.span_label(span, &format!("did you mean `{}::{}(...)`?",
path_str, ident.node));
err.guess(span,
"did you mean",
format!("{}::{}(...)", path_str, ident.node));
return err;
}
_ => {}
Expand All @@ -2219,8 +2224,8 @@ impl<'a> Resolver<'a> {
}
}
}
err.span_label(span, &format!("did you mean `{} {{ /* fields */ }}`?",
path_str));
err.guess(span, "did you mean",
format!("{} {{ /* fields */ }}", path_str));
return err;
}
_ => {}
Expand All @@ -2229,7 +2234,7 @@ impl<'a> Resolver<'a> {

// Try Levenshtein if nothing else worked.
if let Some(candidate) = this.lookup_typo_candidate(path, ns, is_expected) {
err.span_label(span, &format!("did you mean `{}`?", candidate));
err.guess(span, "did you mean", candidate);
return err;
}

Expand Down
6 changes: 3 additions & 3 deletions src/librustc_resolve/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ impl<'a> base::Resolver for Resolver<'a> {
_ => {
let msg = format!("macro undefined: '{}!'", name);
let mut err = self.session.struct_span_err(span, &msg);
self.suggest_macro_name(&name.as_str(), &mut err);
self.suggest_macro_name(span, &name.as_str(), &mut err);
err.emit();
return Err(Determinacy::Determined);
},
Expand Down Expand Up @@ -401,10 +401,10 @@ impl<'a> Resolver<'a> {
}
}

fn suggest_macro_name(&mut self, name: &str, err: &mut DiagnosticBuilder<'a>) {
fn suggest_macro_name(&mut self, span: Span, name: &str, err: &mut DiagnosticBuilder<'a>) {
if let Some(suggestion) = find_best_match_for_name(self.macro_names.iter(), name, None) {
if suggestion != name {
err.help(&format!("did you mean `{}!`?", suggestion));
err.guess(span, "did you mean", format!("{}!", suggestion));
} else {
err.help(&format!("have you added the `#[macro_use]` on the module/import?"));
}
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_typeck/check/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,10 +230,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
if is_arg {
if let PatKind::Binding(..) = inner.node {
if let Ok(snippet) = self.sess().codemap()
.span_to_snippet(pat.span)
.span_to_snippet(inner.span)
{
err.help(&format!("did you mean `{}: &{}`?",
&snippet[1..],
snippet,
expected));
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_typeck/check/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let mut err = self.type_error_struct(call_expr.span, |_| {
format!("`{}` is being called, but it is not a function", path)
}, callee_ty);
err.help(&format!("did you mean to write `{}`?", path));
err.guess(call_expr.span, "did you mean to write", path);
err
} else {
self.type_error_struct(call_expr.span, |actual| {
Expand Down
13 changes: 7 additions & 6 deletions src/librustc_typeck/check/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,7 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
fcx.ty_to_string(self.expr_ty),
cast_ty));
if let Ok(snippet) = fcx.sess().codemap().span_to_snippet(self.expr.span) {
err.span_help(self.expr.span,
&format!("did you mean `*{}`?", snippet));
err.guess(self.expr.span, "did you mean", format!("*{}", snippet));
}
err.emit();
}
Expand Down Expand Up @@ -277,7 +276,9 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
format!("&{}{}", mtstr, s));
}
Err(_) => {
span_help!(err, self.cast_span, "did you mean `&{}{}`?", mtstr, tstr)
err.guess(self.cast_span,
"did you mean",
format!("&{}{}", mtstr, tstr));
}
}
} else {
Expand All @@ -291,9 +292,9 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
ty::TyAdt(def, ..) if def.is_box() => {
match fcx.tcx.sess.codemap().span_to_snippet(self.cast_span) {
Ok(s) => {
err.span_suggestion(self.cast_span,
"try casting to a `Box` instead:",
format!("Box<{}>", s));
err.guess(self.cast_span,
"try casting to a `Box` instead:",
format!("Box<{}>", s));
}
Err(_) => span_help!(err, self.cast_span, "did you mean `Box<{}>`?", tstr),
}
Expand Down
28 changes: 18 additions & 10 deletions src/librustc_typeck/check/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,17 +199,25 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let field_ty = field.ty(tcx, substs);

if self.is_fn_ty(&field_ty, span) {
err.span_note(span,
&format!("use `({0}.{1})(...)` if you \
meant to call the function \
stored in the `{1}` field",
expr_string,
item_name));
if expr.span.expn_id == span.expn_id {
let span = Span {
lo: expr.span.lo,
hi: span.hi,
expn_id: expr.span.expn_id,
};
err.guess(
span,
&format!("did you mean to call the function \
stored in the `{}` field?", item_name),
format!("({}.{})", expr_string, item_name),
Copy link
Member

Choose a reason for hiding this comment

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

I prefer ({}.{})(...)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

see above.

);
} else {
err.help(&format!("did you mean to call the function \
stored in the `{}` field?", item_name));
}
} else {
err.span_note(span,
&format!("did you mean to write `{0}.{1}`?",
expr_string,
item_name));
err.guess(span, "did you mean to write",
format!("{}.{}", expr_string, item_name));
}
break;
}
Expand Down
16 changes: 8 additions & 8 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2995,18 +2995,17 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
ty::TyAdt(def, _) if !def.is_enum() => {
if let Some(suggested_field_name) =
Self::suggest_field_name(def.struct_variant(), field, vec![]) {
err.span_label(field.span,
&format!("did you mean `{}`?", suggested_field_name));
err.guess(field.span, "did you mean", suggested_field_name.to_string());
} else {
err.span_label(field.span,
&format!("unknown field"));
};
}
ty::TyRawPtr(..) => {
err.note(&format!("`{0}` is a native pointer; perhaps you need to deref with \
`(*{0}).{1}`",
self.tcx.hir.node_to_pretty_string(base.id),
field.node));
let pretty = self.tcx.hir.node_to_pretty_string(base.id);
err.guess(expr.span, &format!("`{}` is a raw pointer; \
perhaps you need to deref", pretty),
format!("(*{}).{}", pretty, field.node));
}
_ => {}
}
Expand Down Expand Up @@ -3133,8 +3132,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
if let Some(field_name) = Self::suggest_field_name(variant,
&field.name,
skip_fields.collect()) {
err.span_label(field.name.span,
&format!("field does not exist - did you mean `{}`?", field_name));
err.guess(field.name.span,
"field does not exist - did you mean",
field_name.to_string());
} else {
match ty.sty {
ty::TyAdt(adt, ..) if adt.is_enum() => {
Expand Down
Loading