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
5
- use rustc_errors:: { struct_span_err, Applicability , Diagnostic } ;
6
+ use rustc_ast:: util:: parser:: PREC_POSTFIX ;
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 } ;
8
10
use rustc_hir:: def_id:: DefId ;
@@ -60,6 +62,7 @@ pub fn check_legal_trait_for_method_call(
60
62
}
61
63
}
62
64
65
+ #[ derive( Debug ) ]
63
66
enum CallStep < ' tcx > {
64
67
Builtin ( Ty < ' tcx > ) ,
65
68
DeferredClosure ( LocalDefId , ty:: FnSig < ' tcx > ) ,
@@ -188,6 +191,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
188
191
return None ;
189
192
}
190
193
194
+ ty:: Error ( _) => {
195
+ return None ;
196
+ }
197
+
191
198
_ => { }
192
199
}
193
200
@@ -394,6 +401,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
394
401
}
395
402
ty:: FnPtr ( sig) => ( sig, None ) ,
396
403
_ => {
404
+ if let hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( _, path) ) = & callee_expr. kind
405
+ && let [ segment] = path. segments
406
+ && let Some ( mut diag) = self
407
+ . tcx
408
+ . sess
409
+ . diagnostic ( )
410
+ . steal_diagnostic ( segment. ident . span , StashKey :: CallIntoMethod )
411
+ {
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
+ }
427
+ }
428
+
397
429
self . report_invalid_callee ( call_expr, callee_expr, callee_ty, arg_exprs) ;
398
430
399
431
// This is the "default" function signature, used in case of error.
@@ -441,6 +473,105 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
441
473
fn_sig. output ( )
442
474
}
443
475
476
+ /// Attempts to reinterpret `method(rcvr, args...)` as `rcvr.method(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
+
444
575
fn report_invalid_callee (
445
576
& self ,
446
577
call_expr : & ' tcx hir:: Expr < ' tcx > ,
@@ -459,10 +590,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
459
590
def:: CtorOf :: Struct => "struct" ,
460
591
def:: CtorOf :: Variant => "enum variant" ,
461
592
} ;
462
- let removal_span =
463
- callee_expr. span . shrink_to_hi ( ) . to ( call_expr. span . shrink_to_hi ( ) ) ;
464
- unit_variant =
465
- 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) ) ) ;
466
595
}
467
596
468
597
let callee_ty = self . resolve_vars_if_possible ( callee_ty) ;
@@ -525,7 +654,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
525
654
} ;
526
655
527
656
if !self . maybe_suggest_bad_array_definition ( & mut err, call_expr, callee_expr) {
528
- 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)
529
659
&& !self . type_is_sized_modulo_regions ( self . param_env , output_ty, callee_expr. span )
530
660
{
531
661
let descr = match maybe_def {
0 commit comments