Repository: intellij-rust/intellij-rust
Issue: Links to the online documentation are malformed for union types

## Environment

* **IntelliJ Rust plugin version:** 0.4.147.3871-211
* **Rust toolchain version:** 1.52.0 (88f19c6da 2021-05-03) x86_64-unknown-linux-gnu
* **IDE name and version:** CLion 2021.1 (CL-211.6693.114)
* **Operating system:** Linux 5.4.0-72-generic
* **Macro expansion engine:** new
* **Name resolution engine:** new

## Problem description

Using Quick Documentation on a type or its method opens a popup, at the bottom of which may be located a link to the corresponding online documentation. For example, see `::core::mem::MaybeUninit::uninit()`. However, the link is malformed for union types and leads to a nonexistent page.

Specifically, for `::core::mem::MaybeUninit()` the link produced by the IDE is 

    https://doc.rust-lang.org/core/mem/maybe_uninit/struct.MaybeUninit.html#method.uninit

while the correct link is 

    https://doc.rust-lang.org/core/mem/maybe_uninit/union.MaybeUninit.html#method.uninit

The difference is in the prefix: union types still get a "struct" prefix.

Changed files:
src/test/kotlin/org/rust/ide/docs/RsExternalDocUrlTest.kt, src/main/kotlin/org/rust/lang/core/psi/ext/RsQualifiedNamedElement.kt, src/test/kotlin/org/rust/ide/docs/RsResolveLinkTest.kt

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 1fd2b68a5..39086990b 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
@@ -149,7 +149,7 @@ data class RsQualifiedName private constructor(
         return psiManager.findFile(crateRoot)?.rustFile
     }
 
-    private inline fun <reified T : RsElement, K> lookupInIndex(
+    private inline fun <reified T : RsElement, K : Any> lookupInIndex(
         project: Project,
         indexKey: StubIndexKey<K, T>,
         keyProducer: (String) -> K,
@@ -356,9 +356,13 @@ data class RsQualifiedName private constructor(
         private fun RsQualifiedNamedElement.toParentItem(): Item? {
             val name = ((this as? RsMod)?.modName ?: name) ?: return null
             val itemType = when (this) {
-                is RsStructItem -> STRUCT
+                is RsStructItem -> when (kind) {
+                    RsStructKind.STRUCT -> STRUCT
+                    RsStructKind.UNION -> UNION
+                }
                 is RsEnumItem -> ENUM
                 is RsTraitItem -> TRAIT
+                is RsTraitAlias -> TRAITALIAS
                 is RsTypeAlias -> TYPE
                 is RsFunction -> FN
                 is RsConstant -> CONSTANT
@@ -433,8 +437,10 @@ data class RsQualifiedName private constructor(
 
     enum class ParentItemType : ItemType {
         STRUCT,
+        UNION,
         ENUM,
         TRAIT,
+        TRAITALIAS,
         TYPE,
         FN,
         CONSTANT,
@@ -455,8 +461,10 @@ data class RsQualifiedName private constructor(
             fun fromString(name: String): ParentItemType? {
                 return when (name) {
                     "struct" -> STRUCT
+                    "union" -> UNION
                     "enum" -> ENUM
                     "trait" -> TRAIT
+                    "traitalias" -> TRAITALIAS
                     "type" -> TYPE
                     "fn" -> FN
                     "constant" -> CONSTANT
diff --git a/src/test/kotlin/org/rust/ide/docs/RsExternalDocUrlTest.kt b/src/test/kotlin/org/rust/ide/docs/RsExternalDocUrlTest.kt
index 4db1c5289..a241f24f4 100644
--- a/src/test/kotlin/org/rust/ide/docs/RsExternalDocUrlTest.kt
+++ b/src/test/kotlin/org/rust/ide/docs/RsExternalDocUrlTest.kt
@@ -36,6 +36,20 @@ class RsExternalDocUrlTest : RsDocumentationProviderTest() {
         }
     """, "https://docs.rs/dep-lib/0.0.1/dep_lib_target/enum.Foo.html#variant.Bar.field.baz")
 
+    fun `test union`() = doUrlTestByFileTree("""
+        //- dep-lib/lib.rs
+        pub union Foo { x: i32, y: f32 }
+                 //^
+    """, "https://docs.rs/dep-lib/0.0.1/dep_lib_target/union.Foo.html")
+
+    fun `test trait alias`() = doUrlTestByFileTree("""
+        //- dep-lib/lib.rs
+        pub trait Foo {}
+        pub trait Bar {}
+        pub trait FooBar = Foo + Bar;
+                 //^
+    """, "https://docs.rs/dep-lib/0.0.1/dep_lib_target/traitalias.FooBar.html")
+
     fun `test item with restricted visibility`() = doUrlTestByFileTree("""
         //- dep-lib/lib.rs
         pub(crate) enum Foo { V1, V2 }
diff --git a/src/test/kotlin/org/rust/ide/docs/RsResolveLinkTest.kt b/src/test/kotlin/org/rust/ide/docs/RsResolveLinkTest.kt
index fc7b827cd..cfc416a27 100644
--- a/src/test/kotlin/org/rust/ide/docs/RsResolveLinkTest.kt
+++ b/src/test/kotlin/org/rust/ide/docs/RsResolveLinkTest.kt
@@ -83,6 +83,13 @@ class RsResolveLinkTest : RsTestBase() {
               //^
     """, "test_package/struct.Foo.html")
 
+    fun `test union fqn link`() = doTest("""
+        union Foo { x: i32, y: f32 }
+              //X
+        struct Bar;
+              //^
+    """, "test_package/union.Foo.html")
+
     fun `test enum fqn link`() = doTest("""
         enum Foo { V }
             //X
@@ -104,13 +111,22 @@ class RsResolveLinkTest : RsTestBase() {
               //^
     """, "test_package/type.Foo.html")
 
-    fun `test trait alias fqn link`() = doTest("""
+    fun `test trait fqn link`() = doTest("""
         trait Foo {}
              //X
         struct Bar;
               //^
     """, "test_package/trait.Foo.html")
 
+    fun `test trait alias fqn link`() = doTest("""
+        pub trait Foo {}
+        pub trait Bar {}
+        pub trait FooBar = Foo + Bar;
+                  //X
+        struct Qwe;
+              //^
+    """, "test_package/traitalias.FooBar.html")
+
     fun `test mod fqn link`() = doTest("""
         mod foo {
             //X