]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_typeck/src/check/method/prelude2021.rs
Auto merge of #102189 - davidtwco:translation-derive-enums, r=compiler-errors
[rust.git] / compiler / rustc_typeck / src / check / 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                 |lint| {
86                     let sp = self_expr.span;
87
88                     let mut lint = lint.build(&format!(
89                         "trait method `{}` will become ambiguous in Rust 2021",
90                         segment.ident.name
91                     ));
92
93                     let derefs = "*".repeat(pick.autoderefs);
94
95                     let autoref = match pick.autoref_or_ptr_adjustment {
96                         Some(probe::AutorefOrPtrAdjustment::Autoref {
97                             mutbl: Mutability::Mut,
98                             ..
99                         }) => "&mut ",
100                         Some(probe::AutorefOrPtrAdjustment::Autoref {
101                             mutbl: Mutability::Not,
102                             ..
103                         }) => "&",
104                         Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
105                     };
106                     if let Ok(self_expr) = self.sess().source_map().span_to_snippet(self_expr.span)
107                     {
108                         let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
109                             pick.autoref_or_ptr_adjustment
110                         {
111                             format!("{}{} as *const _", derefs, self_expr)
112                         } else {
113                             format!("{}{}{}", autoref, derefs, self_expr)
114                         };
115
116                         lint.span_suggestion(
117                             sp,
118                             "disambiguate the method call",
119                             format!("({})", self_adjusted),
120                             Applicability::MachineApplicable,
121                         );
122                     } else {
123                         let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
124                             pick.autoref_or_ptr_adjustment
125                         {
126                             format!("{}(...) as *const _", derefs)
127                         } else {
128                             format!("{}{}...", autoref, derefs)
129                         };
130                         lint.span_help(
131                             sp,
132                             &format!("disambiguate the method call with `({})`", self_adjusted,),
133                         );
134                     }
135
136                     lint.emit();
137                 },
138             );
139         } else {
140             // trait implementations require full disambiguation to not clash with the new prelude
141             // additions (i.e. convert from dot-call to fully-qualified call)
142             self.tcx.struct_span_lint_hir(
143                 prelude_or_array_lint,
144                 call_expr.hir_id,
145                 call_expr.span,
146                 |lint| {
147                     let sp = call_expr.span;
148                     let trait_name = self.trait_path_or_bare_name(
149                         span,
150                         call_expr.hir_id,
151                         pick.item.container_id(self.tcx),
152                     );
153
154                     let mut lint = lint.build(&format!(
155                         "trait method `{}` will become ambiguous in Rust 2021",
156                         segment.ident.name
157                     ));
158
159                     let (self_adjusted, precise) = self.adjust_expr(pick, self_expr, sp);
160                     if precise {
161                         let args = args
162                             .iter()
163                             .map(|arg| {
164                                 let span = arg.span.find_ancestor_inside(sp).unwrap_or_default();
165                                 format!(
166                                     ", {}",
167                                     self.sess().source_map().span_to_snippet(span).unwrap()
168                                 )
169                             })
170                             .collect::<String>();
171
172                         lint.span_suggestion(
173                             sp,
174                             "disambiguate the associated function",
175                             format!(
176                                 "{}::{}{}({}{})",
177                                 trait_name,
178                                 segment.ident.name,
179                                 if let Some(args) = segment.args.as_ref().and_then(|args| self
180                                     .sess()
181                                     .source_map()
182                                     .span_to_snippet(args.span_ext)
183                                     .ok())
184                                 {
185                                     // Keep turbofish.
186                                     format!("::{}", args)
187                                 } else {
188                                     String::new()
189                                 },
190                                 self_adjusted,
191                                 args,
192                             ),
193                             Applicability::MachineApplicable,
194                         );
195                     } else {
196                         lint.span_help(
197                             sp,
198                             &format!(
199                                 "disambiguate the associated function with `{}::{}(...)`",
200                                 trait_name, segment.ident,
201                             ),
202                         );
203                     }
204
205                     lint.emit();
206                 },
207             );
208         }
209     }
210
211     pub(super) fn lint_fully_qualified_call_from_2018(
212         &self,
213         span: Span,
214         method_name: Ident,
215         self_ty: Ty<'tcx>,
216         self_ty_span: Span,
217         expr_id: hir::HirId,
218         pick: &Pick<'tcx>,
219     ) {
220         // Rust 2021 and later is already using the new prelude
221         if span.rust_2021() {
222             return;
223         }
224
225         // These are the fully qualified methods added to prelude in Rust 2021
226         if !matches!(method_name.name, sym::try_into | sym::try_from | sym::from_iter) {
227             return;
228         }
229
230         // No need to lint if method came from std/core, as that will now be in the prelude
231         if matches!(self.tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) {
232             return;
233         }
234
235         // For from_iter, check if the type actually implements FromIterator.
236         // If we know it does not, we don't need to warn.
237         if method_name.name == sym::from_iter {
238             if let Some(trait_def_id) = self.tcx.get_diagnostic_item(sym::FromIterator) {
239                 if !self
240                     .infcx
241                     .type_implements_trait(
242                         trait_def_id,
243                         self_ty,
244                         InternalSubsts::empty(),
245                         self.param_env,
246                     )
247                     .may_apply()
248                 {
249                     return;
250                 }
251             }
252         }
253
254         // No need to lint if this is an inherent method called on a specific type, like `Vec::foo(...)`,
255         // since such methods take precedence over trait methods.
256         if matches!(pick.kind, probe::PickKind::InherentImplPick) {
257             return;
258         }
259
260         self.tcx.struct_span_lint_hir(RUST_2021_PRELUDE_COLLISIONS, expr_id, span, |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 =
268                 if trait_generics.params.len() <= trait_generics.has_self as usize {
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 lint = lint.build(&format!(
286                 "trait-associated function `{}` will become ambiguous in Rust 2021",
287                 method_name.name
288             ));
289
290             let mut self_ty_name = self_ty_span
291                 .find_ancestor_inside(span)
292                 .and_then(|span| self.sess().source_map().span_to_snippet(span).ok())
293                 .unwrap_or_else(|| self_ty.to_string());
294
295             // Get the number of generics the self type has (if an Adt) unless we can determine that
296             // the user has written the self type with generics already which we (naively) do by looking
297             // for a "<" in `self_ty_name`.
298             if !self_ty_name.contains('<') {
299                 if let Adt(def, _) = self_ty.kind() {
300                     let generics = self.tcx.generics_of(def.did());
301                     if !generics.params.is_empty() {
302                         let counts = generics.own_counts();
303                         self_ty_name += &format!(
304                             "<{}>",
305                             std::iter::repeat("'_")
306                                 .take(counts.lifetimes)
307                                 .chain(std::iter::repeat("_").take(counts.types + counts.consts))
308                                 .collect::<Vec<_>>()
309                                 .join(", ")
310                         );
311                     }
312                 }
313             }
314             lint.span_suggestion(
315                 span,
316                 "disambiguate the associated function",
317                 format!("<{} as {}>::{}", self_ty_name, trait_name, method_name.name,),
318                 Applicability::MachineApplicable,
319             );
320
321             lint.emit();
322         });
323     }
324
325     fn trait_path_or_bare_name(
326         &self,
327         span: Span,
328         expr_hir_id: HirId,
329         trait_def_id: DefId,
330     ) -> String {
331         self.trait_path(span, expr_hir_id, trait_def_id).unwrap_or_else(|| {
332             let key = self.tcx.def_key(trait_def_id);
333             format!("{}", key.disambiguated_data.data)
334         })
335     }
336
337     fn trait_path(&self, span: Span, expr_hir_id: HirId, trait_def_id: DefId) -> Option<String> {
338         let applicable_traits = self.tcx.in_scope_traits(expr_hir_id)?;
339         let applicable_trait = applicable_traits.iter().find(|t| t.def_id == trait_def_id)?;
340         if applicable_trait.import_ids.is_empty() {
341             // The trait was declared within the module, we only need to use its name.
342             return None;
343         }
344
345         let import_items: Vec<_> = applicable_trait
346             .import_ids
347             .iter()
348             .map(|&import_id| self.tcx.hir().expect_item(import_id))
349             .collect();
350
351         // Find an identifier with which this trait was imported (note that `_` doesn't count).
352         let any_id = import_items
353             .iter()
354             .filter_map(|item| if item.ident.name != Underscore { Some(item.ident) } else { None })
355             .next();
356         if let Some(any_id) = any_id {
357             if any_id.name == Empty {
358                 // Glob import, so just use its name.
359                 return None;
360             } else {
361                 return Some(format!("{}", any_id));
362             }
363         }
364
365         // All that is left is `_`! We need to use the full path. It doesn't matter which one we pick,
366         // so just take the first one.
367         match import_items[0].kind {
368             ItemKind::Use(path, _) => Some(
369                 path.segments
370                     .iter()
371                     .map(|segment| segment.ident.to_string())
372                     .collect::<Vec<_>>()
373                     .join("::"),
374             ),
375             _ => {
376                 span_bug!(span, "unexpected item kind, expected a use: {:?}", import_items[0].kind);
377             }
378         }
379     }
380
381     /// Creates a string version of the `expr` that includes explicit adjustments.
382     /// Returns the string and also a bool indicating whether this is a *precise*
383     /// suggestion.
384     fn adjust_expr(
385         &self,
386         pick: &Pick<'tcx>,
387         expr: &hir::Expr<'tcx>,
388         outer: Span,
389     ) -> (String, bool) {
390         let derefs = "*".repeat(pick.autoderefs);
391
392         let autoref = match pick.autoref_or_ptr_adjustment {
393             Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Mut, .. }) => "&mut ",
394             Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Not, .. }) => "&",
395             Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
396         };
397
398         let (expr_text, precise) = if let Some(expr_text) = expr
399             .span
400             .find_ancestor_inside(outer)
401             .and_then(|span| self.sess().source_map().span_to_snippet(span).ok())
402         {
403             (expr_text, true)
404         } else {
405             ("(..)".to_string(), false)
406         };
407
408         let adjusted_text = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
409             pick.autoref_or_ptr_adjustment
410         {
411             format!("{}{} as *const _", derefs, expr_text)
412         } else {
413             format!("{}{}{}", autoref, derefs, expr_text)
414         };
415
416         (adjusted_text, precise)
417     }
418 }