Skip to content

Commit a466f01

Browse files
authored
Rollup merge of rust-lang#65566 - estebank:let-expr-as-ty, r=Centril
Use heuristics to suggest assignment When detecting a possible `=` -> `:` typo in a `let` binding, suggest assigning instead of setting the type. Partially address rust-lang#57828.
2 parents b7176b4 + b579c5a commit a466f01

File tree

7 files changed

+162
-51
lines changed

7 files changed

+162
-51
lines changed

src/librustc_resolve/late.rs

+58-36
Original file line numberDiff line numberDiff line change
@@ -316,22 +316,8 @@ impl<'a> PathSource<'a> {
316316
}
317317
}
318318

319-
struct LateResolutionVisitor<'a, 'b> {
320-
r: &'b mut Resolver<'a>,
321-
322-
/// The module that represents the current item scope.
323-
parent_scope: ParentScope<'a>,
324-
325-
/// The current set of local scopes for types and values.
326-
/// FIXME #4948: Reuse ribs to avoid allocation.
327-
ribs: PerNS<Vec<Rib<'a>>>,
328-
329-
/// The current set of local scopes, for labels.
330-
label_ribs: Vec<Rib<'a, NodeId>>,
331-
332-
/// The trait that the current context can refer to.
333-
current_trait_ref: Option<(Module<'a>, TraitRef)>,
334-
319+
#[derive(Default)]
320+
struct DiagnosticMetadata {
335321
/// The current trait's associated types' ident, used for diagnostic suggestions.
336322
current_trait_assoc_types: Vec<Ident>,
337323

@@ -350,6 +336,29 @@ struct LateResolutionVisitor<'a, 'b> {
350336

351337
/// Only used for better errors on `fn(): fn()`.
352338
current_type_ascription: Vec<Span>,
339+
340+
/// Only used for better errors on `let <pat>: <expr, not type>;`.
341+
current_let_binding: Option<(Span, Option<Span>, Option<Span>)>,
342+
}
343+
344+
struct LateResolutionVisitor<'a, 'b> {
345+
r: &'b mut Resolver<'a>,
346+
347+
/// The module that represents the current item scope.
348+
parent_scope: ParentScope<'a>,
349+
350+
/// The current set of local scopes for types and values.
351+
/// FIXME #4948: Reuse ribs to avoid allocation.
352+
ribs: PerNS<Vec<Rib<'a>>>,
353+
354+
/// The current set of local scopes, for labels.
355+
label_ribs: Vec<Rib<'a, NodeId>>,
356+
357+
/// The trait that the current context can refer to.
358+
current_trait_ref: Option<(Module<'a>, TraitRef)>,
359+
360+
/// Fields used to add information to diagnostic errors.
361+
diagnostic_metadata: DiagnosticMetadata,
353362
}
354363

355364
/// Walks the whole crate in DFS order, visiting each item, resolving names as it goes.
@@ -373,7 +382,18 @@ impl<'a, 'tcx> Visitor<'tcx> for LateResolutionVisitor<'a, '_> {
373382
self.resolve_expr(expr, None);
374383
}
375384
fn visit_local(&mut self, local: &'tcx Local) {
385+
let local_spans = match local.pat.kind {
386+
// We check for this to avoid tuple struct fields.
387+
PatKind::Wild => None,
388+
_ => Some((
389+
local.pat.span,
390+
local.ty.as_ref().map(|ty| ty.span),
391+
local.init.as_ref().map(|init| init.span),
392+
)),
393+
};
394+
let original = replace(&mut self.diagnostic_metadata.current_let_binding, local_spans);
376395
self.resolve_local(local);
396+
self.diagnostic_metadata.current_let_binding = original;
377397
}
378398
fn visit_ty(&mut self, ty: &'tcx Ty) {
379399
match ty.kind {
@@ -415,7 +435,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LateResolutionVisitor<'a, '_> {
415435
}
416436
}
417437
fn visit_fn(&mut self, fn_kind: FnKind<'tcx>, declaration: &'tcx FnDecl, sp: Span, _: NodeId) {
418-
let previous_value = replace(&mut self.current_function, Some(sp));
438+
let previous_value = replace(&mut self.diagnostic_metadata.current_function, Some(sp));
419439
debug!("(resolving function) entering function");
420440
let rib_kind = match fn_kind {
421441
FnKind::ItemFn(..) => FnItemRibKind,
@@ -441,7 +461,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LateResolutionVisitor<'a, '_> {
441461
debug!("(resolving function) leaving function");
442462
})
443463
});
444-
self.current_function = previous_value;
464+
self.diagnostic_metadata.current_function = previous_value;
445465
}
446466

447467
fn visit_generics(&mut self, generics: &'tcx Generics) {
@@ -475,7 +495,8 @@ impl<'a, 'tcx> Visitor<'tcx> for LateResolutionVisitor<'a, '_> {
475495
// (We however cannot ban `Self` for defaults on *all* generic
476496
// lists; e.g. trait generics can usefully refer to `Self`,
477497
// such as in the case of `trait Add<Rhs = Self>`.)
478-
if self.current_self_item.is_some() { // (`Some` if + only if we are in ADT's generics.)
498+
if self.diagnostic_metadata.current_self_item.is_some() {
499+
// (`Some` if + only if we are in ADT's generics.)
479500
default_ban_rib.bindings.insert(Ident::with_dummy_span(kw::SelfUpper), Res::Err);
480501
}
481502

@@ -527,12 +548,7 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
527548
},
528549
label_ribs: Vec::new(),
529550
current_trait_ref: None,
530-
current_trait_assoc_types: Vec::new(),
531-
current_self_type: None,
532-
current_self_item: None,
533-
current_function: None,
534-
unused_labels: Default::default(),
535-
current_type_ascription: Vec::new(),
551+
diagnostic_metadata: DiagnosticMetadata::default(),
536552
}
537553
}
538554

@@ -892,16 +908,22 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
892908

893909
fn with_current_self_type<T>(&mut self, self_type: &Ty, f: impl FnOnce(&mut Self) -> T) -> T {
894910
// Handle nested impls (inside fn bodies)
895-
let previous_value = replace(&mut self.current_self_type, Some(self_type.clone()));
911+
let previous_value = replace(
912+
&mut self.diagnostic_metadata.current_self_type,
913+
Some(self_type.clone()),
914+
);
896915
let result = f(self);
897-
self.current_self_type = previous_value;
916+
self.diagnostic_metadata.current_self_type = previous_value;
898917
result
899918
}
900919

901920
fn with_current_self_item<T>(&mut self, self_item: &Item, f: impl FnOnce(&mut Self) -> T) -> T {
902-
let previous_value = replace(&mut self.current_self_item, Some(self_item.id));
921+
let previous_value = replace(
922+
&mut self.diagnostic_metadata.current_self_item,
923+
Some(self_item.id),
924+
);
903925
let result = f(self);
904-
self.current_self_item = previous_value;
926+
self.diagnostic_metadata.current_self_item = previous_value;
905927
result
906928
}
907929

@@ -912,14 +934,14 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
912934
f: impl FnOnce(&mut Self) -> T,
913935
) -> T {
914936
let trait_assoc_types = replace(
915-
&mut self.current_trait_assoc_types,
937+
&mut self.diagnostic_metadata.current_trait_assoc_types,
916938
trait_items.iter().filter_map(|item| match &item.kind {
917939
TraitItemKind::Type(bounds, _) if bounds.len() == 0 => Some(item.ident),
918940
_ => None,
919941
}).collect(),
920942
);
921943
let result = f(self);
922-
self.current_trait_assoc_types = trait_assoc_types;
944+
self.diagnostic_metadata.current_trait_assoc_types = trait_assoc_types;
923945
result
924946
}
925947

