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