|
| 1 | +use crate::astconv::AstConv; |
| 2 | +use rustc_ast::util::lev_distance::find_best_match_for_name; |
| 3 | +use rustc_data_structures::fx::FxHashMap; |
| 4 | +use rustc_errors::{pluralize, struct_span_err, Applicability}; |
| 5 | +use rustc_hir as hir; |
| 6 | +use rustc_hir::def_id::DefId; |
| 7 | +use rustc_middle::ty; |
| 8 | +use rustc_session::parse::feature_err; |
| 9 | +use rustc_span::symbol::{sym, Ident}; |
| 10 | +use rustc_span::{Span, DUMMY_SP}; |
| 11 | + |
| 12 | +use std::collections::BTreeSet; |
| 13 | + |
| 14 | +impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { |
| 15 | + /// On missing type parameters, emit an E0393 error and provide a structured suggestion using |
| 16 | + /// the type parameter's name as a placeholder. |
| 17 | + pub(crate) fn complain_about_missing_type_params( |
| 18 | + &self, |
| 19 | + missing_type_params: Vec<String>, |
| 20 | + def_id: DefId, |
| 21 | + span: Span, |
| 22 | + empty_generic_args: bool, |
| 23 | + ) { |
| 24 | + if missing_type_params.is_empty() { |
| 25 | + return; |
| 26 | + } |
| 27 | + let display = |
| 28 | + missing_type_params.iter().map(|n| format!("`{}`", n)).collect::<Vec<_>>().join(", "); |
| 29 | + let mut err = struct_span_err!( |
| 30 | + self.tcx().sess, |
| 31 | + span, |
| 32 | + E0393, |
| 33 | + "the type parameter{} {} must be explicitly specified", |
| 34 | + pluralize!(missing_type_params.len()), |
| 35 | + display, |
| 36 | + ); |
| 37 | + err.span_label( |
| 38 | + self.tcx().def_span(def_id), |
| 39 | + &format!( |
| 40 | + "type parameter{} {} must be specified for this", |
| 41 | + pluralize!(missing_type_params.len()), |
| 42 | + display, |
| 43 | + ), |
| 44 | + ); |
| 45 | + let mut suggested = false; |
| 46 | + if let (Ok(snippet), true) = ( |
| 47 | + self.tcx().sess.source_map().span_to_snippet(span), |
| 48 | + // Don't suggest setting the type params if there are some already: the order is |
| 49 | + // tricky to get right and the user will already know what the syntax is. |
| 50 | + empty_generic_args, |
| 51 | + ) { |
| 52 | + if snippet.ends_with('>') { |
| 53 | + // The user wrote `Trait<'a, T>` or similar. To provide an accurate suggestion |
| 54 | + // we would have to preserve the right order. For now, as clearly the user is |
| 55 | + // aware of the syntax, we do nothing. |
| 56 | + } else { |
| 57 | + // The user wrote `Iterator`, so we don't have a type we can suggest, but at |
| 58 | + // least we can clue them to the correct syntax `Iterator<Type>`. |
| 59 | + err.span_suggestion( |
| 60 | + span, |
| 61 | + &format!( |
| 62 | + "set the type parameter{plural} to the desired type{plural}", |
| 63 | + plural = pluralize!(missing_type_params.len()), |
| 64 | + ), |
| 65 | + format!("{}<{}>", snippet, missing_type_params.join(", ")), |
| 66 | + Applicability::HasPlaceholders, |
| 67 | + ); |
| 68 | + suggested = true; |
| 69 | + } |
| 70 | + } |
| 71 | + if !suggested { |
| 72 | + err.span_label( |
| 73 | + span, |
| 74 | + format!( |
| 75 | + "missing reference{} to {}", |
| 76 | + pluralize!(missing_type_params.len()), |
| 77 | + display, |
| 78 | + ), |
| 79 | + ); |
| 80 | + } |
| 81 | + err.note( |
| 82 | + "because of the default `Self` reference, type parameters must be \ |
| 83 | + specified on object types", |
| 84 | + ); |
| 85 | + err.emit(); |
| 86 | + } |
| 87 | + |
| 88 | + /// When the code is using the `Fn` traits directly, instead of the `Fn(A) -> B` syntax, emit |
| 89 | + /// an error and attempt to build a reasonable structured suggestion. |
| 90 | + pub(crate) fn complain_about_internal_fn_trait( |
| 91 | + &self, |
| 92 | + span: Span, |
| 93 | + trait_def_id: DefId, |
| 94 | + trait_segment: &'a hir::PathSegment<'a>, |
| 95 | + ) { |
| 96 | + let trait_def = self.tcx().trait_def(trait_def_id); |
| 97 | + |
| 98 | + if !self.tcx().features().unboxed_closures |
| 99 | + && trait_segment.generic_args().parenthesized != trait_def.paren_sugar |
| 100 | + { |
| 101 | + let sess = &self.tcx().sess.parse_sess; |
| 102 | + // For now, require that parenthetical notation be used only with `Fn()` etc. |
| 103 | + let (msg, sugg) = if trait_def.paren_sugar { |
| 104 | + ( |
| 105 | + "the precise format of `Fn`-family traits' type parameters is subject to \ |
| 106 | + change", |
| 107 | + Some(format!( |
| 108 | + "{}{} -> {}", |
| 109 | + trait_segment.ident, |
| 110 | + trait_segment |
| 111 | + .args |
| 112 | + .as_ref() |
| 113 | + .and_then(|args| args.args.get(0)) |
| 114 | + .and_then(|arg| match arg { |
| 115 | + hir::GenericArg::Type(ty) => match ty.kind { |
| 116 | + hir::TyKind::Tup(t) => t |
| 117 | + .iter() |
| 118 | + .map(|e| sess.source_map().span_to_snippet(e.span)) |
| 119 | + .collect::<Result<Vec<_>, _>>() |
| 120 | + .map(|a| a.join(", ")), |
| 121 | + _ => sess.source_map().span_to_snippet(ty.span), |
| 122 | + } |
| 123 | + .map(|s| format!("({})", s)) |
| 124 | + .ok(), |
| 125 | + _ => None, |
| 126 | + }) |
| 127 | + .unwrap_or_else(|| "()".to_string()), |
| 128 | + trait_segment |
| 129 | + .generic_args() |
| 130 | + .bindings |
| 131 | + .iter() |
| 132 | + .find_map(|b| match (b.ident.name == sym::Output, &b.kind) { |
| 133 | + (true, hir::TypeBindingKind::Equality { ty }) => { |
| 134 | + sess.source_map().span_to_snippet(ty.span).ok() |
| 135 | + } |
| 136 | + _ => None, |
| 137 | + }) |
| 138 | + .unwrap_or_else(|| "()".to_string()), |
| 139 | + )), |
| 140 | + ) |
| 141 | + } else { |
| 142 | + ("parenthetical notation is only stable when used with `Fn`-family traits", None) |
| 143 | + }; |
| 144 | + let mut err = feature_err(sess, sym::unboxed_closures, span, msg); |
| 145 | + if let Some(sugg) = sugg { |
| 146 | + let msg = "use parenthetical notation instead"; |
| 147 | + err.span_suggestion(span, msg, sugg, Applicability::MaybeIncorrect); |
| 148 | + } |
| 149 | + err.emit(); |
| 150 | + } |
| 151 | + } |
| 152 | + |
| 153 | + pub(crate) fn complain_about_assoc_type_not_found<I>( |
| 154 | + &self, |
| 155 | + all_candidates: impl Fn() -> I, |
| 156 | + ty_param_name: &str, |
| 157 | + assoc_name: Ident, |
| 158 | + span: Span, |
| 159 | + ) where |
| 160 | + I: Iterator<Item = ty::PolyTraitRef<'tcx>>, |
| 161 | + { |
| 162 | + // The fallback span is needed because `assoc_name` might be an `Fn()`'s `Output` without a |
| 163 | + // valid span, so we point at the whole path segment instead. |
| 164 | + let span = if assoc_name.span != DUMMY_SP { assoc_name.span } else { span }; |
| 165 | + let mut err = struct_span_err!( |
| 166 | + self.tcx().sess, |
| 167 | + span, |
| 168 | + E0220, |
| 169 | + "associated type `{}` not found for `{}`", |
| 170 | + assoc_name, |
| 171 | + ty_param_name |
| 172 | + ); |
| 173 | + |
| 174 | + let all_candidate_names: Vec<_> = all_candidates() |
| 175 | + .map(|r| self.tcx().associated_items(r.def_id()).in_definition_order()) |
| 176 | + .flatten() |
| 177 | + .filter_map( |
| 178 | + |item| if item.kind == ty::AssocKind::Type { Some(item.ident.name) } else { None }, |
| 179 | + ) |
| 180 | + .collect(); |
| 181 | + |
| 182 | + if let (Some(suggested_name), true) = ( |
| 183 | + find_best_match_for_name(all_candidate_names.iter(), assoc_name.name, None), |
| 184 | + assoc_name.span != DUMMY_SP, |
| 185 | + ) { |
| 186 | + err.span_suggestion( |
| 187 | + assoc_name.span, |
| 188 | + "there is an associated type with a similar name", |
| 189 | + suggested_name.to_string(), |
| 190 | + Applicability::MaybeIncorrect, |
| 191 | + ); |
| 192 | + } else { |
| 193 | + err.span_label(span, format!("associated type `{}` not found", assoc_name)); |
| 194 | + } |
| 195 | + |
| 196 | + err.emit(); |
| 197 | + } |
| 198 | + |
| 199 | + /// When there are any missing associated types, emit an E0191 error and attempt to supply a |
| 200 | + /// reasonable suggestion on how to write it. For the case of multiple associated types in the |
| 201 | + /// same trait bound have the same name (as they come from different super-traits), we instead |
| 202 | + /// emit a generic note suggesting using a `where` clause to constraint instead. |
| 203 | + pub(crate) fn complain_about_missing_associated_types( |
| 204 | + &self, |
| 205 | + associated_types: FxHashMap<Span, BTreeSet<DefId>>, |
| 206 | + potential_assoc_types: Vec<Span>, |
| 207 | + trait_bounds: &[hir::PolyTraitRef<'_>], |
| 208 | + ) { |
| 209 | + if associated_types.values().all(|v| v.is_empty()) { |
| 210 | + return; |
| 211 | + } |
| 212 | + let tcx = self.tcx(); |
| 213 | + // FIXME: Marked `mut` so that we can replace the spans further below with a more |
| 214 | + // appropriate one, but this should be handled earlier in the span assignment. |
| 215 | + let mut associated_types: FxHashMap<Span, Vec<_>> = associated_types |
| 216 | + .into_iter() |
| 217 | + .map(|(span, def_ids)| { |
| 218 | + (span, def_ids.into_iter().map(|did| tcx.associated_item(did)).collect()) |
| 219 | + }) |
| 220 | + .collect(); |
| 221 | + let mut names = vec![]; |
| 222 | + |
| 223 | + // Account for things like `dyn Foo + 'a`, like in tests `issue-22434.rs` and |
| 224 | + // `issue-22560.rs`. |
| 225 | + let mut trait_bound_spans: Vec<Span> = vec![]; |
| 226 | + for (span, items) in &associated_types { |
| 227 | + if !items.is_empty() { |
| 228 | + trait_bound_spans.push(*span); |
| 229 | + } |
| 230 | + for assoc_item in items { |
| 231 | + let trait_def_id = assoc_item.container.id(); |
| 232 | + names.push(format!( |
| 233 | + "`{}` (from trait `{}`)", |
| 234 | + assoc_item.ident, |
| 235 | + tcx.def_path_str(trait_def_id), |
| 236 | + )); |
| 237 | + } |
| 238 | + } |
| 239 | + if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) { |
| 240 | + match &bound.trait_ref.path.segments[..] { |
| 241 | + // FIXME: `trait_ref.path.span` can point to a full path with multiple |
| 242 | + // segments, even though `trait_ref.path.segments` is of length `1`. Work |
| 243 | + // around that bug here, even though it should be fixed elsewhere. |
| 244 | + // This would otherwise cause an invalid suggestion. For an example, look at |
| 245 | + // `src/test/ui/issues/issue-28344.rs` where instead of the following: |
| 246 | + // |
| 247 | + // error[E0191]: the value of the associated type `Output` |
| 248 | + // (from trait `std::ops::BitXor`) must be specified |
| 249 | + // --> $DIR/issue-28344.rs:4:17 |
| 250 | + // | |
| 251 | + // LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8); |
| 252 | + // | ^^^^^^ help: specify the associated type: |
| 253 | + // | `BitXor<Output = Type>` |
| 254 | + // |
| 255 | + // we would output: |
| 256 | + // |
| 257 | + // error[E0191]: the value of the associated type `Output` |
| 258 | + // (from trait `std::ops::BitXor`) must be specified |
| 259 | + // --> $DIR/issue-28344.rs:4:17 |
| 260 | + // | |
| 261 | + // LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8); |
| 262 | + // | ^^^^^^^^^^^^^ help: specify the associated type: |
| 263 | + // | `BitXor::bitor<Output = Type>` |
| 264 | + [segment] if segment.args.is_none() => { |
| 265 | + trait_bound_spans = vec![segment.ident.span]; |
| 266 | + associated_types = associated_types |
| 267 | + .into_iter() |
| 268 | + .map(|(_, items)| (segment.ident.span, items)) |
| 269 | + .collect(); |
| 270 | + } |
| 271 | + _ => {} |
| 272 | + } |
| 273 | + } |
| 274 | + names.sort(); |
| 275 | + trait_bound_spans.sort(); |
| 276 | + let mut err = struct_span_err!( |
| 277 | + tcx.sess, |
| 278 | + trait_bound_spans, |
| 279 | + E0191, |
| 280 | + "the value of the associated type{} {} must be specified", |
| 281 | + pluralize!(names.len()), |
| 282 | + names.join(", "), |
| 283 | + ); |
| 284 | + let mut suggestions = vec![]; |
| 285 | + let mut types_count = 0; |
| 286 | + let mut where_constraints = vec![]; |
| 287 | + for (span, assoc_items) in &associated_types { |
| 288 | + let mut names: FxHashMap<_, usize> = FxHashMap::default(); |
| 289 | + for item in assoc_items { |
| 290 | + types_count += 1; |
| 291 | + *names.entry(item.ident.name).or_insert(0) += 1; |
| 292 | + } |
| 293 | + let mut dupes = false; |
| 294 | + for item in assoc_items { |
| 295 | + let prefix = if names[&item.ident.name] > 1 { |
| 296 | + let trait_def_id = item.container.id(); |
| 297 | + dupes = true; |
| 298 | + format!("{}::", tcx.def_path_str(trait_def_id)) |
| 299 | + } else { |
| 300 | + String::new() |
| 301 | + }; |
| 302 | + if let Some(sp) = tcx.hir().span_if_local(item.def_id) { |
| 303 | + err.span_label(sp, format!("`{}{}` defined here", prefix, item.ident)); |
| 304 | + } |
| 305 | + } |
| 306 | + if potential_assoc_types.len() == assoc_items.len() { |
| 307 | + // Only suggest when the amount of missing associated types equals the number of |
| 308 | + // extra type arguments present, as that gives us a relatively high confidence |
| 309 | + // that the user forgot to give the associtated type's name. The canonical |
| 310 | + // example would be trying to use `Iterator<isize>` instead of |
| 311 | + // `Iterator<Item = isize>`. |
| 312 | + for (potential, item) in potential_assoc_types.iter().zip(assoc_items.iter()) { |
| 313 | + if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(*potential) { |
| 314 | + suggestions.push((*potential, format!("{} = {}", item.ident, snippet))); |
| 315 | + } |
| 316 | + } |
| 317 | + } else if let (Ok(snippet), false) = |
| 318 | + (tcx.sess.source_map().span_to_snippet(*span), dupes) |
| 319 | + { |
| 320 | + let types: Vec<_> = |
| 321 | + assoc_items.iter().map(|item| format!("{} = Type", item.ident)).collect(); |
| 322 | + let code = if snippet.ends_with('>') { |
| 323 | + // The user wrote `Trait<'a>` or similar and we don't have a type we can |
| 324 | + // suggest, but at least we can clue them to the correct syntax |
| 325 | + // `Trait<'a, Item = Type>` while accounting for the `<'a>` in the |
| 326 | + // suggestion. |
| 327 | + format!("{}, {}>", &snippet[..snippet.len() - 1], types.join(", ")) |
| 328 | + } else { |
| 329 | + // The user wrote `Iterator`, so we don't have a type we can suggest, but at |
| 330 | + // least we can clue them to the correct syntax `Iterator<Item = Type>`. |
| 331 | + format!("{}<{}>", snippet, types.join(", ")) |
| 332 | + }; |
| 333 | + suggestions.push((*span, code)); |
| 334 | + } else if dupes { |
| 335 | + where_constraints.push(*span); |
| 336 | + } |
| 337 | + } |
| 338 | + let where_msg = "consider introducing a new type parameter, adding `where` constraints \ |
| 339 | + using the fully-qualified path to the associated types"; |
| 340 | + if !where_constraints.is_empty() && suggestions.is_empty() { |
| 341 | + // If there are duplicates associated type names and a single trait bound do not |
| 342 | + // use structured suggestion, it means that there are multiple super-traits with |
| 343 | + // the same associated type name. |
| 344 | + err.help(where_msg); |
| 345 | + } |
| 346 | + if suggestions.len() != 1 { |
| 347 | + // We don't need this label if there's an inline suggestion, show otherwise. |
| 348 | + for (span, assoc_items) in &associated_types { |
| 349 | + let mut names: FxHashMap<_, usize> = FxHashMap::default(); |
| 350 | + for item in assoc_items { |
| 351 | + types_count += 1; |
| 352 | + *names.entry(item.ident.name).or_insert(0) += 1; |
| 353 | + } |
| 354 | + let mut label = vec![]; |
| 355 | + for item in assoc_items { |
| 356 | + let postfix = if names[&item.ident.name] > 1 { |
| 357 | + let trait_def_id = item.container.id(); |
| 358 | + format!(" (from trait `{}`)", tcx.def_path_str(trait_def_id)) |
| 359 | + } else { |
| 360 | + String::new() |
| 361 | + }; |
| 362 | + label.push(format!("`{}`{}", item.ident, postfix)); |
| 363 | + } |
| 364 | + if !label.is_empty() { |
| 365 | + err.span_label( |
| 366 | + *span, |
| 367 | + format!( |
| 368 | + "associated type{} {} must be specified", |
| 369 | + pluralize!(label.len()), |
| 370 | + label.join(", "), |
| 371 | + ), |
| 372 | + ); |
| 373 | + } |
| 374 | + } |
| 375 | + } |
| 376 | + if !suggestions.is_empty() { |
| 377 | + err.multipart_suggestion( |
| 378 | + &format!("specify the associated type{}", pluralize!(types_count)), |
| 379 | + suggestions, |
| 380 | + Applicability::HasPlaceholders, |
| 381 | + ); |
| 382 | + if !where_constraints.is_empty() { |
| 383 | + err.span_help(where_constraints, where_msg); |
| 384 | + } |
| 385 | + } |
| 386 | + err.emit(); |
| 387 | + } |
| 388 | +} |
0 commit comments