2 method::probe::{self, Pick},
5 use hir::def_id::DefId;
8 use rustc_ast::Mutability;
9 use rustc_errors::Applicability;
11 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
12 use rustc_middle::ty::{Adt, Array, Ref, Ty};
13 use rustc_session::lint::builtin::RUST_2021_PRELUDE_COLLISIONS;
14 use rustc_span::symbol::kw::{Empty, Underscore};
15 use rustc_span::symbol::{sym, Ident};
17 use rustc_trait_selection::infer::InferCtxtExt;
19 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
20 pub(super) fn lint_dot_call_from_2018(
23 segment: &hir::PathSegment<'_>,
25 call_expr: &'tcx hir::Expr<'tcx>,
26 self_expr: &'tcx hir::Expr<'tcx>,
28 args: &'tcx [hir::Expr<'tcx>],
31 "lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})",
32 segment.ident, self_ty, call_expr, self_expr
35 // Rust 2021 and later is already using the new prelude
40 let prelude_or_array_lint = match segment.ident.name {
41 // `try_into` was added to the prelude in Rust 2021.
42 sym::try_into => RUST_2021_PRELUDE_COLLISIONS,
43 // `into_iter` wasn't added to the prelude,
44 // but `[T; N].into_iter()` doesn't resolve to IntoIterator::into_iter
45 // before Rust 2021, which results in the same problem.
46 // It is only a problem for arrays.
47 sym::into_iter if let Array(..) = self_ty.kind() => {
48 // In this case, it wasn't really a prelude addition that was the problem.
49 // Instead, the problem is that the array-into_iter hack will no longer apply in Rust 2021.
50 rustc_lint::ARRAY_INTO_ITER
55 // No need to lint if method came from std/core, as that will now be in the prelude
56 if matches!(self.tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) {
60 if matches!(pick.kind, probe::PickKind::InherentImplPick | probe::PickKind::ObjectPick) {
61 // avoid repeatedly adding unneeded `&*`s
62 if pick.autoderefs == 1
64 pick.autoref_or_ptr_adjustment,
65 Some(probe::AutorefOrPtrAdjustment::Autoref { .. })
67 && matches!(self_ty.kind(), Ref(..))
72 // if it's an inherent `self` method (not `&self` or `&mut self`), it will take
73 // precedence over the `TryInto` impl, and thus won't break in 2021 edition
74 if pick.autoderefs == 0 && pick.autoref_or_ptr_adjustment.is_none() {
78 // Inherent impls only require not relying on autoref and autoderef in order to
79 // ensure that the trait implementation won't be used
80 self.tcx.struct_span_lint_hir(
81 prelude_or_array_lint,
84 format!("trait method `{}` will become ambiguous in Rust 2021", segment.ident.name),
86 let sp = self_expr.span;
88 let derefs = "*".repeat(pick.autoderefs);
90 let autoref = match pick.autoref_or_ptr_adjustment {
91 Some(probe::AutorefOrPtrAdjustment::Autoref {
92 mutbl: Mutability::Mut,
95 Some(probe::AutorefOrPtrAdjustment::Autoref {
96 mutbl: Mutability::Not,
99 Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
101 if let Ok(self_expr) = self.sess().source_map().span_to_snippet(self_expr.span)
103 let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
104 pick.autoref_or_ptr_adjustment
106 format!("{}{} as *const _", derefs, self_expr)
108 format!("{}{}{}", autoref, derefs, self_expr)
111 lint.span_suggestion(
113 "disambiguate the method call",
114 format!("({})", self_adjusted),
115 Applicability::MachineApplicable,
118 let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
119 pick.autoref_or_ptr_adjustment
121 format!("{}(...) as *const _", derefs)
123 format!("{}{}...", autoref, derefs)
127 &format!("disambiguate the method call with `({})`", self_adjusted,),
135 // trait implementations require full disambiguation to not clash with the new prelude
136 // additions (i.e. convert from dot-call to fully-qualified call)
137 self.tcx.struct_span_lint_hir(
138 prelude_or_array_lint,
141 format!("trait method `{}` will become ambiguous in Rust 2021", segment.ident.name),
143 let sp = call_expr.span;
144 let trait_name = self.trait_path_or_bare_name(
147 pick.item.container_id(self.tcx),
150 let (self_adjusted, precise) = self.adjust_expr(pick, self_expr, sp);
155 let span = arg.span.find_ancestor_inside(sp).unwrap_or_default();
158 self.sess().source_map().span_to_snippet(span).unwrap()
161 .collect::<String>();
163 lint.span_suggestion(
165 "disambiguate the associated function",
170 if let Some(args) = segment.args.as_ref().and_then(|args| self
173 .span_to_snippet(args.span_ext)
177 format!("::{}", args)
184 Applicability::MachineApplicable,
190 "disambiguate the associated function with `{}::{}(...)`",
191 trait_name, segment.ident,
202 pub(super) fn lint_fully_qualified_call_from_2018(
211 // Rust 2021 and later is already using the new prelude
212 if span.rust_2021() {
216 // These are the fully qualified methods added to prelude in Rust 2021
217 if !matches!(method_name.name, sym::try_into | sym::try_from | sym::from_iter) {
221 // No need to lint if method came from std/core, as that will now be in the prelude
222 if matches!(self.tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) {
226 // For from_iter, check if the type actually implements FromIterator.
227 // If we know it does not, we don't need to warn.
228 if method_name.name == sym::from_iter {
229 if let Some(trait_def_id) = self.tcx.get_diagnostic_item(sym::FromIterator) {
230 let any_type = self.infcx.next_ty_var(TypeVariableOrigin {
231 kind: TypeVariableOriginKind::MiscVariable,
236 .type_implements_trait(trait_def_id, [self_ty, any_type], self.param_env)
244 // No need to lint if this is an inherent method called on a specific type, like `Vec::foo(...)`,
245 // since such methods take precedence over trait methods.
246 if matches!(pick.kind, probe::PickKind::InherentImplPick) {
250 self.tcx.struct_span_lint_hir(
251 RUST_2021_PRELUDE_COLLISIONS,
255 "trait-associated function `{}` will become ambiguous in Rust 2021",
259 // "type" refers to either a type or, more likely, a trait from which
260 // the associated function or method is from.
261 let container_id = pick.item.container_id(self.tcx);
262 let trait_path = self.trait_path_or_bare_name(span, expr_id, container_id);
263 let trait_generics = self.tcx.generics_of(container_id);
265 let trait_name = if trait_generics.params.len() <= trait_generics.has_self as usize
269 let counts = trait_generics.own_counts();
273 std::iter::repeat("'_")
274 .take(counts.lifetimes)
275 .chain(std::iter::repeat("_").take(
276 counts.types + counts.consts - trait_generics.has_self as usize
283 let mut self_ty_name = self_ty_span
284 .find_ancestor_inside(span)
285 .and_then(|span| self.sess().source_map().span_to_snippet(span).ok())
286 .unwrap_or_else(|| self_ty.to_string());
288 // Get the number of generics the self type has (if an Adt) unless we can determine that
289 // the user has written the self type with generics already which we (naively) do by looking
290 // for a "<" in `self_ty_name`.
291 if !self_ty_name.contains('<') {
292 if let Adt(def, _) = self_ty.kind() {
293 let generics = self.tcx.generics_of(def.did());
294 if !generics.params.is_empty() {
295 let counts = generics.own_counts();
296 self_ty_name += &format!(
298 std::iter::repeat("'_")
299 .take(counts.lifetimes)
301 std::iter::repeat("_").take(counts.types + counts.consts)
309 lint.span_suggestion(
311 "disambiguate the associated function",
312 format!("<{} as {}>::{}", self_ty_name, trait_name, method_name.name,),
313 Applicability::MachineApplicable,
321 fn trait_path_or_bare_name(
327 self.trait_path(span, expr_hir_id, trait_def_id).unwrap_or_else(|| {
328 let key = self.tcx.def_key(trait_def_id);
329 format!("{}", key.disambiguated_data.data)
333 fn trait_path(&self, span: Span, expr_hir_id: HirId, trait_def_id: DefId) -> Option<String> {
334 let applicable_traits = self.tcx.in_scope_traits(expr_hir_id)?;
335 let applicable_trait = applicable_traits.iter().find(|t| t.def_id == trait_def_id)?;
336 if applicable_trait.import_ids.is_empty() {
337 // The trait was declared within the module, we only need to use its name.
341 let import_items: Vec<_> = applicable_trait
344 .map(|&import_id| self.tcx.hir().expect_item(import_id))
347 // Find an identifier with which this trait was imported (note that `_` doesn't count).
348 let any_id = import_items
350 .filter_map(|item| if item.ident.name != Underscore { Some(item.ident) } else { None })
352 if let Some(any_id) = any_id {
353 if any_id.name == Empty {
354 // Glob import, so just use its name.
357 return Some(format!("{}", any_id));
361 // All that is left is `_`! We need to use the full path. It doesn't matter which one we pick,
362 // so just take the first one.
363 match import_items[0].kind {
364 ItemKind::Use(path, _) => Some(
367 .map(|segment| segment.ident.to_string())
372 span_bug!(span, "unexpected item kind, expected a use: {:?}", import_items[0].kind);
377 /// Creates a string version of the `expr` that includes explicit adjustments.
378 /// Returns the string and also a bool indicating whether this is a *precise*
383 expr: &hir::Expr<'tcx>,
385 ) -> (String, bool) {
386 let derefs = "*".repeat(pick.autoderefs);
388 let autoref = match pick.autoref_or_ptr_adjustment {
389 Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Mut, .. }) => "&mut ",
390 Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Not, .. }) => "&",
391 Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
394 let (expr_text, precise) = if let Some(expr_text) = expr
396 .find_ancestor_inside(outer)
397 .and_then(|span| self.sess().source_map().span_to_snippet(span).ok())
401 ("(..)".to_string(), false)
404 let adjusted_text = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
405 pick.autoref_or_ptr_adjustment
407 format!("{}{} as *const _", derefs, expr_text)
409 format!("{}{}{}", autoref, derefs, expr_text)
412 (adjusted_text, precise)