@@ -50,7 +50,6 @@ 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 ,
@@ -65,7 +64,6 @@ use rustc_hir::def_id::DefId;
65
64
use rustc_hir:: lang_items:: LangItem ;
66
65
use rustc_hir:: { Item , ItemKind , Node } ;
67
66
use rustc_middle:: ty:: error:: TypeError ;
68
- use rustc_middle:: ty:: ParamEnvAnd ;
69
67
use rustc_middle:: ty:: {
70
68
self ,
71
69
subst:: { Subst , SubstsRef } ,
@@ -1621,6 +1619,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
1621
1619
Mismatch :: Variable ( exp_found) => Some ( exp_found) ,
1622
1620
Mismatch :: Fixed ( _) => None ,
1623
1621
} ;
1622
+ let exp_found = match terr {
1623
+ // `terr` has more accurate type information than `exp_found` in match expressions.
1624
+ ty:: error:: TypeError :: Sorts ( terr)
1625
+ if exp_found. map_or ( false , |ef| terr. found == ef. found ) =>
1626
+ {
1627
+ Some ( * terr)
1628
+ }
1629
+ _ => exp_found,
1630
+ } ;
1631
+ debug ! ( "exp_found {:?} terr {:?}" , exp_found, terr) ;
1624
1632
if let Some ( exp_found) = exp_found {
1625
1633
self . suggest_as_ref_where_appropriate ( span, & exp_found, diag) ;
1626
1634
self . suggest_await_on_expect_found ( cause, span, & exp_found, diag) ;
@@ -1642,6 +1650,53 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
1642
1650
self . note_error_origin ( diag, cause, exp_found) ;
1643
1651
}
1644
1652
1653
+ fn get_impl_future_output_ty ( & self , ty : Ty < ' tcx > ) -> Option < Ty < ' tcx > > {
1654
+ if let ty:: Opaque ( def_id, substs) = ty. kind ( ) {
1655
+ let future_trait = self . tcx . require_lang_item ( LangItem :: Future , None ) ;
1656
+ // Future::Output
1657
+ let item_def_id = self
1658
+ . tcx
1659
+ . associated_items ( future_trait)
1660
+ . in_definition_order ( )
1661
+ . next ( )
1662
+ . unwrap ( )
1663
+ . def_id ;
1664
+
1665
+ let bounds = self . tcx . explicit_item_bounds ( * def_id) ;
1666
+
1667
+ for ( predicate, _) in bounds {
1668
+ let predicate = predicate. subst ( self . tcx , substs) ;
1669
+ if let ty:: PredicateAtom :: Projection ( projection_predicate) =
1670
+ predicate. skip_binders ( )
1671
+ {
1672
+ if projection_predicate. projection_ty . item_def_id == item_def_id {
1673
+ // We don't account for multiple `Future::Output = Ty` contraints.
1674
+ return Some ( projection_predicate. ty ) ;
1675
+ }
1676
+ }
1677
+ }
1678
+ }
1679
+ None
1680
+ }
1681
+
1682
+ /// A possible error is to forget to add `.await` when using futures:
1683
+ ///
1684
+ /// ```
1685
+ /// async fn make_u32() -> u32 {
1686
+ /// 22
1687
+ /// }
1688
+ ///
1689
+ /// fn take_u32(x: u32) {}
1690
+ ///
1691
+ /// async fn foo() {
1692
+ /// let x = make_u32();
1693
+ /// take_u32(x);
1694
+ /// }
1695
+ /// ```
1696
+ ///
1697
+ /// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the
1698
+ /// expected type. If this is the case, and we are inside of an async body, it suggests adding
1699
+ /// `.await` to the tail of the expression.
1645
1700
fn suggest_await_on_expect_found (
1646
1701
& self ,
1647
1702
cause : & ObligationCause < ' tcx > ,
@@ -1651,50 +1706,76 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
1651
1706
) {
1652
1707
debug ! (
1653
1708
"suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}" ,
1654
- exp_span, exp_found. expected, exp_found. found
1709
+ exp_span, exp_found. expected, exp_found. found,
1655
1710
) ;
1656
1711
1657
- if let ty:: Opaque ( def_id, _) = * exp_found. expected . kind ( ) {
1658
- let future_trait = self . tcx . require_lang_item ( LangItem :: Future , None ) ;
1659
- // Future::Output
1660
- let item_def_id = self
1661
- . tcx
1662
- . associated_items ( future_trait)
1663
- . in_definition_order ( )
1664
- . next ( )
1665
- . unwrap ( )
1666
- . def_id ;
1712
+ if let ObligationCauseCode :: CompareImplMethodObligation { .. } = & cause. code {
1713
+ return ;
1714
+ }
1667
1715
1668
- let projection_ty = self . tcx . projection_ty_from_predicates ( ( def_id, item_def_id) ) ;
1669
- if let Some ( projection_ty) = projection_ty {
1670
- let projection_query = self . canonicalize_query (
1671
- & ParamEnvAnd { param_env : self . tcx . param_env ( def_id) , value : projection_ty } ,
1672
- & mut OriginalQueryValues :: default ( ) ,
1673
- ) ;
1674
- if let Ok ( resp) = self . tcx . normalize_projection_ty ( projection_query) {
1675
- let normalized_ty = resp. value . value . normalized_ty ;
1676
- debug ! ( "suggest_await_on_expect_found: normalized={:?}" , normalized_ty) ;
1677
- if ty:: TyS :: same_type ( normalized_ty, exp_found. found ) {
1678
- let span = if let ObligationCauseCode :: Pattern {
1679
- span,
1680
- origin_expr : _,
1681
- root_ty : _,
1682
- } = cause. code
1683
- {
1684
- // scrutinee's span
1685
- span. unwrap_or ( exp_span)
1686
- } else {
1687
- exp_span
1688
- } ;
1689
- diag. span_suggestion_verbose (
1690
- span. shrink_to_hi ( ) ,
1691
- "consider awaiting on the future" ,
1692
- ".await" . to_string ( ) ,
1716
+ match (
1717
+ self . get_impl_future_output_ty ( exp_found. expected ) ,
1718
+ self . get_impl_future_output_ty ( exp_found. found ) ,
1719
+ ) {
1720
+ ( Some ( exp) , Some ( found) ) if ty:: TyS :: same_type ( exp, found) => match & cause. code {
1721
+ ObligationCauseCode :: IfExpression ( box IfExpressionCause { then, .. } ) => {
1722
+ diag. multipart_suggestion (
1723
+ "consider `await`ing on both `Future`s" ,
1724
+ vec ! [
1725
+ ( then. shrink_to_hi( ) , ".await" . to_string( ) ) ,
1726
+ ( exp_span. shrink_to_hi( ) , ".await" . to_string( ) ) ,
1727
+ ] ,
1728
+ Applicability :: MaybeIncorrect ,
1729
+ ) ;
1730
+ }
1731
+ ObligationCauseCode :: MatchExpressionArm ( box MatchExpressionArmCause {
1732
+ prior_arms,
1733
+ ..
1734
+ } ) => {
1735
+ if let [ .., arm_span] = & prior_arms[ ..] {
1736
+ diag. multipart_suggestion (
1737
+ "consider `await`ing on both `Future`s" ,
1738
+ vec ! [
1739
+ ( arm_span. shrink_to_hi( ) , ".await" . to_string( ) ) ,
1740
+ ( exp_span. shrink_to_hi( ) , ".await" . to_string( ) ) ,
1741
+ ] ,
1693
1742
Applicability :: MaybeIncorrect ,
1694
1743
) ;
1744
+ } else {
1745
+ diag. help ( "consider `await`ing on both `Future`s" ) ;
1695
1746
}
1696
1747
}
1748
+ _ => {
1749
+ diag. help ( "consider `await`ing on both `Future`s" ) ;
1750
+ }
1751
+ } ,
1752
+ ( _, Some ( ty) ) if ty:: TyS :: same_type ( exp_found. expected , ty) => {
1753
+ let span = match cause. code {
1754
+ // scrutinee's span
1755
+ ObligationCauseCode :: Pattern { span : Some ( span) , .. } => span,
1756
+ _ => exp_span,
1757
+ } ;
1758
+ diag. span_suggestion_verbose (
1759
+ span. shrink_to_hi ( ) ,
1760
+ "consider `await`ing on the `Future`" ,
1761
+ ".await" . to_string ( ) ,
1762
+ Applicability :: MaybeIncorrect ,
1763
+ ) ;
1764
+ }
1765
+ ( Some ( ty) , _) if ty:: TyS :: same_type ( ty, exp_found. found ) => {
1766
+ let span = match cause. code {
1767
+ // scrutinee's span
1768
+ ObligationCauseCode :: Pattern { span : Some ( span) , .. } => span,
1769
+ _ => exp_span,
1770
+ } ;
1771
+ diag. span_suggestion_verbose (
1772
+ span. shrink_to_hi ( ) ,
1773
+ "consider `await`ing on the `Future`" ,
1774
+ ".await" . to_string ( ) ,
1775
+ Applicability :: MaybeIncorrect ,
1776
+ ) ;
1697
1777
}
1778
+ _ => { }
1698
1779
}
1699
1780
}
1700
1781
0 commit comments