@@ -6,6 +6,7 @@ use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
66use rustc_ast:: util:: parser:: { ExprPrecedence , PREC_POSTFIX } ;
77use rustc_errors:: { Applicability , Diagnostic , MultiSpan } ;
88use rustc_hir as hir;
9+ use rustc_hir:: def:: Res ;
910use rustc_hir:: def:: { CtorKind , CtorOf , DefKind } ;
1011use rustc_hir:: lang_items:: LangItem ;
1112use rustc_hir:: {
@@ -1738,4 +1739,83 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17381739 // If the field is hygienic it must come from the same syntax context.
17391740 && self . tcx . def_ident_span ( field. did ) . unwrap ( ) . normalize_to_macros_2_0 ( ) . eq_ctxt ( span)
17401741 }
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+ }
17411821}
0 commit comments