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