Repository: intellij-rust/intellij-rust
Issue: Exceptions while rendering documentation

<!--
Hello and thank you for the issue!
If you would like to report a bug, we have added some points below that you can fill out.
Feel free to remove all the irrelevant text to request a new feature.
-->

## Environment

* **IntelliJ Rust plugin version:** 0.3.130.3306-202-nightly
* **Rust toolchain version:** 1.45.2 (d3fb005a3 2020-07-31) x86_64-apple-darwin
* **IDE name and version:** IntelliJ IDEA 2020.2.1 Preview Ultimate Edition (IU-202.6948.5)
* **Operating system:** macOS 10.13.6

## Problem description

<details> <summary>Exception 1</summary>

```stacktrace
java.lang.IllegalStateException: Unexpected type: `RsEnumVariantImpl(ENUM_VARIANT)`
	at org.rust.lang.core.psi.ext.RsQualifiedName$Companion.toParentItem(RsQualifiedNamedElement.kt:357)
	at org.rust.lang.core.psi.ext.RsQualifiedName$Companion.parentItem(RsQualifiedNamedElement.kt:308)
	at org.rust.lang.core.psi.ext.RsQualifiedName$Companion.from(RsQualifiedNamedElement.kt:228)
	at org.rust.lang.doc.RsDocPipelineKt.documentationAsHtml(RsDocPipeline.kt:79)
	at org.rust.lang.doc.RsDocPipelineKt.documentationAsHtml(RsDocPipeline.kt:62)
	at org.rust.ide.docs.RsDocumentationProvider.generateRenderedDoc(RsDocumentationProvider.kt:18)
	at com.intellij.lang.documentation.CompositeDocumentationProvider.generateRenderedDoc(CompositeDocumentationProvider.java:163)
	at com.intellij.codeInsight.documentation.render.DocRenderPassFactory.calcText(DocRenderPassFactory.java:89)
	at com.intellij.codeInsight.documentation.render.DocRenderPassFactory.lambda$calculateItemsToRender$0(DocRenderPassFactory.java:80)
	at org.rust.ide.docs.RsDocumentationProviderBase.collectDocComments(RsDocumentationProvider.kt:66)
	at com.intellij.lang.documentation.CompositeDocumentationProvider.collectDocComments(CompositeDocumentationProvider.java:175)
	at com.intellij.codeInsight.documentation.render.DocRenderPassFactory.calculateItemsToRender(DocRenderPassFactory.java:77)
	at com.intellij.codeInsight.documentation.render.DocRenderPassFactory$DocRenderPass.doCollectInformation(DocRenderPassFactory.java:63)
	at com.intellij.codeHighlighting.TextEditorHighlightingPass.collectInformation(TextEditorHighlightingPass.java:54)
	at com.intellij.codeInsight.daemon.impl.PassExecutorService$ScheduledPass.lambda$doRun$1(PassExecutorService.java:399)
	at com.intellij.openapi.application.impl.ApplicationImpl.tryRunReadAction(ApplicationImpl.java:1110)
	at com.intellij.codeInsight.daemon.impl.PassExecutorService$ScheduledPass.lambda$doRun$2(PassExecutorService.java:392)
	at com.intellij.openapi.progress.impl.CoreProgressManager.registerIndicatorAndRun(CoreProgressManager.java:629)
	at com.intellij.openapi.progress.impl.CoreProgressManager.executeProcessUnderProgress(CoreProgressManager.java:581)
	at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:60)
	at com.intellij.codeInsight.daemon.impl.PassExecutorService$ScheduledPass.doRun(PassExecutorService.java:391)
	at com.intellij.codeInsight.daemon.impl.PassExecutorService$ScheduledPass.lambda$run$0(PassExecutorService.java:367)
	at com.intellij.openapi.application.impl.ReadMostlyRWLock.executeByImpatientReader(ReadMostlyRWLock.java:170)
	at com.intellij.openapi.application.impl.ApplicationImpl.executeByImpatientReader(ApplicationImpl.java:182)
	at com.intellij.codeInsight.daemon.impl.PassExecutorService$ScheduledPass.run(PassExecutorService.java:365)
	at com.intellij.concurrency.JobLauncherImpl$VoidForkJoinTask$1.exec(JobLauncherImpl.java:181)
	at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
	at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
	at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
	at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
	at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)
```

