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