]> git.lizzy.rs Git - rust.git/blobdiff - compiler/rustc_typeck/src/check/method/prelude2021.rs
Rollup merge of #86783 - mark-i-m:mutex-drop-unsized, r=Xanewok
[rust.git] / compiler / rustc_typeck / src / check / method / prelude2021.rs
index b141a4f6f89dae3153ed287b40f81c650f663e35..e8748dd062f5334beda61776ecccc603e3e628cb 100644 (file)
@@ -1,8 +1,12 @@
+use hir::def_id::DefId;
+use hir::HirId;
+use hir::ItemKind;
 use rustc_ast::Mutability;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
-use rustc_middle::ty::Ty;
+use rustc_middle::ty::{Ref, Ty};
 use rustc_session::lint::builtin::FUTURE_PRELUDE_COLLISION;
+use rustc_span::symbol::kw::Underscore;
 use rustc_span::symbol::{sym, Ident};
 use rustc_span::Span;
 
@@ -20,6 +24,7 @@ pub(super) fn lint_dot_call_from_2018(
         call_expr: &'tcx hir::Expr<'tcx>,
         self_expr: &'tcx hir::Expr<'tcx>,
         pick: &Pick<'tcx>,
+        args: &'tcx [hir::Expr<'tcx>],
     ) {
         debug!(
             "lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})",
@@ -41,20 +46,38 @@ pub(super) fn lint_dot_call_from_2018(
             return;
         }
 
-        self.tcx.struct_span_lint_hir(
-            FUTURE_PRELUDE_COLLISION,
-            call_expr.hir_id,
-            call_expr.span,
-            |lint| {
-                let sp = call_expr.span;
-                let trait_name = self.tcx.def_path_str(pick.item.container.id());
+        if matches!(pick.kind, probe::PickKind::InherentImplPick | probe::PickKind::ObjectPick) {
+            // avoid repeatedly adding unneeded `&*`s
+            if pick.autoderefs == 1
+                && matches!(
+                    pick.autoref_or_ptr_adjustment,
+                    Some(probe::AutorefOrPtrAdjustment::Autoref { .. })
+                )
+                && matches!(self_ty.kind(), Ref(..))
+            {
+                return;
+            }
+
+            // if it's an inherent `self` method (not `&self` or `&mut self`), it will take
+            // precedence over the `TryInto` impl, and thus won't break in 2021 edition
+            if pick.autoderefs == 0 && pick.autoref_or_ptr_adjustment.is_none() {
+                return;
+            }
+
+            // Inherent impls only require not relying on autoref and autoderef in order to
+            // ensure that the trait implementation won't be used
+            self.tcx.struct_span_lint_hir(
+                FUTURE_PRELUDE_COLLISION,
+                self_expr.hir_id,
+                self_expr.span,
+                |lint| {
+                    let sp = self_expr.span;
 
-                let mut lint = lint.build(&format!(
-                    "trait method `{}` will become ambiguous in Rust 2021",
-                    segment.ident.name
-                ));
+                    let mut lint = lint.build(&format!(
+                        "trait method `{}` will become ambiguous in Rust 2021",
+                        segment.ident.name
+                    ));
 
-                if let Ok(self_expr) = self.sess().source_map().span_to_snippet(self_expr.span) {
                     let derefs = "*".repeat(pick.autoderefs);
 
                     let autoref = match pick.autoref_or_ptr_adjustment {
@@ -68,32 +91,95 @@ pub(super) fn lint_dot_call_from_2018(
                         }) => "&",
                         Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
                     };
-                    let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
-                        pick.autoref_or_ptr_adjustment
+                    if let Ok(self_expr) = self.sess().source_map().span_to_snippet(self_expr.span)
                     {
-                        format!("{}{} as *const _", derefs, self_expr)
+                        let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
+                            pick.autoref_or_ptr_adjustment
+                        {
+                            format!("{}{} as *const _", derefs, self_expr)
+                        } else {
+                            format!("{}{}{}", autoref, derefs, self_expr)
+                        };
+
+                        lint.span_suggestion(
+                            sp,
+                            "disambiguate the method call",
+                            format!("({})", self_adjusted),
+                            Applicability::MachineApplicable,
+                        );
                     } else {
-                        format!("{}{}{}", autoref, derefs, self_expr)
-                    };
-                    lint.span_suggestion(
-                        sp,
-                        "disambiguate the associated function",
-                        format!("{}::{}({})", trait_name, segment.ident.name, self_adjusted,),
-                        Applicability::MachineApplicable,
-                    );
-                } else {
-                    lint.span_help(
-                        sp,
-                        &format!(
-                            "disambiguate the associated function with `{}::{}(...)`",
-                            trait_name, segment.ident,
-                        ),
+                        let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
+                            pick.autoref_or_ptr_adjustment
+                        {
+                            format!("{}(...) as *const _", derefs)
+                        } else {
+                            format!("{}{}...", autoref, derefs)
+                        };
+                        lint.span_help(
+                            sp,
+                            &format!("disambiguate the method call with `({})`", self_adjusted,),
+                        );
+                    }
+
+                    lint.emit();
+                },
+            );
+        } else {
+            // trait implementations require full disambiguation to not clash with the new prelude
+            // additions (i.e. convert from dot-call to fully-qualified call)
+            self.tcx.struct_span_lint_hir(
+                FUTURE_PRELUDE_COLLISION,
+                call_expr.hir_id,
+                call_expr.span,
+                |lint| {
+                    let sp = call_expr.span;
+                    let trait_name = self.trait_path_or_bare_name(
+                        span,
+                        call_expr.hir_id,
+                        pick.item.container.id(),
                     );
-                }
 
-                lint.emit();
-            },
-        );
+                    let mut lint = lint.build(&format!(
+                        "trait method `{}` will become ambiguous in Rust 2021",
+                        segment.ident.name
+                    ));
+
+                    let (self_adjusted, precise) = self.adjust_expr(pick, self_expr);
+                    if precise {
+                        let args = args
+                            .iter()
+                            .skip(1)
+                            .map(|arg| {
+                                format!(
+                                    ", {}",
+                                    self.sess().source_map().span_to_snippet(arg.span).unwrap()
+                                )
+                            })
+                            .collect::<String>();
+
+                        lint.span_suggestion(
+                            sp,
+                            "disambiguate the associated function",
+                            format!(
+                                "{}::{}({}{})",
+                                trait_name, segment.ident.name, self_adjusted, args
+                            ),
+                            Applicability::MachineApplicable,
+                        );
+                    } else {
+                        lint.span_help(
+                            sp,
+                            &format!(
+                                "disambiguate the associated function with `{}::{}(...)`",
+                                trait_name, segment.ident,
+                            ),
+                        );
+                    }
+
+                    lint.emit();
+                },
+            );
+        }
     }
 
     pub(super) fn lint_fully_qualified_call_from_2018(
@@ -129,16 +215,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(span, 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(", ")
                 )
             };
