Repository: intellij-rust/intellij-rust
Issue: Auto-import quickfix uses a private module

## Environment

* **Intellij-Rust plugin version:** 0.2.0.2529-183-nightly
* **Rust toolchain version:** 1.33.0-nightly
* **IDE name and version:** CLion 2018.3.3
* **Operating system:** macOS Mojave 10.14.2

## Steps to reproduce

Given the following code:

```rust
#![feature(async_await, futures_api)]

fn foo() -> impl Future/*caret*/<Output = i32> { async { 42 } }
```

The `Future` trait is unresolved. Auto-import quickfix inserts this line:
```rust
use std::future::future::Future; // <-- Module `std::future::future` is private
```
instead of this:
```rust
use std::future::Future;
```


Changed files:
src/main/kotlin/org/rust/lang/core/stubs/index/RsReexportIndex.kt, src/main/kotlin/org/rust/ide/inspections/import/AutoImportFix.kt, src/test/kotlin/org/rust/ide/inspections/import/AutoImportFixTest.kt, src/main/kotlin/org/rust/lang/core/psi/ext/RsUseSpeck.kt, src/test/kotlin/org/rust/ide/docs/RsResolveLinkTest.kt, src/main/kotlin/org/rust/lang/core/stubs/StubImplementations.kt, src/main/kotlin/org/rust/lang/core/psi/ext/RsQualifiedNamedElement.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 2ce981b12..aac36582f 100644
--- a/src/main/kotlin/org/rust/ide/inspections/import/AutoImportFix.kt
+++ b/src/main/kotlin/org/rust/ide/inspections/import/AutoImportFix.kt
@@ -140,6 +140,7 @@ class AutoImportFix(element: RsElement) : LocalQuickFixOnPsiElement(element), Hi
 
             val reexportedItems = RsReexportIndex.findReexportsByName(project, targetName, scope)
                 .asSequence()
