Skip to content

Add hint for people missing TryFrom, TryInto, FromIterator import pre-2021 #90288

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

Merged
merged 6 commits into from
Oct 27, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
56 changes: 49 additions & 7 deletions compiler/rustc_resolve/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>,
}

/// Adjust the impl span so that just the `impl` keyword is taken by removing
Expand Down Expand Up @@ -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,
});
}
}
Expand Down Expand Up @@ -1764,12 +1793,14 @@ crate fn show_candidates(
return;
}

let mut accessible_path_strings: Vec<(String, &str, Option<DefId>)> = Vec::new();
let mut inaccessible_path_strings: Vec<(String, &str, Option<DefId>)> = Vec::new();
let mut accessible_path_strings: Vec<(String, &str, Option<DefId>, &Option<String>)> =
Vec::new();
let mut inaccessible_path_strings: Vec<(String, &str, Option<DefId>, &Option<String>)> =
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
Expand All @@ -1792,6 +1823,10 @@ crate fn show_candidates(
let instead = if instead { " instead" } else { "" };
let mut msg = format!("consider importing {} {}{}", determiner, kind, instead);

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
Expand Down Expand Up @@ -1820,7 +1855,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()) {
Expand All @@ -1832,12 +1867,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 {
Expand All @@ -1848,7 +1886,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);
Expand All @@ -1868,6 +1906,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);
}
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_resolve/src/late/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1502,6 +1502,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
descr: "module",
path,
accessible: true,
note: None,
},
));
} else {
Expand Down
14 changes: 14 additions & 0 deletions compiler/rustc_typeck/src/check/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1203,6 +1203,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut candidates = valid_out_of_scope_traits;
candidates.sort();
candidates.dedup();

// `TryFrom` and `FromIterator` have no methods
let edition_fix = candidates
.iter()
.find(|did| self.tcx.is_diagnostic_item(sym::TryInto, **did))
.map(|&d| d);

err.help("items from traits can only be used if the trait is in scope");
let msg = format!(
"the following {traits_are} implemented but not in scope; \
Expand All @@ -1212,6 +1219,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);

self.suggest_use_candidates(err, msg, candidates);
if let Some(did) = edition_fix {
err.note(&format!(
"'{}' is included in the prelude starting in Edition 2021",
with_crate_prefix(|| self.tcx.def_path_str(did))
));
}

true
} else {
false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ LL | fn try_into(self) -> Result<T, Self::Error>;
| the method is available for `Rc<u8>` here
|
= help: items from traits can only be used if the trait is in scope
= note: 'std::convert::TryInto' is included in the prelude starting in Edition 2021
help: consider wrapping the receiver expression with the appropriate type
|
LL | let _: u32 = Box::new(3u8).try_into().unwrap();
Expand Down
31 changes: 31 additions & 0 deletions src/test/ui/suggestions/suggest-tryinto-edition-change.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// 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();
//~^ 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() {
test();
}
76 changes: 76 additions & 0 deletions src/test/ui/suggestions/suggest-tryinto-edition-change.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
error[E0433]: failed to resolve: use of undeclared type `TryFrom`
--> $DIR/suggest-tryinto-edition-change.rs:11:19
|
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 core::convert::TryFrom;
|
LL | use std::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 core::convert::TryInto;
|
LL | use std::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 core::iter::FromIterator;
|
LL | use std::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
|
LL | fn try_into(self) -> Result<T, Self::Error>;
| -------- the method is available for `i32` here
|
= help: items from traits can only be used if the trait is in scope
= note: 'std::convert::TryInto' is included in the prelude starting in Edition 2021
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
|
LL | use std::convert::TryInto;
|

error: aborting due to 4 previous errors

Some errors have detailed explanations: E0433, E0599.
For more information about an error, try `rustc --explain E0433`.