Suggest impl Trait
return type · rust-lang/rust@4bed748 (original) (raw)
`@@ -8,8 +8,12 @@ use rustc_errors::{Applicability, DiagnosticBuilder};
`
8
8
`use rustc_hir as hir;
`
9
9
`use rustc_hir::def::{CtorOf, DefKind};
`
10
10
`use rustc_hir::lang_items::LangItem;
`
11
``
`-
use rustc_hir::{Expr, ExprKind, ItemKind, Node, Path, QPath, Stmt, StmtKind, TyKind};
`
``
11
`+
use rustc_hir::{
`
``
12
`+
Expr, ExprKind, GenericBound, ItemKind, Node, Path, QPath, Stmt, StmtKind, TyKind,
`
``
13
`+
WherePredicate,
`
``
14
`+
};
`
12
15
`use rustc_infer::infer::{self, TyCtxtInferExt};
`
``
16
+
13
17
`use rustc_middle::lint::in_external_macro;
`
14
18
`use rustc_middle::ty::{self, Binder, Ty};
`
15
19
`use rustc_span::symbol::{kw, sym};
`
`@@ -559,13 +563,123 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
`
559
563
`let ty = self.tcx.erase_late_bound_regions(ty);
`
560
564
`if self.can_coerce(expected, ty) {
`
561
565
`` err.span_label(sp, format!("expected {}
because of return type", expected));
``
``
566
`+
self.try_suggest_return_impl_trait(err, expected, ty, fn_id);
`
562
567
`return true;
`
563
568
`}
`
564
569
`false
`
565
570
`}
`
566
571
`}
`
567
572
`}
`
568
573
``
``
574
`+
/// check whether the return type is a generic type with a trait bound
`
``
575
`+
/// only suggest this if the generic param is not present in the arguments
`
``
576
`` +
/// if this is true, hint them towards changing the return type to impl Trait
``
``
577
/// ```
``
578
`+
/// fn cant_name_it<T: Fn() -> u32>() -> T {
`
``
579
`+
/// || 3
`
``
580
`+
/// }
`
``
581
/// ```
``
582
`+
fn try_suggest_return_impl_trait(
`
``
583
`+
&self,
`
``
584
`+
err: &mut DiagnosticBuilder<'_>,
`
``
585
`+
expected: Ty<'tcx>,
`
``
586
`+
found: Ty<'tcx>,
`
``
587
`+
fn_id: hir::HirId,
`
``
588
`+
) {
`
``
589
`+
// Only apply the suggestion if:
`
``
590
`+
// - the return type is a generic parameter
`
``
591
`+
// - the generic param is not used as a fn param
`
``
592
`+
// - the generic param has at least one bound
`
``
593
`+
// - the generic param doesn't appear in any other bounds where it's not the Self type
`
``
594
`+
// Suggest:
`
``
595
`` +
// - Changing the return type to be impl <all bounds>
``
``
596
+
``
597
`+
debug!("try_suggest_return_impl_trait, expected = {:?}, found = {:?}", expected, found);
`
``
598
+
``
599
`+
let ty::Param(expected_ty_as_param) = expected.kind() else { return };
`
``
600
+
``
601
`+
let fn_node = self.tcx.hir().find(fn_id);
`
``
602
+
``
603
`+
let Some(hir::Node::Item(hir::Item {
`
``
604
`+
kind:
`
``
605
`+
hir::ItemKind::Fn(
`
``
606
`+
hir::FnSig { decl: hir::FnDecl { inputs: fn_parameters, output: fn_return, .. }, .. },
`
``
607
`+
hir::Generics { params, where_clause, .. },
`
``
608
`+
_body_id,
`
``
609
`+
),
`
``
610
`+
..
`
``
611
`+
})) = fn_node else { return };
`
``
612
+
``
613
`+
let Some(expected_generic_param) = params.get(expected_ty_as_param.index as usize) else { return };
`
``
614
+
``
615
`+
// get all where BoundPredicates here, because they are used in to cases below
`
``
616
`+
let where_predicates = where_clause
`
``
617
`+
.predicates
`
``
618
`+
.iter()
`
``
619
`+
.filter_map(|p| match p {
`
``
620
`+
WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
`
``
621
`+
bounds,
`
``
622
`+
bounded_ty,
`
``
623
`+
..
`
``
624
`+
}) => {
`
``
625
`` +
// FIXME: Maybe these calls to ast_ty_to_ty
can be removed (and the ones below)
``
``
626
`+
let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, bounded_ty);
`
``
627
`+
Some((ty, bounds))
`
``
628
`+
}
`
``
629
`+
_ => None,
`
``
630
`+
})
`
``
631
`+
.map(|(ty, bounds)| match ty.kind() {
`
``
632
`+
ty::Param(param_ty) if param_ty == expected_ty_as_param => Ok(Some(bounds)),
`
``
633
`` +
// check whether there is any predicate that contains our T
, like Option<T>: Send
``
``
634
`+
_ => match ty.contains(expected) {
`
``
635
`+
true => Err(()),
`
``
636
`+
false => Ok(None),
`
``
637
`+
},
`
``
638
`+
})
`
``
639
`+
.collect::<Result<Vec<_>, _>>();
`
``
640
+
``
641
`+
let Ok(where_predicates) = where_predicates else { return };
`
``
642
+
``
643
`+
// now get all predicates in the same types as the where bounds, so we can chain them
`
``
644
`+
let predicates_from_where =
`
``
645
`+
where_predicates.iter().flatten().map(|bounds| bounds.iter()).flatten();
`
``
646
+
``
647
`+
// extract all bounds from the source code using their spans
`
``
648
`+
let all_matching_bounds_strs = expected_generic_param
`
``
649
`+
.bounds
`
``
650
`+
.iter()
`
``
651
`+
.chain(predicates_from_where)
`
``
652
`+
.filter_map(|bound| match bound {
`
``
653
`+
GenericBound::Trait(_, _) => {
`
``
654
`+
self.tcx.sess.source_map().span_to_snippet(bound.span()).ok()
`
``
655
`+
}
`
``
656
`+
_ => None,
`
``
657
`+
})
`
``
658
`+
.collect::<Vec>();
`
``
659
+
``
660
`+
if all_matching_bounds_strs.len() == 0 {
`
``
661
`+
return;
`
``
662
`+
}
`
``
663
+
``
664
`+
let all_bounds_str = all_matching_bounds_strs.join(" + ");
`
``
665
+
``
666
`+
let ty_param_used_in_fn_params = fn_parameters.iter().any(|param| {
`
``
667
`+
let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, param);
`
``
668
`+
matches!(ty.kind(), ty::Param(fn_param_ty_param) if expected_ty_as_param == fn_param_ty_param)
`
``
669
`+
});
`
``
670
+
``
671
`+
if ty_param_used_in_fn_params {
`
``
672
`+
return;
`
``
673
`+
}
`
``
674
+
``
675
`+
err.span_suggestion(
`
``
676
`+
fn_return.span(),
`
``
677
`+
"consider using an impl return type",
`
``
678
`+
format!("impl {}", all_bounds_str),
`
``
679
`+
Applicability::MaybeIncorrect,
`
``
680
`+
);
`
``
681
`+
}
`
``
682
+
569
683
`pub(in super::super) fn suggest_missing_break_or_return_expr(
`
570
684
`&self,
`
571
685
`err: &mut DiagnosticBuilder<'_>,
`