@@ -6,6 +6,7 @@ use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
6
6
use rustc_ast:: util:: parser:: { ExprPrecedence , PREC_POSTFIX } ;
7
7
use rustc_errors:: { Applicability , Diagnostic , MultiSpan } ;
8
8
use rustc_hir as hir;
9
+ use rustc_hir:: def:: Res ;
9
10
use rustc_hir:: def:: { CtorKind , CtorOf , DefKind } ;
10
11
use rustc_hir:: lang_items:: LangItem ;
11
12
use rustc_hir:: {
@@ -1738,4 +1739,83 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1738
1739
// If the field is hygienic it must come from the same syntax context.
1739
1740
&& self . tcx . def_ident_span ( field. did ) . unwrap ( ) . normalize_to_macros_2_0 ( ) . eq_ctxt ( span)
1740
1741
}
1742
+
1743
+ pub ( crate ) fn suggest_missing_unwrap_expect (
1744
+ & self ,
1745
+ err : & mut Diagnostic ,
1746
+ expr : & hir:: Expr < ' tcx > ,
1747
+ expected : Ty < ' tcx > ,
1748
+ found : Ty < ' tcx > ,
1749
+ ) -> bool {
1750
+ let ty:: Adt ( adt, args) = found. kind ( ) else { return false } ;
1751
+ let ret_ty_matches = |diagnostic_item| {
1752
+ if let Some ( ret_ty) = self
1753
+ . ret_coercion
1754
+ . as_ref ( )
1755
+ . map ( |c| self . resolve_vars_if_possible ( c. borrow ( ) . expected_ty ( ) ) )
1756
+ && let ty:: Adt ( kind, _) = ret_ty. kind ( )
1757
+ && self . tcx . get_diagnostic_item ( diagnostic_item) == Some ( kind. did ( ) )
1758
+ {
1759
+ true
1760
+ } else {
1761
+ false
1762
+ }
1763
+ } ;
1764
+
1765
+ // don't suggest anything like `Ok(ok_val).unwrap()` , `Some(some_val).unwrap()`,
1766
+ // `None.unwrap()` etc.
1767
+ let is_ctor = matches ! (
1768
+ expr. kind,
1769
+ hir:: ExprKind :: Call (
1770
+ hir:: Expr {
1771
+ kind: hir:: ExprKind :: Path ( hir:: QPath :: Resolved (
1772
+ None ,
1773
+ hir:: Path { res: Res :: Def ( hir:: def:: DefKind :: Ctor ( _, _) , _) , .. } ,
1774
+ ) ) ,
1775
+ ..
1776
+ } ,
1777
+ ..,
1778
+ ) | hir:: ExprKind :: Path ( hir:: QPath :: Resolved (
1779
+ None ,
1780
+ hir:: Path { res: Res :: Def ( hir:: def:: DefKind :: Ctor ( _, _) , _) , .. } ,
1781
+ ) ) ,
1782
+ ) ;
1783
+
1784
+ let ( article, kind, variant, sugg_operator) =
1785
+ if self . tcx . is_diagnostic_item ( sym:: Result , adt. did ( ) ) {
1786
+ ( "a" , "Result" , "Err" , ret_ty_matches ( sym:: Result ) )
1787
+ } else if self . tcx . is_diagnostic_item ( sym:: Option , adt. did ( ) ) {
1788
+ ( "an" , "Option" , "None" , ret_ty_matches ( sym:: Option ) )
1789
+ } else {
1790
+ return false ;
1791
+ } ;
1792
+ if is_ctor || !self . can_coerce ( args. type_at ( 0 ) , expected) {
1793
+ return false ;
1794
+ }
1795
+
1796
+ let ( msg, sugg) = if sugg_operator {
1797
+ (
1798
+ format ! (
1799
+ "use the `?` operator to extract the `{found}` value, propagating \
1800
+ {article} `{kind}::{variant}` value to the caller"
1801
+ ) ,
1802
+ "?" ,
1803
+ )
1804
+ } else {
1805
+ (
1806
+ format ! (
1807
+ "consider using `{kind}::expect` to unwrap the `{found}` value, \
1808
+ panicking if the value is {article} `{kind}::{variant}`"
1809
+ ) ,
1810
+ ".expect(\" REASON\" )" ,
1811
+ )
1812
+ } ;
1813
+ err. span_suggestion_verbose (
1814
+ expr. span . shrink_to_hi ( ) ,
1815
+ msg,
1816
+ sugg,
1817
+ Applicability :: HasPlaceholders ,
1818
+ ) ;
1819
+ return true ;
1820
+ }
1741
1821
}
0 commit comments