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, 'b, 'tcx> {
20 segment_name: Symbol,
21 self_expr_span: Span,
22 pick: &'a Pick<'tcx>,
23 tcx: TyCtxt<'tcx>,
24 edition: &'b str,
25}
26
27impl<'a, 'b, 'c, 'tcx> Diagnostic<'a, ()> for AmbiguousTraitMethodCall<'b, 'c, '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, 'b, 'tcx, 'pcx, 'fnctx> {
84 segment: &'a hir::PathSegment<'pcx>,
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: &'b str,
90 span: Span,
91 this: &'a FnCtxt<'fnctx, 'tcx>,
92}
93
94impl<'a, 'b, 'c, 'tcx, 'pcx, 'fnctx> Diagnostic<'a, ()>
95 for AmbiguousTraitMethod<'b, 'c, 'tcx, 'pcx, 'fnctx>
96{
97 fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
98 let Self { segment, call_expr, self_expr, pick, args, edition, span, this } = self;
99 let mut lint = Diag::new(
100 dcx,
101 level,
102 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("trait method `{0}` will become ambiguous in Rust {1}",
segment.ident.name, edition))
})format!(
103 "trait method `{}` will become ambiguous in Rust {edition}",
104 segment.ident.name
105 ),
106 );
107
108 let sp = call_expr.span;
109 let trait_name =
110 this.trait_path_or_bare_name(span, call_expr.hir_id, pick.item.container_id(this.tcx));
111
112 let (self_adjusted, precise) = this.adjust_expr(pick, self_expr, sp);
113 if precise {
114 let args = args.iter().fold(String::new(), |mut string, arg| {
115 let span = arg.span.find_ancestor_inside(sp).unwrap_or_default();
116 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())
117 .unwrap();
118 string
119 });
120
121 lint.span_suggestion(
122 sp,
123 "disambiguate the associated function",
124 ::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!(
125 "{}::{}{}({}{})",
126 trait_name,
127 segment.ident.name,
128 if let Some(args) = segment.args.as_ref().and_then(|args| this
129 .sess()
130 .source_map()
131 .span_to_snippet(args.span_ext)
132 .ok())
133 {
134 format!("::{args}")
136 } else {
137 String::new()
138 },
139 self_adjusted,
140 args,
141 ),
142 Applicability::MachineApplicable,
143 );
144 } else {
145 lint.span_help(
146 sp,
147 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("disambiguate the associated function with `{0}::{1}(...)`",
trait_name, segment.ident))
})format!(
148 "disambiguate the associated function with `{}::{}(...)`",
149 trait_name, segment.ident,
150 ),
151 );
152 }
153 lint
154 }
155}
156
157impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
158 pub(super) fn lint_edition_dependent_dot_call(
159 &self,
160 self_ty: Ty<'tcx>,
161 segment: &hir::PathSegment<'_>,
162 span: Span,
163 call_expr: &'tcx hir::Expr<'tcx>,
164 self_expr: &'tcx hir::Expr<'tcx>,
165 pick: &Pick<'tcx>,
166 args: &'tcx [hir::Expr<'tcx>],
167 ) {
168 {
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:168",
"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(168u32),
::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!(
169 "lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})",
170 segment.ident, self_ty, call_expr, self_expr
171 );
172
173 let (prelude_or_array_lint, edition) = match segment.ident.name {
174 sym::try_into if !span.at_least_rust_2021() => (RUST_2021_PRELUDE_COLLISIONS, "2021"),
176 sym::poll
178 if !span.at_least_rust_2024()
180 && let ty::Adt(adt_def, args) = self_ty.kind()
181 && self.tcx.is_lang_item(adt_def.did(), hir::LangItem::Pin)
182 && let ty::Ref(_, _, ty::Mutability::Mut) =
183 args[0].as_type().unwrap().kind() =>
184 {
185 (RUST_2024_PRELUDE_COLLISIONS, "2024")
186 }
187 sym::into_future if !span.at_least_rust_2024() => {
189 (RUST_2024_PRELUDE_COLLISIONS, "2024")
190 }
191 sym::into_iter => {
196 if let ty::Array(..) = self_ty.kind()
197 && !span.at_least_rust_2021()
198 {
199 (ARRAY_INTO_ITER, "2021")
203 } else if self_ty.boxed_ty().is_some_and(Ty::is_slice)
204 && !span.at_least_rust_2024()
205 {
206 (BOXED_SLICE_INTO_ITER, "2024")
210 } else {
211 return;
212 }
213 }
214 _ => return,
215 };
216
217 if STDLIB_STABLE_CRATES.contains(&self.tcx.crate_name(pick.item.def_id.krate)) {
219 return;
220 }
221
222 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) {
223 if pick.autoderefs == 1
225 && #[allow(non_exhaustive_omitted_patterns)] match pick.autoref_or_ptr_adjustment
{
Some(probe::AutorefOrPtrAdjustment::Autoref { .. }) => true,
_ => false,
}matches!(
226 pick.autoref_or_ptr_adjustment,
227 Some(probe::AutorefOrPtrAdjustment::Autoref { .. })
228 )
229 && #[allow(non_exhaustive_omitted_patterns)] match self_ty.kind() {
ty::Ref(..) => true,
_ => false,
}matches!(self_ty.kind(), ty::Ref(..))
230 {
231 return;
232 }
233
234 if pick.autoderefs == 0 && pick.autoref_or_ptr_adjustment.is_none() {
237 return;
238 }
239
240 self.tcx.emit_node_span_lint(
243 prelude_or_array_lint,
244 self_expr.hir_id,
245 self_expr.span,
246 AmbiguousTraitMethodCall {
247 segment_name: segment.ident.name,
248 self_expr_span: self_expr.span,
249 pick,
250 tcx: self.tcx,
251 edition,
252 },
253 );
254 } else {
255 self.tcx.emit_node_span_lint(
258 prelude_or_array_lint,
259 call_expr.hir_id,
260 call_expr.span,
261 AmbiguousTraitMethod {
262 segment,
263 call_expr,
264 self_expr,
265 pick,
266 args,
267 edition,
268 span,
269 this: self,
270 },
271 );
272 }
273 }
274
275 pub(super) fn lint_fully_qualified_call_from_2018(
276 &self,
277 span: Span,
278 method_name: Ident,
279 self_ty: Ty<'tcx>,
280 self_ty_span: Span,
281 expr_id: hir::HirId,
282 pick: &Pick<'tcx>,
283 ) {
284 struct AmbiguousTraitAssocFunc<'a, 'fnctx, 'tcx> {
285 method_name: Symbol,
286 this: &'a FnCtxt<'fnctx, 'tcx>,
287 pick: &'a Pick<'tcx>,
288 span: Span,
289 expr_id: hir::HirId,
290 self_ty_span: Span,
291 self_ty: Ty<'tcx>,
292 }
293
294 impl<'a, 'b, 'fnctx, 'tcx> Diagnostic<'a, ()> for AmbiguousTraitAssocFunc<'b, 'fnctx, 'tcx> {
295 fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
296 let Self { method_name, this, pick, span, expr_id, self_ty_span, self_ty } = self;
297 let mut lint = Diag::new(
298 dcx,
299 level,
300 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("trait-associated function `{0}` will become ambiguous in Rust 2021",
method_name))
})format!(
301 "trait-associated function `{}` will become ambiguous in Rust 2021",
302 method_name
303 ),
304 );
305
306 let container_id = pick.item.container_id(this.tcx);
309 let trait_path = this.trait_path_or_bare_name(span, expr_id, container_id);
310 let trait_generics = this.tcx.generics_of(container_id);
311
312 let trait_name =
313 if trait_generics.own_params.len() <= trait_generics.has_self as usize {
314 trait_path
315 } else {
316 let counts = trait_generics.own_counts();
317 ::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!(
318 "{}<{}>",
319 trait_path,
320 std::iter::repeat("'_")
321 .take(counts.lifetimes)
322 .chain(std::iter::repeat("_").take(
323 counts.types + counts.consts - trait_generics.has_self as usize
324 ))
325 .collect::<Vec<_>>()
326 .join(", ")
327 )
328 };
329
330 let mut self_ty_name = self_ty_span
331 .find_ancestor_inside(span)
332 .and_then(|span| this.sess().source_map().span_to_snippet(span).ok())
333 .unwrap_or_else(|| self_ty.to_string());
334
335 if !self_ty_name.contains('<') {
339 if let ty::Adt(def, _) = self_ty.kind() {
340 let generics = this.tcx.generics_of(def.did());
341 if !generics.is_own_empty() {
342 let counts = generics.own_counts();
343 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!(
344 "<{}>",
345 std::iter::repeat("'_")
346 .take(counts.lifetimes)
347 .chain(
348 std::iter::repeat("_").take(counts.types + counts.consts)
349 )
350 .collect::<Vec<_>>()
351 .join(", ")
352 );
353 }
354 }
355 }
356 lint.span_suggestion(
357 span,
358 "disambiguate the associated function",
359 ::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),
360 Applicability::MachineApplicable,
361 );
362 lint
363 }
364 }
365
366 if span.at_least_rust_2021() {
368 return;
369 }
370
371 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) {
373 return;
374 }
375
376 if STDLIB_STABLE_CRATES.contains(&self.tcx.crate_name(pick.item.def_id.krate)) {
378 return;
379 }
380
381 if method_name.name == sym::from_iter {
384 if let Some(trait_def_id) = self.tcx.get_diagnostic_item(sym::FromIterator) {
385 let any_type = self.infcx.next_ty_var(span);
386 if !self
387 .infcx
388 .type_implements_trait(trait_def_id, [self_ty, any_type], self.param_env)
389 .may_apply()
390 {
391 return;
392 }
393 }
394 }
395
396 if #[allow(non_exhaustive_omitted_patterns)] match pick.kind {
probe::PickKind::InherentImplPick => true,
_ => false,
}matches!(pick.kind, probe::PickKind::InherentImplPick) {
399 return;
400 }
401
402 self.tcx.emit_node_span_lint(
403 RUST_2021_PRELUDE_COLLISIONS,
404 expr_id,
405 span,
406 AmbiguousTraitAssocFunc {
407 method_name: method_name.name,
408 this: self,
409 pick,
410 span,
411 expr_id,
412 self_ty_span,
413 self_ty,
414 },
415 );
416 }
417
418 fn trait_path_or_bare_name(
419 &self,
420 span: Span,
421 expr_hir_id: HirId,
422 trait_def_id: DefId,
423 ) -> String {
424 self.trait_path(span, expr_hir_id, trait_def_id).unwrap_or_else(|| {
425 let key = self.tcx.def_key(trait_def_id);
426 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}", key.disambiguated_data.data))
})format!("{}", key.disambiguated_data.data)
427 })
428 }
429
430 fn trait_path(&self, span: Span, expr_hir_id: HirId, trait_def_id: DefId) -> Option<String> {
431 let applicable_traits = self.tcx.in_scope_traits(expr_hir_id)?;
432 let applicable_trait = applicable_traits.iter().find(|t| t.def_id == trait_def_id)?;
433 if applicable_trait.import_ids.is_empty() {
434 return None;
436 }
437
438 let import_items: Vec<_> = applicable_trait
439 .import_ids
440 .iter()
441 .map(|&import_id| self.tcx.hir_expect_item(import_id))
442 .collect();
443
444 for item in import_items.iter() {
446 let (_, kind) = item.expect_use();
447 match kind {
448 hir::UseKind::Single(ident) => {
449 if ident.name != kw::Underscore {
450 return Some(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}", ident.name))
})format!("{}", ident.name));
451 }
452 }
453 hir::UseKind::Glob => return None, hir::UseKind::ListStem => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
455 }
456 }
457
458 match import_items[0].kind {
461 ItemKind::Use(path, _) => {
462 Some(join_path_idents(path.segments.iter().map(|seg| seg.ident)))
463 }
464 _ => {
465 ::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);
466 }
467 }
468 }
469
470 fn adjust_expr(
474 &self,
475 pick: &Pick<'tcx>,
476 expr: &hir::Expr<'tcx>,
477 outer: Span,
478 ) -> (String, bool) {
479 let derefs = "*".repeat(pick.autoderefs);
480
481 let autoref = match pick.autoref_or_ptr_adjustment {
482 Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, .. }) => mutbl.ref_prefix_str(),
483 Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
484 Some(probe::AutorefOrPtrAdjustment::ReborrowPin(mutbl)) => match mutbl {
485 hir::Mutability::Mut => "Pin<&mut ",
486 hir::Mutability::Not => "Pin<&",
487 },
488 };
489
490 let (expr_text, precise) = if let Some(expr_text) = expr
491 .span
492 .find_ancestor_inside(outer)
493 .and_then(|span| self.sess().source_map().span_to_snippet(span).ok())
494 {
495 (expr_text, true)
496 } else {
497 ("(..)".to_string(), false)
498 };
499
500 let mut adjusted_text = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
501 pick.autoref_or_ptr_adjustment
502 {
503 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1} as *const _", derefs,
expr_text))
})format!("{derefs}{expr_text} as *const _")
504 } else {
505 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1}{2}", autoref, derefs,
expr_text))
})format!("{autoref}{derefs}{expr_text}")
506 };
507
508 if let Some(probe::AutorefOrPtrAdjustment::ReborrowPin(_)) = pick.autoref_or_ptr_adjustment
509 {
510 adjusted_text.push('>');
511 }
512
513 (adjusted_text, precise)
514 }
515}