]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_typeck/src/check/method/prelude2021.rs
f853c0fd9ccdb481a2865964870ad8964a521fb8
[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::{Ref, Ty};
9 use rustc_session::lint::builtin::RUST_2021_PRELUDE_COLLISIONS;
10 use rustc_span::symbol::kw::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         // These are the method names that were added to prelude in Rust 2021
42         if !matches!(segment.ident.name, sym::try_into) {
43             return;
44         }
45
46         // No need to lint if method came from std/core, as that will now be in the prelude
47         if matches!(self.tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) {
48             return;
49         }
50
51         if matches!(pick.kind, probe::PickKind::InherentImplPick | probe::PickKind::ObjectPick) {
52             // avoid repeatedly adding unneeded `&*`s
53             if pick.autoderefs == 1
54                 && matches!(
55                     pick.autoref_or_ptr_adjustment,
56                     Some(probe::AutorefOrPtrAdjustment::Autoref { .. })
57                 )
58                 && matches!(self_ty.kind(), Ref(..))
59             {
60                 return;
61             }
62
63             // if it's an inherent `self` method (not `&self` or `&mut self`), it will take
64             // precedence over the `TryInto` impl, and thus won't break in 2021 edition
65             if pick.autoderefs == 0 && pick.autoref_or_ptr_adjustment.is_none() {
66                 return;
67             }
68
69             // Inherent impls only require not relying on autoref and autoderef in order to
70             // ensure that the trait implementation won't be used
71             self.tcx.struct_span_lint_hir(
72                 RUST_2021_PRELUDE_COLLISIONS,
73                 self_expr.hir_id,
74                 self_expr.span,
75                 |lint| {
76                     let sp = self_expr.span;
77
78                     let mut lint = lint.build(&format!(
79                         "trait method `{}` will become ambiguous in Rust 2021",
80                         segment.ident.name
81                     ));
82
83                     let derefs = "*".repeat(pick.autoderefs);
84
85                     let autoref = match pick.autoref_or_ptr_adjustment {
86                         Some(probe::AutorefOrPtrAdjustment::Autoref {
87                             mutbl: Mutability::Mut,
88                             ..
89                         }) => "&mut ",
90                         Some(probe::AutorefOrPtrAdjustment::Autoref {
91                             mutbl: Mutability::Not,
92                             ..
93                         }) => "&",
94                         Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
95                     };
96                     if let Ok(self_expr) = self.sess().source_map().span_to_snippet(self_expr.span)
97                     {
98                         let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
99                             pick.autoref_or_ptr_adjustment
100                         {
101                             format!("{}{} as *const _", derefs, self_expr)
102                         } else {
103                             format!("{}{}{}", autoref, derefs, self_expr)
104                         };
105
106                         lint.span_suggestion(
107                             sp,
108                             "disambiguate the method call",
109                             format!("({})", self_adjusted),
110                             Applicability::MachineApplicable,
111                         );
112                     } else {
113                         let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
114                             pick.autoref_or_ptr_adjustment
115                         {
116                             format!("{}(...) as *const _", derefs)
117                         } else {
118                             format!("{}{}...", autoref, derefs)
119                         };
120                         lint.span_help(
121                             sp,
122                             &format!("disambiguate the method call with `({})`", self_adjusted,),
123                         );
124                     }
125
126                     lint.emit();
127                 },
128             );
129         } else {
130             // trait implementations require full disambiguation to not clash with the new prelude
131             // additions (i.e. convert from dot-call to fully-qualified call)
132             self.tcx.struct_span_lint_hir(
133                 RUST_2021_PRELUDE_COLLISIONS,
134                 call_expr.hir_id,
135                 call_expr.span,
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(),
142                     );
143
144                     let mut lint = lint.build(&format!(
145                         "trait method `{}` will become ambiguous in Rust 2021",
146                         segment.ident.name
147                     ));
148
149                     let (self_adjusted, precise) = self.adjust_expr(pick, self_expr);
150                     if precise {
151                         let args = args
152                             .iter()
153                             .skip(1)
154                             .map(|arg| {
155                                 format!(
156                                     ", {}",
157                                     self.sess().source_map().span_to_snippet(arg.span).unwrap()
158                                 )
159                             })
160                             .collect::<String>();
161
162                         lint.span_suggestion(
163                             sp,
164                             "disambiguate the associated function",
165                             format!(
166                                 "{}::{}({}{})",
167                                 trait_name, segment.ident.name, self_adjusted, args
168                             ),
169                             Applicability::MachineApplicable,
170                         );
171                     } else {
172                         lint.span_help(
173                             sp,
174                             &format!(
175                                 "disambiguate the associated function with `{}::{}(...)`",
176                                 trait_name, segment.ident,
177                             ),
178                         );
179                     }
180
181                     lint.emit();
182                 },
183             );
184         }
185     }
186
187     pub(super) fn lint_fully_qualified_call_from_2018(
188         &self,
189         span: Span,
190         method_name: Ident,
191         self_ty: Ty<'tcx>,
192         self_ty_span: Span,
193         expr_id: hir::HirId,
194         pick: &Pick<'tcx>,
195     ) {
196         // Rust 2021 and later is already using the new prelude
197         if span.rust_2021() {
198             return;
199         }
200
201         // These are the fully qualified methods added to prelude in Rust 2021
202         if !matches!(method_name.name, sym::try_into | sym::try_from | sym::from_iter) {
203             return;
204         }
205
206         // No need to lint if method came from std/core, as that will now be in the prelude
207         if matches!(self.tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) {
208             return;
209         }
210
211         // For from_iter, check if the type actualy implements FromIterator.
212         // If we know it does not, we don't need to warn.
213         if method_name.name == sym::from_iter {
214             if let Some(trait_def_id) = self.tcx.get_diagnostic_item(sym::FromIterator) {
215                 if !self
216                     .infcx
217                     .type_implements_trait(
218                         trait_def_id,
219                         self_ty,
220                         InternalSubsts::empty(),
221                         self.param_env,
222                     )
223                     .may_apply()
224                 {
225                     return;
226                 }
227             }
228         }
229
230         // No need to lint if this is an inherent method called on a specific type, like `Vec::foo(...)`,
231         // since such methods take precedence over trait methods.
232         if matches!(pick.kind, probe::PickKind::InherentImplPick) {
233             return;
234         }
235
236         self.tcx.struct_span_lint_hir(RUST_2021_PRELUDE_COLLISIONS, expr_id, span, |lint| {
237             // "type" refers to either a type or, more likely, a trait from which
238             // the associated function or method is from.
239             let trait_path = self.trait_path_or_bare_name(span, expr_id, pick.item.container.id());
240             let trait_generics = self.tcx.generics_of(pick.item.container.id());
241
242             let parameter_count = trait_generics.count() - (trait_generics.has_self as usize);
243             let trait_name = if parameter_count == 0 {
244                 trait_path
245             } else {
246                 format!(
247                     "{}<{}>",
248                     trait_path,
249                     std::iter::repeat("_").take(parameter_count).collect::<Vec<_>>().join(", ")
250                 )
251             };
252
253             let mut lint = lint.build(&format!(
254                 "trait-associated function `{}` will become ambiguous in Rust 2021",
255                 method_name.name
256             ));
257
258             let self_ty = self
259                 .sess()
260                 .source_map()
261                 .span_to_snippet(self_ty_span)
262                 .unwrap_or_else(|_| self_ty.to_string());
263
264             lint.span_suggestion(
265                 span,
266                 "disambiguate the associated function",
267                 format!("<{} as {}>::{}", self_ty, trait_name, method_name.name,),
268                 Applicability::MachineApplicable,
269             );
270
271             lint.emit();
272         });
273     }
274
275     fn trait_path_or_bare_name(
276         &self,
277         span: Span,
278         expr_hir_id: HirId,
279         trait_def_id: DefId,
280     ) -> String {
281         self.trait_path(span, expr_hir_id, trait_def_id).unwrap_or_else(|| {
282             let key = self.tcx.def_key(trait_def_id);
283             format!("{}", key.disambiguated_data.data)
284         })
285     }
286
287     fn trait_path(&self, span: Span, expr_hir_id: HirId, trait_def_id: DefId) -> Option<String> {
288         let applicable_traits = self.tcx.in_scope_traits(expr_hir_id)?;
289         let applicable_trait = applicable_traits.iter().find(|t| t.def_id == trait_def_id)?;
290         if applicable_trait.import_ids.is_empty() {
291             // The trait was declared within the module, we only need to use its name.
292             return None;
293         }
294
295         let import_items: Vec<_> = applicable_trait
296             .import_ids
297             .iter()
298             .map(|&import_id| {
299                 let hir_id = self.tcx.hir().local_def_id_to_hir_id(import_id);
300                 self.tcx.hir().expect_item(hir_id)
301             })
302             .collect();
303
304         // Find an identifier with which this trait was imported (note that `_` doesn't count).
305         let any_id = import_items
306             .iter()
307             .filter_map(|item| if item.ident.name != Underscore { Some(item.ident) } else { None })
308             .next();
309         if let Some(any_id) = any_id {
310             return Some(format!("{}", any_id));
311         }
312
313         // All that is left is `_`! We need to use the full path. It doesn't matter which one we pick,
314         // so just take the first one.
315         match import_items[0].kind {
316             ItemKind::Use(path, _) => Some(
317                 path.segments
318                     .iter()
319                     .map(|segment| segment.ident.to_string())
320                     .collect::<Vec<_>>()
321                     .join("::"),
322             ),
323             _ => {
324                 span_bug!(span, "unexpected item kind, expected a use: {:?}", import_items[0].kind);
325             }
326         }
327     }
328
329     /// Creates a string version of the `expr` that includes explicit adjustments.
330     /// Returns the string and also a bool indicating whther this is a *precise*
331     /// suggestion.
332     fn adjust_expr(&self, pick: &Pick<'tcx>, expr: &hir::Expr<'tcx>) -> (String, bool) {
333         let derefs = "*".repeat(pick.autoderefs);
334
335         let autoref = match pick.autoref_or_ptr_adjustment {
336             Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Mut, .. }) => "&mut ",
337             Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Not, .. }) => "&",
338             Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
339         };
340
341         let (expr_text, precise) =
342             if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) {
343                 (expr_text, true)
344             } else {
345                 (format!("(..)"), false)
346             };
347
348         let adjusted_text = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
349             pick.autoref_or_ptr_adjustment
350         {
351             format!("{}{} as *const _", derefs, expr_text)
352         } else {
353             format!("{}{}{}", autoref, derefs, expr_text)
354         };
355
356         (adjusted_text, precise)
357     }
358 }