Support propagating inherited extensions from libraries
diff --git a/core/src/main/kotlin/DokkaBootstrapImpl.kt b/core/src/main/kotlin/DokkaBootstrapImpl.kt
index 126a017..4b75f31 100644
--- a/core/src/main/kotlin/DokkaBootstrapImpl.kt
+++ b/core/src/main/kotlin/DokkaBootstrapImpl.kt
@@ -69,7 +69,8 @@
                         languageVersion,
                         apiVersion,
                         cacheRoot,
-                        suppressedFiles.map { File(it) }
+                        suppressedFiles.map { File(it) },
+                        collectInheritedExtensionsFromLibraries
                 )
         )
     }
diff --git a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormat.kt b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormat.kt
index 5148097..dac5b5c 100644
--- a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormat.kt
+++ b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormat.kt
@@ -52,6 +52,10 @@
     fun mainUri(node: DocumentationNode): URI = tryGetMainUri(node) ?: error("Unsupported ${node.kind}")
 
     fun linkTo(to: DocumentationNode, from: URI): String {
+        to.links(NodeKind.ExternalLink).firstOrNull()?.let {
+            return it.name
+        }
+
         return mainUri(to).relativeTo(from).toString()
     }
 
diff --git a/core/src/main/kotlin/Generation/DokkaGenerator.kt b/core/src/main/kotlin/Generation/DokkaGenerator.kt
index 09e5ced..33170f3 100644
--- a/core/src/main/kotlin/Generation/DokkaGenerator.kt
+++ b/core/src/main/kotlin/Generation/DokkaGenerator.kt
@@ -160,6 +160,8 @@
     with(injector.getInstance(DocumentationBuilder::class.java)) {
         documentationModule.appendFragments(fragments, packageDocs.packageContent,
                 injector.getInstance(PackageDocumentationBuilder::class.java))
+
+        propagateExtensionFunctionsToSubclasses(fragments, resolutionFacade)
     }
 
     val javaFiles = coreEnvironment.getJavaSourceFiles().filter(filesToDocumentFilter)
diff --git a/core/src/main/kotlin/Generation/configurationImpl.kt b/core/src/main/kotlin/Generation/configurationImpl.kt
index 34d4154..52e8446 100644
--- a/core/src/main/kotlin/Generation/configurationImpl.kt
+++ b/core/src/main/kotlin/Generation/configurationImpl.kt
@@ -36,27 +36,28 @@
                               override val suppress: Boolean = false) : DokkaConfiguration.PackageOptions
 
 data class DokkaConfigurationImpl(
-        override val moduleName: String,
-        override val classpath: List<String>,
-        override val sourceRoots: List<SourceRootImpl>,
-        override val samples: List<String>,
-        override val includes: List<String>,
-        override val outputDir: String,
-        override val format: String,
-        override val includeNonPublic: Boolean,
-        override val includeRootPackage: Boolean,
-        override val reportUndocumented: Boolean,
-        override val skipEmptyPackages: Boolean,
-        override val skipDeprecated: Boolean,
-        override val jdkVersion: Int,
-        override val generateIndexPages: Boolean,
-        override val sourceLinks: List<SourceLinkDefinitionImpl>,
-        override val impliedPlatforms: List<String>,
-        override val perPackageOptions: List<PackageOptionsImpl>,
-        override val externalDocumentationLinks: List<ExternalDocumentationLinkImpl>,
-        override val noStdlibLink: Boolean,
-        override val cacheRoot: String?,
-        override val suppressedFiles: List<String>,
-        override val languageVersion: String?,
-        override val apiVersion: String?
+    override val moduleName: String,
+    override val classpath: List<String>,
+    override val sourceRoots: List<SourceRootImpl>,
+    override val samples: List<String>,
+    override val includes: List<String>,
+    override val outputDir: String,
+    override val format: String,
+    override val includeNonPublic: Boolean,
+    override val includeRootPackage: Boolean,
+    override val reportUndocumented: Boolean,
+    override val skipEmptyPackages: Boolean,
+    override val skipDeprecated: Boolean,
+    override val jdkVersion: Int,
+    override val generateIndexPages: Boolean,
+    override val sourceLinks: List<SourceLinkDefinitionImpl>,
+    override val impliedPlatforms: List<String>,
+    override val perPackageOptions: List<PackageOptionsImpl>,
+    override val externalDocumentationLinks: List<ExternalDocumentationLinkImpl>,
+    override val noStdlibLink: Boolean,
+    override val cacheRoot: String?,
+    override val suppressedFiles: List<String>,
+    override val languageVersion: String?,
+    override val apiVersion: String?,
+    override val collectInheritedExtensionsFromLibraries: Boolean
 ) : DokkaConfiguration
