]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_typeck/src/check/method/prelude2021.rs
New upstream version 1.56.0~beta.4+dfsg1
[rustc.git] / compiler / rustc_typeck / src / check / method / prelude2021.rs
CommitLineData
136023e0
XL
1use hir::def_id::DefId;
2use hir::HirId;
3use hir::ItemKind;
4use rustc_ast::Mutability;
5use rustc_errors::Applicability;
6use rustc_hir as hir;
7use rustc_middle::ty::subst::InternalSubsts;
94222f64 8use rustc_middle::ty::{Adt, Array, Ref, Ty};
136023e0 9use rustc_session::lint::builtin::RUST_2021_PRELUDE_COLLISIONS;
94222f64 10use rustc_span::symbol::kw::{Empty, Underscore};
136023e0
XL
11use rustc_span::symbol::{sym, Ident};
12use rustc_span::Span;
13use rustc_trait_selection::infer::InferCtxtExt;
14
15use crate::check::{
16 method::probe::{self, Pick},
17 FnCtxt,
18};
19
20impl<'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
94222f64
XL
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 };
136023e0
XL
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(
94222f64 82 prelude_or_array_lint,
136023e0
XL
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(
94222f64 143 prelude_or_array_lint,
136023e0
XL
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(),
152 );
153
154 let mut lint = lint.build(&format!(
155 "trait method `{}` will become ambiguous in Rust 2021",
156 segment.ident.name
157 ));
158
94222f64 159 let (self_adjusted, precise) = self.adjust_expr(pick, self_expr, sp);
136023e0
XL
160 if precise {
161 let args = args
162 .iter()
163 .skip(1)
164 .map(|arg| {
94222f64 165 let span = arg.span.find_ancestor_inside(sp).unwrap_or_default();
136023e0
XL
166 format!(
167 ", {}",
94222f64 168 self.sess().source_map().span_to_snippet(span).unwrap()
136023e0
XL
169 )
170 })
171 .collect::<String>();
172
173 lint.span_suggestion(
174 sp,
175 "disambiguate the associated function",
176 format!(
94222f64
XL
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,
136023e0
XL
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 trait_path = self.trait_path_or_bare_name(span, expr_id, pick.item.container.id());
265 let trait_generics = self.tcx.generics_of(pick.item.container.id());
266
94222f64
XL
267 let trait_name =
268 if trait_generics.params.len() <= trait_generics.has_self as usize {
269 trait_path
270 } else {
271 let counts = trait_generics.own_counts();
272 format!(
273 "{}<{}>",
274 trait_path,
275 std::iter::repeat("'_")
276 .take(counts.lifetimes)
277 .chain(std::iter::repeat("_").take(
278 counts.types + counts.consts - trait_generics.has_self as usize
279 ))
280 .collect::<Vec<_>>()
281 .join(", ")
282 )
283 };
136023e0
XL
284
285 let mut lint = lint.build(&format!(
286 "trait-associated function `{}` will become ambiguous in Rust 2021",
287 method_name.name
288 ));
289
94222f64
XL
290 let mut self_ty_name = self_ty_span
291 .find_ancestor_inside(span)
292 .and_then(|span| self.sess().source_map().span_to_snippet(span).ok())
293 .unwrap_or_else(|| self_ty.to_string());
294
295 // Get the number of generics the self type has (if an Adt) unless we can determine that
296 // the user has written the self type with generics already which we (naively) do by looking
297 // for a "<" in `self_ty_name`.
298 if !self_ty_name.contains('<') {
299 if let Adt(def, _) = self_ty.kind() {
300 let generics = self.tcx.generics_of(def.did);
301 if !generics.params.is_empty() {
302 let counts = generics.own_counts();
303 self_ty_name += &format!(
304 "<{}>",
305 std::iter::repeat("'_")
306 .take(counts.lifetimes)
307 .chain(std::iter::repeat("_").take(counts.types + counts.consts))
308 .collect::<Vec<_>>()
309 .join(", ")
310 );
311 }
312 }
313 }
136023e0
XL
314 lint.span_suggestion(
315 span,
316 "disambiguate the associated function",
94222f64 317 format!("<{} as {}>::{}", self_ty_name, trait_name, method_name.name,),
136023e0
XL
318 Applicability::MachineApplicable,
319 );
320
321 lint.emit();
322 });
323 }
324
325 fn trait_path_or_bare_name(
326 &self,
327 span: Span,
328 expr_hir_id: HirId,
329 trait_def_id: DefId,
330 ) -> String {
331 self.trait_path(span, expr_hir_id, trait_def_id).unwrap_or_else(|| {
332 let key = self.tcx.def_key(trait_def_id);
333 format!("{}", key.disambiguated_data.data)
334 })
335 }
336
337 fn trait_path(&self, span: Span, expr_hir_id: HirId, trait_def_id: DefId) -> Option<String> {
338 let applicable_traits = self.tcx.in_scope_traits(expr_hir_id)?;
339 let applicable_trait = applicable_traits.iter().find(|t| t.def_id == trait_def_id)?;
340 if applicable_trait.import_ids.is_empty() {
341 // The trait was declared within the module, we only need to use its name.
342 return None;
343 }
344
345 let import_items: Vec<_> = applicable_trait
346 .import_ids
347 .iter()
348 .map(|&import_id| {
349 let hir_id = self.tcx.hir().local_def_id_to_hir_id(import_id);
350 self.tcx.hir().expect_item(hir_id)
351 })
352 .collect();
353
354 // Find an identifier with which this trait was imported (note that `_` doesn't count).
355 let any_id = import_items
356 .iter()
357 .filter_map(|item| if item.ident.name != Underscore { Some(item.ident) } else { None })
358 .next();
359 if let Some(any_id) = any_id {
94222f64
XL
360 if any_id.name == Empty {
361 // Glob import, so just use its name.
362 return None;
363 } else {
364 return Some(format!("{}", any_id));
365 }
136023e0
XL
366 }
367
368 // All that is left is `_`! We need to use the full path. It doesn't matter which one we pick,
369 // so just take the first one.
370 match import_items[0].kind {
371 ItemKind::Use(path, _) => Some(
372 path.segments
373 .iter()
374 .map(|segment| segment.ident.to_string())
375 .collect::<Vec<_>>()
376 .join("::"),
377 ),
378 _ => {
379 span_bug!(span, "unexpected item kind, expected a use: {:?}", import_items[0].kind);
380 }
381 }
382 }
383
384 /// Creates a string version of the `expr` that includes explicit adjustments.
385 /// Returns the string and also a bool indicating whther this is a *precise*
386 /// suggestion.
94222f64
XL
387 fn adjust_expr(
388 &self,
389 pick: &Pick<'tcx>,
390 expr: &hir::Expr<'tcx>,
391 outer: Span,
392 ) -> (String, bool) {
136023e0
XL
393 let derefs = "*".repeat(pick.autoderefs);
394
395 let autoref = match pick.autoref_or_ptr_adjustment {
396 Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Mut, .. }) => "&mut ",
397 Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Not, .. }) => "&",
398 Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
399 };
400
94222f64
XL
401 let (expr_text, precise) = if let Some(expr_text) = expr
402 .span
403 .find_ancestor_inside(outer)
404 .and_then(|span| self.sess().source_map().span_to_snippet(span).ok())
405 {
406 (expr_text, true)
407 } else {
408 ("(..)".to_string(), false)
409 };
136023e0
XL
410
411 let adjusted_text = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
412 pick.autoref_or_ptr_adjustment
413 {
414 format!("{}{} as *const _", derefs, expr_text)
415 } else {
416 format!("{}{}{}", autoref, derefs, expr_text)
417 };
418
419 (adjusted_text, precise)
420 }
421}