Skip to content

Commit afe145d

Browse files
committed
Auto merge of #43096 - estebank:ascription-help, r=nikomatsakis
Point at `:` when using it instead of `;` When triggering type ascription in such a way that we can infer a statement end was intended, add a suggestion for the change. Always point out the reason for the expectation of a type is due to type ascription. Fix #42057, #41928.
2 parents f3e26a0 + e39bcec commit afe145d

11 files changed

+174
-18
lines changed

src/librustc_errors/diagnostic.rs

+18
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,22 @@ impl Diagnostic {
209209
self
210210
}
211211

212+
/// Prints out a message with a suggested edit of the code. If the suggestion is presented
213+
/// inline it will only show the text message and not the text.
214+
///
215+
/// See `diagnostic::CodeSuggestion` for more information.
216+
pub fn span_suggestion_short(&mut self, sp: Span, msg: &str, suggestion: String) -> &mut Self {
217+
self.suggestions.push(CodeSuggestion {
218+
substitution_parts: vec![Substitution {
219+
span: sp,
220+
substitutions: vec![suggestion],
221+
}],
222+
msg: msg.to_owned(),
223+
show_code_when_inline: false,
224+
});
225+
self
226+
}
227+
212228
/// Prints out a message with a suggested edit of the code.
213229
///
214230
/// In case of short messages and a simple suggestion,
@@ -231,6 +247,7 @@ impl Diagnostic {
231247
substitutions: vec![suggestion],
232248
}],
233249
msg: msg.to_owned(),
250+
show_code_when_inline: true,
234251
});
235252
self
236253
}
@@ -242,6 +259,7 @@ impl Diagnostic {
242259
substitutions: suggestions,
243260
}],
244261
msg: msg.to_owned(),
262+
show_code_when_inline: true,
245263
});
246264
self
247265
}

src/librustc_errors/diagnostic_builder.rs

