|
1 | 1 | use crate::coercion::CoerceMany;
|
2 | 2 | use crate::errors::SuggestPtrNullMut;
|
3 | 3 | use crate::fn_ctxt::arg_matrix::{ArgMatrix, Compatibility, Error, ExpectedIdx, ProvidedIdx};
|
| 4 | +use crate::fn_ctxt::infer::FnCall; |
4 | 5 | use crate::gather_locals::Declaration;
|
| 6 | +use crate::method::probe::IsSuggestion; |
| 7 | +use crate::method::probe::Mode::MethodCall; |
| 8 | +use crate::method::probe::ProbeScope::TraitsInScope; |
5 | 9 | use crate::method::MethodCallee;
|
6 | 10 | use crate::TupleArgumentsFlag::*;
|
7 | 11 | use crate::{errors, Expectation::*};
|
@@ -532,25 +536,111 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
532 | 536 | let callee_ty = callee_expr
|
533 | 537 | .and_then(|callee_expr| self.typeck_results.borrow().expr_ty_adjusted_opt(callee_expr));
|
534 | 538 |
|
| 539 | + // Obtain another method on `Self` that have similar name. |
| 540 | + let similar_assoc = |call_name: Ident| -> Option<(ty::AssocItem, ty::FnSig<'_>)> { |
| 541 | + if let Some(callee_ty) = callee_ty |
| 542 | + && let Ok(Some(assoc)) = self.probe_op( |
| 543 | + call_name.span, |
| 544 | + MethodCall, |
| 545 | + Some(call_name), |
| 546 | + None, |
| 547 | + IsSuggestion(true), |
| 548 | + callee_ty.peel_refs(), |
| 549 | + callee_expr.unwrap().hir_id, |
| 550 | + TraitsInScope, |
| 551 | + |mut ctxt| ctxt.probe_for_similar_candidate(), |
| 552 | + ) |
| 553 | + && let ty::AssocKind::Fn = assoc.kind |
| 554 | + && assoc.fn_has_self_parameter |
| 555 | + { |
| 556 | + let fn_sig = |
| 557 | + if let ty::Adt(_, args) = callee_ty.peel_refs().kind() { |
| 558 | + let args = ty::GenericArgs::identity_for_item(tcx, assoc.def_id) |
| 559 | + .rebase_onto(tcx, assoc.container_id(tcx), args); |
| 560 | + tcx.fn_sig(assoc.def_id).instantiate(tcx, args) |
| 561 | + } else { |
| 562 | + tcx.fn_sig(assoc.def_id).instantiate_identity() |
| 563 | + }; |
| 564 | + let fn_sig = |
| 565 | + self.instantiate_binder_with_fresh_vars(call_name.span, FnCall, fn_sig); |
| 566 | + Some((assoc, fn_sig)); |
| 567 | + } |
| 568 | + None |
| 569 | + }; |
| 570 | + |
535 | 571 | let suggest_confusable = |err: &mut Diagnostic| {
|
536 | 572 | if let Some(call_name) = call_ident
|
537 | 573 | && let Some(callee_ty) = callee_ty
|
538 | 574 | {
|
539 |
| - // FIXME: check in the following order |
540 |
| - // - methods marked as `rustc_confusables` with the provided arguments (done) |
541 |
| - // - methods marked as `rustc_confusables` with the right number of arguments |
542 |
| - // - methods marked as `rustc_confusables` (done) |
543 |
| - // - methods with the same argument type/count and short levenshtein distance |
544 |
| - // - methods with short levenshtein distance |
545 |
| - // - methods with the same argument type/count |
| 575 | + let input_types: Vec<Ty<'_>> = provided_arg_tys.iter().map(|(ty, _)| *ty).collect(); |
| 576 | + // Check for other methods in the following order |
| 577 | + // - methods marked as `rustc_confusables` with the provided arguments |
| 578 | + // - methods with the same argument type/count and short levenshtein distance |
| 579 | + // - methods marked as `rustc_confusables` (done) |
| 580 | + // - methods with short levenshtein distance |
| 581 | + |
| 582 | + // Look for commonly confusable method names considering arguments. |
546 | 583 | self.confusable_method_name(
|
547 | 584 | err,
|
548 | 585 | callee_ty.peel_refs(),
|
549 | 586 | call_name,
|
550 |
| - Some(provided_arg_tys.iter().map(|(ty, _)| *ty).collect()), |
| 587 | + Some(input_types.clone()), |
551 | 588 | )
|
552 | 589 | .or_else(|| {
|
| 590 | + // Look for method names with short levenshtein distance, considering arguments. |
| 591 | + if let Some((assoc, fn_sig)) = similar_assoc(call_name) |
| 592 | + && fn_sig.inputs()[1..] |
| 593 | + .iter() |
| 594 | + .zip(input_types.iter()) |
| 595 | + .all(|(expected, found)| self.can_coerce(*expected, *found)) |
| 596 | + && fn_sig.inputs()[1..].len() == input_types.len() |
| 597 | + { |
| 598 | + err.span_suggestion_verbose( |
| 599 | + call_name.span, |
| 600 | + format!("you might have meant to use `{}`", assoc.name), |
| 601 | + assoc.name, |
| 602 | + Applicability::MaybeIncorrect, |
| 603 | + ); |
| 604 | + return Some(assoc.name); |
| 605 | + } |
| 606 | + None |
| 607 | + }) |
| 608 | + .or_else(|| { |
| 609 | + // Look for commonly confusable method names disregarding arguments. |
553 | 610 | self.confusable_method_name(err, callee_ty.peel_refs(), call_name, None)
|
| 611 | + }) |
| 612 | + .or_else(|| { |
| 613 | + // Look for similarly named methods with levenshtein distance with the right |
| 614 | + // number of arguments. |
| 615 | + if let Some((assoc, fn_sig)) = similar_assoc(call_name) |
| 616 | + && fn_sig.inputs()[1..].len() == input_types.len() |
| 617 | + { |
| 618 | + err.span_note( |
| 619 | + tcx.def_span(assoc.def_id), |
| 620 | + format!( |
| 621 | + "there's is a method with similar name `{}`, but the arguments \ |
| 622 | + don't match", |
| 623 | + assoc.name, |
| 624 | + ), |
| 625 | + ); |
| 626 | + return Some(assoc.name); |
| 627 | + } |
| 628 | + None |
| 629 | + }) |
| 630 | + .or_else(|| { |
| 631 | + // Fallthrough: look for similarly named methods with levenshtein distance. |
| 632 | + if let Some((assoc, _)) = similar_assoc(call_name) { |
| 633 | + err.span_note( |
| 634 | + tcx.def_span(assoc.def_id), |
| 635 | + format!( |
| 636 | + "there's is a method with similar name `{}`, but their argument \ |
| 637 | + count doesn't match", |
| 638 | + assoc.name, |
| 639 | + ), |
| 640 | + ); |
| 641 | + return Some(assoc.name); |
| 642 | + } |
| 643 | + None |
554 | 644 | });
|
555 | 645 | }
|
556 | 646 | };
|
|
0 commit comments