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