]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_typeck/src/check/method/prelude2021.rs
Rollup merge of #98801 - joshtriplett:file-create-new, r=thomcc
[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                             .skip(1)
164                             .map(|arg| {
165                                 let span = arg.span.find_ancestor_inside(sp).unwrap_or_default();
166                                 format!(
167                                     ", {}",
168                                     self.sess().source_map().span_to_snippet(span).unwrap()
169                                 )
170                             })
171                             .collect::<String>();
172
173                         lint.span_suggestion(
174                             sp,
175                             "disambiguate the associated function",
176                             format!(
177                                 "{}::{}{}({}{})",
178                                 trait_name,
179                                 segment.ident.name,
180                                 if let Some(args) = segment.args.as_ref().and_then(|args| self
181                                     .sess()
182                                     .source_map()
183                                     .span_to_snippet(args.span_ext)
184                                     .ok())
185                                 {
186                                     // Keep turbofish.
187                                     format!("::{}", args)
188                                 } else {
189                                     String::new()
190                                 },
191                                 self_adjusted,
192                                 args,
193                             ),
194                             Applicability::MachineApplicable,
195                         );
196                     } else {
197                         lint.span_help(
198                             sp,
199                             &format!(
200                                 "disambiguate the associated function with `{}::{}(...)`",
201                                 trait_name, segment.ident,
202                             ),
203                         );
204                     }
205
206                     lint.emit();
207                 },
208             );
209         }
210     }
211
212     pub(super) fn lint_fully_qualified_call_from_2018(
213         &self,
214         span: Span,
215         method_name: Ident,
216         self_ty: Ty<'tcx>,
217         self_ty_span: Span,
218         expr_id: hir::HirId,
219         pick: &Pick<'tcx>,
220     ) {
221         // Rust 2021 and later is already using the new prelude
222         if span.rust_2021() {
223             return;
224         }
225
226         // These are the fully qualified methods added to prelude in Rust 2021
227         if !matches!(method_name.name, sym::try_into | sym::try_from | sym::from_iter) {
228             return;
229         }
230
231         // No need to lint if method came from std/core, as that will now be in the prelude
232         if matches!(self.tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) {
233             return;
234         }
235
236         // For from_iter, check if the type actually implements FromIterator.
237         // If we know it does not, we don't need to warn.
238         if method_name.name == sym::from_iter {
239             if let Some(trait_def_id) = self.tcx.get_diagnostic_item(sym::FromIterator) {
240                 if !self
241                     .infcx
242                     .type_implements_trait(
243                         trait_def_id,
244                         self_ty,
245                         InternalSubsts::empty(),
246                         self.param_env,
247                     )
248                     .may_apply()
249                 {
250                     return;
251                 }
252             }
253         }
254
255         // No need to lint if this is an inherent method called on a specific type, like `Vec::foo(...)`,
256         // since such methods take precedence over trait methods.
257         if matches!(pick.kind, probe::PickKind::InherentImplPick) {
258             return;
259         }
260
261         self.tcx.struct_span_lint_hir(RUST_2021_PRELUDE_COLLISIONS, expr_id, span, |lint| {
262             // "type" refers to either a type or, more likely, a trait from which
263             // the associated function or method is from.
264             let container_id = pick.item.container_id(self.tcx);
265             let trait_path = self.trait_path_or_bare_name(span, expr_id, container_id);
266             let trait_generics = self.tcx.generics_of(container_id);
267
268             let trait_name =
269                 if trait_generics.params.len() <= trait_generics.has_self as usize {
270                     trait_path
271                 } else {
272                     let counts = trait_generics.own_counts();
273                     format!(
274                         "{}<{}>",
275                         trait_path,
276                         std::iter::repeat("'_")
277                             .take(counts.lifetimes)
278                             .chain(std::iter::repeat("_").take(
279                                 counts.types + counts.consts - trait_generics.has_self as usize
280                             ))
281                             .collect::<Vec<_>>()
282                             .join(", ")
283                     )
284                 };
285
286             let mut lint = lint.build(&format!(
287                 "trait-associated function `{}` will become ambiguous in Rust 2021",
288                 method_name.name
289             ));
290
291             let mut self_ty_name = self_ty_span
292                 .find_ancestor_inside(span)
293                 .and_then(|span| self.sess().source_map().span_to_snippet(span).ok())
294                 .unwrap_or_else(|| self_ty.to_string());
295
296             // Get the number of generics the self type has (if an Adt) unless we can determine that
297             // the user has written the self type with generics already which we (naively) do by looking
298             // for a "<" in `self_ty_name`.
299             if !self_ty_name.contains('<') {
300                 if let Adt(def, _) = self_ty.kind() {
301                     let generics = self.tcx.generics_of(def.did());
302                     if !generics.params.is_empty() {
303                         let counts = generics.own_counts();
304                         self_ty_name += &format!(
305                             "<{}>",
306                             std::iter::repeat("'_")
307                                 .take(counts.lifetimes)
308                                 .chain(std::iter::repeat("_").take(counts.types + counts.consts))
309                                 .collect::<Vec<_>>()
310                                 .join(", ")
311                         );
312                     }
313                 }
314             }
315             lint.span_suggestion(
316                 span,
317                 "disambiguate the associated function",
318                 format!("<{} as {}>::{}", self_ty_name, trait_name, method_name.name,),
319                 Applicability::MachineApplicable,
320             );
321
322             lint.emit();
323         });
324     }
325
326     fn trait_path_or_bare_name(
327         &self,
328         span: Span,
329         expr_hir_id: HirId,
330         trait_def_id: DefId,
331     ) -> String {
332         self.trait_path(span, expr_hir_id, trait_def_id).unwrap_or_else(|| {
333             let key = self.tcx.def_key(trait_def_id);
334             format!("{}", key.disambiguated_data.data)
335         })
336     }
337
338     fn trait_path(&self, span: Span, expr_hir_id: HirId, trait_def_id: DefId) -> Option<String> {
339         let applicable_traits = self.tcx.in_scope_traits(expr_hir_id)?;
340         let applicable_trait = applicable_traits.iter().find(|t| t.def_id == trait_def_id)?;
341         if applicable_trait.import_ids.is_empty() {
342             // The trait was declared within the module, we only need to use its name.
343             return None;
344         }
345
346         let import_items: Vec<_> = applicable_trait
347             .import_ids
348             .iter()
349             .map(|&import_id| self.tcx.hir().expect_item(import_id))
350             .collect();
351
352         // Find an identifier with which this trait was imported (note that `_` doesn't count).
353         let any_id = import_items
354             .iter()
355             .filter_map(|item| if item.ident.name != Underscore { Some(item.ident) } else { None })
356             .next();
357         if let Some(any_id) = any_id {
358             if any_id.name == Empty {
359                 // Glob import, so just use its name.
360                 return None;
361             } else {
362                 return Some(format!("{}", any_id));
363             }
364         }
365
366         // All that is left is `_`! We need to use the full path. It doesn't matter which one we pick,
367         // so just take the first one.
368         match import_items[0].kind {
369             ItemKind::Use(path, _) => Some(
370                 path.segments
371                     .iter()
372                     .map(|segment| segment.ident.to_string())
373                     .collect::<Vec<_>>()
374                     .join("::"),
375             ),
376             _ => {
377                 span_bug!(span, "unexpected item kind, expected a use: {:?}", import_items[0].kind);
378             }
379         }
380     }
381
382     /// Creates a string version of the `expr` that includes explicit adjustments.
383     /// Returns the string and also a bool indicating whether this is a *precise*
384     /// suggestion.
385     fn adjust_expr(
386         &self,
387         pick: &Pick<'tcx>,
388         expr: &hir::Expr<'tcx>,
389         outer: Span,
390     ) -> (String, bool) {
391         let derefs = "*".repeat(pick.autoderefs);
392
393         let autoref = match pick.autoref_or_ptr_adjustment {
394             Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Mut, .. }) => "&mut ",
395             Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Not, .. }) => "&",
396             Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
397         };
398
399         let (expr_text, precise) = if let Some(expr_text) = expr
400             .span
401             .find_ancestor_inside(outer)
402             .and_then(|span| self.sess().source_map().span_to_snippet(span).ok())
403         {
404             (expr_text, true)
405         } else {
406             ("(..)".to_string(), false)
407         };
408
409         let adjusted_text = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
410             pick.autoref_or_ptr_adjustment
411         {
412             format!("{}{} as *const _", derefs, expr_text)
413         } else {
414             format!("{}{}{}", autoref, derefs, expr_text)
415         };
416
417         (adjusted_text, precise)
418     }
419 }