\ No newline at end of file
diff --git a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
index 0024862..aaaf6b9 100644
--- a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
+++ b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
@@ -11,6 +11,9 @@
 import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
 import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor
 import org.jetbrains.kotlin.idea.kdoc.findKDoc
+import org.jetbrains.kotlin.idea.util.fuzzyExtensionReceiverType
+import org.jetbrains.kotlin.idea.util.makeNotNullable
+import org.jetbrains.kotlin.idea.util.toFuzzyType
 import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
 import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection
 import org.jetbrains.kotlin.lexer.KtTokens
@@ -23,10 +26,11 @@
 import org.jetbrains.kotlin.resolve.descriptorUtil.*
 import org.jetbrains.kotlin.resolve.findTopMostOverriddenDescriptors
 import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver
+import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
+import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
 import org.jetbrains.kotlin.resolve.source.PsiSourceElement
 import org.jetbrains.kotlin.resolve.source.getPsi
 import org.jetbrains.kotlin.types.*
-import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
 import org.jetbrains.kotlin.types.typeUtil.supertypes
 import java.io.File
 import java.nio.file.Path
@@ -51,7 +55,8 @@
                            val languageVersion: String?,
                            val apiVersion: String?,
                            cacheRoot: String? = null,
-                           val suppressedFiles: List<File> = emptyList()) {
+                           val suppressedFiles: List<File> = emptyList(),
+                           val collectInheritedExtensionsFromLibraries: Boolean = false) {
     init {
         if (perPackageOptions.any { it.prefix == "" })
             throw IllegalArgumentException("Please do not register packageOptions with all match pattern, use global settings instead")
@@ -136,8 +141,20 @@
         refGraph.register(descriptor.signature(), node)
     }
 
-    fun <T> nodeForDescriptor(descriptor: T, kind: NodeKind): DocumentationNode where T : DeclarationDescriptor, T : Named {
-        val (doc, callback) = descriptorDocumentationParser.parseDocumentationAndDetails(descriptor, kind == NodeKind.Parameter)
+    fun <T> nodeForDescriptor(
+        descriptor: T,
+        kind: NodeKind,
+        external: Boolean = false
+    ): DocumentationNode where T : DeclarationDescriptor, T : Named {
+        val (doc, callback) =
+                if (external) {
+                    Content.Empty to { node -> }
+                } else {
+                    descriptorDocumentationParser.parseDocumentationAndDetails(
+                        descriptor,
+                        kind == NodeKind.Parameter
+                    )
+                }
         val node = DocumentationNode(descriptor.name.asString(), doc, kind).withModifiers(descriptor)
         node.appendSignature(descriptor)
         callback(node)
@@ -224,14 +241,20 @@
             node.appendTextNode("?", NodeKind.NullabilityModifier)
         }
         if (classifierDescriptor != null) {
-            val externalLink = linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(classifierDescriptor)
+            val externalLink =
+                linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(classifierDescriptor)
             if (externalLink != null) {
-                val targetNode = refGraph.lookup(classifierDescriptor.signature()) ?: classifierDescriptor.build(true)
-                node.append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link)
-                node.append(targetNode, RefKind.ExternalType)
+                if (classifierDescriptor !is TypeParameterDescriptor) {
+                    val targetNode =
+                        refGraph.lookup(classifierDescriptor.signature()) ?: classifierDescriptor.build(true)
+                    node.append(targetNode, RefKind.ExternalType)
+                    node.append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link)
+                }
             } else {
-                link(node, classifierDescriptor,
-                        if (classifierDescriptor.isBoringBuiltinClass()) RefKind.HiddenLink else RefKind.Link)
+                link(
+                    node, classifierDescriptor,
+                    if (classifierDescriptor.isBoringBuiltinClass()) RefKind.HiddenLink else RefKind.Link
+                )
             }
             if (classifierDescriptor !is TypeParameterDescriptor) {
                 node.append(
@@ -278,6 +301,17 @@
         }
     }
 
+    fun DocumentationNode.appendExternalLink(externalLink: String) {
+        append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link)
+    }
+
+    fun DocumentationNode.appendExternalLink(descriptor: DeclarationDescriptor) {
+        val target = linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(descriptor)
+        if (target != null) {
+            appendExternalLink(target)
+        }
+    }
+
     fun DocumentationNode.appendSinceKotlin(annotation: DocumentationNode) {
         val kotlinVersion = annotation
                 .detail(NodeKind.Parameter)
@@ -424,15 +458,44 @@
                     declarations, allFqNames)
         }
 
