From 958e64594672ccd11fba5ec6925e33ef223301ee Mon Sep 17 00:00:00 2001 From: Jakob Degen Date: Mon, 25 Oct 2021 20:04:35 -0400 Subject: [PATCH] Adds hint if a trait fails to resolve and a newly added one in Edition 2021 is suggested --- compiler/rustc_resolve/src/diagnostics.rs | 57 ++++++++++++++-- .../rustc_resolve/src/late/diagnostics.rs | 1 + .../suggest-tryinto-edition-change.rs | 26 ++++++- .../suggest-tryinto-edition-change.stderr | 67 +++++++++++++++++-- 4 files changed, 135 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index f94266c3aea..57acca16d94 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -66,6 +66,8 @@ crate struct ImportSuggestion { pub descr: &'static str, pub path: Path, pub accessible: bool, + /// An extra note that should be issued if this item is suggested + pub note: Option, } /// Adjust the impl span so that just the `impl` keyword is taken by removing @@ -872,11 +874,38 @@ impl<'a> Resolver<'a> { } if candidates.iter().all(|v: &ImportSuggestion| v.did != did) { + // See if we're recommending TryFrom, TryInto, or FromIterator and add + // a note about editions + let note = if let Some(did) = did { + let requires_note = !did.is_local() + && this.cstore().item_attrs(did, this.session).iter().any( + |attr| { + if attr.has_name(sym::rustc_diagnostic_item) { + [sym::TryInto, sym::TryFrom, sym::FromIterator] + .map(|x| Some(x)) + .contains(&attr.value_str()) + } else { + false + } + }, + ); + + requires_note.then(|| { + format!( + "'{}' is included in the prelude starting in Edition 2021", + path_names_to_string(&path) + ) + }) + } else { + None + }; + candidates.push(ImportSuggestion { did, descr: res.descr(), path, accessible: child_accessible, + note, }); } } @@ -1764,12 +1793,14 @@ crate fn show_candidates( return; } - let mut accessible_path_strings: Vec<(String, &str, Option)> = Vec::new(); - let mut inaccessible_path_strings: Vec<(String, &str, Option)> = Vec::new(); + let mut accessible_path_strings: Vec<(String, &str, Option, &Option)> = + Vec::new(); + let mut inaccessible_path_strings: Vec<(String, &str, Option, &Option)> = + Vec::new(); candidates.iter().for_each(|c| { (if c.accessible { &mut accessible_path_strings } else { &mut inaccessible_path_strings }) - .push((path_names_to_string(&c.path), c.descr, c.did)) + .push((path_names_to_string(&c.path), c.descr, c.did, &c.note)) }); // we want consistent results across executions, but candidates are produced @@ -1792,6 +1823,11 @@ crate fn show_candidates( let instead = if instead { " instead" } else { "" }; let mut msg = format!("consider importing {} {}{}", determiner, kind, instead); + // Issue notes + for note in accessible_path_strings.iter().map(|cand| cand.3.as_ref()).flatten() { + err.note(note); + } + if let Some(span) = use_placement_span { for candidate in &mut accessible_path_strings { // produce an additional newline to separate the new use statement @@ -1820,7 +1856,7 @@ crate fn show_candidates( assert!(!inaccessible_path_strings.is_empty()); if inaccessible_path_strings.len() == 1 { - let (name, descr, def_id) = &inaccessible_path_strings[0]; + let (name, descr, def_id, note) = &inaccessible_path_strings[0]; let msg = format!("{} `{}` exists but is inaccessible", descr, name); if let Some(local_def_id) = def_id.and_then(|did| did.as_local()) { @@ -1832,12 +1868,15 @@ crate fn show_candidates( } else { err.note(&msg); } + if let Some(note) = (*note).as_deref() { + err.note(note); + } } else { - let (_, descr_first, _) = &inaccessible_path_strings[0]; + let (_, descr_first, _, _) = &inaccessible_path_strings[0]; let descr = if inaccessible_path_strings .iter() .skip(1) - .all(|(_, descr, _)| descr == descr_first) + .all(|(_, descr, _, _)| descr == descr_first) { descr_first.to_string() } else { @@ -1848,7 +1887,7 @@ crate fn show_candidates( let mut has_colon = false; let mut spans = Vec::new(); - for (name, _, def_id) in &inaccessible_path_strings { + for (name, _, def_id, _) in &inaccessible_path_strings { if let Some(local_def_id) = def_id.and_then(|did| did.as_local()) { let span = definitions.def_span(local_def_id); let span = session.source_map().guess_head_span(span); @@ -1868,6 +1907,10 @@ crate fn show_candidates( multi_span.push_span_label(span, format!("`{}`: not accessible", name)); } + for note in inaccessible_path_strings.iter().map(|cand| cand.3.as_ref()).flatten() { + err.note(note); + } + err.span_note(multi_span, &msg); } } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 1748a9be8e1..5f90fcdfa64 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1502,6 +1502,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { descr: "module", path, accessible: true, + note: None, }, )); } else { diff --git a/src/test/ui/suggestions/suggest-tryinto-edition-change.rs b/src/test/ui/suggestions/suggest-tryinto-edition-change.rs index f60ef587cfa..f03b42bbe47 100644 --- a/src/test/ui/suggestions/suggest-tryinto-edition-change.rs +++ b/src/test/ui/suggestions/suggest-tryinto-edition-change.rs @@ -1,11 +1,31 @@ -// Make sure that calling `.try_into()` in pre-2021 mentions Edition 2021 change +// Make sure that trying to access `TryInto`, `TryFrom`, `FromIterator` in pre-2021 mentions +// Edition 2021 change // edition:2018 fn test() { - let i: i16 = 0_i32.try_into().unwrap(); + let _i: i16 = 0_i32.try_into().unwrap(); //~^ ERROR no method named `try_into` found for type `i32` in the current scope //~| NOTE method not found in `i32` //~| NOTE 'std::convert::TryInto' is included in the prelude starting in Edition 2021 + + let _i: i16 = TryFrom::try_from(0_i32).unwrap(); + //~^ ERROR failed to resolve: use of undeclared type + //~| NOTE not found in this scope + //~| NOTE 'std::convert::TryFrom' is included in the prelude starting in Edition 2021 + //~| NOTE 'core::convert::TryFrom' is included in the prelude starting in Edition 2021 + + let _i: i16 = TryInto::try_into(0_i32).unwrap(); + //~^ ERROR failed to resolve: use of undeclared type + //~| NOTE not found in this scope + //~| NOTE 'std::convert::TryInto' is included in the prelude starting in Edition 2021 + //~| NOTE 'core::convert::TryInto' is included in the prelude starting in Edition 2021 + + let _v: Vec<_> = FromIterator::from_iter(&[1]); + //~^ ERROR failed to resolve: use of undeclared type + //~| NOTE 'std::iter::FromIterator' is included in the prelude starting in Edition 2021 + //~| NOTE 'core::iter::FromIterator' is included in the prelude starting in Edition 2021 } -fn main() {} +fn main() { + test(); +} diff --git a/src/test/ui/suggestions/suggest-tryinto-edition-change.stderr b/src/test/ui/suggestions/suggest-tryinto-edition-change.stderr index 7915937023c..80191ec4fd7 100644 --- a/src/test/ui/suggestions/suggest-tryinto-edition-change.stderr +++ b/src/test/ui/suggestions/suggest-tryinto-edition-change.stderr @@ -1,8 +1,62 @@ -error[E0599]: no method named `try_into` found for type `i32` in the current scope - --> $DIR/suggest-tryinto-edition-change.rs:5:24 +error[E0433]: failed to resolve: use of undeclared type `TryFrom` + --> $DIR/suggest-tryinto-edition-change.rs:11:19 | -LL | let i: i16 = 0_i32.try_into().unwrap(); - | ^^^^^^^^ method not found in `i32` +LL | let _i: i16 = TryFrom::try_from(0_i32).unwrap(); + | ^^^^^^^ not found in this scope + | + = note: 'std::convert::TryFrom' is included in the prelude starting in Edition 2021 + = note: 'core::convert::TryFrom' is included in the prelude starting in Edition 2021 +help: consider importing one of these items + | +LL | use std::convert::TryFrom; + | +LL | use core::convert::TryFrom; + | + +error[E0433]: failed to resolve: use of undeclared type `TryInto` + --> $DIR/suggest-tryinto-edition-change.rs:17:19 + | +LL | let _i: i16 = TryInto::try_into(0_i32).unwrap(); + | ^^^^^^^ not found in this scope + | + = note: 'std::convert::TryInto' is included in the prelude starting in Edition 2021 + = note: 'core::convert::TryInto' is included in the prelude starting in Edition 2021 +help: consider importing one of these items + | +LL | use std::convert::TryInto; + | +LL | use core::convert::TryInto; + | + +error[E0433]: failed to resolve: use of undeclared type `FromIterator` + --> $DIR/suggest-tryinto-edition-change.rs:23:22 + | +LL | let _v: Vec<_> = FromIterator::from_iter(&[1]); + | ^^^^^^^^^^^^ + | + ::: $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL + | +LL | pub trait IntoIterator { + | ---------------------- similarly named trait `IntoIterator` defined here + | + = note: 'std::iter::FromIterator' is included in the prelude starting in Edition 2021 + = note: 'core::iter::FromIterator' is included in the prelude starting in Edition 2021 +help: a trait with a similar name exists + | +LL | let _v: Vec<_> = IntoIterator::from_iter(&[1]); + | ~~~~~~~~~~~~ +help: consider importing one of these items + | +LL | use std::iter::FromIterator; + | +LL | use core::iter::FromIterator; + | + +error[E0599]: no method named `try_into` found for type `i32` in the current scope + --> $DIR/suggest-tryinto-edition-change.rs:6:25 + | +LL | let _i: i16 = 0_i32.try_into().unwrap(); + | ^^^^^^^^ method not found in `i32` | ::: $SRC_DIR/core/src/convert/mod.rs:LL:COL | @@ -16,6 +70,7 @@ help: the following trait is implemented but not in scope; perhaps add a `use` f LL | use std::convert::TryInto; | -error: aborting due to previous error +error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0599`. +Some errors have detailed explanations: E0433, E0599. +For more information about an error, try `rustc --explain E0433`.