|
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