Skip to content

Commit aea71bd

Browse files
committed
distinguish guesses from suggestions
1 parent 1b6b20a commit aea71bd

File tree

19 files changed

+207
-89
lines changed

19 files changed

+207
-89
lines changed

src/librustc_borrowck/borrowck/mod.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,12 +1008,12 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
10081008
.span_label(err.span,
10091009
&format!("may outlive borrowed value {}",
10101010
cmt_path_or_string))
1011-
.span_suggestion(err.span,
1012-
&format!("to force the closure to take ownership of {} \
1013-
(and any other referenced variables), \
1014-
use the `move` keyword, as shown:",
1015-
cmt_path_or_string),
1016-
suggestion)
1011+
.guess(err.span,
1012+
&format!("to force the closure to take ownership of {} \
1013+
(and any other referenced variables), \
1014+
use the `move` keyword, as shown:",
1015+
cmt_path_or_string),
1016+
suggestion)
10171017
.emit();
10181018
}
10191019

src/librustc_errors/diagnostic.rs

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
use CodeSuggestion;
1212
use Level;
1313
use RenderSpan;
14-
use RenderSpan::Suggestion;
15-
use std::fmt;
14+
use RenderSpan::{Suggestion, Guesses};
15+
use std::{fmt, iter};
1616
use syntax_pos::{MultiSpan, Span};
1717
use snippet::Style;
1818