</details>


<details><summary>Exception 2</summary>

```stacktrace
java.lang.IllegalStateException: Unexpected type: `RsMacro2Impl(MACRO_2)`
	at org.rust.lang.core.psi.ext.RsQualifiedName$Companion.toParentItem(RsQualifiedNamedElement.kt:357)
	at org.rust.lang.core.psi.ext.RsQualifiedName$Companion.from(RsQualifiedNamedElement.kt:233)
	at org.rust.lang.doc.RsDocPipelineKt.documentationAsHtml(RsDocPipeline.kt:79)
	at org.rust.lang.doc.RsDocPipelineKt.documentationAsHtml(RsDocPipeline.kt:62)
	at org.rust.ide.docs.RsDocumentationProvider.generateRenderedDoc(RsDocumentationProvider.kt:18)
	at com.intellij.lang.documentation.CompositeDocumentationProvider.generateRenderedDoc(CompositeDocumentationProvider.java:163)
	at com.intellij.codeInsight.documentation.render.DocRenderPassFactory.calcText(DocRenderPassFactory.java:89)
	at com.intellij.codeInsight.documentation.render.DocRenderPassFactory.lambda$calculateItemsToRender$0(DocRenderPassFactory.java:80)
	at org.rust.ide.docs.RsDocumentationProviderBase.collectDocComments(RsDocumentationProvider.kt:66)
	at com.intellij.lang.documentation.CompositeDocumentationProvider.collectDocComments(CompositeDocumentationProvider.java:175)
	at com.intellij.codeInsight.documentation.render.DocRenderPassFactory.calculateItemsToRender(DocRenderPassFactory.java:77)
	at com.intellij.codeInsight.documentation.render.DocRenderPassFactory$DocRenderPass.doCollectInformation(DocRenderPassFactory.java:63)
	at com.intellij.codeHighlighting.TextEditorHighlightingPass.collectInformation(TextEditorHighlightingPass.java:54)
	at com.intellij.codeInsight.daemon.impl.PassExecutorService$ScheduledPass.lambda$doRun$1(PassExecutorService.java:399)
	at com.intellij.openapi.application.impl.ApplicationImpl.tryRunReadAction(ApplicationImpl.java:1110)
	at com.intellij.codeInsight.daemon.impl.PassExecutorService$ScheduledPass.lambda$doRun$2(PassExecutorService.java:392)
	at com.intellij.openapi.progress.impl.CoreProgressManager.registerIndicatorAndRun(CoreProgressManager.java:629)
	at com.intellij.openapi.progress.impl.CoreProgressManager.executeProcessUnderProgress(CoreProgressManager.java:581)
	at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:60)
	at com.intellij.codeInsight.daemon.impl.PassExecutorService$ScheduledPass.doRun(PassExecutorService.java:391)
	at com.intellij.codeInsight.daemon.impl.PassExecutorService$ScheduledPass.lambda$run$0(PassExecutorService.java:367)
	at com.intellij.openapi.application.impl.ReadMostlyRWLock.executeByImpatientReader(ReadMostlyRWLock.java:170)
	at com.intellij.openapi.application.impl.ApplicationImpl.executeByImpatientReader(ApplicationImpl.java:182)
	at com.intellij.codeInsight.daemon.impl.PassExecutorService$ScheduledPass.run(PassExecutorService.java:365)
	at com.intellij.concurrency.JobLauncherImpl$VoidForkJoinTask$1.exec(JobLauncherImpl.java:181)
	at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
	at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
	at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
	at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
	at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)
```

</details>

## Steps to reproduce
```rust
pub enum Foo {
    Bar {
        /// some doc
        x: i32
    }
}

/// Some doc
pub macro Baz() {

}
```

