1use std::fmt::Write;
2
3use hir::def_id::DefId;
4use hir::{HirId, ItemKind};
5use rustc_ast::join_path_idents;
6use rustc_errors::Applicability;
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};
11use rustc_session::lint::builtin::{RUST_2021_PRELUDE_COLLISIONS, RUST_2024_PRELUDE_COLLISIONS};
12use rustc_span::{Ident, STDLIB_STABLE_CRATES, Span, kw, sym};
13use rustc_trait_selection::infer::InferCtxtExt;
14use tracing::debug;
15
16use crate::FnCtxt;
17use crate::method::probe::{self, Pick};
18
19impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
20 pub(super) fn lint_edition_dependent_dot_call(
21 &self,
22 self_ty: Ty<'tcx>,
23 segment: &hir::PathSegment<'_>,
24 span: Span,
25 call_expr: &'tcx hir::Expr<'tcx>,
26 self_expr: &'tcx hir::Expr<'tcx>,
27 pick: &Pick<'tcx>,
28 args: &'tcx [hir::Expr<'tcx>],
29 ) {
30 debug!(
31 "lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})",
32 segment.ident, self_ty, call_expr, self_expr
33 );
34
35 let (prelude_or_array_lint, edition) = match segment.ident.name {
36 sym::try_into if !span.at_least_rust_2021() => (RUST_2021_PRELUDE_COLLISIONS, "2021"),
38 sym::poll
40 if !span.at_least_rust_2024()
42 && let ty::Adt(adt_def, args) = self_ty.kind()
43 && self.tcx.is_lang_item(adt_def.did(), hir::LangItem::Pin)
44 && let ty::Ref(_, _, ty::Mutability::Mut) =
45 args[0].as_type().unwrap().kind() =>
46 {
47 (RUST_2024_PRELUDE_COLLISIONS, "2024")
48 }
49 sym::into_future if !span.at_least_rust_2024() => {
51 (RUST_2024_PRELUDE_COLLISIONS, "2024")
52 }
53 sym::into_iter => {
58 if let ty::Array(..) = self_ty.kind()
59 && !span.at_least_rust_2021()
60 {
61 (ARRAY_INTO_ITER, "2021")
65 } else if self_ty.boxed_ty().is_some_and(Ty::is_slice)
66 && !span.at_least_rust_2024()
67 {
68 (BOXED_SLICE_INTO_ITER, "2024")
72 } else {
73 return;
74 }
75 }
76 _ => return,
77 };
78
79 if STDLIB_STABLE_CRATES.contains(&self.tcx.crate_name(pick.item.def_id.krate)) {
81 return;
82 }
83
84 if matches!(pick.kind, probe::PickKind::InherentImplPick | probe::PickKind::ObjectPick) {
85 if pick.autoderefs == 1
87 && matches!(
88 pick.autoref_or_ptr_adjustment,
89 Some(probe::AutorefOrPtrAdjustment::Autoref { .. })
90 )
91 && matches!(self_ty.kind(), ty::Ref(..))
92 {
93 return;
94 }
95
96 if pick.autoderefs == 0 && pick.autoref_or_ptr_adjustment.is_none() {
99 return;
100 }
101
102 self.tcx.node_span_lint(
105 prelude_or_array_lint,
106 self_expr.hir_id,
107 self_expr.span,
108 |lint| {
109 lint.primary_message(format!(
110 "trait method `{}` will become ambiguous in Rust {edition}",
111 segment.ident.name
112 ));
113
114 let sp = self_expr.span;
115
116 let derefs = "*".repeat(pick.autoderefs);
117
118 let autoref = match pick.autoref_or_ptr_adjustment {
119 Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, .. }) => {
120 mutbl.ref_prefix_str()
121 }
122 Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
123 Some(probe::AutorefOrPtrAdjustment::ReborrowPin(mutbl)) => match mutbl {
124 hir::Mutability::Mut => "Pin<&mut ",
125 hir::Mutability::Not => "Pin<&",
126 },
127 };
128 if let Ok(self_expr) = self.sess().source_map().span_to_snippet(self_expr.span)
129 {
130 let mut self_adjusted =
131 if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
132 pick.autoref_or_ptr_adjustment
133 {
134 format!("{derefs}{self_expr} as *const _")
135 } else {
136 format!("{autoref}{derefs}{self_expr}")
137 };
138
139 if let Some(probe::AutorefOrPtrAdjustment::ReborrowPin(_)) =
140 pick.autoref_or_ptr_adjustment
141 {
142 self_adjusted.push('>');
143 }
144
145 lint.span_suggestion(
146 sp,
147 "disambiguate the method call",
148 format!("({self_adjusted})"),
149 Applicability::MachineApplicable,
150 );
151 } else {
152 let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
153 pick.autoref_or_ptr_adjustment
154 {
155 format!("{derefs}(...) as *const _")
156 } else {
157 format!("{autoref}{derefs}...")
158 };
159 lint.span_help(
160 sp,
161 format!("disambiguate the method call with `({self_adjusted})`",),
162 );
163 }
164 },
165 );
166 } else {
167 self.tcx.node_span_lint(
170 prelude_or_array_lint,
171 call_expr.hir_id,
172 call_expr.span,
173 |lint| {
174 lint.primary_message(format!(
175 "trait method `{}` will become ambiguous in Rust {edition}",
176 segment.ident.name
177 ));
178
179 let sp = call_expr.span;
180 let trait_name = self.trait_path_or_bare_name(
181 span,
182 call_expr.hir_id,
183 pick.item.container_id(self.tcx),
184 );
185
186 let (self_adjusted, precise) = self.adjust_expr(pick, self_expr, sp);
187 if precise {
188 let args = args.iter().fold(String::new(), |mut string, arg| {
189 let span = arg.span.find_ancestor_inside(sp).unwrap_or_default();
190 write!(
191 string,
192 ", {}",
193 self.sess().source_map().span_to_snippet(span).unwrap()
194 )
195 .unwrap();
196 string
197 });
198
199 lint.span_suggestion(
200 sp,
201 "disambiguate the associated function",
202 format!(
203 "{}::{}{}({}{})",
204 trait_name,
205 segment.ident.name,
206 if let Some(args) = segment.args.as_ref().and_then(|args| self
207 .sess()
208 .source_map()
209 .span_to_snippet(args.span_ext)
210 .ok())
211 {
212 format!("::{args}")
214 } else {
215 String::new()
216 },
217 self_adjusted,
218 args,
219 ),
220 Applicability::MachineApplicable,
221 );
222 } else {
223 lint.span_help(
224 sp,
225 format!(
226 "disambiguate the associated function with `{}::{}(...)`",
227 trait_name, segment.ident,
228 ),
229 );
230 }
231 },
232 );
233 }
234 }
235
236 pub(super) fn lint_fully_qualified_call_from_2018(
237 &self,
238 span: Span,
239 method_name: Ident,
240 self_ty: Ty<'tcx>,
241 self_ty_span: Span,
242 expr_id: hir::HirId,
243 pick: &Pick<'tcx>,
244 ) {
245 if span.at_least_rust_2021() {
247 return;
248 }
249
250 if !matches!(method_name.name, sym::try_into | sym::try_from | sym::from_iter) {
252 return;
253 }
254
255 if STDLIB_STABLE_CRATES.contains(&self.tcx.crate_name(pick.item.def_id.krate)) {
257 return;
258 }
259
260 if method_name.name == sym::from_iter {
263 if let Some(trait_def_id) = self.tcx.get_diagnostic_item(sym::FromIterator) {
264 let any_type = self.infcx.next_ty_var(span);
265 if !self
266 .infcx
267 .type_implements_trait(trait_def_id, [self_ty, any_type], self.param_env)
268 .may_apply()
269 {
270 return;
271 }
272 }
273 }
274
275 if matches!(pick.kind, probe::PickKind::InherentImplPick) {
278 return;
279 }
280
281 self.tcx.node_span_lint(RUST_2021_PRELUDE_COLLISIONS, expr_id, span, |lint| {
282 lint.primary_message(format!(
283 "trait-associated function `{}` will become ambiguous in Rust 2021",
284 method_name.name
285 ));
286
287 let container_id = pick.item.container_id(self.tcx);
290 let trait_path = self.trait_path_or_bare_name(span, expr_id, container_id);
291 let trait_generics = self.tcx.generics_of(container_id);
292
293 let trait_name =
294 if trait_generics.own_params.len() <= trait_generics.has_self as usize {
295 trait_path
296 } else {
297 let counts = trait_generics.own_counts();
298 format!(
299 "{}<{}>",
300 trait_path,
301 std::iter::repeat("'_")
302 .take(counts.lifetimes)
303 .chain(std::iter::repeat("_").take(
304 counts.types + counts.consts - trait_generics.has_self as usize
305 ))
306 .collect::<Vec<_>>()
307 .join(", ")
308 )
309 };
310
311 let mut self_ty_name = self_ty_span
312 .find_ancestor_inside(span)
313 .and_then(|span| self.sess().source_map().span_to_snippet(span).ok())
314 .unwrap_or_else(|| self_ty.to_string());
315
316 if !self_ty_name.contains('<') {
320 if let ty::Adt(def, _) = self_ty.kind() {
321 let generics = self.tcx.generics_of(def.did());
322 if !generics.is_own_empty() {
323 let counts = generics.own_counts();
324 self_ty_name += &format!(
325 "<{}>",
326 std::iter::repeat("'_")
327 .take(counts.lifetimes)
328 .chain(std::iter::repeat("_").take(counts.types + counts.consts))
329 .collect::<Vec<_>>()
330 .join(", ")
331 );
332 }
333 }
334 }
335 lint.span_suggestion(
336 span,
337 "disambiguate the associated function",
338 format!("<{} as {}>::{}", self_ty_name, trait_name, method_name.name,),
339 Applicability::MachineApplicable,
340 );
341 });
342 }
343
344 fn trait_path_or_bare_name(
345 &self,
346 span: Span,
347 expr_hir_id: HirId,
348 trait_def_id: DefId,
349 ) -> String {
350 self.trait_path(span, expr_hir_id, trait_def_id).unwrap_or_else(|| {
351 let key = self.tcx.def_key(trait_def_id);
352 format!("{}", key.disambiguated_data.data)
353 })
354 }
355
356 fn trait_path(&self, span: Span, expr_hir_id: HirId, trait_def_id: DefId) -> Option<String> {
357 let applicable_traits = self.tcx.in_scope_traits(expr_hir_id)?;
358 let applicable_trait = applicable_traits.iter().find(|t| t.def_id == trait_def_id)?;
359 if applicable_trait.import_ids.is_empty() {
360 return None;
362 }
363
364 let import_items: Vec<_> = applicable_trait
365 .import_ids
366 .iter()
367 .map(|&import_id| self.tcx.hir_expect_item(import_id))
368 .collect();
369
370 for item in import_items.iter() {
372 let (_, kind) = item.expect_use();
373 match kind {
374 hir::UseKind::Single(ident) => {
375 if ident.name != kw::Underscore {
376 return Some(format!("{}", ident.name));
377 }
378 }
379 hir::UseKind::Glob => return None, hir::UseKind::ListStem => unreachable!(),
381 }
382 }
383
384 match import_items[0].kind {
387 ItemKind::Use(path, _) => {
388 Some(join_path_idents(path.segments.iter().map(|seg| seg.ident)))
389 }
390 _ => {
391 span_bug!(span, "unexpected item kind, expected a use: {:?}", import_items[0].kind);
392 }
393 }
394 }
395
396 fn adjust_expr(
400 &self,
401 pick: &Pick<'tcx>,
402 expr: &hir::Expr<'tcx>,
403 outer: Span,
404 ) -> (String, bool) {
405 let derefs = "*".repeat(pick.autoderefs);
406
407 let autoref = match pick.autoref_or_ptr_adjustment {
408 Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, .. }) => mutbl.ref_prefix_str(),
409 Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
410 Some(probe::AutorefOrPtrAdjustment::ReborrowPin(mutbl)) => match mutbl {
411 hir::Mutability::Mut => "Pin<&mut ",
412 hir::Mutability::Not => "Pin<&",
413 },
414 };
415
416 let (expr_text, precise) = if let Some(expr_text) = expr
417 .span
418 .find_ancestor_inside(outer)
419 .and_then(|span| self.sess().source_map().span_to_snippet(span).ok())
420 {
421 (expr_text, true)
422 } else {
423 ("(..)".to_string(), false)
424 };
425
426 let mut adjusted_text = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
427 pick.autoref_or_ptr_adjustment
428 {
429 format!("{derefs}{expr_text} as *const _")
430 } else {
431 format!("{autoref}{derefs}{expr_text}")
432 };
433
434 if let Some(probe::AutorefOrPtrAdjustment::ReborrowPin(_)) = pick.autoref_or_ptr_adjustment
435 {
436 adjusted_text.push('>');
437 }
438
439 (adjusted_text, precise)
440 }
441}