@@ -1746,7 +1768,7 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
17461768

17471769
fn with_resolved_label(&mut self, label: Option<Label>, id: NodeId, f: impl FnOnce(&mut Self)) {
17481770
if let Some(label) = label {
1749-
self.unused_labels.insert(id, label.ident.span);
1771+
self.diagnostic_metadata.unused_labels.insert(id, label.ident.span);
17501772
self.with_label_rib(NormalRibKind, |this| {
17511773
let ident = label.ident.modern_and_legacy();
17521774
this.label_ribs.last_mut().unwrap().bindings.insert(ident, id);
@@ -1850,7 +1872,7 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
18501872
Some(node_id) => {
18511873
// Since this res is a label, it is never read.
18521874
self.r.label_res_map.insert(expr.id, node_id);
1853-
self.unused_labels.remove(&node_id);
1875+
self.diagnostic_metadata.unused_labels.remove(&node_id);
18541876
}
18551877
}
18561878

@@ -1912,9 +1934,9 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
19121934
}
19131935
}
19141936
ExprKind::Type(ref type_expr, _) => {
1915-
self.current_type_ascription.push(type_expr.span);
1937+
self.diagnostic_metadata.current_type_ascription.push(type_expr.span);
19161938
visit::walk_expr(self, expr);
1917-
self.current_type_ascription.pop();
1939+
self.diagnostic_metadata.current_type_ascription.pop();
19181940
}
19191941
// `async |x| ...` gets desugared to `|x| future_from_generator(|| ...)`, so we need to
19201942
// resolve the arguments within the proper scopes so that usages of them inside the
@@ -2073,7 +2095,7 @@ impl<'a> Resolver<'a> {
20732095
pub(crate) fn late_resolve_crate(&mut self, krate: &Crate) {
20742096
let mut late_resolution_visitor = LateResolutionVisitor::new(self);
20752097
visit::walk_crate(&mut late_resolution_visitor, krate);
2076-
for (id, span) in late_resolution_visitor.unused_labels.iter() {
2098+
for (id, span) in late_resolution_visitor.diagnostic_metadata.unused_labels.iter() {
20772099
self.session.buffer_lint(lint::builtin::UNUSED_LABELS, *id, *span, "unused label");
20782100
}
20792101
}

src/librustc_resolve/late/diagnostics.rs

+38-9
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,26 @@ impl<'a> LateResolutionVisitor<'a, '_> {
7272
let path_str = Segment::names_to_string(path);
7373
let item_str = path.last().unwrap().ident;
7474
let code = source.error_code(res.is_some());
75-
let (base_msg, fallback_label, base_span) = if let Some(res) = res {
75+
let (base_msg, fallback_label, base_span, could_be_expr) = if let Some(res) = res {
7676
(format!("expected {}, found {} `{}`", expected, res.descr(), path_str),
7777
format!("not a {}", expected),
78-
span)
78+
span,
79+
match res {
80+
Res::Def(DefKind::Fn, _) => {
81+
// Verify whether this is a fn call or an Fn used as a type.
82+
self.r.session.source_map().span_to_snippet(span).map(|snippet| {
83+
snippet.ends_with(')')
84+
}).unwrap_or(false)
85+
}
86+
Res::Def(DefKind::Ctor(..), _) |
87+
Res::Def(DefKind::Method, _) |
88+
Res::Def(DefKind::Const, _) |
89+
Res::Def(DefKind::AssocConst, _) |
90+
Res::SelfCtor(_) |
91+
Res::PrimTy(_) |
92+
Res::Local(_) => true,
93+
_ => false,
94+
})
7995
} else {
8096
let item_span = path.last().unwrap().ident.span;
8197
let (mod_prefix, mod_str) = if path.len() == 1 {
@@ -94,7 +110,8 @@ impl<'a> LateResolutionVisitor<'a, '_> {
94110
};
95111
(format!("cannot find {} `{}` in {}{}", expected, item_str, mod_prefix, mod_str),
96112
format!("not found in {}", mod_str),
97-
item_span)
113+
item_span,
114+
false)
98115
};
99116

100117
let code = DiagnosticId::Error(code.into());
@@ -134,7 +151,7 @@ impl<'a> LateResolutionVisitor<'a, '_> {
134151
"`self` value is a keyword only available in methods with a `self` parameter",
135152
),
136153
});
137-
if let Some(span) = &self.current_function {
154+
if let Some(span) = &self.diagnostic_metadata.current_function {
138155
err.span_label(*span, "this function doesn't have a `self` parameter");
139156
}
140157
return (err, Vec::new());
@@ -257,6 +274,18 @@ impl<'a> LateResolutionVisitor<'a, '_> {
257274
if !levenshtein_worked {
258275
err.span_label(base_span, fallback_label);
259276
self.type_ascription_suggestion(&mut err, base_span);
277+
match self.diagnostic_metadata.current_let_binding {
278+
Some((pat_sp, Some(ty_sp), None))
279+
if ty_sp.contains(base_span) && could_be_expr => {
280+
err.span_suggestion_short(
281+
pat_sp.between(ty_sp),
282+
"use `=` if you meant to assign",
283+
" = ".to_string(),
284+
Applicability::MaybeIncorrect,
285+
);
286+
}
287+
_ => {}
288+
}
260289
}
261290
(err, candidates)
262291
}
@@ -491,7 +520,9 @@ impl<'a> LateResolutionVisitor<'a, '_> {
491520

