Repository: intellij-rust/intellij-rust
Issue: Move refactoring: self-references are not handled in some cases

## Environment

* **IntelliJ Rust plugin version:** 0.4.155.4114-212
* **Rust toolchain version:** 1.56.0-nightly (29ef6cf16 2021-08-31) x86_64-unknown-linux-gnu
* **IDE name and version:** CLion 2021.2.2 (CL-212.5284.51)
* **Operating system:** Linux 5.14.6-zen1-1-zen
* **Macro expansion engine:** new
* **Name resolution engine:** new

## Problem description
When moving mod and function with self-reference (inside function there is reference to item in mod), then import for such reference will not be added.

## Steps to reproduce
```rust
mod mod1 {
    use foo::Foo;
    fn func/*caret*/(_: Foo) {}
    mod foo/*caret*/ {
        pub struct Foo;
    }
}

mod mod2;
```

After move: 
```rust
//- lib.rs
mod mod1 {}

mod mod2;

//- mod2.rs
fn func(_: Foo) {}  // ← unresolved reference

pub mod foo {
    pub struct Foo;
}
```

Changed files:
src/main/kotlin/org/rust/ide/refactoring/move/common/RsMoveUtil.kt, src/main/kotlin/org/rust/ide/refactoring/move/common/RsMoveCommonProcessor.kt, src/test/kotlin/org/rust/ide/refactoring/move/RsMoveTopLevelItemsTest.kt

diff --git a/src/main/kotlin/org/rust/ide/refactoring/move/common/RsMoveCommonProcessor.kt b/src/main/kotlin/org/rust/ide/refactoring/move/common/RsMoveCommonProcessor.kt
index c36daab7a..88c3d095e 100644
--- a/src/main/kotlin/org/rust/ide/refactoring/move/common/RsMoveCommonProcessor.kt
+++ b/src/main/kotlin/org/rust/ide/refactoring/move/common/RsMoveCommonProcessor.kt
@@ -126,16 +126,56 @@ class RsMoveCommonProcessor(
         }
     }
 
