1
+ use super :: method:: probe:: { IsSuggestion , Mode , ProbeScope } ;
1
2
use super :: method:: MethodCallee ;
2
3
use super :: { DefIdOrName , Expectation , FnCtxt , TupleArgumentsFlag } ;
3
4
use crate :: type_error_struct;
4
5
6
+ use rustc_ast:: util:: parser:: PREC_POSTFIX ;
5
7
use rustc_errors:: { struct_span_err, Applicability , Diagnostic , StashKey } ;
6
8
use rustc_hir as hir;
7
9
use rustc_hir:: def:: { self , Namespace , Res } ;
@@ -407,7 +409,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
407
409
. diagnostic ( )
408
410
. steal_diagnostic ( segment. ident . span , StashKey :: CallIntoMethod )
409
411
{
410
- diag. emit ( ) ;
412
+ // Try suggesting `foo(a)` -> `a.foo()` if possible.
413
+ if let Some ( ty) =
414
+ self . suggest_call_as_method (
415
+ & mut diag,
416
+ segment,
417
+ arg_exprs,
418
+ call_expr,
419
+ expected
420
+ )
421
+ {
422
+ diag. emit ( ) ;
423
+ return ty;
424
+ } else {
425
+ diag. emit ( ) ;
426
+ }
411
427
}
412
428
413
429
self . report_invalid_callee ( call_expr, callee_expr, callee_ty, arg_exprs) ;
@@ -457,6 +473,105 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
457
473
fn_sig. output ( )
458
474
}
459
475
476
+ /// Attempts to reinterpret `method(rcvr, args...)` as `method.rcvr(args...)`
477
+ /// and suggesting the fix if the method probe is successful.
478
+ fn suggest_call_as_method (
479
+ & self ,
480
+ diag : & mut Diagnostic ,
481
+ segment : & ' tcx hir:: PathSegment < ' tcx > ,
482
+ arg_exprs : & ' tcx [ hir:: Expr < ' tcx > ] ,
483
+ call_expr : & ' tcx hir:: Expr < ' tcx > ,
484
+ expected : Expectation < ' tcx > ,
485
+ ) -> Option < Ty < ' tcx > > {
486
+ if let [ callee_expr, rest @ ..] = arg_exprs {
487
+ let callee_ty = self . check_expr ( callee_expr) ;
488
+ // First, do a probe with `IsSuggestion(true)` to avoid emitting
489
+ // any strange errors. If it's successful, then we'll do a true
490
+ // method lookup.
491
+ let Ok ( pick) = self
492
+ . probe_for_name (
493
+ call_expr. span ,
494
+ Mode :: MethodCall ,
495
+ segment. ident ,
496
+ IsSuggestion ( true ) ,
497
+ callee_ty,
498
+ call_expr. hir_id ,
499
+ // We didn't record the in scope traits during late resolution
500
+ // so we need to probe AllTraits unfortunately
501
+ ProbeScope :: AllTraits ,
502
+ ) else {
503
+ return None ;
504
+ } ;
505
+
506
+ let pick = self . confirm_method (
507
+ call_expr. span ,
508
+ callee_expr,
509
+ call_expr,
510
+ callee_ty,
511
+ pick,
512
+ segment,
513
+ ) ;
514
+ if pick. illegal_sized_bound . is_some ( ) {
515
+ return None ;
516
+ }
517
+
518
+ let up_to_rcvr_span = segment. ident . span . until ( callee_expr. span ) ;
519
+ let rest_span = callee_expr. span . shrink_to_hi ( ) . to ( call_expr. span . shrink_to_hi ( ) ) ;
520
+ let rest_snippet = if let Some ( first) = rest. first ( ) {
521
+ self . tcx
522
+ . sess
523
+ . source_map ( )
524
+ . span_to_snippet ( first. span . to ( call_expr. span . shrink_to_hi ( ) ) )
525
+ } else {
526
+ Ok ( ")" . to_string ( ) )
527
+ } ;
528
+
529
+ if let Ok ( rest_snippet) = rest_snippet {
530
+ let sugg = if callee_expr. precedence ( ) . order ( ) >= PREC_POSTFIX {
531
+ vec ! [
532
+ ( up_to_rcvr_span, "" . to_string( ) ) ,
533
+ ( rest_span, format!( ".{}({rest_snippet}" , segment. ident) ) ,
534
+ ]
535
+ } else {
536
+ vec ! [
537
+ ( up_to_rcvr_span, "(" . to_string( ) ) ,
538
+ ( rest_span, format!( ").{}({rest_snippet}" , segment. ident) ) ,
539
+ ]
540
+ } ;
541
+ let self_ty = self . resolve_vars_if_possible ( pick. callee . sig . inputs ( ) [ 0 ] ) ;
542
+ diag. multipart_suggestion (
543
+ format ! (
544
+ "use the `.` operator to call the method `{}{}` on `{self_ty}`" ,
545
+ self . tcx
546
+ . associated_item( pick. callee. def_id)
547
+ . trait_container( self . tcx)
548
+ . map_or_else(
549
+ || String :: new( ) ,
550
+ |trait_def_id| self . tcx. def_path_str( trait_def_id) + "::"
551
+ ) ,
552
+ segment. ident
553
+ ) ,
554
+ sugg,
555
+ Applicability :: MaybeIncorrect ,
556
+ ) ;
557
+
558
+ // Let's check the method fully now
559
+ let return_ty = self . check_method_argument_types (
560
+ segment. ident . span ,
561
+ call_expr,
562
+ Ok ( pick. callee ) ,
563
+ rest,
564
+ TupleArgumentsFlag :: DontTupleArguments ,
565
+ expected,
566
+ ) ;
567
+
568
+ return Some ( return_ty) ;
569
+ }
570
+ }
571
+
572
+ None
573
+ }
574
+
460
575
fn report_invalid_callee (
461
576
& self ,
462
577
call_expr : & ' tcx hir:: Expr < ' tcx > ,
@@ -475,10 +590,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
475
590
def:: CtorOf :: Struct => "struct" ,
476
591
def:: CtorOf :: Variant => "enum variant" ,
477
592
} ;
478
- let removal_span =
479
- callee_expr. span . shrink_to_hi ( ) . to ( call_expr. span . shrink_to_hi ( ) ) ;
480
- unit_variant =
481
- Some ( ( removal_span, descr, rustc_hir_pretty:: qpath_to_string ( qpath) ) ) ;
593
+ let removal_span = callee_expr. span . shrink_to_hi ( ) . to ( call_expr. span . shrink_to_hi ( ) ) ;
594
+ unit_variant = Some ( ( removal_span, descr, rustc_hir_pretty:: qpath_to_string ( qpath) ) ) ;
482
595
}
483
596
484
597
let callee_ty = self . resolve_vars_if_possible ( callee_ty) ;
@@ -541,7 +654,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
541
654
} ;
542
655
543
656
if !self . maybe_suggest_bad_array_definition ( & mut err, call_expr, callee_expr) {
544
- if let Some ( ( maybe_def, output_ty, _) ) = self . extract_callable_info ( callee_expr, callee_ty)
657
+ if let Some ( ( maybe_def, output_ty, _) ) =
658
+ self . extract_callable_info ( callee_expr, callee_ty)
545
659
&& !self . type_is_sized_modulo_regions ( self . param_env , output_ty, callee_expr. span )
546
660
{
547
661
let descr = match maybe_def {
0 commit comments