@@ -978,45 +978,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
978978 label_span_not_found ( & mut err) ;
979979 }
980980
981- if let SelfSource :: MethodCall ( expr) = source
982- && let Some ( ( fields, substs) ) = self . get_field_candidates ( span, actual)
983- {
984- let call_expr =
985- self . tcx . hir ( ) . expect_expr ( self . tcx . hir ( ) . get_parent_node ( expr. hir_id ) ) ;
986- for candidate_field in fields. iter ( ) {
987- if let Some ( field_path) = self . check_for_nested_field_satisfying (
988- span,
989- & |_, field_ty| {
990- self . lookup_probe (
991- span,
992- item_name,
993- field_ty,
994- call_expr,
995- ProbeScope :: AllTraits ,
996- )
997- . is_ok ( )
998- } ,
999- candidate_field,
1000- substs,
1001- vec ! [ ] ,
1002- self . tcx . parent_module ( expr. hir_id ) . to_def_id ( ) ,
1003- ) {
1004- let field_path_str = field_path
1005- . iter ( )
1006- . map ( |id| id. name . to_ident_string ( ) )
1007- . collect :: < Vec < String > > ( )
1008- . join ( "." ) ;
1009- debug ! ( "field_path_str: {:?}" , field_path_str) ;
981+ self . check_for_field_method ( & mut err, source, span, actual, item_name) ;
1010982
1011- err. span_suggestion_verbose (
1012- item_name. span . shrink_to_lo ( ) ,
1013- "one of the expressions' fields has a method of the same name" ,
1014- format ! ( "{field_path_str}." ) ,
1015- Applicability :: MaybeIncorrect ,
1016- ) ;
1017- }
1018- }
1019- }
983+ self . check_for_unwrap_self ( & mut err, source, span, actual, item_name) ;
1020984
1021985 bound_spans. sort ( ) ;
1022986 bound_spans. dedup ( ) ;
@@ -1343,6 +1307,145 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13431307 false
13441308 }
13451309
1310+ fn check_for_field_method (
1311+ & self ,
1312+ err : & mut DiagnosticBuilder < ' tcx , ErrorGuaranteed > ,
1313+ source : SelfSource < ' tcx > ,
1314+ span : Span ,
1315+ actual : Ty < ' tcx > ,
1316+ item_name : Ident ,
1317+ ) {
1318+ if let SelfSource :: MethodCall ( expr) = source
1319+ && let Some ( ( fields, substs) ) = self . get_field_candidates ( span, actual)
1320+ {
1321+ let call_expr = self . tcx . hir ( ) . expect_expr ( self . tcx . hir ( ) . get_parent_node ( expr. hir_id ) ) ;
1322+ for candidate_field in fields. iter ( ) {
1323+ if let Some ( field_path) = self . check_for_nested_field_satisfying (
1324+ span,
1325+ & |_, field_ty| {
1326+ self . lookup_probe (
1327+ span,
1328+ item_name,
1329+ field_ty,
1330+ call_expr,
1331+ ProbeScope :: AllTraits ,
1332+ )
1333+ . is_ok ( )
1334+ } ,
1335+ candidate_field,
1336+ substs,
1337+ vec ! [ ] ,
1338+ self . tcx . parent_module ( expr. hir_id ) . to_def_id ( ) ,
1339+ ) {
1340+ let field_path_str = field_path
1341+ . iter ( )
1342+ . map ( |id| id. name . to_ident_string ( ) )
1343+ . collect :: < Vec < String > > ( )
1344+ . join ( "." ) ;
1345+ debug ! ( "field_path_str: {:?}" , field_path_str) ;
1346+
1347+ err. span_suggestion_verbose (
1348+ item_name. span . shrink_to_lo ( ) ,
1349+ "one of the expressions' fields has a method of the same name" ,
1350+ format ! ( "{field_path_str}." ) ,
1351+ Applicability :: MaybeIncorrect ,
1352+ ) ;
1353+ }
1354+ }
1355+ }
1356+ }
1357+
1358+ fn check_for_unwrap_self (
1359+ & self ,
1360+ err : & mut DiagnosticBuilder < ' tcx , ErrorGuaranteed > ,
1361+ source : SelfSource < ' tcx > ,
1362+ span : Span ,
1363+ actual : Ty < ' tcx > ,
1364+ item_name : Ident ,
1365+ ) {
1366+ let tcx = self . tcx ;
1367+ let SelfSource :: MethodCall ( expr) = source else { return ; } ;
1368+ let call_expr = tcx. hir ( ) . expect_expr ( tcx. hir ( ) . get_parent_node ( expr. hir_id ) ) ;
1369+
1370+ let ty:: Adt ( kind, substs) = actual. kind ( ) else { return ; } ;
1371+ if !kind. is_enum ( ) {
1372+ return ;
1373+ }
1374+
1375+ let matching_variants: Vec < _ > = kind
1376+ . variants ( )
1377+ . iter ( )
1378+ . flat_map ( |variant| {
1379+ let [ field] = & variant. fields [ ..] else { return None ; } ;
1380+ let field_ty = field. ty ( tcx, substs) ;
1381+
1382+ // Skip `_`, since that'll just lead to ambiguity.
1383+ if self . resolve_vars_if_possible ( field_ty) . is_ty_var ( ) {
1384+ return None ;
1385+ }
1386+
1387+ self . lookup_probe ( span, item_name, field_ty, call_expr, ProbeScope :: AllTraits )
1388+ . ok ( )
1389+ . map ( |pick| ( variant, field, pick) )
1390+ } )
1391+ . collect ( ) ;
1392+
1393+ let ret_ty_matches = |diagnostic_item| {
1394+ if let Some ( ret_ty) = self
1395+ . ret_coercion
1396+ . as_ref ( )
1397+ . map ( |c| self . resolve_vars_if_possible ( c. borrow ( ) . expected_ty ( ) ) )
1398+ && let ty:: Adt ( kind, _) = ret_ty. kind ( )
1399+ && tcx. get_diagnostic_item ( diagnostic_item) == Some ( kind. did ( ) )
1400+ {
1401+ true
1402+ } else {
1403+ false
1404+ }
1405+ } ;
1406+
1407+ match & matching_variants[ ..] {
1408+ [ ( _, field, pick) ] => {
1409+ let self_ty = field. ty ( tcx, substs) ;
1410+ err. span_note (
1411+ tcx. def_span ( pick. item . def_id ) ,
1412+ & format ! ( "the method `{item_name}` exists on the type `{self_ty}`" ) ,
1413+ ) ;
1414+ let ( article, kind, variant, question) =
1415+ if Some ( kind. did ( ) ) == tcx. get_diagnostic_item ( sym:: Result ) {
1416+ ( "a" , "Result" , "Err" , ret_ty_matches ( sym:: Result ) )
1417+ } else if Some ( kind. did ( ) ) == tcx. get_diagnostic_item ( sym:: Option ) {
1418+ ( "an" , "Option" , "None" , ret_ty_matches ( sym:: Option ) )
1419+ } else {
1420+ return ;
1421+ } ;
1422+ if question {
1423+ err. span_suggestion_verbose (
1424+ expr. span . shrink_to_hi ( ) ,
1425+ format ! (
1426+ "use the `?` operator to extract the `{self_ty}` value, propagating \
1427+ {article} `{kind}::{variant}` value to the caller"
1428+ ) ,
1429+ "?" . to_owned ( ) ,
1430+ Applicability :: MachineApplicable ,
1431+ ) ;
1432+ } else {
1433+ err. span_suggestion_verbose (
1434+ expr. span . shrink_to_hi ( ) ,
1435+ format ! (
1436+ "consider using `{kind}::expect` to unwrap the `{self_ty}` value, \
1437+ panicking if the value is {article} `{kind}::{variant}`"
1438+ ) ,
1439+ ".expect(\" REASON\" )" . to_owned ( ) ,
1440+ Applicability :: HasPlaceholders ,
1441+ ) ;
1442+ }
1443+ }
1444+ // FIXME(compiler-errors): Support suggestions for other matching enum variants
1445+ _ => { }
1446+ }
1447+ }
1448+
13461449 pub ( crate ) fn note_unmet_impls_on_type (
13471450 & self ,
13481451 err : & mut Diagnostic ,
@@ -1662,13 +1765,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
16621765 ( self . tcx . mk_mut_ref ( self . tcx . lifetimes . re_erased , rcvr_ty) , "&mut " ) ,
16631766 ( self . tcx . mk_imm_ref ( self . tcx . lifetimes . re_erased , rcvr_ty) , "&" ) ,
16641767 ] {
1665- match self . lookup_probe (
1666- span,
1667- item_name,
1668- * rcvr_ty,
1669- rcvr,
1670- crate :: check:: method:: probe:: ProbeScope :: AllTraits ,
1671- ) {
1768+ match self . lookup_probe ( span, item_name, * rcvr_ty, rcvr, ProbeScope :: AllTraits ) {
16721769 Ok ( pick) => {
16731770 // If the method is defined for the receiver we have, it likely wasn't `use`d.
16741771 // We point at the method, but we just skip the rest of the check for arbitrary
@@ -1700,13 +1797,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17001797 ( self . tcx . mk_diagnostic_item ( * rcvr_ty, sym:: Arc ) , "Arc::new" ) ,
17011798 ( self . tcx . mk_diagnostic_item ( * rcvr_ty, sym:: Rc ) , "Rc::new" ) ,
17021799 ] {
1703- if let Some ( new_rcvr_t) = * rcvr_ty && let Ok ( pick) = self . lookup_probe (
1704- span,
1705- item_name,
1706- new_rcvr_t,
1707- rcvr,
1708- crate :: check:: method:: probe:: ProbeScope :: AllTraits ,
1709- ) {
1800+ if let Some ( new_rcvr_t) = * rcvr_ty
1801+ && let Ok ( pick) = self . lookup_probe (
1802+ span,
1803+ item_name,
1804+ new_rcvr_t,
1805+ rcvr,
1806+ ProbeScope :: AllTraits ,
1807+ )
1808+ {
17101809 debug ! ( "try_alt_rcvr: pick candidate {:?}" , pick) ;
17111810 let did = Some ( pick. item . container . id ( ) ) ;
17121811 // We don't want to suggest a container type when the missing
0 commit comments