diff --git a/src/expressions/method-call-expr.md b/src/expressions/method-call-expr.md
index 9535cd207..5339d1f29 100644
--- a/src/expressions/method-call-expr.md
+++ b/src/expressions/method-call-expr.md
@@ -15,22 +15,168 @@ let log_pi = pi.unwrap_or(1.0).log(2.72);
 
 When looking up a method call, the receiver may be automatically dereferenced or borrowed in order to call a method.
 This requires a more complex lookup process than for other functions, since there may be a number of possible methods to call.
+
 The following procedure is used:
 
-The first step is to build a list of candidate receiver types.
-Obtain these by repeatedly [dereferencing][dereference] the receiver expression's type, adding each type encountered to the list, then finally attempting an [unsized coercion] at the end, and adding the result type if that is successful.
-Then, for each candidate `T`, add `&T` and `&mut T` to the list immediately after `T`.
+## Determining candidate types
+
+First, a list of "candidate types" is assembled.
+
+These types are found by taking the receiver type and iterating, following either:
+
+* The built-in [dereference]; or
+* `<T as Receiver>::Target`
+
+to the next type. (If a step involved following the `Receiver` target, we also
+note whether it would have been reachable by following `<T as
+Deref::Target>` - this information is used later).
+
+If the final type is an array, an additional candidate type is added
+by [unsized coercion] of that array.
+
+Each step of this chain provides a possible `self` type for methods that
+might be called. The list will be used in two different ways:
+
+* To find types that might have methods. This is used in the
+  "determining candidate methods" step, described below. This considers
+  the full list.
+* To find types to which the receiver can be converted. This is used in the
+  "picking a method from the candidates" step, also described below - in this
+  case, we only consider the types reachable via `Deref` or built-in
+  dereferencing.
+
+There is a built-in implementation of `Receiver` for all `T: Deref`, so
+most of the time, every step can be reached through either mechanism.
+Sometimes, more types can be reached via the `Receiver` chain, and so
+more types will be considered for the former usage than the latter usage.
+
+For instance, if the receiver has type `Box<[i32;2]>`, then the candidate types
+will be `Box<[i32;2]>`,`[i32; 2]` (by dereferencing), and `[i32]` (by unsized
+coercion).
+
+If `SmartPtr<T>: Receiver<Target=T>`, and the receiver type is `&SmartPtr<Foo>`,
+then the candidate types would be `&SmartPtr<Foo>`, `SmartPtr<Foo>` and `Foo`.
+
+## Determining candidate methods
+
+This list of candidate types is then converted to a list of candidate methods.
+For each step, the candidate type is used to determine what searches to perform:
+
+* For a struct, enum, foreign type, or various simpler types (listed below)
+  there is a search for inherent impl candidates for the type.
+* For a type param, there's a search for inherent candidates on the param.
+* For a trait object, there is first a search for inherent candidates for
+  the trait (for example in `impl Trait` blocks), then inherent impl
+  candidates for the trait object itself (for example found in `impl dyn Trait`
+  blocks).
+
+After these occur, there's a further search for extension candidates from
+all traits in scope (irrespective of whether the trait is remotely relevant
+to the `self` type - that's considered later).
+
+"Various simpler types" currently means bool, char, all numbers, str, array,
+slices, raw pointers, references, never and tuple.
+
+["Inherent"][inherent] means a candidate method from a block directly
+corresponding to the type in the signature. For example, the `impl` blocks
+corresponding to a struct or a trait. "Extension" means a candidate gathered
+by considering [methods on traits] in scope.
+
+Each of these searches looks for [visible] candidates where the method name
+matches.
+
+These searches contribute to list of all the candidate methods found;
+separate lists are maintained for the inherent and extension candidates.
 
-For instance, if the receiver has type `Box<[i32;2]>`, then the candidate types will be `Box<[i32;2]>`, `&Box<[i32;2]>`, `&mut Box<[i32;2]>`, `[i32; 2]` (by dereferencing), `&[i32; 2]`, `&mut [i32; 2]`, `[i32]` (by unsized coercion), `&[i32]`, and finally `&mut [i32]`.
+## Picking a method from the candidates
 
-Then, for each candidate type `T`, search for a [visible] method with a receiver of that type in the following places:
+Once the list of candidate methods is assembled, the "picking" process
+starts.
 
-1. `T`'s inherent methods (methods implemented directly on `T`).
-1. Any of the methods provided by a [visible] trait implemented by `T`.
-   If `T` is a type parameter, methods provided by trait bounds on `T` are looked up first.
-   Then all remaining methods in scope are looked up.
+Once again, the candidate types are iterated. This time, only those types
+are iterated which can be reached via the `Deref` trait or built-in derefs;
+as noted above, this may be a shorter list than those that can be reached
+using the `Receiver` trait.
 
-> Note: the lookup is done for each type in order, which can occasionally lead to surprising results.
+For each step, picking is attempted in this order:
+
+* First, a by-value method, where the `self` type precisely matches
+  * First for inherent methods
+  * Then for extension methods
+* Then, a method where `self` is received by immutable reference (`&T`)
+  * First for inherent methods
+  * Then for extension methods
+* Then, a method where `self` is received by mutable reference (`&mut T`)
+  * First for inherent methods
+  * Then for extension methods
+* Then, a method where the `self` type is a `*const T` - this is only considered
+  if the self type is `*mut T`
+  * First for inherent methods
+  * Then for extension methods
+* And finally, a method with a `Pin` that's reborrowed, if the `pin_ergonomics`
+  feature is enabled.
+  * First for inherent methods
+  * Then for extension methods
+
+For each of those searches, if exactly one candidate is identified,
+it's picked, and the search stops. If this results in multiple possible candidates,
+then it is an error, and the user must [disambiguate][disambiguate call]
+the call and convert the receiver to an appropriate receiver type.
+
+With the example above of `SmartPtr<T>: Receiver<Target=T>`, and the receiver
+type `&SmartPtr<Foo>`, this mechanism would pick:
+
+```rust,ignore
+impl Foo {
+   fn method(self: &SmartPtr<Foo>) {}
+}
+```
+
+but would not pick
+
+```rust,ignore
+impl Foo {
+   fn method(self: &Foo) {}
+}
+```
+
+because the receiver could not be converted to `&Foo` using the `Deref` chain,
+only the `Receiver` chain.
+
+## Extra details
+
+There are a few details not considered in this overview:
+
+* The search for candidate methods will search more widely (potentially
+  across crates) for certain [incoherent] types: that includes any of
+  the "various simpler types" listed above; and any `dyn`, struct, enum, or
+  foreign type where the standard-library-internal attribute
+  `rustc_has_incoherent_inherent_impls` is active.
+* If there are multiple candidates from traits, they may in fact be
+  identical, and the picking operation collapses them to a single pick to avoid
+  reporting conflicts.
+* Extra searches are performed to spot "shadowing" of pointee methods
+  by smart pointer methods, during the picking process. If a by-value pick
+  is going to be returned, an extra search is performed for a `&T` or
+  `&mut T` method. Similarly, if a `&T` method is to be returned, an extra
+  search is performed for `&mut T` methods. These extra searches consider
+  only inherent methods, where `T` is identical, but the method is
+  found from a step further along the `Receiver` chain. If any such method
+  is found, an ambiguity error is emitted.
+* An error is emitted if we reached a recursion limit.
+* The picking process emits some adjustments which must be made to the
+  receiver type in order to get to the correct `self` type. This includes
+  a number of dereferences, a possible autoreferencing, a conversion from
+  a mutable pointer to a constant pointer, or a pin reborrow.
+* Extra lists are maintained for diagnostic purposes:
+  unstable candidates, unsatisfied predicates, and static candidates.
+* For diagnostic purposes, the search may be performed slightly differently,
+  for instance searching all traits not just those in scope, or also noting
+  inaccessible candidates, or noting those with similar names.
+
+## Net results
+
+> The lookup is done for each type in order, which can occasionally lead to surprising results.
 > The below code will print "In trait impl!", because `&self` methods are looked up first, the trait method is found before the struct's `&mut self` method is found.
 >
 > ```rust
@@ -58,13 +204,30 @@ Then, for each candidate type `T`, search for a [visible] method with a receiver
 > }
 > ```
 
-If this results in multiple possible candidates, then it is an error, and the receiver must be [converted][disambiguate call] to an appropriate receiver type to make the method call.
+The types and number of parameters in the method call expression aren't taken
+into account in method resolution. So the following won't compile:
 
-This process does not take into account the mutability or lifetime of the receiver, or whether a method is `unsafe`.
-Once a method is looked up, if it can't be called for one (or more) of those reasons, the result is a compiler error.
+```rust,nocompile
+trait NoParameter {
+    fn method(self);
+}
 
-If a step is reached where there is more than one possible method, such as where generic methods or traits are considered the same, then it is a compiler error.
-These cases require a [disambiguating function call syntax] for method and function invocation.
+trait OneParameter {
+    fn method(&self, jj: i32);
+}
+
+impl NoParameter for char {
+    fn method(self) {}  // found first and picked, but doesn't work
+}
+
+impl OneParameter for char {
+    fn method(&self, jj: i32) {}  // found second, thus ignored
+}
+
+fn f() {
+    'x'.method(123);
+}
+```
 
 > **Edition differences**: Before the 2021 edition, during the search for visible methods, if the candidate receiver type is an [array type], methods provided by the standard library [`IntoIterator`] trait are ignored.
 >
@@ -91,3 +254,6 @@ These cases require a [disambiguating function call syntax] for method and funct
 [methods]: ../items/associated-items.md#methods
 [unsized coercion]: ../type-coercions.md#unsized-coercions
 [`IntoIterator`]: std::iter::IntoIterator
+[inherent]: ../items/implementations.md#inherent-implementations
+[methods on traits]: ../items/implementations.md#trait-implementations
+[incoherent]: ../items/implementations.md#trait-implementation-coherence