<!--
Please include as much of your codebase as needed to reproduce the error.
If the relevant files are large, please provide a link to a public repository or a [Gist](https://gist.github.com/).
-->            

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

diff --git a/src/main/kotlin/org/rust/lang/core/psi/ext/RsFieldDecl.kt b/src/main/kotlin/org/rust/lang/core/psi/ext/RsFieldDecl.kt
index 4ff739aaf..9edc2990f 100644
--- a/src/main/kotlin/org/rust/lang/core/psi/ext/RsFieldDecl.kt
+++ b/src/main/kotlin/org/rust/lang/core/psi/ext/RsFieldDecl.kt
@@ -5,12 +5,21 @@
 
 package org.rust.lang.core.psi.ext
 
+import org.rust.lang.core.psi.RsEnumVariant
 import org.rust.lang.core.psi.RsTypeReference
 
-val RsFieldDecl.parentStruct: RsFieldsOwner? get() = stubAncestorStrict()
+val RsFieldDecl.owner: RsFieldsOwner? get() = stubAncestorStrict()
 
 val RsFieldDecl.escapedName: String? get() = (this as? RsNamedElement)?.escapedName ?: name
 
 interface RsFieldDecl : RsOuterAttributeOwner, RsVisibilityOwner {
     val typeReference: RsTypeReference?
+
+    @JvmDefault
+    override val visibility: RsVisibility
+        get() = (owner as? RsEnumVariant)?.visibility ?: super.visibility
+
+    @JvmDefault
+    override val isPublic: Boolean
+        get() = (owner as? RsEnumVariant)?.isPublic ?: super.isPublic
 }
diff --git a/src/main/kotlin/org/rust/lang/core/psi/ext/RsNamedFieldDecl.kt b/src/main/kotlin/org/rust/lang/core/psi/ext/RsNamedFieldDecl.kt
index 00a95cfd4..a61ed9357 100644
--- a/src/main/kotlin/org/rust/lang/core/psi/ext/RsNamedFieldDecl.kt
+++ b/src/main/kotlin/org/rust/lang/core/psi/ext/RsNamedFieldDecl.kt
@@ -21,7 +21,7 @@ abstract class RsNamedFieldDeclImplMixin : RsStubbedNamedElementImpl<RsNamedFiel
     constructor(stub: RsNamedFieldDeclStub, nodeType: IStubElementType<*, *>) : super(stub, nodeType)
 
     override fun getIcon(flags: Int): Icon =
-        if (parent.parent is RsEnumVariant) RsIcons.FIELD else iconWithVisibility(flags, RsIcons.FIELD)
+        if (owner is RsEnumVariant) RsIcons.FIELD else iconWithVisibility(flags, RsIcons.FIELD)
 
     // temporary solution.
     override val crateRelativePath: String? get() = RsPsiImplUtil.crateRelativePath(this)
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 b29ccfc80..30efa66a6 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
@@ -71,7 +71,7 @@ data class RsQualifiedName private constructor(
     val crateName: String,
     val modSegments: List<String>,
     val parentItem: Item,
-    val childItem: Item?
+    val childItems: List<Item>
 ) {
 
     fun toUrlPath(): String {
@@ -80,7 +80,7 @@ data class RsQualifiedName private constructor(
         val (pageName, anchor) = if (parentItem.type == MOD || parentItem.type == CRATE) {
             "index.html" to ""
         } else {
-            "$parentItem.html" to (if (childItem != null) "#$childItem" else "")
+            "$parentItem.html" to if (childItems.isNotEmpty()) childItems.joinToString(separator = ".", prefix = "#") else ""
         }
         segments += pageName
         return segments.joinToString(separator = "/", postfix = anchor)
@@ -88,7 +88,7 @@ data class RsQualifiedName private constructor(
 
     /** Tries to find rust qualified name element related to this qualified name */
     fun findPsiElement(psiManager: PsiManager, context: RsElement): RsQualifiedNamedElement? {
-        val item = childItem ?: parentItem
+        val item = childItems.lastOrNull() ?: parentItem
 
         // If it's link to crate, try to find the corresponding `lib.rs` file
         if (item.type == CRATE) {
@@ -154,7 +154,7 @@ data class RsQualifiedName private constructor(
         crossinline transform: (T) -> QualifiedNamedItem?
     ): RsQualifiedNamedElement? {
         var result: RsQualifiedNamedElement? = null
-        val item = childItem ?: parentItem
+        val item = childItems.lastOrNull() ?: parentItem
         StubIndex.getInstance().processElements(
             indexKey,
             keyProducer(item.name),
@@ -182,26 +182,26 @@ data class RsQualifiedName private constructor(
             val segments = path.split("/")
             if (segments.size < 2) return null
 
-            val (parentItem, childItem) = if (segments.size == 2 && segments[1] == "index.html") {
-                Item(segments[0], CRATE) to null
+            val (parentItem, childItems) = if (segments.size == 2 && segments[1] == "index.html") {
+                Item(segments[0], CRATE) to emptyList()
             } else {
                 // Last segment contains info about item type and name
                 // and it should have the following structure:
-                // parentItem ( '#' childItem )?
+                // parentItem ( '#' childItem (. childItem)? )?
                 val itemParts = segments.last().split("#")
                 val parentRaw = itemParts[0]
-                val childRaw = itemParts.getOrNull(1)
+                val childrenRaw = itemParts.getOrNull(1)
 
                 val parentItem = parentItem(segments[segments.lastIndex - 1], parentRaw) ?: return null
-                val childItem = if (childRaw != null) {
-                    childItem(childRaw) ?: return null
+                val childItems = if (childrenRaw != null) {
+                    childItems(childrenRaw) ?: return null
                 } else {
-                    null
+                    emptyList()
                 }
-                parentItem to childItem
+                parentItem to childItems
             }
 
-            return RsQualifiedName(segments[0], segments.subList(1, segments.lastIndex), parentItem, childItem)
+            return RsQualifiedName(segments[0], segments.subList(1, segments.lastIndex), parentItem, childItems)
         }
 
         private fun parentItem(prevSegment: String, raw: String): Item? {
@@ -216,22 +216,25 @@ data class RsQualifiedName private constructor(
             return Item(parts[1], type)
         }
 
-        private fun childItem(raw: String): Item? {
+        private fun childItems(raw: String): List<Item>? {
             val parts = raw.split(".")
-            if (parts.size != 2) return null
-            val type = ChildItemType.fromString(parts[0]) ?: return null
-            return Item(parts[1], type)
+            if (parts.size != 2 && parts.size != 4) return null
+
+            return parts.windowed(size = 2, step = 2).map { (type, name) ->
+                val itemType = ChildItemType.fromString(type) ?: return null
+                Item(name, itemType)
+            }
         }
 
         @JvmStatic
         fun from(element: RsQualifiedNamedElement): RsQualifiedName? {
             val parent = parentItem(element)
 
-            val (parentItem, childItem) = if (parent != null) {
-                parent to (element.toChildItem() ?: return null)
+            val (parentItem, childItems) = if (parent != null) {
+                parent to (element.toChildItems() ?: return null)
             } else {
                 val parentItem = element.toParentItem() ?: return null
-                parentItem to null
+                parentItem to emptyList()
             }
             val crateName = if (parentItem.type == PRIMITIVE) {
                 STD
@@ -250,7 +253,7 @@ data class RsQualifiedName private constructor(
                     .map { it.modName ?: return null }
             }
 
-            return RsQualifiedName(crateName, modSegments, parentItem, childItem)
+            return RsQualifiedName(crateName, modSegments, parentItem, childItems)
         }
 
         @JvmStatic
@@ -276,17 +279,17 @@ data class RsQualifiedName private constructor(
         @JvmStatic
         fun from(path: RsPath): RsQualifiedName? {
             val primitiveType = TyPrimitive.fromPath(path) ?: return null
-            return RsQualifiedName(STD, emptyList(), Item.primitive(primitiveType.name), null)
+            return RsQualifiedName(STD, emptyList(), Item.primitive(primitiveType.name), emptyList())
         }
 
-        private fun RsQualifiedNamedElement.toItems(): Pair<Item, Item?>? {
+        private fun RsQualifiedNamedElement.toItems(): Pair<Item, List<Item>>? {
             val parent = parentItem(this)
 
             return if (parent != null) {
-                parent to (toChildItem() ?: return null)
+                parent to (toChildItems() ?: return null)
             } else {
                 val parentItem = toParentItem() ?: return null
-                parentItem to null
+                parentItem to emptyList()
             }
         }
 
@@ -302,7 +305,10 @@ data class RsQualifiedName private constructor(
                     }
                 }
                 is RsEnumVariant -> element.parentEnum
-                is RsNamedFieldDecl -> element.parentStruct
+                is RsNamedFieldDecl -> {
+                    val owner = element.owner
+                    (owner as? RsEnumVariant)?.parentEnum ?: owner
+                }
                 else -> return null
             }
             return parentItem?.toParentItem()
@@ -345,7 +351,7 @@ data class RsQualifiedName private constructor(
                 is RsTypeAlias -> TYPE
                 is RsFunction -> FN
                 is RsConstant -> CONSTANT
-                is RsMacro -> MACRO
+                is RsMacro, is RsMacro2 -> MACRO
                 is RsMod -> {
                     if (isCrateRoot) {
                         val crateName = containingCrate?.normName ?: return null
@@ -359,17 +365,29 @@ data class RsQualifiedName private constructor(
             return Item(name, itemType, this)
         }
 
-        private fun RsQualifiedNamedElement.toChildItem(): Item? {
+        private fun RsQualifiedNamedElement.toChildItems(): List<Item>? {
             val name = name ?: return null
+            val result = mutableListOf<Item>()
             val type = when (this) {
                 is RsEnumVariant -> VARIANT
-                is RsNamedFieldDecl -> if (parentStruct != null) STRUCTFIELD else return null
+                is RsNamedFieldDecl -> {
+                    when (val owner = owner) {
+                        is RsStructItem -> STRUCTFIELD
+                        is RsEnumVariant -> {
+                            val variantName = owner.name ?: return null
+                            result += Item(variantName, VARIANT, owner)
+                            FIELD
+                        }
+                        else -> return null
+                    }
+                }
                 is RsTypeAlias -> ASSOCIATEDTYPE
                 is RsConstant -> ASSOCIATEDCONSTANT
                 is RsFunction -> if (isAbstract) TYMETHOD else METHOD
                 else -> return null
             }
-            return Item(name, type, this)
+            result += Item(name, type, this)
+            return result
         }
     }
 
@@ -441,6 +459,7 @@ data class RsQualifiedName private constructor(
 
     enum class ChildItemType : ItemType {
         VARIANT,
+        FIELD,
         STRUCTFIELD,
         ASSOCIATEDTYPE,
         ASSOCIATEDCONSTANT,
@@ -453,6 +472,7 @@ data class RsQualifiedName private constructor(
             fun fromString(name: String): ChildItemType? {
                 return when (name) {
                     "variant" -> VARIANT
+                    "field" -> FIELD
                     "structfield" -> STRUCTFIELD
                     "associatedtype" -> ASSOCIATEDTYPE
                     "associatedconstant" -> ASSOCIATEDCONSTANT
diff --git a/src/main/kotlin/org/rust/lang/core/psi/ext/RsTupleFieldDecl.kt b/src/main/kotlin/org/rust/lang/core/psi/ext/RsTupleFieldDecl.kt
index b0d27059e..92fa4284d 100644
--- a/src/main/kotlin/org/rust/lang/core/psi/ext/RsTupleFieldDecl.kt
+++ b/src/main/kotlin/org/rust/lang/core/psi/ext/RsTupleFieldDecl.kt
@@ -16,7 +16,7 @@ import org.rust.lang.core.stubs.RsPlaceholderStub
 import javax.swing.Icon
 
 val RsTupleFieldDecl.position: Int?
-    get() = parentStruct?.positionalFields?.withIndex()?.firstOrNull { it.value === this }?.index
+    get() = owner?.positionalFields?.withIndex()?.firstOrNull { it.value === this }?.index
 
 
 abstract class RsTupleFieldDeclImplMixin : RsStubbedElementImpl<RsPlaceholderStub>, RsTupleFieldDecl {
@@ -24,7 +24,7 @@ abstract class RsTupleFieldDeclImplMixin : RsStubbedElementImpl<RsPlaceholderStu
     constructor(stub: RsPlaceholderStub, nodeType: IStubElementType<*, *>) : super(stub, nodeType)
 
     override fun getIcon(flags: Int): Icon =
-        if (parent.parent is RsEnumVariant) RsIcons.FIELD else iconWithVisibility(flags, RsIcons.FIELD)
+        if (owner is RsEnumVariant) RsIcons.FIELD else iconWithVisibility(flags, RsIcons.FIELD)
 
     override fun getName(): String? = position?.toString()
 
diff --git a/src/test/kotlin/org/rust/ide/docs/RsExternalDocUrlTest.kt b/src/test/kotlin/org/rust/ide/docs/RsExternalDocUrlTest.kt
index 971757bd1..2a048432d 100644
--- a/src/test/kotlin/org/rust/ide/docs/RsExternalDocUrlTest.kt
+++ b/src/test/kotlin/org/rust/ide/docs/RsExternalDocUrlTest.kt
@@ -27,6 +27,15 @@ class RsExternalDocUrlTest : RsDocumentationProviderTest() {
         }
     """, "https://docs.rs/dep-lib/0.0.1/dep_lib_target/struct.Foo.html#associatedconstant.BAR")
 
+    fun `test enum variant field`() = doUrlTestByFileTree("""
+        //- dep-lib/lib.rs
+        pub enum Foo {
+            Bar {
+                baz: i32
+            }  //^
+        }
+    """, "https://docs.rs/dep-lib/0.0.1/dep_lib_target/enum.Foo.html#variant.Bar.field.baz")
+
     fun `test item with restricted visibility`() = doUrlTestByFileTree("""
         //- dep-lib/lib.rs
         pub(crate) enum Foo { V1, V2 }
@@ -99,6 +108,12 @@ class RsExternalDocUrlTest : RsDocumentationProviderTest() {
         }
     """, null, Testmarks.notExportedMacro)
 
+    fun `test macro 2`() = doUrlTestByFileTree("""
+        //- dep-lib/lib.rs
+        pub macro Bar() {}
+                 //^
+    """, "https://docs.rs/dep-lib/0.0.1/dep_lib_target/macro.Bar.html")
+
     fun `test not external url for workspace package`() = doUrlTestByFileTree("""
         //- lib.rs
         pub enum Foo { FOO, BAR }
diff --git a/src/test/kotlin/org/rust/ide/docs/RsQuickDocumentationTest.kt b/src/test/kotlin/org/rust/ide/docs/RsQuickDocumentationTest.kt
index a905c0171..7e8f9521c 100644
--- a/src/test/kotlin/org/rust/ide/docs/RsQuickDocumentationTest.kt
+++ b/src/test/kotlin/org/rust/ide/docs/RsQuickDocumentationTest.kt
@@ -682,6 +682,20 @@ class RsQuickDocumentationTest : RsDocumentationProviderTest() {
         <div class='content'><p>Outer documentation</p></div>
     """)
 
+    fun `test macro 2 outer docs`() = doTest("""
+        pub struct Foo;
+
+        /// Outer doc
+        /// [link](Foo)
+        pub macro Bar() {}
+                 //^
+    """, """
+        <div class='definition'><pre>test_package::Bar
+        </pre></div>
+        <div class='content'><p>Outer doc
+        <a href="psi_element://test_package/Foo">link</a></p></div>
+    """)
+
     fun `test qualified name`() = doTest("""
         mod q {
             /// Blurb.
@@ -793,6 +807,24 @@ class RsQuickDocumentationTest : RsDocumentationProviderTest() {
         <div class='content'><p>Documented</p></div>
     """)
 
+    fun `test enum variant field`() = doTest("""
+        enum Foo {
+            Bar {
+                /// Documented
+                baz: i32
+            }
+        }
+
+        fn foo(f: Foo) {
+            if let Foo::Bar { baz: x } = f {}
+                             //^
+        }
+    """, """
+        <div class='definition'><pre>test_package::Foo::Bar
+        <b>baz</b>: i32</pre></div>
+        <div class='content'><p>Documented</p></div>
+    """)
+
     fun `test type parameter`() = doTest("""
         fn foo<T>() { unimplemented!() }
              //^
diff --git a/src/test/kotlin/org/rust/ide/docs/RsResolveLinkTest.kt b/src/test/kotlin/org/rust/ide/docs/RsResolveLinkTest.kt
index e6fbd54d8..fc7b827cd 100644
--- a/src/test/kotlin/org/rust/ide/docs/RsResolveLinkTest.kt
+++ b/src/test/kotlin/org/rust/ide/docs/RsResolveLinkTest.kt
@@ -183,6 +183,17 @@ class RsResolveLinkTest : RsTestBase() {
               //^
     """, "test_package/struct.Foo.html#structfield.foo")
 
+    fun `test enum variant field fqn link`() = doTest("""
+        pub enum Foo {
+            Bar {
+                baz: i32
+            }  //X
+        }
+
+        struct S;
+             //^
+    """, "test_package/enum.Foo.html#variant.Bar.field.baz")
+
     fun `test assoc type fqn link 1`() = doTest("""
         trait Foo {
             type Bar;