|
1 | 1 | use crate::astconv::AstConv;
|
2 |
| -use crate::errors::{ManualImplementation, MissingTypeParams}; |
| 2 | +use crate::errors::{ |
| 3 | + AssocTypeBindingNotAllowed, ManualImplementation, MissingTypeParams, |
| 4 | + ParenthesizedFnTraitExpansion, |
| 5 | +}; |
3 | 6 | use rustc_data_structures::fx::FxHashMap;
|
4 | 7 | use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, ErrorGuaranteed};
|
5 | 8 | use rustc_hir as hir;
|
6 | 9 | use rustc_hir::def_id::DefId;
|
7 | 10 | use rustc_infer::traits::FulfillmentError;
|
| 11 | +use rustc_middle::ty::TyCtxt; |
8 | 12 | use rustc_middle::ty::{self, Ty};
|
9 | 13 | use rustc_session::parse::feature_err;
|
10 | 14 | use rustc_span::edit_distance::find_best_match_for_name;
|
@@ -78,43 +82,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
78 | 82 | // Do not suggest the other syntax if we are in trait impl:
|
79 | 83 | // the desugaring would contain an associated type constraint.
|
80 | 84 | if !is_impl {
|
81 |
| - let args = trait_segment |
82 |
| - .args |
83 |
| - .as_ref() |
84 |
| - .and_then(|args| args.args.get(0)) |
85 |
| - .and_then(|arg| match arg { |
86 |
| - hir::GenericArg::Type(ty) => match ty.kind { |
87 |
| - hir::TyKind::Tup(t) => t |
88 |
| - .iter() |
89 |
| - .map(|e| sess.source_map().span_to_snippet(e.span)) |
90 |
| - .collect::<Result<Vec<_>, _>>() |
91 |
| - .map(|a| a.join(", ")), |
92 |
| - _ => sess.source_map().span_to_snippet(ty.span), |
93 |
| - } |
94 |
| - .map(|s| format!("({})", s)) |
95 |
| - .ok(), |
96 |
| - _ => None, |
97 |
| - }) |
98 |
| - .unwrap_or_else(|| "()".to_string()); |
99 |
| - let ret = trait_segment |
100 |
| - .args() |
101 |
| - .bindings |
102 |
| - .iter() |
103 |
| - .find_map(|b| match (b.ident.name == sym::Output, &b.kind) { |
104 |
| - (true, hir::TypeBindingKind::Equality { term }) => { |
105 |
| - let span = match term { |
106 |
| - hir::Term::Ty(ty) => ty.span, |
107 |
| - hir::Term::Const(c) => self.tcx().hir().span(c.hir_id), |
108 |
| - }; |
109 |
| - sess.source_map().span_to_snippet(span).ok() |
110 |
| - } |
111 |
| - _ => None, |
112 |
| - }) |
113 |
| - .unwrap_or_else(|| "()".to_string()); |
114 | 85 | err.span_suggestion(
|
115 | 86 | span,
|
116 | 87 | "use parenthetical notation instead",
|
117 |
| - format!("{}{} -> {}", trait_segment.ident, args, ret), |
| 88 | + fn_trait_to_string(self.tcx(), trait_segment, true), |
118 | 89 | Applicability::MaybeIncorrect,
|
119 | 90 | );
|
120 | 91 | }
|
@@ -629,3 +600,69 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
629 | 600 | err.emit();
|
630 | 601 | }
|
631 | 602 | }
|
| 603 | + |
| 604 | +/// Emits an error regarding forbidden type binding associations |
| 605 | +pub fn prohibit_assoc_ty_binding( |
| 606 | + tcx: TyCtxt<'_>, |
| 607 | + span: Span, |
| 608 | + segment: Option<(&hir::PathSegment<'_>, Span)>, |
| 609 | +) { |
| 610 | + tcx.sess.emit_err(AssocTypeBindingNotAllowed { span, fn_trait_expansion: if let Some((segment, span)) = segment && segment.args().parenthesized { |
| 611 | + Some(ParenthesizedFnTraitExpansion { span, expanded_type: fn_trait_to_string(tcx, segment, false) }) |
| 612 | + } else { |
| 613 | + None |
| 614 | + }}); |
| 615 | +} |
| 616 | + |
| 617 | +pub(crate) fn fn_trait_to_string( |
| 618 | + tcx: TyCtxt<'_>, |
| 619 | + trait_segment: &hir::PathSegment<'_>, |
| 620 | + parenthesized: bool, |
| 621 | +) -> String { |
| 622 | + let args = trait_segment |
| 623 | + .args |
| 624 | + .as_ref() |
| 625 | + .and_then(|args| args.args.get(0)) |
| 626 | + .and_then(|arg| match arg { |
| 627 | + hir::GenericArg::Type(ty) => match ty.kind { |
| 628 | + hir::TyKind::Tup(t) => t |
| 629 | + .iter() |
| 630 | + .map(|e| tcx.sess.source_map().span_to_snippet(e.span)) |
| 631 | + .collect::<Result<Vec<_>, _>>() |
| 632 | + .map(|a| a.join(", ")), |
| 633 | + _ => tcx.sess.source_map().span_to_snippet(ty.span), |
| 634 | + } |
| 635 | + .map(|s| { |
| 636 | + // `s.empty()` checks to see if the type is the unit tuple, if so we don't want a comma |
| 637 | + if parenthesized || s.is_empty() { format!("({})", s) } else { format!("({},)", s) } |
| 638 | + }) |
| 639 | + .ok(), |
| 640 | + _ => None, |
| 641 | + }) |
| 642 | + .unwrap_or_else(|| "()".to_string()); |
| 643 | + |
| 644 | + let ret = trait_segment |
| 645 | + .args() |
| 646 | + .bindings |
| 647 | + .iter() |
| 648 | + .find_map(|b| match (b.ident.name == sym::Output, &b.kind) { |
| 649 | + (true, hir::TypeBindingKind::Equality { term }) => { |
| 650 | + let span = match term { |
| 651 | + hir::Term::Ty(ty) => ty.span, |
| 652 | + hir::Term::Const(c) => tcx.hir().span(c.hir_id), |
| 653 | + }; |
| 654 | + |
| 655 | + (span != tcx.hir().span(trait_segment.hir_id)) |
| 656 | + .then_some(tcx.sess.source_map().span_to_snippet(span).ok()) |
| 657 | + .flatten() |
| 658 | + } |
| 659 | + _ => None, |
| 660 | + }) |
| 661 | + .unwrap_or_else(|| "()".to_string()); |
| 662 | + |
| 663 | + if parenthesized { |
| 664 | + format!("{}{} -> {}", trait_segment.ident, args, ret) |
| 665 | + } else { |
| 666 | + format!("{}<{}, Output={}>", trait_segment.ident, args, ret) |
| 667 | + } |
| 668 | +} |
0 commit comments