+                .filter { !it.isStarImport }
                 .mapNotNull {
                     val item = it.path?.reference?.resolve() as? RsQualifiedNamedElement ?: return@mapNotNull null
                     QualifiedNamedItem.ReexportedItem(it, item)
diff --git a/src/main/kotlin/org/rust/lang/core/psi/ext/RsQualifiedNamedElement.kt b/src/main/kotlin/org/rust/lang/core/psi/ext/RsQualifiedNamedElement.kt
index bdeceee65..60a3a4fd6 100644
--- a/src/main/kotlin/org/rust/lang/core/psi/ext/RsQualifiedNamedElement.kt
+++ b/src/main/kotlin/org/rust/lang/core/psi/ext/RsQualifiedNamedElement.kt
@@ -79,6 +79,7 @@ data class RsQualifiedName private constructor(
             // generate sequence of all possible reexports for each element and check
             // if any variant has the same `RsQualifiedName`
             result = lookupInIndex(project, RsReexportIndex.KEY) { useSpeck ->
+                if (useSpeck.isStarImport) return@lookupInIndex null
                 val candidate = useSpeck.path?.reference?.resolve() as? RsQualifiedNamedElement ?: return@lookupInIndex null
                 QualifiedNamedItem.ReexportedItem(useSpeck, candidate)
             }
@@ -457,7 +458,7 @@ sealed class QualifiedNamedItem(val item: RsQualifiedNamedElement) {
 }
 
 /**
- * Collect all possible imports using reexports of modules from original import item path
+ * Collect all possible imports from original import item path using reexports of modules and wildcard reexports
  */
 fun QualifiedNamedItem.withModuleReexports(project: Project): List<QualifiedNamedItem> {
     check(this is QualifiedNamedItem.ExplicitItem || this is QualifiedNamedItem.ReexportedItem) {
@@ -473,26 +474,62 @@ fun QualifiedNamedItem.withModuleReexports(project: Project): List<QualifiedName
     fun QualifiedNamedItem.collectImportItems(): List<QualifiedNamedItem> {
         val importItems = mutableListOf(this)
         val superMods = superMods.orEmpty()
-        superMods
-            // only public items can be reexported
-            .filter { it.isPublic }
-            .forEachIndexed { index, ancestorMod ->
-                val modName = ancestorMod.modName ?: return@forEachIndexed
-                RsReexportIndex.findReexportsByName(project, modName)
-                    .mapNotNull {
-                        if (it in visited) return@mapNotNull null
-                        val reexportedMod = it.path?.reference?.resolve() as? RsMod
-                        if (reexportedMod != ancestorMod) return@mapNotNull null
-                        it to reexportedMod
+        superMods.forEachIndexed { index, ancestorMod ->
+            val modName = ancestorMod.modName ?: return@forEachIndexed
+            RsReexportIndex.findReexportsByName(project, modName)
+                .mapNotNull {
+                    if (it in visited) return@mapNotNull null
+                    val reexportedMod = it.pathOrQualifier?.reference?.resolve() as? RsMod
+                    if (reexportedMod != ancestorMod) return@mapNotNull null
+                    it to reexportedMod
+                }
+                .forEach { (useSpeck, reexportedMod) ->
+                    // only public items can be reexported
+                    if (!useSpeck.isStarImport && !reexportedMod.isPublic) return@forEach
+                    visited += useSpeck
+                    val (mod, endModIndex) = if (!useSpeck.isStarImport) {
+                        // In case of general reexport
+                        //
+                        // ```rust
+                        // mod foo {
+                        //     pub use bar::baz; // <---
+                        //     mod bar {
+                        //         pub mod baz {
+                        //             public struct Baz;
+                        //         }
+                        //     }
+                        // }
+                        // ```
+                        //
+                        // reexportedMod is `baz` (from `pub use bar::baz` use item).
+                        // And we should generate "foo::baz::Baz" item from "foo::[bar::]baz::Baz"
+                        reexportedMod to index + 1
+                    } else {
+                        // Otherwise (when use item has wildcard)
+                        //
+                        // ```rust
+                        // mod foo {
+                        //     pub use bar::baz::* // <---
+                        //     mod bar {
+                        //         pub mod baz {
+                        //             public struct Baz;
+                        //         }
+                        //     }
+                        // }
+                        // ```
+                        //
+                        // reexportedMod is still `baz` (from `pub use bar::baz::*` use item).
+                        // But we should replace it with `foo` mod because use item with wildcard reexports
+                        // children items of `baz` to `foo` instead of `baz` itself,
+                        // i.e. generate "foo::Baz" item from "foo::[bar::baz::]Baz"
+                        useSpeck.containingMod to index
                     }
-                    .forEach { (useSpeck, reexportedMod) ->
-                        visited += useSpeck
-                        val items = QualifiedNamedItem.ReexportedItem(useSpeck, reexportedMod).collectImportItems()
-                        importItems += items.map {
-                            QualifiedNamedItem.CompositeItem(itemName, isPublic, it, superMods.subList(0, index + 1), item)
-                        }
-                        visited -= useSpeck
+                    val items = QualifiedNamedItem.ReexportedItem(useSpeck, mod).collectImportItems()
+                    importItems += items.map {
+                        QualifiedNamedItem.CompositeItem(itemName, isPublic, it, superMods.subList(0, endModIndex), item)
                     }
+                    visited -= useSpeck
+                }
             }
         return importItems
     }
diff --git a/src/main/kotlin/org/rust/lang/core/psi/ext/RsUseSpeck.kt b/src/main/kotlin/org/rust/lang/core/psi/ext/RsUseSpeck.kt
index be9cd8ac8..258ff8d0b 100644
--- a/src/main/kotlin/org/rust/lang/core/psi/ext/RsUseSpeck.kt
+++ b/src/main/kotlin/org/rust/lang/core/psi/ext/RsUseSpeck.kt
@@ -15,9 +15,11 @@ import org.rust.lang.core.stubs.RsUseSpeckStub
 val RsUseSpeck.isStarImport: Boolean get() = stub?.isStarImport ?: (mul != null) // I hate operator precedence
 val RsUseSpeck.qualifier: RsPath? get() {
     val parentUseSpeck = (context as? RsUseGroup)?.parentUseSpeck ?: return null
-    return parentUseSpeck.path ?: parentUseSpeck.qualifier
+    return parentUseSpeck.pathOrQualifier
 }
 
+val RsUseSpeck.pathOrQualifier: RsPath? get() = path ?: qualifier
+
 val RsUseSpeck.nameInScope: String? get() {
     if (useGroup != null) return null
     alias?.name?.let { return it }
diff --git a/src/main/kotlin/org/rust/lang/core/stubs/StubImplementations.kt b/src/main/kotlin/org/rust/lang/core/stubs/StubImplementations.kt
index 5b1603e17..98bb251f9 100644
--- a/src/main/kotlin/org/rust/lang/core/stubs/StubImplementations.kt
+++ b/src/main/kotlin/org/rust/lang/core/stubs/StubImplementations.kt
@@ -33,7 +33,7 @@ class RsFileStub : PsiFileStubImpl<RsFile> {
 
     object Type : IStubFileElementType<RsFileStub>(RsLanguage) {
         // Bump this number if Stub structure changes
-        override fun getStubVersion(): Int = 153
+        override fun getStubVersion(): Int = 154
 
         override fun getBuilder(): StubBuilder = object : DefaultStubBuilder() {
             override fun createStubForFile(file: PsiFile): StubElement<*> = RsFileStub(file as RsFile)
diff --git a/src/main/kotlin/org/rust/lang/core/stubs/index/RsReexportIndex.kt b/src/main/kotlin/org/rust/lang/core/stubs/index/RsReexportIndex.kt
index d938c4d09..a6bca98f6 100644
--- a/src/main/kotlin/org/rust/lang/core/stubs/index/RsReexportIndex.kt
+++ b/src/main/kotlin/org/rust/lang/core/stubs/index/RsReexportIndex.kt
@@ -14,6 +14,7 @@ import org.rust.lang.core.psi.RsUseItem
 import org.rust.lang.core.psi.RsUseSpeck
 import org.rust.lang.core.psi.ext.ancestorStrict
 import org.rust.lang.core.psi.ext.nameInScope
+import org.rust.lang.core.psi.ext.pathOrQualifier
 import org.rust.lang.core.stubs.RsFileStub
 import org.rust.lang.core.stubs.RsUseSpeckStub
 import org.rust.openapiext.getElements
@@ -30,7 +31,11 @@ class RsReexportIndex : StringStubIndexExtension<RsUseSpeck>() {
             val useSpeck = stub.psi
             val isPublic = useSpeck.ancestorStrict<RsUseItem>()?.isPublic == true
             if (!isPublic) return
-            val name = useSpeck.nameInScope ?: return
+            val name = if (stub.isStarImport) {
+                useSpeck.pathOrQualifier?.referenceName
+            } else {
+                useSpeck.nameInScope
+            } ?: return
             sink.occurrence(KEY, name)
         }
 
diff --git a/src/test/kotlin/org/rust/ide/docs/RsResolveLinkTest.kt b/src/test/kotlin/org/rust/ide/docs/RsResolveLinkTest.kt
index 33f4daaf4..d8bc0fa53 100644
--- a/src/test/kotlin/org/rust/ide/docs/RsResolveLinkTest.kt
+++ b/src/test/kotlin/org/rust/ide/docs/RsResolveLinkTest.kt
@@ -260,6 +260,21 @@ class RsResolveLinkTest : RsTestBase() {
               //^
     """, "test_package/foo/baz/struct.Baz.html")
 
+    fun `test fqn link with wildcard reexport`() = doTest("""
+        mod foo {
+            pub mod bar {
+                pub mod baz {
+                    pub struct Baz;
+                              //X
+                }
+            }
+            pub use foo::bar::*;
+        }
+
+        struct Foo;
+              //^
+    """, "test_package/foo/baz/struct.Baz.html")
+
     private fun doTest(@Language("Rust") code: String, link: String) {
         InlineFile(code)
         val context = findElementInEditor<RsNamedElement>("^")
diff --git a/src/test/kotlin/org/rust/ide/inspections/import/AutoImportFixTest.kt b/src/test/kotlin/org/rust/ide/inspections/import/AutoImportFixTest.kt
index ffbaaded8..67ec7487f 100644
--- a/src/test/kotlin/org/rust/ide/inspections/import/AutoImportFixTest.kt
+++ b/src/test/kotlin/org/rust/ide/inspections/import/AutoImportFixTest.kt
@@ -1392,4 +1392,57 @@ class AutoImportFixTest : AutoImportFixTestBase() {
             }
         }
     """)
+
+    fun `test import with wildcard reexport 1`() = checkAutoImportFixByText("""
+        mod c {
+            mod a {
+                pub struct A;
+            }
+            pub use self::a::*;
+        }
+
+        fn main() {
+            let a = <error descr="Unresolved reference: `A`">A/*caret*/</error>;
+        }
+    """, """
+        use c::A;
+
+        mod c {
+            mod a {
+                pub struct A;
+            }
+            pub use self::a::*;
+        }
+
+        fn main() {
+            let a = A;
+        }
+    """)
+
+    fun `test import with wildcard reexport 2`() = checkAutoImportFixByText("""
+        mod c {
+            mod a {
+                pub struct A;
+            }
+            pub use self::a::{{*}};
+        }
+
+        fn main() {
+            let a = <error descr="Unresolved reference: `A`">A/*caret*/</error>;
+        }
+    """, """
+        use c::A;
+
+        mod c {
+            mod a {
+                pub struct A;
+            }
+            pub use self::a::{{*}};
+        }
+
+        fn main() {
+            let a = A;
+        }
+    """)
+
 }