492521
// Fields are generally expected in the same contexts as locals.
493522
if filter_fn(Res::Local(ast::DUMMY_NODE_ID)) {
494-
if let Some(node_id) = self.current_self_type.as_ref().and_then(extract_node_id) {
523+
if let Some(node_id) = self.diagnostic_metadata.current_self_type.as_ref()
524+
.and_then(extract_node_id)
525+
{
495526
// Look for a field with the same name in the current self_type.
496527
if let Some(resolution) = self.r.partial_res_map.get(&node_id) {
497528
match resolution.base_res() {
@@ -510,7 +541,7 @@ impl<'a> LateResolutionVisitor<'a, '_> {
510541
}
511542
}
512543

513-
for assoc_type_ident in &self.current_trait_assoc_types {
544+
for assoc_type_ident in &self.diagnostic_metadata.current_trait_assoc_types {
514545
if *assoc_type_ident == ident {
515546
return Some(AssocSuggestion::AssocItem);
516547
}
@@ -644,11 +675,9 @@ impl<'a> LateResolutionVisitor<'a, '_> {
644675
err: &mut DiagnosticBuilder<'_>,
645676
base_span: Span,
646677
) {
647-
debug!("type_ascription_suggetion {:?}", base_span);
648678
let cm = self.r.session.source_map();
649679
let base_snippet = cm.span_to_snippet(base_span);
650-
debug!("self.current_type_ascription {:?}", self.current_type_ascription);
651-
if let Some(sp) = self.current_type_ascription.last() {
680+
if let Some(sp) = self.diagnostic_metadata.current_type_ascription.last() {
652681
let mut sp = *sp;
653682
loop {
654683
// Try to find the `:`; bail on first non-':' / non-whitespace.

src/libsyntax/parse/parser/stmt.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ impl<'a> Parser<'a> {
239239
err.span_suggestion_short(
240240
colon_sp,
241241
"use `=` if you meant to assign",
242-
"=".to_string(),
242+
" =".to_string(),
243243
Applicability::MachineApplicable
244244
);
245245
err.emit();

src/test/ui/privacy/privacy-ns2.rs

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ fn test_single2() {
3939
use foo2::Bar;
4040

4141
let _x : Box<Bar>; //~ ERROR expected type, found function `Bar`
42+
let _x : Bar(); //~ ERROR expected type, found function `Bar`
4243
}
4344

4445
fn test_list2() {

src/test/ui/privacy/privacy-ns2.stderr

+24-5
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,26 @@ LL | use foo3::Bar;
4848
|
4949

5050
error[E0573]: expected type, found function `Bar`
51-
--> $DIR/privacy-ns2.rs:47:17
51+
--> $DIR/privacy-ns2.rs:42:14
52+
|
53+
LL | let _x : Bar();
54+
| ^^^^^ not a type
55+
|
56+
help: use `=` if you meant to assign
57+
|
58+
LL | let _x = Bar();
59+
| ^
60+
help: possible better candidates are found in other modules, you can import them into scope
61+
|
62+
LL | use foo1::Bar;
63+
|
64+
LL | use foo2::Bar;
65+
|
66+
LL | use foo3::Bar;
67+
|
68+
69+
error[E0573]: expected type, found function `Bar`
70+
--> $DIR/privacy-ns2.rs:48:17
5271
|
5372
LL | let _x: Box<Bar>;
5473
| ^^^
@@ -67,24 +86,24 @@ LL | use foo3::Bar;
6786
|
6887

6988
error[E0603]: trait `Bar` is private
70-
--> $DIR/privacy-ns2.rs:60:15
89+
--> $DIR/privacy-ns2.rs:61:15
7190
|
7291
LL | use foo3::Bar;
7392
| ^^^
7493

7594
error[E0603]: trait `Bar` is private
76-
--> $DIR/privacy-ns2.rs:64:15
95+
--> $DIR/privacy-ns2.rs:65:15
7796
|
7897
LL | use foo3::Bar;
7998
| ^^^
8099

81100
error[E0603]: trait `Bar` is private
82-
--> $DIR/privacy-ns2.rs:71:16
101+
--> $DIR/privacy-ns2.rs:72:16
83102
|
84103
LL | use foo3::{Bar,Baz};
85104
| ^^^
86105

87-
error: aborting due to 7 previous errors
106+
error: aborting due to 8 previous errors
88107

89108
Some errors have detailed explanations: E0423, E0573, E0603.
90109
For more information about an error, try `rustc --explain E0423`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
pub fn foo(num: i32) -> i32 {
2+
let foo: i32::from_be(num);
3+
//~^ ERROR expected type, found local variable `num`
4+
//~| ERROR parenthesized type parameters may only be used with a `Fn` trait
5+
//~| ERROR ambiguous associated type
6+
//~| WARNING this was previously accepted by the compiler but is being phased out
7+
foo
8+
}
9+
10+
fn main() {
11+
let _ = foo(42);
12+
}

0 commit comments

Comments
 (0)