1use std::fmt::Write;
2
3use hir::def_id::DefId;
4use hir::{HirId, ItemKind};
5use rustc_ast::join_path_idents;
6use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, Level};
7use rustc_hir as hir;
8use rustc_lint::{ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER};
9use rustc_middle::span_bug;
10use rustc_middle::ty::{self, Ty, TyCtxt};
11use rustc_session::lint::builtin::{RUST_2021_PRELUDE_COLLISIONS, RUST_2024_PRELUDE_COLLISIONS};
12use rustc_span::{Ident, STDLIB_STABLE_CRATES, Span, Symbol, kw, sym};
13use rustc_trait_selection::infer::InferCtxtExt;
14use tracing::debug;
15
16use crate::FnCtxt;
17use crate::method::probe::{self, Pick};
18
19struct AmbiguousTraitMethodCall<'a, 'tcx> {
20 segment_name: Symbol,
21 self_expr_span: Span,
22 pick: &'a Pick<'tcx>,
23 tcx: TyCtxt<'tcx>,
24 edition: &'static str,
25}
26
27impl<'a, 'b, 'tcx> Diagnostic<'a, ()> for AmbiguousTraitMethodCall<'b, 'tcx> {
28 fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
29 let Self { segment_name, self_expr_span, pick, tcx, edition } = self;
30 let mut lint = Diag::new(
31 dcx,
32 level,
33 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("trait method `{0}` will become ambiguous in Rust {1}",
segment_name, edition))
})format!("trait method `{}` will become ambiguous in Rust {edition}", segment_name),
34 );
35 let derefs = "*".repeat(pick.autoderefs);
36
37 let autoref = match pick.autoref_or_ptr_adjustment {
38 Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, .. }) => mutbl.ref_prefix_str(),
39 Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
40 Some(probe::AutorefOrPtrAdjustment::ReborrowPin(mutbl)) => match mutbl {
41 hir::Mutability::Mut => "Pin<&mut ",
42 hir::Mutability::Not => "Pin<&",
43 },
44 };
45 if let Ok(self_expr) = tcx.sess.source_map().span_to_snippet(self_expr_span) {
46 let mut self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
47 pick.autoref_or_ptr_adjustment
48 {
49 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1} as *const _", derefs,
self_expr))
})format!("{derefs}{self_expr} as *const _")
50 } else {
51 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1}{2}", autoref, derefs,
self_expr))
})format!("{autoref}{derefs}{self_expr}")
52 };
53
54 if let Some(probe::AutorefOrPtrAdjustment::ReborrowPin(_)) =
55 pick.autoref_or_ptr_adjustment
56 {
57 self_adjusted.push('>');
58 }
59
60 lint.span_suggestion(
61 self_expr_span,
62 "disambiguate the method call",
63 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("({0})", self_adjusted))
})format!("({self_adjusted})"),
64 Applicability::MachineApplicable,
65 );
66 } else {
67 let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
68 pick.autoref_or_ptr_adjustment
69 {
70 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}(...) as *const _", derefs))
})format!("{derefs}(...) as *const _")
71 } else {
72 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1}...", autoref, derefs))
})format!("{autoref}{derefs}...")
73 };
74 lint.span_help(
75 self_expr_span,
76 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("disambiguate the method call with `({0})`",
self_adjusted))
})format!("disambiguate the method call with `({self_adjusted})`",),
77 );
78 }
79 lint
80 }
81}
82
83struct AmbiguousTraitMethod<'a, 'tcx, 'fnctx> {
84 segment: &'a hir::PathSegment<'tcx>,
85 call_expr: &'tcx hir::Expr<'tcx>,
86 self_expr: &'tcx hir::Expr<'tcx>,
87 pick: &'a Pick<'tcx>,
88 args: &'tcx [hir::Expr<'tcx>],
89 edition: &'static str,
90 span: Span,
91 this: &'a FnCtxt<'fnctx, 'tcx>,
92}
93
94impl<'a, 'c, 'tcx, 'fnctx> Diagnostic<'a, ()> for AmbiguousTraitMethod<'c, 'tcx, 'fnctx> {
95 fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
96 let Self { segment, call_expr, self_expr, pick, args, edition, span, this } = self;
97 let mut lint = Diag::new(
98 dcx,
99 level,
100 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("trait method `{0}` will become ambiguous in Rust {1}",
segment.ident.name, edition))
})format!(
101 "trait method `{}` will become ambiguous in Rust {edition}",
102 segment.ident.name
103 ),
104 );
105
106 let sp = call_expr.span;
107 let trait_name =
108 this.trait_path_or_bare_name(span, call_expr.hir_id, pick.item.container_id(this.tcx));
109
110 let (self_adjusted, precise) = this.adjust_expr(pick, self_expr, sp);
111 if precise {
112 let args = args.iter().fold(String::new(), |mut string, arg| {
113 let span = arg.span.find_ancestor_inside(sp).unwrap_or_default();
114 string.write_fmt(format_args!(", {0}",
this.sess().source_map().span_to_snippet(span).unwrap()))write!(string, ", {}", this.sess().source_map().span_to_snippet(span).unwrap())
115 .unwrap();
116 string
117 });
118
119 lint.span_suggestion(
120 sp,
121 "disambiguate the associated function",
122 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}::{1}{2}({3}{4})", trait_name,
segment.ident.name,
if let Some(args) =
segment.args.as_ref().and_then(|args|
this.sess().source_map().span_to_snippet(args.span_ext).ok())
{
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("::{0}", args))
})
} else { String::new() }, self_adjusted, args))
})format!(
123 "{}::{}{}({}{})",
124 trait_name,
125 segment.ident.name,
126 if let Some(args) = segment.args.as_ref().and_then(|args| this
127 .sess()
128 .source_map()
129 .span_to_snippet(args.span_ext)
130 .ok())
131 {
132 format!("::{args}")
134 } else {
135 String::new()
136 },
137 self_adjusted,
138 args,
139 ),
140 Applicability::MachineApplicable,
141 );
142 } else {
143 lint.span_help(
144 sp,
145 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("disambiguate the associated function with `{0}::{1}(...)`",
trait_name, segment.ident))
})format!(
146 "disambiguate the associated function with `{}::{}(...)`",
147 trait_name, segment.ident,
148 ),
149 );
150 }
151 lint
152 }
153}
154
155impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
156 pub(super) fn lint_edition_dependent_dot_call(
157 &self,
158 self_ty: Ty<'tcx>,
159 segment: &hir::PathSegment<'tcx>,
160 span: Span,
161 call_expr: &'tcx hir::Expr<'tcx>,
162 self_expr: &'tcx hir::Expr<'tcx>,
163 pick: &Pick<'tcx>,
164 args: &'tcx [hir::Expr<'tcx>],
165 ) {
166 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs:166",
"rustc_hir_typeck::method::prelude_edition_lints",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs"),
::tracing_core::__macro_support::Option::Some(166u32),
::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::method::prelude_edition_lints"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("lookup(method_name={0}, self_ty={1:?}, call_expr={2:?}, self_expr={3:?})",
segment.ident, self_ty, call_expr, self_expr) as
&dyn Value))])
});
} else { ; }
};debug!(
167 "lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})",
168 segment.ident, self_ty, call_expr, self_expr
169 );
170
171 let (prelude_or_array_lint, edition) = match segment.ident.name {
172 sym::try_into if !span.at_least_rust_2021() => (RUST_2021_PRELUDE_COLLISIONS, "2021"),
174 sym::poll
176 if !span.at_least_rust_2024()
178 && let ty::Adt(adt_def, args) = self_ty.kind()
179 && self.tcx.is_lang_item(adt_def.did(), hir::LangItem::Pin)
180 && let ty::Ref(_, _, ty::Mutability::Mut) =
181 args[0].as_type().unwrap().kind() =>
182 {
183 (RUST_2024_PRELUDE_COLLISIONS, "2024")
184 }
185 sym::into_future if !span.at_least_rust_2024() => {
187 (RUST_2024_PRELUDE_COLLISIONS, "2024")
188 }
189 sym::into_iter => {
194 if let ty::Array(..) = self_ty.kind()
195 && !span.at_least_rust_2021()
196 {
197 (ARRAY_INTO_ITER, "2021")
201 } else if self_ty.boxed_ty().is_some_and(Ty::is_slice)
202 && !span.at_least_rust_2024()
203 {
204 (BOXED_SLICE_INTO_ITER, "2024")
208 } else {
209 return;
210 }
211 }
212 _ => return,
213 };
214
215 if STDLIB_STABLE_CRATES.contains(&self.tcx.crate_name(pick.item.def_id.krate)) {
217 return;
218 }
219
220 if #[allow(non_exhaustive_omitted_patterns)] match pick.kind {
probe::PickKind::InherentImplPick | probe::PickKind::ObjectPick => true,
_ => false,
}matches!(pick.kind, probe::PickKind::InherentImplPick | probe::PickKind::ObjectPick) {
221 if pick.autoderefs == 1
223 && #[allow(non_exhaustive_omitted_patterns)] match pick.autoref_or_ptr_adjustment
{
Some(probe::AutorefOrPtrAdjustment::Autoref { .. }) => true,
_ => false,
}matches!(
224 pick.autoref_or_ptr_adjustment,
225 Some(probe::AutorefOrPtrAdjustment::Autoref { .. })
226 )
227 && #[allow(non_exhaustive_omitted_patterns)] match self_ty.kind() {
ty::Ref(..) => true,
_ => false,
}matches!(self_ty.kind(), ty::Ref(..))
228 {
229 return;
230 }
231
232 if pick.autoderefs == 0 && pick.autoref_or_ptr_adjustment.is_none() {
235 return;
236 }
237
238 self.tcx.emit_node_span_lint(
241 prelude_or_array_lint,
242 self_expr.hir_id,
243 self_expr.span,
244 AmbiguousTraitMethodCall {
245 segment_name: segment.ident.name,
246 self_expr_span: self_expr.span,
247 pick,
248 tcx: self.tcx,
249 edition,
250 },
251 );
252 } else {
253 self.tcx.emit_node_span_lint(
256 prelude_or_array_lint,
257 call_expr.hir_id,
258 call_expr.span,
259 AmbiguousTraitMethod {
260 segment,
261 call_expr,
262 self_expr,
263 pick,
264 args,
265 edition,
266 span,
267 this: self,
268 },
269 );
270 }
271 }
272
273 pub(super) fn lint_fully_qualified_call_from_2018(
274 &self,
275 span: Span,
276 method_name: Ident,
277 self_ty: Ty<'tcx>,
278 self_ty_span: Span,
279 expr_id: hir::HirId,
280 pick: &Pick<'tcx>,
281 ) {
282 struct AmbiguousTraitAssocFunc<'a, 'fnctx, 'tcx> {
283 method_name: Symbol,
284 this: &'a FnCtxt<'fnctx, 'tcx>,
285 pick: &'a Pick<'tcx>,
286 span: Span,
287 expr_id: hir::HirId,
288 self_ty_span: Span,
289 self_ty: Ty<'tcx>,
290 }
291
292 impl<'a, 'b, 'fnctx, 'tcx> Diagnostic<'a, ()> for AmbiguousTraitAssocFunc<'b, 'fnctx, 'tcx> {
293 fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
294 let Self { method_name, this, pick, span, expr_id, self_ty_span, self_ty } = self;
295 let mut lint = Diag::new(
296 dcx,
297 level,
298 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("trait-associated function `{0}` will become ambiguous in Rust 2021",
method_name))
})format!(
299 "trait-associated function `{}` will become ambiguous in Rust 2021",
300 method_name
301 ),
302 );
303
304 let container_id = pick.item.container_id(this.tcx);
307 let trait_path = this.trait_path_or_bare_name(span, expr_id, container_id);
308 let trait_generics = this.tcx.generics_of(container_id);
309
310 let trait_name =
311 if trait_generics.own_params.len() <= trait_generics.has_self as usize {
312 trait_path
313 } else {
314 let counts = trait_generics.own_counts();
315 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}<{1}>", trait_path,
std::iter::repeat("'_").take(counts.lifetimes).chain(std::iter::repeat("_").take(counts.types
+ counts.consts -
trait_generics.has_self as
usize)).collect::<Vec<_>>().join(", ")))
})format!(
316 "{}<{}>",
317 trait_path,
318 std::iter::repeat("'_")
319 .take(counts.lifetimes)
320 .chain(std::iter::repeat("_").take(
321 counts.types + counts.consts - trait_generics.has_self as usize
322 ))
323 .collect::<Vec<_>>()
324 .join(", ")
325 )
326 };
327
328 let mut self_ty_name = self_ty_span
329 .find_ancestor_inside(span)
330 .and_then(|span| this.sess().source_map().span_to_snippet(span).ok())
331 .unwrap_or_else(|| self_ty.to_string());
332
333 if !self_ty_name.contains('<') {
337 if let ty::Adt(def, _) = self_ty.kind() {
338 let generics = this.tcx.generics_of(def.did());
339 if !generics.is_own_empty() {
340 let counts = generics.own_counts();
341 self_ty_name += &::alloc::__export::must_use({
::alloc::fmt::format(format_args!("<{0}>",
std::iter::repeat("'_").take(counts.lifetimes).chain(std::iter::repeat("_").take(counts.types
+ counts.consts)).collect::<Vec<_>>().join(", ")))
})format!(
342 "<{}>",
343 std::iter::repeat("'_")
344 .take(counts.lifetimes)
345 .chain(
346 std::iter::repeat("_").take(counts.types + counts.consts)
347 )
348 .collect::<Vec<_>>()
349 .join(", ")
350 );
351 }
352 }
353 }
354 lint.span_suggestion(
355 span,
356 "disambiguate the associated function",
357 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("<{0} as {1}>::{2}", self_ty_name,
trait_name, method_name))
})format!("<{} as {}>::{}", self_ty_name, trait_name, method_name),
358 Applicability::MachineApplicable,
359 );
360 lint
361 }
362 }
363
364 if span.at_least_rust_2021() {
366 return;
367 }
368
369 if !#[allow(non_exhaustive_omitted_patterns)] match method_name.name {
sym::try_into | sym::try_from | sym::from_iter => true,
_ => false,
}matches!(method_name.name, sym::try_into | sym::try_from | sym::from_iter) {
371 return;
372 }
373
374 if STDLIB_STABLE_CRATES.contains(&self.tcx.crate_name(pick.item.def_id.krate)) {
376 return;
377 }
378
379 if method_name.name == sym::from_iter {
382 if let Some(trait_def_id) = self.tcx.get_diagnostic_item(sym::FromIterator) {
383 let any_type = self.infcx.next_ty_var(span);
384 if !self
385 .infcx
386 .type_implements_trait(trait_def_id, [self_ty, any_type], self.param_env)
387 .may_apply()
388 {
389 return;
390 }
391 }
392 }
393
394 if #[allow(non_exhaustive_omitted_patterns)] match pick.kind {
probe::PickKind::InherentImplPick => true,
_ => false,
}matches!(pick.kind, probe::PickKind::InherentImplPick) {
397 return;
398 }
399
400 self.tcx.emit_node_span_lint(
401 RUST_2021_PRELUDE_COLLISIONS,
402 expr_id,
403 span,
404 AmbiguousTraitAssocFunc {
405 method_name: method_name.name,
406 this: self,
407 pick,
408 span,
409 expr_id,
410 self_ty_span,
411 self_ty,
412 },
413 );
414 }
415
416 fn trait_path_or_bare_name(
417 &self,
418 span: Span,
419 expr_hir_id: HirId,
420 trait_def_id: DefId,
421 ) -> String {
422 self.trait_path(span, expr_hir_id, trait_def_id).unwrap_or_else(|| {
423 let key = self.tcx.def_key(trait_def_id);
424 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}", key.disambiguated_data.data))
})format!("{}", key.disambiguated_data.data)
425 })
426 }
427
428 fn trait_path(&self, span: Span, expr_hir_id: HirId, trait_def_id: DefId) -> Option<String> {
429 let applicable_traits = self.tcx.in_scope_traits(expr_hir_id)?;
430 let applicable_trait = applicable_traits.iter().find(|t| t.def_id == trait_def_id)?;
431 if applicable_trait.import_ids.is_empty() {
432 return None;
434 }
435
436 let import_items: Vec<_> = applicable_trait
437 .import_ids
438 .iter()
439 .map(|&import_id| self.tcx.hir_expect_item(import_id))
440 .collect();
441
442 for item in import_items.iter() {
444 let (_, kind) = item.expect_use();
445 match kind {
446 hir::UseKind::Single(ident) => {
447 if ident.name != kw::Underscore {
448 return Some(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}", ident.name))
})format!("{}", ident.name));
449 }
450 }
451 hir::UseKind::Glob => return None, hir::UseKind::ListStem => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
453 }
454 }
455
456 match import_items[0].kind {
459 ItemKind::Use(path, _) => {
460 Some(join_path_idents(path.segments.iter().map(|seg| seg.ident)))
461 }
462 _ => {
463 ::rustc_middle::util::bug::span_bug_fmt(span,
format_args!("unexpected item kind, expected a use: {0:?}",
import_items[0].kind));span_bug!(span, "unexpected item kind, expected a use: {:?}", import_items[0].kind);
464 }
465 }
466 }
467
468 fn adjust_expr(
472 &self,
473 pick: &Pick<'tcx>,
474 expr: &hir::Expr<'tcx>,
475 outer: Span,
476 ) -> (String, bool) {
477 let derefs = "*".repeat(pick.autoderefs);
478
479 let autoref = match pick.autoref_or_ptr_adjustment {
480 Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, .. }) => mutbl.ref_prefix_str(),
481 Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
482 Some(probe::AutorefOrPtrAdjustment::ReborrowPin(mutbl)) => match mutbl {
483 hir::Mutability::Mut => "Pin<&mut ",
484 hir::Mutability::Not => "Pin<&",
485 },
486 };
487
488 let (expr_text, precise) = if let Some(expr_text) = expr
489 .span
490 .find_ancestor_inside(outer)
491 .and_then(|span| self.sess().source_map().span_to_snippet(span).ok())
492 {
493 (expr_text, true)
494 } else {
495 ("(..)".to_string(), false)
496 };
497
498 let mut adjusted_text = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
499 pick.autoref_or_ptr_adjustment
500 {
501 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1} as *const _", derefs,
expr_text))
})format!("{derefs}{expr_text} as *const _")
502 } else {
503 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1}{2}", autoref, derefs,
expr_text))
})format!("{autoref}{derefs}{expr_text}")
504 };
505
506 if let Some(probe::AutorefOrPtrAdjustment::ReborrowPin(_)) = pick.autoref_or_ptr_adjustment
507 {
508 adjusted_text.push('>');
509 }
510
511 (adjusted_text, precise)
512 }
513}