+5
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,11 @@ impl<'a> DiagnosticBuilder<'a> {
146146
sp: S,
147147
msg: &str)
148148
-> &mut Self);
149+
forward!(pub fn span_suggestion_short(&mut self,
150+
sp: Span,
151+
msg: &str,
152+
suggestion: String)
153+
-> &mut Self);
149154
forward!(pub fn span_suggestion(&mut self,
150155
sp: Span,
151156
msg: &str,

src/librustc_errors/emitter.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,9 @@ impl Emitter for EmitterWriter {
4747
// don't display multiline suggestions as labels
4848
sugg.substitution_parts[0].substitutions[0].find('\n').is_none() {
4949
let substitution = &sugg.substitution_parts[0].substitutions[0];
50-
let msg = if substitution.len() == 0 {
51-
// This substitution is only removal, don't show it
50+
let msg = if substitution.len() == 0 || !sugg.show_code_when_inline {
51+
// This substitution is only removal or we explicitely don't want to show the
52+
// code inline, don't show it
5253
format!("help: {}", sugg.msg)
5354
} else {
5455
format!("help: {}: `{}`", sugg.msg, substitution)

src/librustc_errors/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ pub struct CodeSuggestion {
8484
/// ```
8585
pub substitution_parts: Vec<Substitution>,
8686
pub msg: String,
87+
pub show_code_when_inline: bool,
8788
}
8889

8990
#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)]

src/librustc_resolve/lib.rs

+63-14
Original file line numberDiff line numberDiff line change
@@ -599,19 +599,24 @@ impl<'a, 'tcx> Visitor<'tcx> for Resolver<'a> {
599599
self.resolve_local(local);
600600
}
601601
fn visit_ty(&mut self, ty: &'tcx Ty) {
602-
if let TyKind::Path(ref qself, ref path) = ty.node {
603-
self.smart_resolve_path(ty.id, qself.as_ref(), path, PathSource::Type);
604-
} else if let TyKind::ImplicitSelf = ty.node {
605-
let self_ty = keywords::SelfType.ident();
606-
let def = self.resolve_ident_in_lexical_scope(self_ty, TypeNS, true, ty.span)
607-
.map_or(Def::Err, |d| d.def());
608-
self.record_def(ty.id, PathResolution::new(def));
609-
} else if let TyKind::Array(ref element, ref length) = ty.node {
610-
self.visit_ty(element);
611-
self.with_constant_rib(|this| {
612-
this.visit_expr(length);
613-
});
614-
return;
602+
match ty.node {
603+
TyKind::Path(ref qself, ref path) => {
604+
self.smart_resolve_path(ty.id, qself.as_ref(), path, PathSource::Type);
605+
}
606+
TyKind::ImplicitSelf => {
607+
let self_ty = keywords::SelfType.ident();
608+
let def = self.resolve_ident_in_lexical_scope(self_ty, TypeNS, true, ty.span)
609+
.map_or(Def::Err, |d| d.def());
610+
self.record_def(ty.id, PathResolution::new(def));
611+
}
612+
TyKind::Array(ref element, ref length) => {
613+
self.visit_ty(element);
614+
self.with_constant_rib(|this| {
615+
this.visit_expr(length);
616+
});
617+
return;
618+
}
619+
_ => (),
615620
}
616621
visit::walk_ty(self, ty);
617622
}
@@ -1221,6 +1226,9 @@ pub struct Resolver<'a> {
12211226
// This table maps struct IDs into struct constructor IDs,
12221227
// it's not used during normal resolution, only for better error reporting.
12231228
struct_constructors: DefIdMap<(Def, ty::Visibility)>,
1229+
1230+
// Only used for better errors on `fn(): fn()`
1231+
current_type_ascription: Vec<Span>,
12241232
}
12251233

12261234
pub struct ResolverArenas<'a> {
@@ -1411,6 +1419,7 @@ impl<'a> Resolver<'a> {
14111419
struct_constructors: DefIdMap(),
14121420
found_unresolved_macro: false,
14131421
unused_macros: FxHashSet(),
1422+
current_type_ascription: Vec::new(),
14141423
}
14151424
}
14161425

@@ -2499,6 +2508,7 @@ impl<'a> Resolver<'a> {
24992508
// Fallback label.
25002509
if !levenshtein_worked {
25012510
err.span_label(base_span, fallback_label);
2511+
this.type_ascription_suggestion(&mut err, base_span);
25022512
}
25032513
err
25042514
};
@@ -2554,6 +2564,41 @@ impl<'a> Resolver<'a> {
25542564
resolution
25552565
}
25562566

2567+
fn type_ascription_suggestion(&self,
2568+
err: &mut DiagnosticBuilder,
2569+
base_span: Span) {
2570+
debug!("type_ascription_suggetion {:?}", base_span);
2571+
let cm = self.session.codemap();
2572+
debug!("self.current_type_ascription {:?}", self.current_type_ascription);
2573+
if let Some(sp) = self.current_type_ascription.last() {
2574+
let mut sp = *sp;
2575+
loop { // try to find the `:`, bail on first non-':'/non-whitespace
2576+
sp = sp.next_point();
2577+
if let Ok(snippet) = cm.span_to_snippet(sp.to(sp.next_point())) {
2578+
debug!("snippet {:?}", snippet);
2579+
let line_sp = cm.lookup_char_pos(sp.hi).line;
2580+
let line_base_sp = cm.lookup_char_pos(base_span.lo).line;
2581+
debug!("{:?} {:?}", line_sp, line_base_sp);
2582+
if snippet == ":" {
2583+
err.span_label(base_span,
2584+
"expecting a type here because of type ascription");
2585+
if line_sp != line_base_sp {
2586+
err.span_suggestion_short(sp,
2587+
"did you mean to use `;` here instead?",
2588+
";".to_string());
2589+
}
2590+
break;
2591+
} else if snippet.trim().len() != 0 {
2592+
debug!("tried to find type ascription `:` token, couldn't find it");
2593+
break;
2594+
}
2595+
} else {
2596+
break;
2597+
}
2598+
}
2599+
}
2600+
}
2601+
25572602
fn self_type_is_available(&mut self, span: Span) -> bool {
25582603
let binding = self.resolve_ident_in_lexical_scope(keywords::SelfType.ident(),
25592604
TypeNS, false, span);
@@ -3170,7 +3215,11 @@ impl<'a> Resolver<'a> {
31703215
self.resolve_expr(argument, None);
31713216
}
31723217
}
3173-
3218+
ExprKind::Type(ref type_expr, _) => {
3219+
self.current_type_ascription.push(type_expr.span);
3220+
visit::walk_expr(self, expr);
3221+
self.current_type_ascription.pop();
3222+
}
31743223
_ => {
31753224
visit::walk_expr(self, expr);
31763225
}

src/libsyntax/parse/parser.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -2798,7 +2798,22 @@ impl<'a> Parser<'a> {
27982798
lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Cast)?;
27992799
continue
28002800
} else if op == AssocOp::Colon {
2801-
lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Type)?;
2801+
lhs = match self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Type) {
2802+
Ok(lhs) => lhs,
2803+
Err(mut err) => {
2804+
err.span_label(self.span,
2805+
"expecting a type here because of type ascription");
2806+
let cm = self.sess.codemap();
2807+
let cur_pos = cm.lookup_char_pos(self.span.lo);
2808+
let op_pos = cm.lookup_char_pos(cur_op_span.hi);
2809+
if cur_pos.line != op_pos.line {
2810+
err.span_suggestion_short(cur_op_span,
2811+
"did you mean to use `;` here?",
2812+
";".to_string());
2813+
}
2814+
return Err(err);
2815+
}
2816+
};
28022817
continue
28032818
} else if op == AssocOp::DotDot || op == AssocOp::DotDotDot {
28042819
// If we didn’t have to handle `x..`/`x...`, it would be pretty easy to

src/test/ui/issue-22644.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -80,5 +80,5 @@ error: expected type, found `4`
8080
--> $DIR/issue-22644.rs:38:28
8181
|
8282
38 | println!("{}", a: &mut 4);
83-
| ^
83+
| ^ expecting a type here because of type ascription
8484

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(type_ascription)]
12+
13+
fn main() {
14+
println!("test"):
15+
0;
16+
}
17+
18+
fn foo() {
19+
println!("test"): 0;
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error: expected type, found `0`
2+
--> $DIR/type-ascription-instead-of-statement-end.rs:15:5
3+
|
4+
14 | println!("test"):
5+
| - help: did you mean to use `;` here?
6+
15 | 0;
7+
| ^ expecting a type here because of type ascription
8+
9+
error: expected type, found `0`
10+
--> $DIR/type-ascription-instead-of-statement-end.rs:19:23
11+
|
12+
19 | println!("test"): 0;
13+
| ^ expecting a type here because of type ascription
14+
15+
error: aborting due to 2 previous errors
16+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(type_ascription)]
12+
13+
fn main() {
14+
f() :
15+
f();
16+
}
17+
18+
fn f() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error[E0573]: expected type, found function `f`
2+
--> $DIR/type-ascription-with-fn-call.rs:15:5
3+
|
4+
14 | f() :
5+
| - help: did you mean to use `;` here instead?
6+
15 | f();
7+
| ^^^
8+
| |
9+
| not a type
10+
| expecting a type here because of type ascription
11+
12+
error: aborting due to previous error
13+

0 commit comments

Comments
 (0)