+    /**
+     * - Direct reference is reference to one of [elementsToMove]:
+     * ```rust
+     * fn usages() {
+     *     item1();
+     *     ~~~~~ direct reference
+     *     item2::func();
+     *     ~~~~~ direct reference
+     * }
+     *
+     * fn item1/*caret*/() {}
+     * mod item2/*caret*/ { pub fn func() {} }
+     * ```
+     *
+     * - Indirect reference is reference to item inside moved mod:
+     *   (if we can't find direct reference using base/parent path)
+     * ```rust
+     * use inner::func;  // this import will remain in source mod!
+     * fn usages() {
+     *     func();
+     *     ~~~~ indirect reference
+     * }
+     *
+     * mod inner/*caret*/ { pub fn func() {} }
+     * ```
+     */
     fun findUsages(): Array<RsMoveUsageInfo> {
-        return elementsToMove
-            .flatMap { ReferencesSearch.search(it.element, GlobalSearchScope.projectScope(project)) }
-            .filterNotNull()
+        val referencesDirect = findDirectInsideReferences()
+        val referencesIndirect = findIndirectInsideReferences()
+        return (referencesDirect + referencesIndirect)
             .mapNotNull { createMoveUsageInfo(it) }
             // sorting is needed for stable results in tests
             .sortedWith(compareBy({ it.element.containingMod.crateRelativePath }, { it.element.startOffset }))
             .toTypedArray()
     }
 
+    private fun findDirectInsideReferences(): List<PsiReference> = elementsToMove
+        .flatMap { ReferencesSearch.search(it.element, GlobalSearchScope.projectScope(project)) }
+
+    private fun findIndirectInsideReferences(): List<PsiReference> =
+        movedElementsShallowDescendantsOfType<RsPath>(elementsToMove, processInlineModules = false)
+            .filter { path ->
+                if (path.ancestorStrict<RsUseGroup>() != null || path.path != null) return@filter false
+                val target = path.reference?.resolve() ?: return@filter false
+                // these are direct references
+                if (elementsToMove.any { it is ItemToMove && it.item == target }) return@filter false
+                target.isInsideMovedElements(elementsToMove)
+            }
+            .mapNotNull { it.reference }
+
     private fun createMoveUsageInfo(reference: PsiReference): RsMoveUsageInfo? {
         val element = reference.element
         val target = reference.resolve() ?: return null
@@ -162,6 +202,7 @@ class RsMoveCommonProcessor(
                 runReadAction {
                     // TODO: two threads
                     outsideReferences = collectOutsideReferences()
+                    /** Also contains self-references */
                     val insideReferences = preprocessInsideReferences(usages)
                     traitMethodsProcessor.preprocessOutsideReferences(conflicts, elementsToMove)
                     traitMethodsProcessor.preprocessInsideReferences(conflicts, elementsToMove)
@@ -277,6 +318,7 @@ class RsMoveCommonProcessor(
         return RsMoveReferenceInfo(path, pathOriginal, pathNewAccessible, pathNewFallback, target)
     }
 
+    /** Also processes self references */
     private fun preprocessInsideReferences(usages: Array<UsageInfo>): List<RsMoveReferenceInfo> {
         val pathUsages = usages.filterIsInstance<RsPathUsageInfo>()
         for (usage in pathUsages) {
@@ -299,13 +341,19 @@ class RsMoveCommonProcessor(
         val path = convertFromPathOriginal(pathOriginal, codeFragmentFactory)
 
         val isSelfReference = pathOriginal.isInsideMovedElements(elementsToMove)
-        if (isSelfReference) {
-            // after move path will be in `targetMod`
-            // so we can refer to moved item just with its name
-            check(target.containingModStrict == sourceMod)  // any inside reference is reference to moved item
-            if (path.containingMod == sourceMod) {
+        if (isSelfReference && path.containingMod == sourceMod) {
+            if (target.containingModStrict == sourceMod) {  // inside reference to moved item
+                // after move path will be in `targetMod`, so we can refer to moved item just with its name
                 val pathNew = target.name?.toRsPath(codeFragmentFactory, targetMod)
                 if (pathNew != null) return RsMoveReferenceInfo(path, pathOriginal, pathNew, pathNew, target)
+            } else run {
+                val pathOldAbsolute = RsImportHelper.findPath(path, target) ?: return@run
+                val sourceModPrefix = "crate${sourceMod.crateRelativePath}::"
+                if (pathOldAbsolute.startsWith(sourceModPrefix)) {
+                    val pathRelativeToSourceMod = pathOldAbsolute.removePrefix(sourceModPrefix)
+                    val pathNew = pathRelativeToSourceMod.toRsPath(codeFragmentFactory, targetMod)
+                    return RsMoveReferenceInfo(path, pathOriginal, pathNew, pathNew, target)
+                }
             }
         }
 
diff --git a/src/main/kotlin/org/rust/ide/refactoring/move/common/RsMoveUtil.kt b/src/main/kotlin/org/rust/ide/refactoring/move/common/RsMoveUtil.kt
index bdd9c81ea..ab4aa46ae 100644
--- a/src/main/kotlin/org/rust/ide/refactoring/move/common/RsMoveUtil.kt
+++ b/src/main/kotlin/org/rust/ide/refactoring/move/common/RsMoveUtil.kt
@@ -67,6 +67,8 @@ class RsMoveReferenceInfo(
 ) {
     val pathNew: RsPath? get() = pathNewAccessible ?: pathNewFallback
     val isInsideUseDirective: Boolean get() = pathOldOriginal.ancestorStrict<RsUseItem>() != null
+
+    override fun toString(): String = "'${pathOld.text}' → '${target.qualifiedName}'"
 }
 
 /**
diff --git a/src/test/kotlin/org/rust/ide/refactoring/move/RsMoveTopLevelItemsTest.kt b/src/test/kotlin/org/rust/ide/refactoring/move/RsMoveTopLevelItemsTest.kt
index 1a0ebcba9..824f798b4 100644
--- a/src/test/kotlin/org/rust/ide/refactoring/move/RsMoveTopLevelItemsTest.kt
+++ b/src/test/kotlin/org/rust/ide/refactoring/move/RsMoveTopLevelItemsTest.kt
@@ -1782,7 +1782,7 @@ class RsMoveTopLevelItemsTest : RsMoveTopLevelItemsTestBase() {
         }
     """)
 
-    fun `test self references to inner mod`() = doTest("""
+    fun `test self references to inner mod (absolute path)`() = doTest("""
     //- lib.rs
         mod mod1 {
             mod foo1/*caret*/ {
@@ -1803,6 +1803,30 @@ class RsMoveTopLevelItemsTest : RsMoveTopLevelItemsTestBase() {
         }
     """)
 
+    fun `test self references to inner mod (using import)`() = doTest("""
+    //- lib.rs
+        mod mod1 {
+            use foo1::func;
+            mod foo1/*caret*/ {
+                pub fn func() {}
+            }
+            fn foo2/*caret*/() { func(); }
+        }
+        mod mod2/*target*/ {}
+    """, """
+    //- lib.rs
+        mod mod1 {}
+        mod mod2 {
+            use foo1::func;
+
+            pub mod foo1 {
+                pub fn func() {}
+            }
+
+            fn foo2() { func(); }
+        }
+    """)
+
     fun `test inside references from new mod`() = doTest("""
     //- lib.rs
         mod mod1 {