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<'_>,

`