]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_hir_typeck/src/method/prelude2021.rs
ca4cdf5a0d01c9b010216e0936f8d33f1ae10303
[rust.git] / compiler / rustc_hir_typeck / src / method / prelude2021.rs
1 use hir::def_id::DefId;
2 use hir::HirId;
3 use hir::ItemKind;
4 use rustc_ast::Mutability;
5 use rustc_errors::Applicability;
6 use rustc_hir as hir;
7 use rustc_middle::ty::subst::InternalSubsts;
8 use rustc_middle::ty::{Adt, Array, Ref, Ty};
9 use rustc_session::lint::builtin::RUST_2021_PRELUDE_COLLISIONS;
10 use rustc_span::symbol::kw::{Empty, Underscore};
11 use rustc_span::symbol::{sym, Ident};
12 use rustc_span::Span;
13 use rustc_trait_selection::infer::InferCtxtExt;
14
15 use crate::check::{
16     method::probe::{self, Pick},
17     FnCtxt,
18 };
19
20 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
21     pub(super) fn lint_dot_call_from_2018(
22         &self,
23         self_ty: Ty<'tcx>,
24         segment: &hir::PathSegment<'_>,
25         span: Span,
26         call_expr: &'tcx hir::Expr<'tcx>,
27         self_expr: &'tcx hir::Expr<'tcx>,
28         pick: &Pick<'tcx>,
29         args: &'tcx [hir::Expr<'tcx>],
30     ) {
31         debug!(
32             "lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})",
33             segment.ident, self_ty, call_expr, self_expr
34         );
35
36         // Rust 2021 and later is already using the new prelude
37         if span.rust_2021() {
38             return;
39         }
40
41         let prelude_or_array_lint = match segment.ident.name {
42             // `try_into` was added to the prelude in Rust 2021.
43             sym::try_into => RUST_2021_PRELUDE_COLLISIONS,
44             // `into_iter` wasn't added to the prelude,
45             // but `[T; N].into_iter()` doesn't resolve to IntoIterator::into_iter
46             // before Rust 2021, which results in the same problem.
47             // It is only a problem for arrays.
48             sym::into_iter if let Array(..) = self_ty.kind() => {
49                 // In this case, it wasn't really a prelude addition that was the problem.
50                 // Instead, the problem is that the array-into_iter hack will no longer apply in Rust 2021.
51                 rustc_lint::ARRAY_INTO_ITER
52             }
53             _ => return,
54         };
55
56         // No need to lint if method came from std/core, as that will now be in the prelude
57         if matches!(self.tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) {
58             return;
59         }
60
61         if matches!(pick.kind, probe::PickKind::InherentImplPick | probe::PickKind::ObjectPick) {
62             // avoid repeatedly adding unneeded `&*`s
63             if pick.autoderefs == 1
64                 && matches!(
65                     pick.autoref_or_ptr_adjustment,
66                     Some(probe::AutorefOrPtrAdjustment::Autoref { .. })
67                 )
68                 && matches!(self_ty.kind(), Ref(..))
69             {
70                 return;
71             }
72
73             // if it's an inherent `self` method (not `&self` or `&mut self`), it will take
74             // precedence over the `TryInto` impl, and thus won't break in 2021 edition
75             if pick.autoderefs == 0 && pick.autoref_or_ptr_adjustment.is_none() {
76                 return;
77             }
78
79             // Inherent impls only require not relying on autoref and autoderef in order to
80             // ensure that the trait implementation won't be used
81             self.tcx.struct_span_lint_hir(
82                 prelude_or_array_lint,
83                 self_expr.hir_id,
84                 self_expr.span,
85                 format!("trait method `{}` will become ambiguous in Rust 2021", segment.ident.name),
86                 |lint| {
87                     let sp = self_expr.span;
88
89                     let derefs = "*".repeat(pick.autoderefs);
90
91                     let autoref = match pick.autoref_or_ptr_adjustment {
92                         Some(probe::AutorefOrPtrAdjustment::Autoref {
93                             mutbl: Mutability::Mut,
94                             ..
95                         }) => "&mut ",
96                         Some(probe::AutorefOrPtrAdjustment::Autoref {
97                             mutbl: Mutability::Not,
98                             ..
99                         }) => "&",
100                         Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
101                     };
102                     if let Ok(self_expr) = self.sess().source_map().span_to_snippet(self_expr.span)
103                     {
104                         let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
105                             pick.autoref_or_ptr_adjustment
106                         {
107                             format!("{}{} as *const _", derefs, self_expr)
108                         } else {
109                             format!("{}{}{}", autoref, derefs, self_expr)
110                         };
111
112                         lint.span_suggestion(
113                             sp,
114                             "disambiguate the method call",
115                             format!("({})", self_adjusted),
116                             Applicability::MachineApplicable,
117                         );
118                     } else {
119                         let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
120                             pick.autoref_or_ptr_adjustment
121                         {
122                             format!("{}(...) as *const _", derefs)
123                         } else {
124                             format!("{}{}...", autoref, derefs)
125                         };
126                         lint.span_help(
127                             sp,
128                             &format!("disambiguate the method call with `({})`", self_adjusted,),
129                         );
130                     }
131
132                     lint
133                 },
134             );
135         } else {
136             // trait implementations require full disambiguation to not clash with the new prelude
137             // additions (i.e. convert from dot-call to fully-qualified call)
138             self.tcx.struct_span_lint_hir(
139                 prelude_or_array_lint,
140                 call_expr.hir_id,
141                 call_expr.span,
142                 format!("trait method `{}` will become ambiguous in Rust 2021", segment.ident.name),
143                 |lint| {
144                     let sp = call_expr.span;
145                     let trait_name = self.trait_path_or_bare_name(
146                         span,
147                         call_expr.hir_id,
148                         pick.item.container_id(self.tcx),
149                     );
150
151                     let (self_adjusted, precise) = self.adjust_expr(pick, self_expr, sp);
152                     if precise {
153                         let args = args
154                             .iter()
155                             .map(|arg| {
156                                 let span = arg.span.find_ancestor_inside(sp).unwrap_or_default();
157                                 format!(
158                                     ", {}",
159                                     self.sess().source_map().span_to_snippet(span).unwrap()
160                                 )
161                             })
162                             .collect::<String>();
163
164                         lint.span_suggestion(
165                             sp,
166                             "disambiguate the associated function",
167                             format!(
168                                 "{}::{}{}({}{})",
169                                 trait_name,
170                                 segment.ident.name,
171                                 if let Some(args) = segment.args.as_ref().and_then(|args| self
172                                     .sess()
173                                     .source_map()
174                                     .span_to_snippet(args.span_ext)
175                                     .ok())
176                                 {
177                                     // Keep turbofish.
178                                     format!("::{}", args)
179                                 } else {
180                                     String::new()
181                                 },
182                                 self_adjusted,
183                                 args,
184                             ),
185                             Applicability::MachineApplicable,
186                         );
187                     } else {
188                         lint.span_help(
189                             sp,
190                             &format!(
191                                 "disambiguate the associated function with `{}::{}(...)`",
192                                 trait_name, segment.ident,
193                             ),
194                         );
195                     }
196
197                     lint
198                 },
199             );
200         }
201     }
202
203     pub(super) fn lint_fully_qualified_call_from_2018(
204         &self,
205         span: Span,
206         method_name: Ident,
207         self_ty: Ty<'tcx>,
208         self_ty_span: Span,
209         expr_id: hir::HirId,
210         pick: &Pick<'tcx>,
211     ) {
212         // Rust 2021 and later is already using the new prelude
213         if span.rust_2021() {
214             return;
215         }
216
217         // These are the fully qualified methods added to prelude in Rust 2021
218         if !matches!(method_name.name, sym::try_into | sym::try_from | sym::from_iter) {
219             return;
220         }
221
222         // No need to lint if method came from std/core, as that will now be in the prelude
223         if matches!(self.tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) {
224             return;
225         }
226
227         // For from_iter, check if the type actually implements FromIterator.
228         // If we know it does not, we don't need to warn.
229         if method_name.name == sym::from_iter {
230             if let Some(trait_def_id) = self.tcx.get_diagnostic_item(sym::FromIterator) {
231                 if !self
232                     .infcx
233                     .type_implements_trait(
234                         trait_def_id,
235                         self_ty,
236                         InternalSubsts::empty(),
237                         self.param_env,
238                     )
239                     .may_apply()
240                 {
241                     return;
242                 }
243             }
244         }
245
246         // No need to lint if this is an inherent method called on a specific type, like `Vec::foo(...)`,
247         // since such methods take precedence over trait methods.
248         if matches!(pick.kind, probe::PickKind::InherentImplPick) {
249             return;
250         }
251
252         self.tcx.struct_span_lint_hir(
253             RUST_2021_PRELUDE_COLLISIONS,
254             expr_id,
255             span,
256             format!(
257                 "trait-associated function `{}` will become ambiguous in Rust 2021",
258                 method_name.name
259             ),
260             |lint| {
261                 // "type" refers to either a type or, more likely, a trait from which
262                 // the associated function or method is from.
263                 let container_id = pick.item.container_id(self.tcx);
264                 let trait_path = self.trait_path_or_bare_name(span, expr_id, container_id);
265                 let trait_generics = self.tcx.generics_of(container_id);
266
267                 let trait_name = if trait_generics.params.len() <= trait_generics.has_self as usize
268                 {
269                     trait_path
270                 } else {
271                     let counts = trait_generics.own_counts();
272                     format!(
273                         "{}<{}>",
274                         trait_path,
275                         std::iter::repeat("'_")
276                             .take(counts.lifetimes)
277                             .chain(std::iter::repeat("_").take(
278                                 counts.types + counts.consts - trait_generics.has_self as usize
279                             ))
280                             .collect::<Vec<_>>()
281                             .join(", ")
282                     )
283                 };
284
285                 let mut self_ty_name = self_ty_span
286                     .find_ancestor_inside(span)
287                     .and_then(|span| self.sess().source_map().span_to_snippet(span).ok())
288                     .unwrap_or_else(|| self_ty.to_string());
289
290                 // Get the number of generics the self type has (if an Adt) unless we can determine that
291                 // the user has written the self type with generics already which we (naively) do by looking
292                 // for a "<" in `self_ty_name`.
293                 if !self_ty_name.contains('<') {
294                     if let Adt(def, _) = self_ty.kind() {
295                         let generics = self.tcx.generics_of(def.did());
296                         if !generics.params.is_empty() {
297                             let counts = generics.own_counts();
298                             self_ty_name += &format!(
299                                 "<{}>",
300                                 std::iter::repeat("'_")
301                                     .take(counts.lifetimes)
302                                     .chain(
303                                         std::iter::repeat("_").take(counts.types + counts.consts)
304                                     )
305                                     .collect::<Vec<_>>()
306                                     .join(", ")
307                             );
308                         }
309                     }
310                 }
311                 lint.span_suggestion(
312                     span,
313                     "disambiguate the associated function",
314                     format!("<{} as {}>::{}", self_ty_name, trait_name, method_name.name,),
315                     Applicability::MachineApplicable,
316                 );
317
318                 lint
319             },
320         );
321     }
322
323     fn trait_path_or_bare_name(
324         &self,
325         span: Span,
326         expr_hir_id: HirId,
327         trait_def_id: DefId,
328     ) -> String {
329         self.trait_path(span, expr_hir_id, trait_def_id).unwrap_or_else(|| {
330             let key = self.tcx.def_key(trait_def_id);
331             format!("{}", key.disambiguated_data.data)
332         })
333     }
334
335     fn trait_path(&self, span: Span, expr_hir_id: HirId, trait_def_id: DefId) -> Option<String> {
336         let applicable_traits = self.tcx.in_scope_traits(expr_hir_id)?;
337         let applicable_trait = applicable_traits.iter().find(|t| t.def_id == trait_def_id)?;
338         if applicable_trait.import_ids.is_empty() {
339             // The trait was declared within the module, we only need to use its name.
340             return None;
341         }
342
343         let import_items: Vec<_> = applicable_trait
344             .import_ids
345             .iter()
346             .map(|&import_id| self.tcx.hir().expect_item(import_id))
347             .collect();
348
349         // Find an identifier with which this trait was imported (note that `_` doesn't count).
350         let any_id = import_items
351             .iter()
352             .filter_map(|item| if item.ident.name != Underscore { Some(item.ident) } else { None })
353             .next();
354         if let Some(any_id) = any_id {
355             if any_id.name == Empty {
356                 // Glob import, so just use its name.
357                 return None;
358             } else {
359                 return Some(format!("{}", any_id));
360             }
361         }
362
363         // All that is left is `_`! We need to use the full path. It doesn't matter which one we pick,
364         // so just take the first one.
365         match import_items[0].kind {
366             ItemKind::Use(path, _) => Some(
367                 path.segments
368                     .iter()
369                     .map(|segment| segment.ident.to_string())
370                     .collect::<Vec<_>>()
371                     .join("::"),
372             ),
373             _ => {
374                 span_bug!(span, "unexpected item kind, expected a use: {:?}", import_items[0].kind);
375             }
376         }
377     }
378
379     /// Creates a string version of the `expr` that includes explicit adjustments.
380     /// Returns the string and also a bool indicating whether this is a *precise*
381     /// suggestion.
382     fn adjust_expr(
383         &self,
384         pick: &Pick<'tcx>,
385         expr: &hir::Expr<'tcx>,
386         outer: Span,
387     ) -> (String, bool) {
388         let derefs = "*".repeat(pick.autoderefs);
389
390         let autoref = match pick.autoref_or_ptr_adjustment {
391             Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Mut, .. }) => "&mut ",
392             Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Not, .. }) => "&",
393             Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
394         };
395
396         let (expr_text, precise) = if let Some(expr_text) = expr
397             .span
398             .find_ancestor_inside(outer)
399             .and_then(|span| self.sess().source_map().span_to_snippet(span).ok())
400         {
401             (expr_text, true)
402         } else {
403             ("(..)".to_string(), false)
404         };
405
406         let adjusted_text = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
407             pick.autoref_or_ptr_adjustment
408         {
409             format!("{}{} as *const _", derefs, expr_text)
410         } else {
411             format!("{}{}{}", autoref, derefs, expr_text)
412         };
413
414         (adjusted_text, precise)
415     }
416 }