@@ -155,11 +155,7 @@ impl Diagnostic {
155155
/// Prints out a message with a suggested edit of the code.
156156
///
157157
/// See `diagnostic::RenderSpan::Suggestion` for more information.
158-
pub fn span_suggestion<S: Into<MultiSpan>>(&mut self,
159-
sp: S,
160-
msg: &str,
161-
suggestion: String)
162-
-> &mut Self {
158+
pub fn span_suggestion(&mut self, sp: Span, msg: &str, suggestion: String) -> &mut Self {
163159
self.sub(Level::Help,
164160
msg,
165161
MultiSpan::new(),
@@ -170,6 +166,28 @@ impl Diagnostic {
170166
self
171167
}
172168

169+
/// Prints out a message with one or multiple suggested edits of the
170+
/// code, which may break the code, require manual intervention
171+
/// or be plain out wrong, but might possibly be the correct solution.
172+
///
173+
/// See `diagnostic::RenderSpan::Guesses` for more information.
174+
pub fn guesses<I>(&mut self, sp: Span, msg: &str, guesses: I) -> &mut Self
175+
where I: IntoIterator<Item = String>
176+
{
177+
self.sub(Level::Help,
178+
msg,
179+
MultiSpan::new(),
180+
Some(Guesses(guesses.into_iter().map(|guess| CodeSuggestion {
181+
msp: sp.into(),
182+
substitutes: vec![guess],
183+
}).collect())));
184+
self
185+
}
186+
187+
pub fn guess(&mut self, sp: Span, msg: &str, guess: String) -> &mut Self {
188+
self.guesses(sp, msg, iter::once(guess))
189+
}
190+
173191
pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
174192
self.span = sp.into();
175193
self

src/librustc_errors/diagnostic_builder.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -139,20 +139,28 @@ impl<'a> DiagnosticBuilder<'a> {
139139
sp: S,
140140
msg: &str)
141141
-> &mut Self);
142-
forward!(pub fn span_suggestion<S: Into<MultiSpan>>(&mut self,
143-
sp: S,
144-
msg: &str,
145-
suggestion: String)
146-
-> &mut Self);
142+
forward!(pub fn span_suggestion(&mut self,
143+
sp: Span,
144+
msg: &str,
145+
suggestion: String)
146+
-> &mut Self);
147147
forward!(pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self);
148148
forward!(pub fn code(&mut self, s: String) -> &mut Self);
149+
forward!(pub fn guess(&mut self, sp: Span, msg: &str, guess: String) -> &mut Self);
149150

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

157+
pub fn guesses<I>(&mut self, sp: Span, msg: &str, guesses: I) -> &mut Self
158+
where I: IntoIterator<Item = String>
159+
{
160+
self.diagnostic.guesses(sp, msg, guesses);
161+
self
162+
}
163+
156164
/// Convenience function for internal use, clients should use one of the
157165
/// struct_* methods on Handler.
158166
pub fn new_with_code(handler: &'a Handler,

src/librustc_errors/emitter.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,6 +1025,15 @@ impl EmitterWriter {
10251025
_ => ()
10261026
}
10271027
},
1028+
Some(Guesses(ref guesses)) => for cs in guesses {
1029+
match self.emit_suggestion_default(cs,
1030+
&child.level,
1031+
&child.styled_message(),
1032+
max_line_num_len) {
1033+
Err(e) => panic!("failed to emit error: {}", e),
1034+
_ => ()
1035+
}
1036+
},
10281037
None => {
10291038
match self.emit_message_default(&child.span,
10301039
&child.styled_message(),

src/librustc_errors/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ pub enum RenderSpan {
6161
/// of hypothetical source code, where each `String` is spliced
6262
/// into the lines in place of the code covered by each span.
6363
Suggestion(CodeSuggestion),
64+
65+
/// Guesses work just like suggestions, but there can be one or
66+
/// multiple guesses that aren't guaranteed to be correct.
67+
/// This allows updating `did you mean` style error messages
68+
/// to automatically applicable suggestions, but notifying the
69+
/// user that care must be taken when doing so.
70+
Guesses(Vec<CodeSuggestion>),
6471
}
6572

6673
#[derive(Clone, Debug, PartialEq)]

src/librustc_resolve/lib.rs

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2167,18 +2167,20 @@ impl<'a> Resolver<'a> {
21672167
let self_is_available = this.self_value_is_available(path[0].ctxt);
21682168
match candidate {
21692169
AssocSuggestion::Field => {
2170-
err.span_label(span, &format!("did you mean `self.{}`?", path_str));
2170+
err.guess(span, "did you intend to access a struct field?",
2171+
format!("self.{}`?", path_str));
21712172
if !self_is_available {
21722173
err.span_label(span, &format!("`self` value is only available in \
21732174
methods with `self` parameter"));
21742175
}
21752176
}
21762177
AssocSuggestion::MethodWithSelf if self_is_available => {
2177-
err.span_label(span, &format!("did you mean `self.{}(...)`?",
2178-
path_str));
2178+
err.guess(span, "did you intend to call a method of the same name?",
2179+
format!("self.{}()", path_str));
21792180
}
21802181
AssocSuggestion::MethodWithSelf | AssocSuggestion::AssocItem => {
2181-
err.span_label(span, &format!("did you mean `Self::{}`?", path_str));
2182+
err.guess(span, "did you mean an associated item of the same name?",
2183+
format!("Self::{}", path_str));
21822184
}
21832185
}
21842186
return err;
@@ -2189,22 +2191,25 @@ impl<'a> Resolver<'a> {
21892191
if let Some(def) = def {
21902192
match (def, source) {
21912193
(Def::Macro(..), _) => {
2192-
err.span_label(span, &format!("did you mean `{}!(...)`?", path_str));
2194+
err.guess(span, "did you intend to invoke a macro of the same name?",
2195+
format!("{}!(...)", path_str));
21932196
return err;
21942197
}
21952198
(Def::TyAlias(..), PathSource::Trait) => {
2196-
err.span_label(span, &format!("type aliases cannot be used for traits"));
2199+
err.span_label(span, &"type aliases cannot be used for traits");
21972200
return err;
21982201
}
21992202
(Def::Mod(..), PathSource::Expr(Some(parent))) => match *parent {
22002203
ExprKind::Field(_, ident) => {
2201-
err.span_label(span, &format!("did you mean `{}::{}`?",
2202-
path_str, ident.node));
2204+
err.guess(span,
2205+
"did you mean",
2206+
format!("{}::{}", path_str, ident.node));
22032207
return err;
22042208
}
22052209
ExprKind::MethodCall(ident, ..) => {
2206-
err.span_label(span, &format!("did you mean `{}::{}(...)`?",
2207-
path_str, ident.node));
2210+
err.guess(span,
2211+
"did you mean",
2212+
format!("{}::{}(...)", path_str, ident.node));
22082213
return err;
22092214
}
22102215
_ => {}
@@ -2219,8 +2224,8 @@ impl<'a> Resolver<'a> {
22192224
}
22202225
}
22212226
}
2222-
err.span_label(span, &format!("did you mean `{} {{ /* fields */ }}`?",
2223-
path_str));
2227+
err.guess(span, "did you mean",
2228+
format!("{} {{ /* fields */ }}", path_str));
22242229
return err;
22252230
}
22262231
_ => {}
@@ -2229,7 +2234,7 @@ impl<'a> Resolver<'a> {
22292234

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

src/librustc_resolve/macros.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ impl<'a> base::Resolver for Resolver<'a> {
238238
_ => {
239239
let msg = format!("macro undefined: '{}!'", name);
240240
let mut err = self.session.struct_span_err(span, &msg);
241-
self.suggest_macro_name(&name.as_str(), &mut err);
241+
self.suggest_macro_name(span, &name.as_str(), &mut err);
242242
err.emit();
243243
return Err(Determinacy::Determined);
244244
},
@@ -401,10 +401,10 @@ impl<'a> Resolver<'a> {
401401
}
402402
}
403403

404-
fn suggest_macro_name(&mut self, name: &str, err: &mut DiagnosticBuilder<'a>) {
404+
fn suggest_macro_name(&mut self, span: Span, name: &str, err: &mut DiagnosticBuilder<'a>) {
405405
if let Some(suggestion) = find_best_match_for_name(self.macro_names.iter(), name, None) {
406406
if suggestion != name {
407-
err.help(&format!("did you mean `{}!`?", suggestion));
407+
err.guess(span, "did you mean", format!("{}!", suggestion));
408408
} else {
409409
err.help(&format!("have you added the `#[macro_use]` on the module/import?"));
410410
}

src/librustc_typeck/check/_match.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,10 +230,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
230230
if is_arg {
231231
if let PatKind::Binding(..) = inner.node {
232232
if let Ok(snippet) = self.sess().codemap()
233-
.span_to_snippet(pat.span)
233+
.span_to_snippet(inner.span)
234234
{
235235
err.help(&format!("did you mean `{}: &{}`?",
236-
&snippet[1..],
236+
snippet,
237237
expected));
238238
}
239239
}

src/librustc_typeck/check/callee.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
210210
let mut err = self.type_error_struct(call_expr.span, |_| {
211211
format!("`{}` is being called, but it is not a function", path)
212212
}, callee_ty);
213-
err.help(&format!("did you mean to write `{}`?", path));
213+
err.guess(call_expr.span, "did you mean to write", path);
214214
err
215215
} else {
216216
self.type_error_struct(call_expr.span, |actual| {

src/librustc_typeck/check/cast.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,7 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
156156
fcx.ty_to_string(self.expr_ty),
157157
cast_ty));
158158
if let Ok(snippet) = fcx.sess().codemap().span_to_snippet(self.expr.span) {
159-
err.span_help(self.expr.span,
160-
&format!("did you mean `*{}`?", snippet));
159+
err.guess(self.expr.span, "did you mean", format!("*{}", snippet));
161160
}
162161
err.emit();
163162
}
@@ -277,7 +276,9 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
277276
format!("&{}{}", mtstr, s));
278277
}
279278
Err(_) => {
280-
span_help!(err, self.cast_span, "did you mean `&{}{}`?", mtstr, tstr)
279+
err.guess(self.cast_span,
280+
"did you mean",
281+
format!("&{}{}", mtstr, tstr));
281282
}
282283
}
283284
} else {
@@ -291,9 +292,9 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
291292
ty::TyAdt(def, ..) if def.is_box() => {
292293
match fcx.tcx.sess.codemap().span_to_snippet(self.cast_span) {
293294
Ok(s) => {
294-
err.span_suggestion(self.cast_span,
295-
"try casting to a `Box` instead:",
296-
format!("Box<{}>", s));
295+
err.guess(self.cast_span,
296+
"try casting to a `Box` instead:",
297+
format!("Box<{}>", s));
297298
}
298299
Err(_) => span_help!(err, self.cast_span, "did you mean `Box<{}>`?", tstr),
299300
}

src/librustc_typeck/check/method/suggest.rs

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -199,17 +199,25 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
199199
let field_ty = field.ty(tcx, substs);
200200

201201
if self.is_fn_ty(&field_ty, span) {
202-
err.span_note(span,
203-
&format!("use `({0}.{1})(...)` if you \
204-
meant to call the function \
205-
stored in the `{1}` field",
206-
expr_string,
207-
item_name));
202+
if expr.span.expn_id == span.expn_id {
203+
let span = Span {
204+
lo: expr.span.lo,
205+
hi: span.hi,
206+
expn_id: expr.span.expn_id,
207+
};
208+
err.guess(
209+
span,
210+
&format!("did you mean to call the function \
211+
stored in the `{}` field?", item_name),
212+
format!("({}.{})", expr_string, item_name),
213+
);
214+
} else {
215+
err.help(&format!("did you mean to call the function \
216+
stored in the `{}` field?", item_name));
217+
}
208218
} else {
209-
err.span_note(span,
210-
&format!("did you mean to write `{0}.{1}`?",
211-
expr_string,
212-
item_name));
219+
err.guess(span, "did you mean to write",
220+
format!("{}.{}", expr_string, item_name));
213221
}
214222
break;
215223
}

src/librustc_typeck/check/mod.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2995,18 +2995,17 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
29952995
ty::TyAdt(def, _) if !def.is_enum() => {
29962996
if let Some(suggested_field_name) =
29972997
Self::suggest_field_name(def.struct_variant(), field, vec![]) {
2998-
err.span_label(field.span,
2999-
&format!("did you mean `{}`?", suggested_field_name));
2998+
err.guess(field.span, "did you mean", suggested_field_name.to_string());
30002999
} else {
30013000
err.span_label(field.span,
30023001
&format!("unknown field"));
30033002
};
30043003
}
30053004
ty::TyRawPtr(..) => {
3006-
err.note(&format!("`{0}` is a native pointer; perhaps you need to deref with \
3007-
`(*{0}).{1}`",
3008-
self.tcx.hir.node_to_pretty_string(base.id),
3009-
field.node));
3005+
let pretty = self.tcx.hir.node_to_pretty_string(base.id);
3006+
err.guess(expr.span, &format!("`{}` is a raw pointer; \
3007+
perhaps you need to deref", pretty),
3008+
format!("(*{}).{}", pretty, field.node));
30103009
}
30113010
_ => {}
30123011
}
@@ -3133,8 +3132,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
31333132
if let Some(field_name) = Self::suggest_field_name(variant,
31343133
&field.name,
31353134
skip_fields.collect()) {
3136-
err.span_label(field.name.span,
3137-
&format!("field does not exist - did you mean `{}`?", field_name));
3135+
err.guess(field.name.span,
3136+
"field does not exist - did you mean",
3137+
field_name.to_string());
31383138
} else {
31393139
match ty.sty {
31403140
ty::TyAdt(adt, ..) if adt.is_enum() => {

0 commit comments

Comments
 (0)