@@ -164,4 +250,88 @@ pub(super) fn lint_fully_qualified_call_from_2018(
             lint.emit();
         });
     }
+
+    fn trait_path_or_bare_name(
+        &self,
+        span: Span,
+        expr_hir_id: HirId,
+        trait_def_id: DefId,
+    ) -> String {
+        self.trait_path(span, 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, span: Span, 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;
+        }
+
+        let import_items: Vec<_> = applicable_trait
+            .import_ids
+            .iter()
+            .map(|&import_id| {
+                let hir_id = self.tcx.hir().local_def_id_to_hir_id(import_id);
+                self.tcx.hir().expect_item(hir_id)
+            })
+            .collect();
+
+        // Find an identifier with which this trait was imported (note that `_` doesn't count).
+        let any_id = import_items
+            .iter()
+            .filter_map(|item| if item.ident.name != Underscore { Some(item.ident) } else { None })
+            .next();
+        if let Some(any_id) = any_id {
+            return Some(format!("{}", any_id));
+        }
+
+        // All that is left is `_`! We need to use the full path. It doesn't matter which one we pick,
+        // so just take the first one.
+        match import_items[0].kind {
+            ItemKind::Use(path, _) => Some(
+                path.segments
+                    .iter()
+                    .map(|segment| segment.ident.to_string())
+                    .collect::<Vec<_>>()
+                    .join("::"),
+            ),
+            _ => {
+                span_bug!(span, "unexpected item kind, expected a use: {:?}", import_items[0].kind);
+            }
+        }
+    }
+
+    /// Creates a string version of the `expr` that includes explicit adjustments.
+    /// Returns the string and also a bool indicating whther this is a *precise*
+    /// suggestion.
+    fn adjust_expr(&self, pick: &Pick<'tcx>, expr: &hir::Expr<'tcx>) -> (String, bool) {
+        let derefs = "*".repeat(pick.autoderefs);
+
+        let autoref = match pick.autoref_or_ptr_adjustment {
+            Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Mut, .. }) => "&mut ",
+            Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Not, .. }) => "&",
+            Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
+        };
+
+        let (expr_text, precise) =
+            if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) {
+                (expr_text, true)
+            } else {
+                (format!("(..)"), false)
+            };
+
+        let adjusted_text = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
+            pick.autoref_or_ptr_adjustment
+        {
+            format!("{}{} as *const _", derefs, expr_text)
+        } else {
+            format!("{}{}{}", autoref, derefs, expr_text)
+        };
+
+        (adjusted_text, precise)
+    }
 }