Repository: intellij-rust/intellij-rust
Issue: Auto import doesn't take into account extern crate alias

Crate `X`:
```rust
pub mod foo {
    pub struct Foo;
}
```
Out project:
```rust
extern crate X as Y;

fn main() {
    let foo = Foo/*caret*/;
}
```
Try to import `Foo`

**Expected**:
```rust
extern crate X as Y;

use Y::foo::Foo;

fn main() {
    let foo = Foo;
}
```

**Actual**:
```rust
extern crate X as Y;

use X::foo::Foo;

fn main() {
    let foo = Foo;
}
```

Changed files:
src/test/kotlin/org/rust/ide/inspections/import/AutoImportFixStdTest.kt, src/main/kotlin/org/rust/ide/inspections/import/AutoImportFix.kt, src/main/kotlin/org/rust/ide/inspections/import/ui.kt

diff --git a/src/main/kotlin/org/rust/ide/inspections/import/AutoImportFix.kt b/src/main/kotlin/org/rust/ide/inspections/import/AutoImportFix.kt
index a61a63739..e66c8366c 100644
--- a/src/main/kotlin/org/rust/ide/inspections/import/AutoImportFix.kt
+++ b/src/main/kotlin/org/rust/ide/inspections/import/AutoImportFix.kt
@@ -64,10 +64,11 @@ class AutoImportFix(path: RsPath) : LocalQuickFixOnPsiElement(path), HighPriorit
         // we uses this info to create correct relative use item path if needed
         var relativeDepth: Int? = null
 
+        val info = candidate.info
         // if crate of importing element differs from current crate
         // we need to add new extern crate item