-        propagateExtensionFunctionsToSubclasses(fragments)
     }
 
-    private fun propagateExtensionFunctionsToSubclasses(fragments: Collection<PackageFragmentDescriptor>) {
-        val allDescriptors = fragments.flatMap { it.getMemberScope().getContributedDescriptors() }
-        val allClasses = allDescriptors.filterIsInstance<ClassDescriptor>()
-        val classHierarchy = buildClassHierarchy(allClasses)
+    fun propagateExtensionFunctionsToSubclasses(
+        fragments: Collection<PackageFragmentDescriptor>,
+        resolutionFacade: DokkaResolutionFacade
+    ) {
 
-        val allExtensionFunctions = allDescriptors
+        val moduleDescriptor = resolutionFacade.moduleDescriptor
+
+        // Wide-collect all view descriptors
+        val allPackageViewDescriptors = generateSequence(listOf(moduleDescriptor.getPackage(FqName.ROOT))) { packages ->
+            packages
+                .flatMap { pkg ->
+                    moduleDescriptor.getSubPackagesOf(pkg.fqName) { true }
+                }.map { fqName ->
+                    moduleDescriptor.getPackage(fqName)
+                }.takeUnless { it.isEmpty() }
+        }.flatten()
+
+        val allDescriptors =
+            if (options.collectInheritedExtensionsFromLibraries) {
+                allPackageViewDescriptors.map { it.memberScope }
+            } else {
+                fragments.asSequence().map { it.getMemberScope() }
+            }.flatMap {
+                it.getDescriptorsFiltered(
+                    DescriptorKindFilter.CALLABLES
+                ).asSequence()
+            }
+
+
+        val documentingDescriptors = fragments.flatMap { it.getMemberScope().getContributedDescriptors() }
+        val documentingClasses = documentingDescriptors.filterIsInstance<ClassDescriptor>()
+
+        val classHierarchy = buildClassHierarchy(documentingClasses)
+
+        val allExtensionFunctions =
+            allDescriptors
                 .filterIsInstance<CallableMemberDescriptor>()
                 .filter { it.extensionReceiverParameter != null }
         val extensionFunctionsByName = allExtensionFunctions.groupBy { it.name }
@@ -440,15 +503,31 @@
         for (extensionFunction in allExtensionFunctions) {
             if (extensionFunction.dispatchReceiverParameter != null) continue
             val possiblyShadowingFunctions = extensionFunctionsByName[extensionFunction.name]
-                    ?.filter { fn -> fn.canShadow(extensionFunction) }
+                ?.filter { fn -> fn.canShadow(extensionFunction) }
                     ?: emptyList()
 
             if (extensionFunction.extensionReceiverParameter?.type?.isDynamic() == true) continue
-            val classDescriptor = extensionFunction.getExtensionClassDescriptor() ?: continue
-            val subclasses = classHierarchy[classDescriptor] ?: continue
-            subclasses.forEach { subclass ->
+            val subclasses =
+                classHierarchy.filter { (key) -> key.isExtensionApplicable(extensionFunction) }
+            if (subclasses.isEmpty()) continue
+            subclasses.values.flatten().forEach { subclass ->
                 if (subclass.isExtensionApplicable(extensionFunction) &&
-                        possiblyShadowingFunctions.none { subclass.isExtensionApplicable(it) }) {
+                    possiblyShadowingFunctions.none { subclass.isExtensionApplicable(it) }) {
+
+                    val hasExternalLink =
+                        linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(
+                            extensionFunction
+                        ) != null
+                    if (hasExternalLink) {
+                        val containerDesc =
+                            extensionFunction.containingDeclaration as? PackageFragmentDescriptor
+                        if (containerDesc != null) {
+                            val container = refGraph.lookup(containerDesc.signature())
+                                    ?: containerDesc.buildExternal()
+                            container.append(extensionFunction.buildExternal(), RefKind.Member)
+                        }
+                    }
+
                     refGraph.link(subclass.signature(), extensionFunction.signature(), RefKind.Extension)
                 }
             }
@@ -456,12 +535,9 @@
     }
 
     private fun ClassDescriptor.isExtensionApplicable(extensionFunction: CallableMemberDescriptor): Boolean {
-        val receiverType = extensionFunction.extensionReceiverParameter!!.type
-        if (receiverType.arguments.any { it.type.constructor.declarationDescriptor is TypeParameterDescriptor }) {
-            val receiverClass = receiverType.constructor.declarationDescriptor
-            return receiverClass is ClassDescriptor && DescriptorUtils.isSubclass(this, receiverClass)
-        }
-        return defaultType.isSubtypeOf(receiverType)
+        val receiverType = extensionFunction.fuzzyExtensionReceiverType()?.makeNotNullable()
+        val classType = defaultType.toFuzzyType(declaredTypeParameters)
+        return receiverType != null && classType.checkIsSubtypeOf(receiverType) != null
     }
 
     private fun buildClassHierarchy(classes: List<ClassDescriptor>): Map<ClassDescriptor, List<ClassDescriptor>> {
@@ -509,6 +585,24 @@
         else -> throw IllegalStateException("Descriptor $this is not known")
     }
 
+    fun PackageFragmentDescriptor.buildExternal(): DocumentationNode {
+        val node = DocumentationNode(fqName.asString(), Content.Empty, NodeKind.Package)
+
+        val externalLink = linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(this)
+        if (externalLink != null) {
+            node.append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link)
+        }
+        register(this, node)
+        return node
+    }
+
+    fun CallableDescriptor.buildExternal(): DocumentationNode = when(this) {
+        is FunctionDescriptor -> build(true)
+        is PropertyDescriptor -> build(true)
+        else -> throw IllegalStateException("Descriptor $this is not known")
+    }
+
+
     fun ClassifierDescriptor.build(external: Boolean = false): DocumentationNode = when (this) {
         is ClassDescriptor -> build(external)
         is TypeAliasDescriptor -> build(external)
@@ -545,7 +639,7 @@
             isSubclassOfThrowable() -> NodeKind.Exception
             else -> NodeKind.Class
         }
-        val node = nodeForDescriptor(this, kind)
+        val node = nodeForDescriptor(this, kind, external)
         register(this, node)
         typeConstructor.supertypes.forEach {
             node.appendSupertype(this, it)
@@ -630,12 +724,12 @@
         return (receiver?.type?.constructor?.declarationDescriptor as? ClassDescriptor)?.isCompanionObject ?: false
     }
 
-    fun FunctionDescriptor.build(): DocumentationNode {
+    fun FunctionDescriptor.build(external: Boolean = false): DocumentationNode {
         if (ErrorUtils.containsErrorType(this)) {
             logger.warn("Found an unresolved type in ${signatureWithSourceLocation()}")
         }
 
-        val node = nodeForDescriptor(this, if (inCompanionObject()) NodeKind.CompanionObjectFunction else NodeKind.Function)
+        val node = nodeForDescriptor(this, if (inCompanionObject()) NodeKind.CompanionObjectFunction else NodeKind.Function, external)
 
         node.appendInPageChildren(typeParameters, RefKind.Detail)
         extensionReceiverParameter?.let { node.appendChild(it, RefKind.Detail) }
@@ -643,8 +737,12 @@
         node.appendType(returnType)
         node.appendAnnotations(this)
         node.appendModifiers(this)
-        node.appendSourceLink(source)
-        node.appendDefaultPlatforms(this)
+        if (!external) {
+            node.appendSourceLink(source)
+            node.appendDefaultPlatforms(this)
+        } else {
+            node.appendExternalLink(this)
+        }
 
         overriddenDescriptors.forEach {
             addOverrideLink(it, this)
@@ -665,32 +763,42 @@
         }
     }
 
-    fun PropertyDescriptor.build(): DocumentationNode {
-        val node = nodeForDescriptor(this, if (inCompanionObject()) NodeKind.CompanionObjectProperty else NodeKind.Property)
+    fun PropertyDescriptor.build(external: Boolean = false): DocumentationNode {
+        val node = nodeForDescriptor(
+            this,
+            if (inCompanionObject()) NodeKind.CompanionObjectProperty else NodeKind.Property,
+            external
+        )
         node.appendInPageChildren(typeParameters, RefKind.Detail)
         extensionReceiverParameter?.let { node.appendChild(it, RefKind.Detail) }
         node.appendType(returnType)
         node.appendAnnotations(this)
         node.appendModifiers(this)
-        node.appendSourceLink(source)
-        if (isVar) {
-            node.appendTextNode("var", NodeKind.Modifier)
-        }
-        getter?.let {
-            if (!it.isDefault) {
-                node.addAccessorDocumentation(descriptorDocumentationParser.parseDocumentation(it), "Getter")
+        if (!external) {
+            node.appendSourceLink(source)
+            if (isVar) {
+                node.appendTextNode("var", NodeKind.Modifier)
             }
-        }
-        setter?.let {
-            if (!it.isDefault) {
-                node.addAccessorDocumentation(descriptorDocumentationParser.parseDocumentation(it), "Setter")
+
+            getter?.let {
+                if (!it.isDefault) {
+                    node.addAccessorDocumentation(descriptorDocumentationParser.parseDocumentation(it), "Getter")
+                }
             }
+            setter?.let {
+                if (!it.isDefault) {
+                    node.addAccessorDocumentation(descriptorDocumentationParser.parseDocumentation(it), "Setter")
+                }
+            }
+            node.appendDefaultPlatforms(this)
+        }
+        if (external) {
+            node.appendExternalLink(this)
         }
 
         overriddenDescriptors.forEach {
             addOverrideLink(it, this)
         }
-        node.appendDefaultPlatforms(this)
 
         register(this, node)
         return node
diff --git a/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt b/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt
index 108cee7..330e76a 100644
--- a/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt
+++ b/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt
@@ -154,8 +154,8 @@
     fun buildExternalDocumentationLink(symbol: DeclarationDescriptor): String? {
         val packageFqName: FqName =
                 when (symbol) {
-                    is DeclarationDescriptorNonRoot -> symbol.parents.firstOrNull { it is PackageFragmentDescriptor }?.fqNameSafe ?: return null
                     is PackageFragmentDescriptor -> symbol.fqName
+                    is DeclarationDescriptorNonRoot -> symbol.parents.firstOrNull { it is PackageFragmentDescriptor }?.fqNameSafe ?: return null
                     else -> return null
                 }
 
diff --git a/core/src/main/kotlin/Model/DocumentationNode.kt b/core/src/main/kotlin/Model/DocumentationNode.kt
index a792460..85ecdf8 100644
--- a/core/src/main/kotlin/Model/DocumentationNode.kt
+++ b/core/src/main/kotlin/Model/DocumentationNode.kt
@@ -209,6 +209,8 @@
 fun DocumentationNode.qualifiedName(): String {
     if (kind == NodeKind.Type) {
         return qualifiedNameFromType()
+    } else if (kind == NodeKind.Package) {
+        return name
     }
     return path.drop(1).map { it.name }.filter { it.length > 0 }.joinToString(".")
 }
diff --git a/core/src/test/kotlin/TestAPI.kt b/core/src/test/kotlin/TestAPI.kt
index 559b715..aeff928 100644
--- a/core/src/test/kotlin/TestAPI.kt
+++ b/core/src/test/kotlin/TestAPI.kt
@@ -25,22 +25,24 @@
                 includeNonPublic: Boolean = true,
                 perPackageOptions: List<DokkaConfiguration.PackageOptions> = emptyList(),
                 noStdlibLink: Boolean = true,
+                collectInheritedExtensionsFromLibraries: Boolean = false,
                 verifier: (DocumentationModule) -> Unit) {
     val documentation = DocumentationModule("test")
 
     val options = DocumentationOptions(
-            "",
-            format,
-            includeNonPublic = includeNonPublic,
-            skipEmptyPackages = false,
-            includeRootPackage = true,
-            sourceLinks = listOf(),
-            perPackageOptions = perPackageOptions,
-            generateIndexPages = false,
-            noStdlibLink = noStdlibLink,
-            cacheRoot = "default",
-            languageVersion = null,
-            apiVersion = null
+        "",
+        format,
+        includeNonPublic = includeNonPublic,
+        skipEmptyPackages = false,
+        includeRootPackage = true,
+        sourceLinks = listOf(),
+        perPackageOptions = perPackageOptions,
+        generateIndexPages = false,
+        noStdlibLink = noStdlibLink,
+        cacheRoot = "default",
+        languageVersion = null,
+        apiVersion = null,
+        collectInheritedExtensionsFromLibraries = collectInheritedExtensionsFromLibraries
     )
 
     appendDocumentation(documentation, *roots,
@@ -162,6 +164,7 @@
                  format: String = "html",
                  includeNonPublic: Boolean = true,
                  noStdlibLink: Boolean = true,
+                 collectInheritedExtensionsFromLibraries: Boolean = false,
                  outputGenerator: (DocumentationModule, StringBuilder) -> Unit) {
     verifyModel(
         *roots,
@@ -169,7 +172,8 @@
         withKotlinRuntime = withKotlinRuntime,
         format = format,
         includeNonPublic = includeNonPublic,
-        noStdlibLink = noStdlibLink
+        noStdlibLink = noStdlibLink,
+        collectInheritedExtensionsFromLibraries = collectInheritedExtensionsFromLibraries
     ) {
         verifyModelOutput(it, outputExtension, roots.first().path, outputGenerator)
     }
@@ -194,6 +198,7 @@
     format: String = "html",
     includeNonPublic: Boolean = true,
     noStdlibLink: Boolean = true,
+    collectInheritedExtensionsFromLibraries: Boolean = false,
     outputGenerator: (DocumentationModule, StringBuilder) -> Unit
 ) {
     verifyOutput(
@@ -204,6 +209,7 @@
         format,
         includeNonPublic,
         noStdlibLink,
+        collectInheritedExtensionsFromLibraries,
         outputGenerator
     )
 }
diff --git a/core/src/test/kotlin/format/JavaLayoutHtmlFormatTest.kt b/core/src/test/kotlin/format/JavaLayoutHtmlFormatTest.kt
index f93139e..5da49d3 100644
--- a/core/src/test/kotlin/format/JavaLayoutHtmlFormatTest.kt
+++ b/core/src/test/kotlin/format/JavaLayoutHtmlFormatTest.kt
@@ -16,33 +16,5 @@
         verifyPackageNode("topLevel.kt")
     }
 
-    private fun verifyNode(fileName: String) {
-        verifyOutput(
-            "testdata/format/java-layout-html/$fileName",
-            ".html",
-            format = "java-layout-html",
-            withKotlinRuntime = true,
-            noStdlibLink = false
-        ) { model, output ->
-            buildPagesAndReadInto(
-                model,
-                listOf(model.members.single().members.single()),
-                output
-            )
-        }
-    }
 
-    private fun verifyPackageNode(fileName: String) {
-        verifyOutput(
-            "testdata/format/java-layout-html/$fileName",
-            ".package-summary.html",
-            format = "java-layout-html"
-        ) { model, output ->
-            buildPagesAndReadInto(
-                model,
-                listOf(model.members.single()),
-                output
-            )
-        }
-    }
 }
\ No newline at end of file
diff --git a/core/src/test/kotlin/format/JavaLayoutHtmlFormatTestCase.kt b/core/src/test/kotlin/format/JavaLayoutHtmlFormatTestCase.kt
index 928a7d2..0b8fca2 100644
--- a/core/src/test/kotlin/format/JavaLayoutHtmlFormatTestCase.kt
+++ b/core/src/test/kotlin/format/JavaLayoutHtmlFormatTestCase.kt
@@ -30,7 +30,8 @@
             apiVersion = null,
             languageVersion = null,
             generateIndexPages = false,
-            noStdlibLink = false
+            noStdlibLink = false,
+            collectInheritedExtensionsFromLibraries = true
         )
 
     val injector: Injector by lazy {
@@ -58,7 +59,7 @@
     }
 
 
-    fun buildPagesAndReadInto(model: DocumentationNode, nodes: List<DocumentationNode>, sb: StringBuilder) =
+    protected fun buildPagesAndReadInto(model: DocumentationNode, nodes: List<DocumentationNode>, sb: StringBuilder) =
         with(injector.getInstance(Generator::class.java)) {
             this as JavaLayoutHtmlFormatGenerator
             buildPages(listOf(model))
@@ -69,4 +70,34 @@
             }
         }
 
+    protected fun verifyNode(fileName: String) {
+        verifyOutput(
+            "testdata/format/java-layout-html/$fileName",
+            ".html",
+            format = "java-layout-html",
+            withKotlinRuntime = true,
+            noStdlibLink = false,
+            collectInheritedExtensionsFromLibraries = true
+        ) { model, output ->
+            buildPagesAndReadInto(
+                model,
+                listOf(model.members.single().members.single()),
+                output
+            )
+        }
+    }
+
+    protected fun verifyPackageNode(fileName: String) {
+        verifyOutput(
+            "testdata/format/java-layout-html/$fileName",
+            ".package-summary.html",
+            format = "java-layout-html"
+        ) { model, output ->
+            buildPagesAndReadInto(
+                model,
+                listOf(model.members.single()),
+                output
+            )
+        }
+    }
 }
\ No newline at end of file
diff --git a/integration/src/main/kotlin/org/jetbrains/dokka/configuration.kt b/integration/src/main/kotlin/org/jetbrains/dokka/configuration.kt
index 90e5b5f..46a5727 100644
--- a/integration/src/main/kotlin/org/jetbrains/dokka/configuration.kt
+++ b/integration/src/main/kotlin/org/jetbrains/dokka/configuration.kt
@@ -41,6 +41,7 @@
     val noStdlibLink: Boolean
     val cacheRoot: String?
     val suppressedFiles: List<String>
+    val collectInheritedExtensionsFromLibraries: Boolean
 
     interface SourceRoot {
         val path: String
@@ -82,29 +83,30 @@
 }
 
 data class SerializeOnlyDokkaConfiguration(
-        override val moduleName: String,
-        override val classpath: List<String>,
-        override val sourceRoots: List<DokkaConfiguration.SourceRoot>,
-        override val samples: List<String>,
-        override val includes: List<String>,
-        override val outputDir: String,
-        override val format: String,
-        override val includeNonPublic: Boolean,
-        override val includeRootPackage: Boolean,
-        override val reportUndocumented: Boolean,
-        override val skipEmptyPackages: Boolean,
-        override val skipDeprecated: Boolean,
-        override val jdkVersion: Int,
-        override val generateIndexPages: Boolean,
-        override val sourceLinks: List<DokkaConfiguration.SourceLinkDefinition>,
-        override val impliedPlatforms: List<String>,
-        override val perPackageOptions: List<DokkaConfiguration.PackageOptions>,
-        override val externalDocumentationLinks: List<DokkaConfiguration.ExternalDocumentationLink>,
-        override val noStdlibLink: Boolean,
-        override val cacheRoot: String?,
-        override val suppressedFiles: List<String>,
-        override val languageVersion: String?,
-        override val apiVersion: String?
+    override val moduleName: String,
+    override val classpath: List<String>,
+    override val sourceRoots: List<DokkaConfiguration.SourceRoot>,
+    override val samples: List<String>,
+    override val includes: List<String>,
+    override val outputDir: String,
+    override val format: String,
+    override val includeNonPublic: Boolean,
+    override val includeRootPackage: Boolean,
+    override val reportUndocumented: Boolean,
+    override val skipEmptyPackages: Boolean,
+    override val skipDeprecated: Boolean,
+    override val jdkVersion: Int,
+    override val generateIndexPages: Boolean,
+    override val sourceLinks: List<DokkaConfiguration.SourceLinkDefinition>,
+    override val impliedPlatforms: List<String>,
+    override val perPackageOptions: List<DokkaConfiguration.PackageOptions>,
+    override val externalDocumentationLinks: List<DokkaConfiguration.ExternalDocumentationLink>,
+    override val noStdlibLink: Boolean,
+    override val cacheRoot: String?,
+    override val suppressedFiles: List<String>,
+    override val languageVersion: String?,
+    override val apiVersion: String?,
+    override val collectInheritedExtensionsFromLibraries: Boolean
 ) : DokkaConfiguration
 
 
diff --git a/runners/cli/src/main/kotlin/cli/main.kt b/runners/cli/src/main/kotlin/cli/main.kt
index fe945ed..111e142 100644
--- a/runners/cli/src/main/kotlin/cli/main.kt
+++ b/runners/cli/src/main/kotlin/cli/main.kt
@@ -62,6 +62,9 @@
     @set:Argument(value = "apiVersion", description = "Kotlin Api Version to pass to Kotlin Analysis")
     var apiVersion: String? = null
 
+    @set:Argument(value = "collectInheritedExtensionsFromLibraries", description = "Search for applicable extensions in libraries")
+    var collectInheritedExtensionsFromLibraries: Boolean = false
+
 }
 
 
@@ -106,18 +109,19 @@
         val classPath = arguments.classpath.split(File.pathSeparatorChar).toList()
 
         val documentationOptions = DocumentationOptions(
-                arguments.outputDir.let { if (it.endsWith('/')) it else it + '/' },
-                arguments.outputFormat,
-                skipDeprecated = arguments.nodeprecated,
-                sourceLinks = sourceLinks,
-                impliedPlatforms = arguments.impliedPlatforms.split(','),
-                perPackageOptions = parsePerPackageOptions(arguments.packageOptions),
-                jdkVersion = arguments.jdkVersion,
-                externalDocumentationLinks = parseLinks(arguments.links),
-                noStdlibLink = arguments.noStdlibLink,
-                cacheRoot = arguments.cacheRoot,
-                languageVersion = arguments.languageVersion,
-                apiVersion = arguments.apiVersion
+            arguments.outputDir.let { if (it.endsWith('/')) it else it + '/' },
+            arguments.outputFormat,
+            skipDeprecated = arguments.nodeprecated,
+            sourceLinks = sourceLinks,
+            impliedPlatforms = arguments.impliedPlatforms.split(','),
+            perPackageOptions = parsePerPackageOptions(arguments.packageOptions),
+            jdkVersion = arguments.jdkVersion,
+            externalDocumentationLinks = parseLinks(arguments.links),
+            noStdlibLink = arguments.noStdlibLink,
+            cacheRoot = arguments.cacheRoot,
+            languageVersion = arguments.languageVersion,
+            apiVersion = arguments.apiVersion,
+            collectInheritedExtensionsFromLibraries = arguments.collectInheritedExtensionsFromLibraries
         )
 
         val generator = DokkaGenerator(
diff --git a/runners/gradle-plugin/src/main/kotlin/main.kt b/runners/gradle-plugin/src/main/kotlin/main.kt
index bdecc3f..5138fce 100644
--- a/runners/gradle-plugin/src/main/kotlin/main.kt
+++ b/runners/gradle-plugin/src/main/kotlin/main.kt
@@ -122,6 +122,9 @@
     @Optional @Input
     var apiVersion: String? = null
 
+    @Input
+    var collectInheritedExtensionsFromLibraries: Boolean = false
+
     @get:Input
     internal val kotlinCompileBasedClasspathAndSourceRoots: ClasspathAndSourceRoots by lazy { extractClasspathAndSourceRootsFromKotlinTasks() }
 
@@ -269,29 +272,31 @@
             val bootstrapProxy: DokkaBootstrap = automagicTypedProxy(javaClass.classLoader, bootstrapInstance)
 
             val configuration = SerializeOnlyDokkaConfiguration(
-                    moduleName,
-                    fullClasspath.map { it.absolutePath },
-                    sourceRoots,
-                    samples.filterNotNull().map { project.file(it).absolutePath },
-                    includes.filterNotNull().map { project.file(it).absolutePath },
-                    outputDirectory,
-                    outputFormat,
-                    includeNonPublic,
-                    false,
-                    reportNotDocumented,
-                    skipEmptyPackages,
-                    skipDeprecated,
-                    jdkVersion,
-                    true,
-                    linkMappings,
-                    impliedPlatforms,
-                    perPackageOptions,
-                    externalDocumentationLinks,
-                    noStdlibLink,
-                    cacheRoot,
-                    collectSuppressedFiles(sourceRoots),
-                    languageVersion,
-                    apiVersion)
+                moduleName,
+                fullClasspath.map { it.absolutePath },
+                sourceRoots,
+                samples.filterNotNull().map { project.file(it).absolutePath },
+                includes.filterNotNull().map { project.file(it).absolutePath },
+                outputDirectory,
+                outputFormat,
+                includeNonPublic,
+                false,
+                reportNotDocumented,
+                skipEmptyPackages,
+                skipDeprecated,
+                jdkVersion,
+                true,
+                linkMappings,
+                impliedPlatforms,
+                perPackageOptions,
+                externalDocumentationLinks,
+                noStdlibLink,
+                cacheRoot,
+                collectSuppressedFiles(sourceRoots),
+                languageVersion,
+                apiVersion,
+                collectInheritedExtensionsFromLibraries
+            )
 
 
             bootstrapProxy.configure(