]> git.lizzy.rs Git - rust.git/commitdiff
WIP: Find the imports that were used to reach a method
authorNiko Matsakis <niko@alum.mit.edu>
Thu, 17 Jun 2021 10:10:38 +0000 (06:10 -0400)
committerNiko Matsakis <niko@alum.mit.edu>
Thu, 17 Jun 2021 10:10:38 +0000 (06:10 -0400)
And add tests for some corner cases we have to consider.

compiler/rustc_typeck/src/check/method/prelude2021.rs
src/test/ui/rust-2021/future-prelude-collision-imported.rs [new file with mode: 0644]
src/test/ui/rust-2021/future-prelude-collision-shadow.rs [new file with mode: 0644]

index b7e41525bde47156a28cc55031a5d3237369618e..987ec032476d3eb1bd033c4334cb73bc938bdd4f 100644 (file)
@@ -1,3 +1,5 @@
+use hir::def_id::DefId;
+use hir::HirId;
 use rustc_ast::Mutability;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -48,7 +50,8 @@ pub(super) fn lint_dot_call_from_2018(
             call_expr.span,
             |lint| {
                 let sp = call_expr.span;
-                let trait_name = self.tcx.def_path_str(pick.item.container.id());
+                let trait_name =
+                    self.trait_path_or_bare_name(call_expr.hir_id, pick.item.container.id());
 
                 let mut lint = lint.build(&format!(
                     "trait method `{}` will become ambiguous in Rust 2021",
@@ -144,16 +147,16 @@ pub(super) fn lint_fully_qualified_call_from_2018(
         self.tcx.struct_span_lint_hir(FUTURE_PRELUDE_COLLISION, expr_id, span, |lint| {
             // "type" refers to either a type or, more likely, a trait from which
             // the associated function or method is from.
-            let type_name = self.tcx.def_path_str(pick.item.container.id());
-            let type_generics = self.tcx.generics_of(pick.item.container.id());
+            let trait_path = self.trait_path_or_bare_name(expr_id, pick.item.container.id());
+            let trait_generics = self.tcx.generics_of(pick.item.container.id());
 
-            let parameter_count = type_generics.count() - (type_generics.has_self as usize);
+            let parameter_count = trait_generics.count() - (trait_generics.has_self as usize);
             let trait_name = if parameter_count == 0 {
-                type_name
+                trait_path
             } else {
                 format!(
                     "{}<{}>",
-                    type_name,
+                    trait_path,
                     std::iter::repeat("_").take(parameter_count).collect::<Vec<_>>().join(", ")
                 )
             };
@@ -179,4 +182,28 @@ pub(super) fn lint_fully_qualified_call_from_2018(
             lint.emit();
         });
     }
+
+    fn trait_path_or_bare_name(&self, expr_hir_id: HirId, trait_def_id: DefId) -> String {
+        self.trait_path(expr_hir_id, trait_def_id).unwrap_or_else(|| {
+            let key = self.tcx.def_key(trait_def_id);
+            format!("{}", key.disambiguated_data.data)
+        })
+    }
+
+    fn trait_path(&self, expr_hir_id: HirId, trait_def_id: DefId) -> Option<String> {
+        let applicable_traits = self.tcx.in_scope_traits(expr_hir_id)?;
+        let applicable_trait = applicable_traits.iter().find(|t| t.def_id == trait_def_id)?;
+        if applicable_trait.import_ids.is_empty() {
+            // The trait was declared within the module, we only need to use its name.
+            return None;
+        }
+
+        for &import_id in &applicable_trait.import_ids {
+            let hir_id = self.tcx.hir().local_def_id_to_hir_id(import_id);
+            let item = self.tcx.hir().expect_item(hir_id);
+            debug!(?item, ?import_id, "import_id");
+        }
+
+        return None;
+    }
 }
diff --git a/src/test/ui/rust-2021/future-prelude-collision-imported.rs b/src/test/ui/rust-2021/future-prelude-collision-imported.rs
new file mode 100644 (file)
index 0000000..e85a0bd
--- /dev/null
@@ -0,0 +1,52 @@
+// run-rustfix
+// edition:2018
+// check-pass
+#![warn(future_prelude_collision)]
+#![allow(dead_code)]
+
+mod m {
+    pub trait TryIntoU32 {
+        fn try_into(self) -> Result<u32, ()>;
+    }
+
+    impl TryIntoU32 for u8 {
+        fn try_into(self) -> Result<u32, ()> {
+            Ok(self as u32)
+        }
+    }
+
+    pub trait AnotherTrick {}
+}
+
+mod a {
+    use crate::m::TryIntoU32;
+
+    fn main() {
+        // In this case, we can just use `TryIntoU32`
+        let _: u32 = 3u8.try_into().unwrap();
+    }
+}
+
+mod b {
+    use crate::m::AnotherTrick as TryIntoU32;
+    use crate::m::TryIntoU32 as _;
+
+    fn main() {
+        // In this case, a `TryIntoU32::try_into` rewrite will not work, and we need to use
+        // the path `crate::m::TryIntoU32` (with which it was imported).
+        let _: u32 = 3u8.try_into().unwrap();
+    }
+}
+
+mod c {
+    use super::m::TryIntoU32 as _;
+    use crate::m::AnotherTrick as TryIntoU32;
+
+    fn main() {
+        // In this case, a `TryIntoU32::try_into` rewrite will not work, and we need to use
+        // the path `super::m::TryIntoU32` (with which it was imported).
+        let _: u32 = 3u8.try_into().unwrap();
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/rust-2021/future-prelude-collision-shadow.rs b/src/test/ui/rust-2021/future-prelude-collision-shadow.rs
new file mode 100644 (file)
index 0000000..ef19cf4
--- /dev/null
@@ -0,0 +1,33 @@
+// run-rustfix
+// edition:2018
+// check-pass
+#![warn(future_prelude_collision)]
+#![allow(dead_code)]
+
+mod m {
+    pub trait TryIntoU32 {
+        fn try_into(self) -> Result<u32, ()>;
+    }
+
+    impl TryIntoU32 for u8 {
+        fn try_into(self) -> Result<u32, ()> {
+            Ok(self as u32)
+        }
+    }
+
+    pub trait AnotherTrick {}
+}
+
+mod d {
+    use crate::m::AnotherTrick as TryIntoU32;
+    use crate::m::*;
+
+    fn main() {
+        // Here, `TryIntoU32` is imported but shadowed, but in that case we don't permit its methods
+        // to be available.
+        let _: u32 = 3u8.try_into().unwrap();
+        //~^ ERROR no method name `try_into` found
+    }
+}
+
+fn main() {}