-        if (candidate.info is ImportInfo.ExternCrateImportInfo) {
-            val target = candidate.info.target
+        if (info is ImportInfo.ExternCrateImportInfo) {
+            val target = info.target
             val crateRoot = originalPath.crateRoot
             val attributes = crateRoot?.stdlibAttributes ?: RsFile.Attributes.NONE
             when {
@@ -79,18 +80,12 @@ class AutoImportFix(path: RsPath) : LocalQuickFixOnPsiElement(path), HighPriorit
                 // we don't add corresponding extern crate item manually for the same reason
                 attributes == RsFile.Attributes.NO_STD && target.isCore -> Testmarks.autoInjectedCoreCrate.hit()
                 else -> {
-                    val superModWithDepth = mod.superMods.withIndex().find { (_, superMod) ->
-                        val externCrateItems = superMod.childrenOfType<RsExternCrateItem>()
-                        externCrateItems.any { it.reference.resolve() == candidate.info.externCrateMod }
-                    }
-
-                    if (superModWithDepth == null) {
-                        crateRoot?.insertExternCrateItem(psiFactory, target.normName)
+                    if (info.needInsertExternCrateItem) {
+                        crateRoot?.insertExternCrateItem(psiFactory, info.externCrateName)
                     } else {
-                        val (depth, superMod) = superModWithDepth
-                        if (superMod != crateRoot) {
+                        if (info.depth != null) {
                             Testmarks.externCrateItemInNotCrateRoot.hit()
-                            relativeDepth = depth
+                            relativeDepth = info.depth
                         }
                     }
                 }
@@ -102,7 +97,7 @@ class AutoImportFix(path: RsPath) : LocalQuickFixOnPsiElement(path), HighPriorit
             0 -> "self::"
             else -> "super::".repeat(relativeDepth)
         }
-        mod.insertUseItem(psiFactory, "$prefix${candidate.info.usePath}")
+        mod.insertUseItem(psiFactory, "$prefix${info.usePath}")
     }
 
     private fun RsMod.insertExternCrateItem(psiFactory: RsPsiFactory, crateName: String) {
@@ -146,7 +141,7 @@ class AutoImportFix(path: RsPath) : LocalQuickFixOnPsiElement(path), HighPriorit
             }
 
             val pathMod = path.containingMod
-            val pathSuperMods = HashSet(pathMod.superMods)
+            val pathSuperMods = LinkedHashSet(pathMod.superMods)
 
             val explicitItems = RsNamedElementIndex.findElementsByName(project, basePath.referenceName)
                 .asSequence()
@@ -223,7 +218,7 @@ class AutoImportFix(path: RsPath) : LocalQuickFixOnPsiElement(path), HighPriorit
         // Semantic signature of method is `ImportItem.canBeImported(mod: RsMod)`
         // but in our case `mod` is always same and `mod` needs only to get set of its super mods
         // so we pass `superMods` instead of `mod` for optimization
-        private fun ImportItem.canBeImported(superMods: Set<RsMod>): ImportInfo? {
+        private fun ImportItem.canBeImported(superMods: LinkedHashSet<RsMod>): ImportInfo? {
             if (item !is RsVisible) return null
 
             val ourSuperMods = this.superMods ?: return null
@@ -237,7 +232,25 @@ class AutoImportFix(path: RsPath) : LocalQuickFixOnPsiElement(path), HighPriorit
             val (shouldBePublicMods, importInfo) = if (lca == null) {
                 if (!isPublic) return null
                 val target = containingCargoTarget ?: return null
-                ourSuperMods to ImportInfo.ExternCrateImportInfo(ourSuperMods.last(), target, crateRelativePath)
+                val externCrateMod = ourSuperMods.last()
+
+                val externCrateWithDepth = superMods.withIndex().mapNotNull { (index, superMod) ->
+                    val externCrateItem = superMod.childrenOfType<RsExternCrateItem>()
+                        .find { it.reference.resolve() == externCrateMod } ?: return@mapNotNull null
+                    val depth = if (superMod.isCrateRoot) null else index
+                    externCrateItem to depth
+                }.singleOrNull()
+
+                val (externCrateName, needInsertExternCrateItem, depth) = if (externCrateWithDepth == null) {
+                    Triple(target.normName, true, null)
+                } else {
+                    val (externCrateItem, depth) = externCrateWithDepth
+                    Triple(externCrateItem.alias?.name ?: externCrateItem.referenceName, false, depth)
+                }
+
+                val importInfo = ImportInfo.ExternCrateImportInfo(target, externCrateName,
+                    needInsertExternCrateItem, depth, crateRelativePath)
+                ourSuperMods to importInfo
             } else {
                 // if current item is direct child of some ancestor of `mod` then it can be not public
                 if (parentMod == lca) return ImportInfo.LocalImportInfo(crateRelativePath)
@@ -259,7 +272,7 @@ class AutoImportFix(path: RsPath) : LocalQuickFixOnPsiElement(path), HighPriorit
                 attributes == RsFile.Attributes.NO_STD && info.target.isCore) {
                 null
             } else {
-                info.target.normName
+                info.externCrateName
             }
             val path = RsCodeFragmentFactory(project)
                 .createPathInTmpMod(context, this, info.usePath, externCrateName) ?: return false
@@ -417,11 +430,37 @@ sealed class ImportInfo {
     class LocalImportInfo(override val usePath: String): ImportInfo()
 
     class ExternCrateImportInfo(
-        val externCrateMod: RsMod,
         val target: CargoWorkspace.Target,
+        val externCrateName: String,
+        val needInsertExternCrateItem: Boolean,
+        /**
+         * Relative depth of importing path's module to module with extern crate item.
+         * Used for creation of relative use path.
+         *
+         * For example, in the following case
+         * ```rust
+         * // lib.rs from bar crate
+         * pub struct Bar {}
+         * ```
+         *
+         * ```rust
+         * // main.rs from our crate
+         * mod foo {
+         *     extern crate bar;
+         *     mod baz {
+         *          fn f(bar: Bar/*caret*/) {}
+         *     }
+         * }
+         * ```
+         *
+         * relative depth of path `Bar` is `1`, so we should add `self::` prefix to use path.
+         *
+         * Can be null if extern crate item is absent or it is in crate root.
+         */
+        val depth: Int?,
         crateRelativePath: String
     ) : ImportInfo() {
-        override val usePath: String = "${target.normName}::$crateRelativePath"
+        override val usePath: String = "$externCrateName::$crateRelativePath"
     }
 }
 
@@ -447,9 +486,7 @@ private val RsPath.namespaceFilter: (RsQualifiedNamedElement) -> Boolean get() =
         }
     }
     is RsTraitRef -> { e -> e is RsTraitItem }
-    is RsStructLiteral -> { e ->
-        e is RsFieldsOwner && e.blockFields != null
-    }
+    is RsStructLiteral -> { e -> e is RsFieldsOwner && e.blockFields != null }
     is RsPatBinding -> { e ->
         when (e) {
             is RsEnumItem,
diff --git a/src/main/kotlin/org/rust/ide/inspections/import/ui.kt b/src/main/kotlin/org/rust/ide/inspections/import/ui.kt
index 893481935..6a998a648 100644
--- a/src/main/kotlin/org/rust/ide/inspections/import/ui.kt
+++ b/src/main/kotlin/org/rust/ide/inspections/import/ui.kt
@@ -79,8 +79,8 @@ private class PopupImportItemUi(private val project: Project, private val dataCo
                     val panel = JPanel(BorderLayout())
                     baseRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus)
                     panel.add(baseRenderer.nextStepLabel, BorderLayout.EAST)
-                    val importItem = (value as? ImportCandidate)?.importItem
-                    panel.add(psiRenderer.getListCellRendererComponent(list, importItem, index, isSelected, cellHasFocus))
+                    val importCandidate = value as? ImportCandidate
+                    panel.add(psiRenderer.getListCellRendererComponent(list, importCandidate, index, isSelected, cellHasFocus))
                     panel
                 }
             }
@@ -94,31 +94,36 @@ private class RsElementCellRenderer : DefaultPsiElementCellRenderer() {
 
     private val rightRender: LibraryCellRender = LibraryCellRender()
 
-    private var importItem: ImportItem? = null
+    private var importCandidate: ImportCandidate? = null
 
     override fun getRightCellRenderer(value: Any?): DefaultListCellRenderer? = rightRender
 
     override fun getListCellRendererComponent(list: JList<*>?, value: Any?, index: Int, isSelected: Boolean, cellHasFocus: Boolean): Component {
-        val realValue = if (value is ImportItem) {
+        val realValue = if (value is ImportCandidate) {
             // Generally, it's rather hacky but I don't know another way
             // how to use functionality of `PsiElementListCellRenderer`
             // and pass additional info with psi element at same time
-            importItem = value
-            value.item
+            importCandidate = value
+            value.importItem.item
         } else {
             value
         }
         return super.getListCellRendererComponent(list, realValue, index, isSelected, cellHasFocus)
     }
 
-    override fun getElementText(element: PsiElement): String = importItem?.itemName ?: super.getElementText(element)
+    override fun getElementText(element: PsiElement): String = importCandidate?.importItem?.itemName ?: super.getElementText(element)
 
     override fun getContainerText(element: PsiElement, name: String): String? {
-        val importItem = importItem
-        return if (importItem != null) {
-            val crateName = importItem.containingCargoTarget?.normName ?: return null
-            val parentPath = importItem.parentCrateRelativePath ?: return null
-            "($crateName::$parentPath)"
+        val importCandidate = importCandidate
+        return if (importCandidate != null) {
+            val crateName = (importCandidate.info as? ImportInfo.ExternCrateImportInfo)?.externCrateName
+            val parentPath = importCandidate.importItem.parentCrateRelativePath ?: return null
+            val container = when {
+                crateName == null -> parentPath
+                parentPath.isEmpty() -> crateName
+                else -> "$crateName::$parentPath"
+            }
+            "($container)"
         } else {
             super.getContainerText(element, name)
         }
@@ -142,7 +147,7 @@ private class RsElementCellRenderer : DefaultPsiElementCellRenderer() {
         }
 
         private fun textWithIcon(): Pair<String, Icon>? {
-            val pkg = importItem?.containingCargoTarget?.pkg ?: return null
+            val pkg = importCandidate?.importItem?.containingCargoTarget?.pkg ?: return null
             return when (pkg.origin) {
                 PackageOrigin.STDLIB -> pkg.normName to RsIcons.RUST
                 PackageOrigin.DEPENDENCY, PackageOrigin.TRANSITIVE_DEPENDENCY -> pkg.normName to CargoIcons.ICON
diff --git a/src/test/kotlin/org/rust/ide/inspections/import/AutoImportFixStdTest.kt b/src/test/kotlin/org/rust/ide/inspections/import/AutoImportFixStdTest.kt
index ec637be7a..9feef0070 100644
--- a/src/test/kotlin/org/rust/ide/inspections/import/AutoImportFixStdTest.kt
+++ b/src/test/kotlin/org/rust/ide/inspections/import/AutoImportFixStdTest.kt
@@ -308,4 +308,28 @@ class AutoImportFixStdTest : AutoImportFixTestBase() {
             let x = f32/*error*/::zero();
         }
     """)
+
+    fun `test extern crate alias`() = checkAutoImportFixByFileTree("""
+        //- dep-lib/lib.rs
+        pub struct Foo;
+
+        //- main.rs
+        extern crate dep_lib_target as dep_lib;
+
+        fn main() {
+            let foo = <error descr="Unresolved reference: `Foo`">Foo/*caret*/</error>;
+        }
+    """, """
+        //- dep-lib/lib.rs
+        pub struct Foo;
+
+        //- main.rs
+        extern crate dep_lib_target as dep_lib;
+
+        use dep_lib::Foo;
+
+        fn main() {
+            let foo = Foo/*caret*/;
+        }
+    """)
 }