1 use crate::{ 2 method::probe::{self, Pick}, 3 FnCtxt, 4 }; 5 use hir::def_id::DefId; 6 use hir::HirId; 7 use hir::ItemKind; 8 use rustc_errors::Applicability; 9 use rustc_hir as hir; 10 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; 11 use rustc_middle::ty::{Adt, Array, Ref, Ty}; 12 use rustc_session::lint::builtin::RUST_2021_PRELUDE_COLLISIONS; 13 use rustc_span::symbol::kw::{Empty, Underscore}; 14 use rustc_span::symbol::{sym, Ident}; 15 use rustc_span::Span; 16 use rustc_trait_selection::infer::InferCtxtExt; 17 18 impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lint_dot_call_from_2018( &self, self_ty: Ty<'tcx>, segment: &hir::PathSegment<'_>, span: Span, call_expr: &'tcx hir::Expr<'tcx>, self_expr: &'tcx hir::Expr<'tcx>, pick: &Pick<'tcx>, args: &'tcx [hir::Expr<'tcx>], )19 pub(super) fn lint_dot_call_from_2018( 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 // Rust 2021 and later is already using the new prelude 35 if span.rust_2021() { 36 return; 37 } 38 39 let prelude_or_array_lint = match segment.ident.name { 40 // `try_into` was added to the prelude in Rust 2021. 41 sym::try_into => RUST_2021_PRELUDE_COLLISIONS, 42 // `into_iter` wasn't added to the prelude, 43 // but `[T; N].into_iter()` doesn't resolve to IntoIterator::into_iter 44 // before Rust 2021, which results in the same problem. 45 // It is only a problem for arrays. 46 sym::into_iter if let Array(..) = self_ty.kind() => { 47 // In this case, it wasn't really a prelude addition that was the problem. 48 // Instead, the problem is that the array-into_iter hack will no longer apply in Rust 2021. 49 rustc_lint::ARRAY_INTO_ITER 50 } 51 _ => return, 52 }; 53 54 // No need to lint if method came from std/core, as that will now be in the prelude 55 if matches!(self.tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) { 56 return; 57 } 58 59 if matches!(pick.kind, probe::PickKind::InherentImplPick | probe::PickKind::ObjectPick) { 60 // avoid repeatedly adding unneeded `&*`s 61 if pick.autoderefs == 1 62 && matches!( 63 pick.autoref_or_ptr_adjustment, 64 Some(probe::AutorefOrPtrAdjustment::Autoref { .. }) 65 ) 66 && matches!(self_ty.kind(), Ref(..)) 67 { 68 return; 69 } 70 71 // if it's an inherent `self` method (not `&self` or `&mut self`), it will take 72 // precedence over the `TryInto` impl, and thus won't break in 2021 edition 73 if pick.autoderefs == 0 && pick.autoref_or_ptr_adjustment.is_none() { 74 return; 75 } 76 77 // Inherent impls only require not relying on autoref and autoderef in order to 78 // ensure that the trait implementation won't be used 79 self.tcx.struct_span_lint_hir( 80 prelude_or_array_lint, 81 self_expr.hir_id, 82 self_expr.span, 83 format!("trait method `{}` will become ambiguous in Rust 2021", segment.ident.name), 84 |lint| { 85 let sp = self_expr.span; 86 87 let derefs = "*".repeat(pick.autoderefs); 88 89 let autoref = match pick.autoref_or_ptr_adjustment { 90 Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, .. }) => { 91 mutbl.ref_prefix_str() 92 } 93 Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "", 94 }; 95 if let Ok(self_expr) = self.sess().source_map().span_to_snippet(self_expr.span) 96 { 97 let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) = 98 pick.autoref_or_ptr_adjustment 99 { 100 format!("{}{} as *const _", derefs, self_expr) 101 } else { 102 format!("{}{}{}", autoref, derefs, self_expr) 103 }; 104 105 lint.span_suggestion( 106 sp, 107 "disambiguate the method call", 108 format!("({})", self_adjusted), 109 Applicability::MachineApplicable, 110 ); 111 } else { 112 let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) = 113 pick.autoref_or_ptr_adjustment 114 { 115 format!("{}(...) as *const _", derefs) 116 } else { 117 format!("{}{}...", autoref, derefs) 118 }; 119 lint.span_help( 120 sp, 121 format!("disambiguate the method call with `({})`", self_adjusted,), 122 ); 123 } 124 125 lint 126 }, 127 ); 128 } else { 129 // trait implementations require full disambiguation to not clash with the new prelude 130 // additions (i.e. convert from dot-call to fully-qualified call) 131 self.tcx.struct_span_lint_hir( 132 prelude_or_array_lint, 133 call_expr.hir_id, 134 call_expr.span, 135 format!("trait method `{}` will become ambiguous in Rust 2021", segment.ident.name), 136 |lint| { 137 let sp = call_expr.span; 138 let trait_name = self.trait_path_or_bare_name( 139 span, 140 call_expr.hir_id, 141 pick.item.container_id(self.tcx), 142 ); 143 144 let (self_adjusted, precise) = self.adjust_expr(pick, self_expr, sp); 145 if precise { 146 let args = args 147 .iter() 148 .map(|arg| { 149 let span = arg.span.find_ancestor_inside(sp).unwrap_or_default(); 150 format!( 151 ", {}", 152 self.sess().source_map().span_to_snippet(span).unwrap() 153 ) 154 }) 155 .collect::<String>(); 156 157 lint.span_suggestion( 158 sp, 159 "disambiguate the associated function", 160 format!( 161 "{}::{}{}({}{})", 162 trait_name, 163 segment.ident.name, 164 if let Some(args) = segment.args.as_ref().and_then(|args| self 165 .sess() 166 .source_map() 167 .span_to_snippet(args.span_ext) 168 .ok()) 169 { 170 // Keep turbofish. 171 format!("::{}", args) 172 } else { 173 String::new() 174 }, 175 self_adjusted, 176 args, 177 ), 178 Applicability::MachineApplicable, 179 ); 180 } else { 181 lint.span_help( 182 sp, 183 format!( 184 "disambiguate the associated function with `{}::{}(...)`", 185 trait_name, segment.ident, 186 ), 187 ); 188 } 189 190 lint 191 }, 192 ); 193 } 194 } 195 lint_fully_qualified_call_from_2018( &self, span: Span, method_name: Ident, self_ty: Ty<'tcx>, self_ty_span: Span, expr_id: hir::HirId, pick: &Pick<'tcx>, )196 pub(super) fn lint_fully_qualified_call_from_2018( 197 &self, 198 span: Span, 199 method_name: Ident, 200 self_ty: Ty<'tcx>, 201 self_ty_span: Span, 202 expr_id: hir::HirId, 203 pick: &Pick<'tcx>, 204 ) { 205 // Rust 2021 and later is already using the new prelude 206 if span.rust_2021() { 207 return; 208 } 209 210 // These are the fully qualified methods added to prelude in Rust 2021 211 if !matches!(method_name.name, sym::try_into | sym::try_from | sym::from_iter) { 212 return; 213 } 214 215 // No need to lint if method came from std/core, as that will now be in the prelude 216 if matches!(self.tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) { 217 return; 218 } 219 220 // For from_iter, check if the type actually implements FromIterator. 221 // If we know it does not, we don't need to warn. 222 if method_name.name == sym::from_iter { 223 if let Some(trait_def_id) = self.tcx.get_diagnostic_item(sym::FromIterator) { 224 let any_type = self.infcx.next_ty_var(TypeVariableOrigin { 225 kind: TypeVariableOriginKind::MiscVariable, 226 span, 227 }); 228 if !self 229 .infcx 230 .type_implements_trait(trait_def_id, [self_ty, any_type], self.param_env) 231 .may_apply() 232 { 233 return; 234 } 235 } 236 } 237 238 // No need to lint if this is an inherent method called on a specific type, like `Vec::foo(...)`, 239 // since such methods take precedence over trait methods. 240 if matches!(pick.kind, probe::PickKind::InherentImplPick) { 241 return; 242 } 243 244 self.tcx.struct_span_lint_hir( 245 RUST_2021_PRELUDE_COLLISIONS, 246 expr_id, 247 span, 248 format!( 249 "trait-associated function `{}` will become ambiguous in Rust 2021", 250 method_name.name 251 ), 252 |lint| { 253 // "type" refers to either a type or, more likely, a trait from which 254 // the associated function or method is from. 255 let container_id = pick.item.container_id(self.tcx); 256 let trait_path = self.trait_path_or_bare_name(span, expr_id, container_id); 257 let trait_generics = self.tcx.generics_of(container_id); 258 259 let trait_name = if trait_generics.params.len() <= trait_generics.has_self as usize 260 { 261 trait_path 262 } else { 263 let counts = trait_generics.own_counts(); 264 format!( 265 "{}<{}>", 266 trait_path, 267 std::iter::repeat("'_") 268 .take(counts.lifetimes) 269 .chain(std::iter::repeat("_").take( 270 counts.types + counts.consts - trait_generics.has_self as usize 271 )) 272 .collect::<Vec<_>>() 273 .join(", ") 274 ) 275 }; 276 277 let mut self_ty_name = self_ty_span 278 .find_ancestor_inside(span) 279 .and_then(|span| self.sess().source_map().span_to_snippet(span).ok()) 280 .unwrap_or_else(|| self_ty.to_string()); 281 282 // Get the number of generics the self type has (if an Adt) unless we can determine that 283 // the user has written the self type with generics already which we (naively) do by looking 284 // for a "<" in `self_ty_name`. 285 if !self_ty_name.contains('<') { 286 if let Adt(def, _) = self_ty.kind() { 287 let generics = self.tcx.generics_of(def.did()); 288 if !generics.params.is_empty() { 289 let counts = generics.own_counts(); 290 self_ty_name += &format!( 291 "<{}>", 292 std::iter::repeat("'_") 293 .take(counts.lifetimes) 294 .chain( 295 std::iter::repeat("_").take(counts.types + counts.consts) 296 ) 297 .collect::<Vec<_>>() 298 .join(", ") 299 ); 300 } 301 } 302 } 303 lint.span_suggestion( 304 span, 305 "disambiguate the associated function", 306 format!("<{} as {}>::{}", self_ty_name, trait_name, method_name.name,), 307 Applicability::MachineApplicable, 308 ); 309 310 lint 311 }, 312 ); 313 } 314 trait_path_or_bare_name( &self, span: Span, expr_hir_id: HirId, trait_def_id: DefId, ) -> String315 fn trait_path_or_bare_name( 316 &self, 317 span: Span, 318 expr_hir_id: HirId, 319 trait_def_id: DefId, 320 ) -> String { 321 self.trait_path(span, expr_hir_id, trait_def_id).unwrap_or_else(|| { 322 let key = self.tcx.def_key(trait_def_id); 323 format!("{}", key.disambiguated_data.data) 324 }) 325 } 326 trait_path(&self, span: Span, expr_hir_id: HirId, trait_def_id: DefId) -> Option<String>327 fn trait_path(&self, span: Span, expr_hir_id: HirId, trait_def_id: DefId) -> Option<String> { 328 let applicable_traits = self.tcx.in_scope_traits(expr_hir_id)?; 329 let applicable_trait = applicable_traits.iter().find(|t| t.def_id == trait_def_id)?; 330 if applicable_trait.import_ids.is_empty() { 331 // The trait was declared within the module, we only need to use its name. 332 return None; 333 } 334 335 let import_items: Vec<_> = applicable_trait 336 .import_ids 337 .iter() 338 .map(|&import_id| self.tcx.hir().expect_item(import_id)) 339 .collect(); 340 341 // Find an identifier with which this trait was imported (note that `_` doesn't count). 342 let any_id = import_items 343 .iter() 344 .find_map(|item| if item.ident.name != Underscore { Some(item.ident) } else { None }); 345 if let Some(any_id) = any_id { 346 if any_id.name == Empty { 347 // Glob import, so just use its name. 348 return None; 349 } else { 350 return Some(format!("{}", any_id)); 351 } 352 } 353 354 // All that is left is `_`! We need to use the full path. It doesn't matter which one we pick, 355 // so just take the first one. 356 match import_items[0].kind { 357 ItemKind::Use(path, _) => Some( 358 path.segments 359 .iter() 360 .map(|segment| segment.ident.to_string()) 361 .collect::<Vec<_>>() 362 .join("::"), 363 ), 364 _ => { 365 span_bug!(span, "unexpected item kind, expected a use: {:?}", import_items[0].kind); 366 } 367 } 368 } 369 370 /// Creates a string version of the `expr` that includes explicit adjustments. 371 /// Returns the string and also a bool indicating whether this is a *precise* 372 /// suggestion. adjust_expr( &self, pick: &Pick<'tcx>, expr: &hir::Expr<'tcx>, outer: Span, ) -> (String, bool)373 fn adjust_expr( 374 &self, 375 pick: &Pick<'tcx>, 376 expr: &hir::Expr<'tcx>, 377 outer: Span, 378 ) -> (String, bool) { 379 let derefs = "*".repeat(pick.autoderefs); 380 381 let autoref = match pick.autoref_or_ptr_adjustment { 382 Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, .. }) => mutbl.ref_prefix_str(), 383 Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "", 384 }; 385 386 let (expr_text, precise) = if let Some(expr_text) = expr 387 .span 388 .find_ancestor_inside(outer) 389 .and_then(|span| self.sess().source_map().span_to_snippet(span).ok()) 390 { 391 (expr_text, true) 392 } else { 393 ("(..)".to_string(), false) 394 }; 395 396 let adjusted_text = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) = 397 pick.autoref_or_ptr_adjustment 398 { 399 format!("{}{} as *const _", derefs, expr_text) 400 } else { 401 format!("{}{}{}", autoref, derefs, expr_text) 402 }; 403 404 (adjusted_text, precise) 405 } 406 } 407