@@ -50,10 +50,10 @@ use super::region_constraints::GenericKind;
50
50
use super :: { InferCtxt , RegionVariableOrigin , SubregionOrigin , TypeTrace , ValuePairs } ;
51
51
52
52
use crate :: infer;
53
- use crate :: infer:: OriginalQueryValues ;
54
53
use crate :: traits:: error_reporting:: report_object_safety_error;
55
54
use crate :: traits:: {
56
55
IfExpressionCause , MatchExpressionArmCause , ObligationCause , ObligationCauseCode ,
56
+ StatementAsExpression ,
57
57
} ;
58
58
59
59
use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
@@ -64,7 +64,6 @@ use rustc_hir::def_id::DefId;
64
64
use rustc_hir:: lang_items:: LangItem ;
65
65
use rustc_hir:: { Item , ItemKind , Node } ;
66
66
use rustc_middle:: ty:: error:: TypeError ;
67
- use rustc_middle:: ty:: ParamEnvAnd ;
68
67
use rustc_middle:: ty:: {
69
68
self ,
70
69
subst:: { Subst , SubstsRef } ,
@@ -688,13 +687,36 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
688
687
} ;
689
688
let msg = "`match` arms have incompatible types" ;
690
689
err. span_label ( outer_error_span, msg) ;
691
- if let Some ( sp) = semi_span {
692
- err. span_suggestion_short (
693
- sp,
694
- "consider removing this semicolon" ,
695
- String :: new ( ) ,
696
- Applicability :: MachineApplicable ,
697
- ) ;
690
+ if let Some ( ( sp, boxed) ) = semi_span {
691
+ if let ( StatementAsExpression :: NeedsBoxing , [ .., prior_arm] ) =
692
+ ( boxed, & prior_arms[ ..] )
693
+ {
694
+ err. multipart_suggestion (
695
+ "consider removing this semicolon and boxing the expressions" ,
696
+ vec ! [
697
+ ( prior_arm. shrink_to_lo( ) , "Box::new(" . to_string( ) ) ,
698
+ ( prior_arm. shrink_to_hi( ) , ")" . to_string( ) ) ,
699
+ ( arm_span. shrink_to_lo( ) , "Box::new(" . to_string( ) ) ,
700
+ ( arm_span. shrink_to_hi( ) , ")" . to_string( ) ) ,
701
+ ( sp, String :: new( ) ) ,
702
+ ] ,
703
+ Applicability :: HasPlaceholders ,
704
+ ) ;
705
+ } else if matches ! ( boxed, StatementAsExpression :: NeedsBoxing ) {
706
+ err. span_suggestion_short (
707
+ sp,
708
+ "consider removing this semicolon and boxing the expressions" ,
709
+ String :: new ( ) ,
710
+ Applicability :: MachineApplicable ,
711
+ ) ;
712
+ } else {
713
+ err. span_suggestion_short (
714
+ sp,
715
+ "consider removing this semicolon" ,
716
+ String :: new ( ) ,
717
+ Applicability :: MachineApplicable ,
718
+ ) ;
719
+ }
698
720
}
699
721
if let Some ( ret_sp) = opt_suggest_box_span {
700
722
// Get return type span and point to it.
@@ -717,13 +739,27 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
717
739
if let Some ( sp) = outer {
718
740
err. span_label ( sp, "`if` and `else` have incompatible types" ) ;
719
741
}
720
- if let Some ( sp) = semicolon {
721
- err. span_suggestion_short (
722
- sp,
723
- "consider removing this semicolon" ,
724
- String :: new ( ) ,
725
- Applicability :: MachineApplicable ,
726
- ) ;
742
+ if let Some ( ( sp, boxed) ) = semicolon {
743
+ if matches ! ( boxed, StatementAsExpression :: NeedsBoxing ) {
744
+ err. multipart_suggestion (
745
+ "consider removing this semicolon and boxing the expression" ,
746
+ vec ! [
747
+ ( then. shrink_to_lo( ) , "Box::new(" . to_string( ) ) ,
748
+ ( then. shrink_to_hi( ) , ")" . to_string( ) ) ,
749
+ ( else_sp. shrink_to_lo( ) , "Box::new(" . to_string( ) ) ,
750
+ ( else_sp. shrink_to_hi( ) , ")" . to_string( ) ) ,
751
+ ( sp, String :: new( ) ) ,
752
+ ] ,
753
+ Applicability :: MachineApplicable ,
754
+ ) ;
755
+ } else {
756
+ err. span_suggestion_short (
757
+ sp,
758
+ "consider removing this semicolon" ,
759
+ String :: new ( ) ,
760
+ Applicability :: MachineApplicable ,
761
+ ) ;
762
+ }
727
763
}
728
764
if let Some ( ret_sp) = opt_suggest_box_span {
729
765
self . suggest_boxing_for_return_impl_trait (
@@ -1602,6 +1638,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
1602
1638
Mismatch :: Variable ( exp_found) => Some ( exp_found) ,
1603
1639
Mismatch :: Fixed ( _) => None ,
1604
1640
} ;
1641
+ let exp_found = match terr {
1642
+ // `terr` has more accurate type information than `exp_found` in match expressions.
1643
+ ty:: error:: TypeError :: Sorts ( terr)
1644
+ if exp_found. map_or ( false , |ef| terr. found == ef. found ) =>
1645
+ {
1646
+ Some ( * terr)
1647
+ }
1648
+ _ => exp_found,
1649
+ } ;
1650
+ debug ! ( "exp_found {:?} terr {:?}" , exp_found, terr) ;
1605
1651
if let Some ( exp_found) = exp_found {
1606
1652
self . suggest_as_ref_where_appropriate ( span, & exp_found, diag) ;
1607
1653
self . suggest_await_on_expect_found ( cause, span, & exp_found, diag) ;
@@ -1623,6 +1669,53 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
1623
1669
self . note_error_origin ( diag, cause, exp_found) ;
1624
1670
}
1625
1671
1672
+ fn get_impl_future_output_ty ( & self , ty : Ty < ' tcx > ) -> Option < Ty < ' tcx > > {
1673
+ if let ty:: Opaque ( def_id, substs) = ty. kind ( ) {
1674
+ let future_trait = self . tcx . require_lang_item ( LangItem :: Future , None ) ;
1675
+ // Future::Output
1676
+ let item_def_id = self
1677
+ . tcx
1678
+ . associated_items ( future_trait)
1679
+ . in_definition_order ( )
1680
+ . next ( )
1681
+ . unwrap ( )
1682
+ . def_id ;
1683
+
1684
+ let bounds = self . tcx . explicit_item_bounds ( * def_id) ;
1685
+
1686
+ for ( predicate, _) in bounds {
1687
+ let predicate = predicate. subst ( self . tcx , substs) ;
1688
+ if let ty:: PredicateAtom :: Projection ( projection_predicate) =
1689
+ predicate. skip_binders ( )
1690
+ {
1691
+ if projection_predicate. projection_ty . item_def_id == item_def_id {
1692
+ // We don't account for multiple `Future::Output = Ty` contraints.
1693
+ return Some ( projection_predicate. ty ) ;
1694
+ }
1695
+ }
1696
+ }
1697
+ }
1698
+ None
1699
+ }
1700
+
1701
+ /// A possible error is to forget to add `.await` when using futures:
1702
+ ///
1703
+ /// ```
1704
+ /// async fn make_u32() -> u32 {
1705
+ /// 22
1706
+ /// }
1707
+ ///
1708
+ /// fn take_u32(x: u32) {}
1709
+ ///
1710
+ /// async fn foo() {
1711
+ /// let x = make_u32();
1712
+ /// take_u32(x);
1713
+ /// }
1714
+ /// ```
1715
+ ///
1716
+ /// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the
1717
+ /// expected type. If this is the case, and we are inside of an async body, it suggests adding
1718
+ /// `.await` to the tail of the expression.
1626
1719
fn suggest_await_on_expect_found (
1627
1720
& self ,
1628
1721
cause : & ObligationCause < ' tcx > ,
@@ -1632,50 +1725,76 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
1632
1725
) {
1633
1726
debug ! (
1634
1727
"suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}" ,
1635
- exp_span, exp_found. expected, exp_found. found
1728
+ exp_span, exp_found. expected, exp_found. found,
1636
1729
) ;
1637
1730
1638
- if let ty:: Opaque ( def_id, _) = * exp_found. expected . kind ( ) {
1639
- let future_trait = self . tcx . require_lang_item ( LangItem :: Future , None ) ;
1640
- // Future::Output
1641
- let item_def_id = self
1642
- . tcx
1643
- . associated_items ( future_trait)
1644
- . in_definition_order ( )
1645
- . next ( )
1646
- . unwrap ( )
1647
- . def_id ;
1731
+ if let ObligationCauseCode :: CompareImplMethodObligation { .. } = & cause. code {
1732
+ return ;
1733
+ }
1648
1734
1649
- let projection_ty = self . tcx . projection_ty_from_predicates ( ( def_id, item_def_id) ) ;
1650
- if let Some ( projection_ty) = projection_ty {
1651
- let projection_query = self . canonicalize_query (
1652
- & ParamEnvAnd { param_env : self . tcx . param_env ( def_id) , value : projection_ty } ,
1653
- & mut OriginalQueryValues :: default ( ) ,
1654
- ) ;
1655
- if let Ok ( resp) = self . tcx . normalize_projection_ty ( projection_query) {
1656
- let normalized_ty = resp. value . value . normalized_ty ;
1657
- debug ! ( "suggest_await_on_expect_found: normalized={:?}" , normalized_ty) ;
1658
- if ty:: TyS :: same_type ( normalized_ty, exp_found. found ) {
1659
- let span = if let ObligationCauseCode :: Pattern {
1660
- span,
1661
- origin_expr : _,
1662
- root_ty : _,
1663
- } = cause. code
1664
- {
1665
- // scrutinee's span
1666
- span. unwrap_or ( exp_span)
1667
- } else {
1668
- exp_span
1669
- } ;
1670
- diag. span_suggestion_verbose (
1671
- span. shrink_to_hi ( ) ,
1672
- "consider awaiting on the future" ,
1673
- ".await" . to_string ( ) ,
1735
+ match (
1736
+ self . get_impl_future_output_ty ( exp_found. expected ) ,
1737
+ self . get_impl_future_output_ty ( exp_found. found ) ,
1738
+ ) {
1739
+ ( Some ( exp) , Some ( found) ) if ty:: TyS :: same_type ( exp, found) => match & cause. code {
1740
+ ObligationCauseCode :: IfExpression ( box IfExpressionCause { then, .. } ) => {
1741
+ diag. multipart_suggestion (
1742
+ "consider `await`ing on both `Future`s" ,
1743
+ vec ! [
1744
+ ( then. shrink_to_hi( ) , ".await" . to_string( ) ) ,
1745
+ ( exp_span. shrink_to_hi( ) , ".await" . to_string( ) ) ,
1746
+ ] ,
1747
+ Applicability :: MaybeIncorrect ,
1748
+ ) ;
1749
+ }
1750
+ ObligationCauseCode :: MatchExpressionArm ( box MatchExpressionArmCause {
1751
+ prior_arms,
1752
+ ..
1753
+ } ) => {
1754
+ if let [ .., arm_span] = & prior_arms[ ..] {
1755
+ diag. multipart_suggestion (
1756
+ "consider `await`ing on both `Future`s" ,
1757
+ vec ! [
1758
+ ( arm_span. shrink_to_hi( ) , ".await" . to_string( ) ) ,
1759
+ ( exp_span. shrink_to_hi( ) , ".await" . to_string( ) ) ,
1760
+ ] ,
1674
1761
Applicability :: MaybeIncorrect ,
1675
1762
) ;
1763
+ } else {
1764
+ diag. help ( "consider `await`ing on both `Future`s" ) ;
1676
1765
}
1677
1766
}
1767
+ _ => {
1768
+ diag. help ( "consider `await`ing on both `Future`s" ) ;
1769
+ }
1770
+ } ,
1771
+ ( _, Some ( ty) ) if ty:: TyS :: same_type ( exp_found. expected , ty) => {
1772
+ let span = match cause. code {
1773
+ // scrutinee's span
1774
+ ObligationCauseCode :: Pattern { span : Some ( span) , .. } => span,
1775
+ _ => exp_span,
1776
+ } ;
1777
+ diag. span_suggestion_verbose (
1778
+ span. shrink_to_hi ( ) ,
1779
+ "consider `await`ing on the `Future`" ,
1780
+ ".await" . to_string ( ) ,
1781
+ Applicability :: MaybeIncorrect ,
1782
+ ) ;
1783
+ }
1784
+ ( Some ( ty) , _) if ty:: TyS :: same_type ( ty, exp_found. found ) => {
1785
+ let span = match cause. code {
1786
+ // scrutinee's span
1787
+ ObligationCauseCode :: Pattern { span : Some ( span) , .. } => span,
1788
+ _ => exp_span,
1789
+ } ;
1790
+ diag. span_suggestion_verbose (
1791
+ span. shrink_to_hi ( ) ,
1792
+ "consider `await`ing on the `Future`" ,
1793
+ ".await" . to_string ( ) ,
1794
+ Applicability :: MaybeIncorrect ,
1795
+ ) ;
1678
1796
}
1797
+ _ => { }
1679
1798
}
1680
1799
}
1681
1800
0 commit comments