1 use hir::def_id::DefId;
4 use rustc_ast::Mutability;
5 use rustc_errors::Applicability;
7 use rustc_middle::ty::subst::InternalSubsts;
8 use rustc_middle::ty::{Ref, Ty};
9 use rustc_session::lint::builtin::RUST_2021_PRELUDE_COLLISIONS;
10 use rustc_span::symbol::kw::Underscore;
11 use rustc_span::symbol::{sym, Ident};
13 use rustc_trait_selection::infer::InferCtxtExt;
16 method::probe::{self, Pick},
20 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
21 pub(super) fn lint_dot_call_from_2018(
24 segment: &hir::PathSegment<'_>,
26 call_expr: &'tcx hir::Expr<'tcx>,
27 self_expr: &'tcx hir::Expr<'tcx>,
29 args: &'tcx [hir::Expr<'tcx>],
32 "lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})",
33 segment.ident, self_ty, call_expr, self_expr
36 // Rust 2021 and later is already using the new prelude
41 // These are the method names that were added to prelude in Rust 2021
42 if !matches!(segment.ident.name, sym::try_into) {
46 // No need to lint if method came from std/core, as that will now be in the prelude
47 if matches!(self.tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) {
51 if matches!(pick.kind, probe::PickKind::InherentImplPick | probe::PickKind::ObjectPick) {
52 // avoid repeatedly adding unneeded `&*`s
53 if pick.autoderefs == 1
55 pick.autoref_or_ptr_adjustment,
56 Some(probe::AutorefOrPtrAdjustment::Autoref { .. })
58 && matches!(self_ty.kind(), Ref(..))
63 // if it's an inherent `self` method (not `&self` or `&mut self`), it will take
64 // precedence over the `TryInto` impl, and thus won't break in 2021 edition
65 if pick.autoderefs == 0 && pick.autoref_or_ptr_adjustment.is_none() {
69 // Inherent impls only require not relying on autoref and autoderef in order to
70 // ensure that the trait implementation won't be used
71 self.tcx.struct_span_lint_hir(
72 RUST_2021_PRELUDE_COLLISIONS,
76 let sp = self_expr.span;
78 let mut lint = lint.build(&format!(
79 "trait method `{}` will become ambiguous in Rust 2021",
83 let derefs = "*".repeat(pick.autoderefs);
85 let autoref = match pick.autoref_or_ptr_adjustment {
86 Some(probe::AutorefOrPtrAdjustment::Autoref {
87 mutbl: Mutability::Mut,
90 Some(probe::AutorefOrPtrAdjustment::Autoref {
91 mutbl: Mutability::Not,
94 Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
96 if let Ok(self_expr) = self.sess().source_map().span_to_snippet(self_expr.span)
98 let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
99 pick.autoref_or_ptr_adjustment
101 format!("{}{} as *const _", derefs, self_expr)
103 format!("{}{}{}", autoref, derefs, self_expr)
106 lint.span_suggestion(
108 "disambiguate the method call",
109 format!("({})", self_adjusted),
110 Applicability::MachineApplicable,
113 let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
114 pick.autoref_or_ptr_adjustment
116 format!("{}(...) as *const _", derefs)
118 format!("{}{}...", autoref, derefs)
122 &format!("disambiguate the method call with `({})`", self_adjusted,),
130 // trait implementations require full disambiguation to not clash with the new prelude
131 // additions (i.e. convert from dot-call to fully-qualified call)
132 self.tcx.struct_span_lint_hir(
133 RUST_2021_PRELUDE_COLLISIONS,
137 let sp = call_expr.span;
138 let trait_name = self.trait_path_or_bare_name(
141 pick.item.container.id(),
144 let mut lint = lint.build(&format!(
145 "trait method `{}` will become ambiguous in Rust 2021",
149 let (self_adjusted, precise) = self.adjust_expr(pick, self_expr);
157 self.sess().source_map().span_to_snippet(arg.span).unwrap()
160 .collect::<String>();
162 lint.span_suggestion(
164 "disambiguate the associated function",
167 trait_name, segment.ident.name, self_adjusted, args
169 Applicability::MachineApplicable,
175 "disambiguate the associated function with `{}::{}(...)`",
176 trait_name, segment.ident,
187 pub(super) fn lint_fully_qualified_call_from_2018(
196 // Rust 2021 and later is already using the new prelude
197 if span.rust_2021() {
201 // These are the fully qualified methods added to prelude in Rust 2021
202 if !matches!(method_name.name, sym::try_into | sym::try_from | sym::from_iter) {
206 // No need to lint if method came from std/core, as that will now be in the prelude
207 if matches!(self.tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) {
211 // For from_iter, check if the type actualy implements FromIterator.
212 // If we know it does not, we don't need to warn.
213 if method_name.name == sym::from_iter {
214 if let Some(trait_def_id) = self.tcx.get_diagnostic_item(sym::FromIterator) {
217 .type_implements_trait(
220 InternalSubsts::empty(),
230 // No need to lint if this is an inherent method called on a specific type, like `Vec::foo(...)`,
231 // since such methods take precedence over trait methods.
232 if matches!(pick.kind, probe::PickKind::InherentImplPick) {
236 self.tcx.struct_span_lint_hir(RUST_2021_PRELUDE_COLLISIONS, expr_id, span, |lint| {
237 // "type" refers to either a type or, more likely, a trait from which
238 // the associated function or method is from.
239 let trait_path = self.trait_path_or_bare_name(span, expr_id, pick.item.container.id());
240 let trait_generics = self.tcx.generics_of(pick.item.container.id());
242 let parameter_count = trait_generics.count() - (trait_generics.has_self as usize);
243 let trait_name = if parameter_count == 0 {
249 std::iter::repeat("_").take(parameter_count).collect::<Vec<_>>().join(", ")
253 let mut lint = lint.build(&format!(
254 "trait-associated function `{}` will become ambiguous in Rust 2021",
261 .span_to_snippet(self_ty_span)
262 .unwrap_or_else(|_| self_ty.to_string());
264 lint.span_suggestion(
266 "disambiguate the associated function",
267 format!("<{} as {}>::{}", self_ty, trait_name, method_name.name,),
268 Applicability::MachineApplicable,
275 fn trait_path_or_bare_name(
281 self.trait_path(span, expr_hir_id, trait_def_id).unwrap_or_else(|| {
282 let key = self.tcx.def_key(trait_def_id);
283 format!("{}", key.disambiguated_data.data)
287 fn trait_path(&self, span: Span, expr_hir_id: HirId, trait_def_id: DefId) -> Option<String> {
288 let applicable_traits = self.tcx.in_scope_traits(expr_hir_id)?;
289 let applicable_trait = applicable_traits.iter().find(|t| t.def_id == trait_def_id)?;
290 if applicable_trait.import_ids.is_empty() {
291 // The trait was declared within the module, we only need to use its name.
295 let import_items: Vec<_> = applicable_trait
299 let hir_id = self.tcx.hir().local_def_id_to_hir_id(import_id);
300 self.tcx.hir().expect_item(hir_id)
304 // Find an identifier with which this trait was imported (note that `_` doesn't count).
305 let any_id = import_items
307 .filter_map(|item| if item.ident.name != Underscore { Some(item.ident) } else { None })
309 if let Some(any_id) = any_id {
310 return Some(format!("{}", any_id));
313 // All that is left is `_`! We need to use the full path. It doesn't matter which one we pick,
314 // so just take the first one.
315 match import_items[0].kind {
316 ItemKind::Use(path, _) => Some(
319 .map(|segment| segment.ident.to_string())
324 span_bug!(span, "unexpected item kind, expected a use: {:?}", import_items[0].kind);
329 /// Creates a string version of the `expr` that includes explicit adjustments.
330 /// Returns the string and also a bool indicating whther this is a *precise*
332 fn adjust_expr(&self, pick: &Pick<'tcx>, expr: &hir::Expr<'tcx>) -> (String, bool) {
333 let derefs = "*".repeat(pick.autoderefs);
335 let autoref = match pick.autoref_or_ptr_adjustment {
336 Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Mut, .. }) => "&mut ",
337 Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Not, .. }) => "&",
338 Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
341 let (expr_text, precise) =
342 if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) {
345 (format!("(..)"), false)
348 let adjusted_text = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
349 pick.autoref_or_ptr_adjustment
351 format!("{}{} as *const _", derefs, expr_text)
353 format!("{}{}{}", autoref, derefs, expr_text)
356 (adjusted_text, precise)