about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJakob Degen <jakob@degen.com>2021-10-25 20:04:35 -0400
committerJakob Degen <jakob@degen.com>2021-10-26 22:17:01 -0400
commit958e64594672ccd11fba5ec6925e33ef223301ee (patch)
treeb010b428a007c3dbd68c7c71f824bcacfd0b1b30
parente91d5ca197b966b841c4e49c921215a69f9d9c19 (diff)
downloadrust-958e64594672ccd11fba5ec6925e33ef223301ee.tar.gz
rust-958e64594672ccd11fba5ec6925e33ef223301ee.zip
Adds hint if a trait fails to resolve and a newly added one in Edition 2021 is suggested
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs57
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs1
-rw-r--r--src/test/ui/suggestions/suggest-tryinto-edition-change.rs26
-rw-r--r--src/test/ui/suggestions/suggest-tryinto-edition-change.stderr65
4 files changed, 134 insertions, 15 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<String>,
 }
 
 /// 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<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
@@ -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[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 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:5:24
+  --> $DIR/suggest-tryinto-edition-change.rs:6:25
    |
-LL |     let i: i16 = 0_i32.try_into().unwrap();
-   |                        ^^^^^^^^ method not found in `i32`
+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`.