Consider only source-compatible modules in allImplementingModules use-sites

So #KT-17400 Fixed
So #KT-15680 Fixed
So #KT-15660 Fixed
Likely also #KT-16838 Fixed
This commit is contained in:
Mikhail Glukhikh
2017-04-18 12:34:11 +03:00
committed by Mikhail Glukhikh
parent 956ace3463
commit 0798e43524
24 changed files with 86 additions and 32 deletions
@@ -19,11 +19,14 @@ package org.jetbrains.kotlin.idea.highlighter
import com.intellij.lang.annotation.AnnotationHolder
import com.intellij.lang.annotation.Annotator
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
import org.jetbrains.kotlin.descriptors.MemberDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.idea.caches.resolve.ModuleProductionSourceInfo
import org.jetbrains.kotlin.idea.caches.resolve.ModuleTestSourceInfo
import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor
import org.jetbrains.kotlin.idea.core.toDescriptor
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
@@ -34,6 +37,22 @@ import org.jetbrains.kotlin.resolve.checkers.HeaderImplDeclarationChecker
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
import org.jetbrains.kotlin.resolve.diagnostics.SimpleDiagnostics
val ModuleDescriptor.sourceKind: SourceKind
get() = when (getCapability(ModuleInfo.Capability)) {
is ModuleProductionSourceInfo -> SourceKind.PRODUCTION
is ModuleTestSourceInfo -> SourceKind.TEST
else -> SourceKind.OTHER
}
enum class SourceKind { OTHER, PRODUCTION, TEST }
val ModuleDescriptor.allImplementingCompatibleModules
get() = allImplementingModules.filter {
sourceKind == SourceKind.OTHER ||
it.sourceKind == SourceKind.OTHER ||
it.sourceKind == sourceKind
}
class PlatformHeaderAnnotator : Annotator {
override fun annotate(element: PsiElement, holder: AnnotationHolder) {
@@ -43,7 +62,7 @@ class PlatformHeaderAnnotator : Annotator {
if (TargetPlatformDetector.getPlatform(declaration.containingKtFile) !is TargetPlatform.Default) return
val defaultModuleDescriptor = declaration.findModuleDescriptor()
val dependentDescriptors = defaultModuleDescriptor.allImplementingModules
val dependentDescriptors = defaultModuleDescriptor.allImplementingCompatibleModules
if (dependentDescriptors.isEmpty()) return
val diagnostics = validate(declaration, dependentDescriptors)
@@ -16,14 +16,10 @@
package org.jetbrains.kotlin.idea.highlighter.markers
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.MemberDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.ModuleProductionSourceInfo
import org.jetbrains.kotlin.idea.caches.resolve.ModuleTestSourceInfo
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor
import org.jetbrains.kotlin.idea.core.toDescriptor
import org.jetbrains.kotlin.idea.highlighter.sourceKind
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
@@ -39,15 +35,6 @@ fun ModuleDescriptor.commonModuleOrNull(): ModuleDescriptor? {
}
}
private val ModuleDescriptor.sourceKind: SourceKind
get() = when (getCapability(ModuleInfo.Capability)) {
is ModuleProductionSourceInfo -> SourceKind.PRODUCTION
is ModuleTestSourceInfo -> SourceKind.TEST
else -> SourceKind.NONE
}
private enum class SourceKind { NONE, PRODUCTION, TEST }
fun ModuleDescriptor.hasDeclarationOf(descriptor: MemberDescriptor) = declarationOf(descriptor) != null
private fun ModuleDescriptor.declarationOf(descriptor: MemberDescriptor): DeclarationDescriptor? =
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.descriptors.MemberDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor
import org.jetbrains.kotlin.idea.core.toDescriptor
import org.jetbrains.kotlin.idea.highlighter.allImplementingCompatibleModules
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.MultiTargetPlatform
@@ -42,7 +43,7 @@ fun getPlatformImplementationTooltip(declaration: KtDeclaration): String? {
val descriptor = declaration.toDescriptor() as? MemberDescriptor ?: return null
val commonModuleDescriptor = declaration.containingKtFile.findModuleDescriptor()
val platformModulesWithImplementation = commonModuleDescriptor.allImplementingModules.filter {
val platformModulesWithImplementation = commonModuleDescriptor.allImplementingCompatibleModules.filter {
it.hasImplementationsOf(descriptor)
}
if (platformModulesWithImplementation.isEmpty()) return null
@@ -67,7 +68,7 @@ fun navigateToPlatformImplementation(e: MouseEvent?, declaration: KtDeclaration)
internal fun KtDeclaration.headerImplementations(): Set<KtDeclaration> {
val descriptor = toDescriptor() as? MemberDescriptor ?: return emptySet()
val commonModuleDescriptor = containingKtFile.findModuleDescriptor()
return commonModuleDescriptor.allImplementingModules.flatMap {
return commonModuleDescriptor.allImplementingCompatibleModules.flatMap {
it.implementationsOf(descriptor)
}.mapNotNullTo(LinkedHashSet()) { DescriptorToSourceUtils.descriptorToDeclaration(it) as? KtDeclaration }
}
@@ -41,6 +41,7 @@ import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.idea.KotlinIcons
import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor
import org.jetbrains.kotlin.idea.core.toDescriptor
import org.jetbrains.kotlin.idea.highlighter.allImplementingCompatibleModules
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
@@ -286,7 +287,7 @@ private fun collectImplementationMarkers(declaration: KtNamedDeclaration,
val descriptor = declaration.toDescriptor() as? MemberDescriptor ?: return
val commonModuleDescriptor = declaration.containingKtFile.findModuleDescriptor()
if (commonModuleDescriptor.allImplementingModules.none { it.hasImplementationsOf(descriptor) }) return
if (commonModuleDescriptor.allImplementingCompatibleModules.none { it.hasImplementationsOf(descriptor) }) return
val anchor = declaration.nameIdentifier ?: declaration
@@ -55,6 +55,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.core.toDescriptor
import org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory
import org.jetbrains.kotlin.idea.findUsages.handlers.KotlinFindClassUsagesHandler
import org.jetbrains.kotlin.idea.highlighter.allImplementingCompatibleModules
import org.jetbrains.kotlin.idea.highlighter.markers.hasImplementationsOf
import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.references.mainReference
@@ -335,7 +336,7 @@ class UnusedSymbolInspection : AbstractKotlinInspection() {
descriptor as? MemberDescriptor ?: return false
val commonModuleDescriptor = declaration.containingKtFile.findModuleDescriptor()
return commonModuleDescriptor.allImplementingModules.any { it.hasImplementationsOf(descriptor) } ||
return commonModuleDescriptor.allImplementingCompatibleModules.any { it.hasImplementationsOf(descriptor) } ||
commonModuleDescriptor.hasImplementationsOf(descriptor)
}
@@ -1,3 +1,3 @@
header class <error descr="[HEADER_WITHOUT_IMPLEMENTATION] Header declaration 'My' has no implementation in module jvm (test) for JVM"><error descr="[HEADER_WITHOUT_IMPLEMENTATION] Header declaration 'My' has no implementation in module jvm for JVM">My</error></error> {
header class <error descr="[HEADER_WITHOUT_IMPLEMENTATION] Header declaration 'My' has no implementation in module jvm for JVM">My</error> {
}
@@ -1,3 +1,3 @@
header class <error descr="[HEADER_WITHOUT_IMPLEMENTATION] Header declaration 'My' has no implementation in module js (test) for JS"><error descr="[HEADER_WITHOUT_IMPLEMENTATION] Header declaration 'My' has no implementation in module js for JS"><error descr="[HEADER_WITHOUT_IMPLEMENTATION] Header declaration 'My' has no implementation in module jvm (test) for JVM"><error descr="[HEADER_WITHOUT_IMPLEMENTATION] Header declaration 'My' has no implementation in module jvm for JVM">My</error></error></error></error> {
header class <error descr="[HEADER_WITHOUT_IMPLEMENTATION] Header declaration 'My' has no implementation in module js for JS"><error descr="[HEADER_WITHOUT_IMPLEMENTATION] Header declaration 'My' has no implementation in module jvm for JVM">My</error></error> {
}
@@ -4,4 +4,4 @@
header interface Event
@Suppress("SOMETHING_WRONG")
header class <error descr="[HEADER_WITHOUT_IMPLEMENTATION] Header declaration 'Wrong' has no implementation in module jvm (test) for JVM"><error descr="[HEADER_WITHOUT_IMPLEMENTATION] Header declaration 'Wrong' has no implementation in module jvm for JVM">Wrong</error></error>
header class <error descr="[HEADER_WITHOUT_IMPLEMENTATION] Header declaration 'Wrong' has no implementation in module jvm for JVM">Wrong</error>
@@ -0,0 +1,3 @@
header fun foo(arg: Int): Int
<error>header fun foo(): Int</error>
@@ -0,0 +1 @@
impl fun foo(arg: Int) = arg
@@ -0,0 +1,3 @@
header fun <lineMarker descr="Has implementations in JVM">foo</lineMarker>(): Int
header fun <lineMarker descr="Has implementations in JVM">foo</lineMarker>(arg: Int): Int
@@ -0,0 +1,5 @@
// !CHECK_HIGHLIGHTING
impl fun foo() = 42
impl fun foo(arg: Int) = 7 * arg
@@ -0,0 +1,5 @@
// "Create header function implementation for platform JVM" "true"
package test
header fun <caret>testHelper()
@@ -0,0 +1,5 @@
// "Create header function implementation for platform JVM" "true"
package test
header fun testHelper()
@@ -0,0 +1 @@
// testHelper: NOT to be implemented
@@ -0,0 +1 @@
// testHelper: NOT to be implemented
@@ -0,0 +1,3 @@
// testHelper: to be implemented
package test
@@ -0,0 +1,5 @@
// testHelper: to be implemented
package test
impl fun testHelper() {}
@@ -93,6 +93,10 @@ class MultiModuleHighlightingTest : AbstractMultiModuleHighlightingTest() {
doMultiPlatformTest(TargetPlatformKind.Jvm[JvmTarget.JVM_1_6], withStdlibCommon = true)
}
fun testWithOverrides() {
doMultiPlatformTest(TargetPlatformKind.Jvm[JvmTarget.JVM_1_6])
}
fun testUseCorrectBuiltInsForCommonModule() {
doMultiPlatformTest(TargetPlatformKind.Jvm[JvmTarget.JVM_1_8], TargetPlatformKind.JavaScript,
withStdlibCommon = true, useFullJdk = true, configureModule = { module, platform ->
@@ -32,4 +32,8 @@ class MultiModuleLineMarkerTest : AbstractMultiModuleLineMarkerTest() {
fun testFromClassToAlias() {
doMultiPlatformTest(TargetPlatformKind.Jvm[JvmTarget.JVM_1_6])
}
fun testWithOverloads() {
doMultiPlatformTest(TargetPlatformKind.Jvm[JvmTarget.JVM_1_6])
}
}
@@ -27,7 +27,6 @@ import junit.framework.ComparisonFailure
import junit.framework.TestCase
import org.jetbrains.kotlin.idea.inspections.findExistingEditor
import org.jetbrains.kotlin.idea.project.PluginJetFilesProvider
import org.jetbrains.kotlin.idea.refactoring.createKotlinFile
import org.jetbrains.kotlin.idea.stubs.AbstractMultiModuleTest
import org.jetbrains.kotlin.idea.test.DirectiveBasedActionUtils
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
@@ -78,13 +77,13 @@ abstract class AbstractQuickFixMultiModuleTest : AbstractMultiModuleTest() {
val testDirectory = File(testPath)
val projectDirectory = File("$testPath${getTestName(true)}")
for (moduleDirectory in projectDirectory.listFiles()) {
for (file in moduleDirectory.listFiles()) {
for (file in moduleDirectory.walkTopDown()) {
if (!file.path.endsWith(".after")) continue
try {
val editedFile = allFilesInProject.find {
it.name.toLowerCase() == file.name.removeSuffix(".after").toLowerCase()
} ?: allFilesInProject.mapNotNull {
it.containingDirectory?.findFile(file.name.removeSuffix(".after"))
val packageName = file.readLines().find { it.startsWith("package") }?.substringAfter(" ") ?: "<root>"
val editedFile = allFilesInProject.mapNotNull {
val candidate = it.containingDirectory?.findFile(file.name.removeSuffix(".after")) as? KtFile
if (candidate?.packageFqName?.toString() == packageName) candidate else null
}.single()
setActiveEditor(editedFile.findExistingEditor() ?: createEditor(editedFile.virtualFile))
checkResultByFile(file.relativeTo(testDirectory).path)
@@ -24,11 +24,12 @@ class QuickFixMultiModuleTest : AbstractQuickFixMultiModuleTest() {
private fun doMultiPlatformTest(headerName: String = "header",
implName: String = "jvm",
implKind: TargetPlatformKind<*> = TargetPlatformKind.Jvm[JvmTarget.JVM_1_6]) {
val header = module(headerName)
implKind: TargetPlatformKind<*> = TargetPlatformKind.Jvm[JvmTarget.JVM_1_6],
withTests: Boolean = false) {
val header = module(headerName, hasTestRoot = withTests)
header.createFacet(TargetPlatformKind.Common)
val jvm = module(implName)
val jvm = module(implName, hasTestRoot = withTests)
jvm.createFacet(implKind)
jvm.enableMultiPlatform()
jvm.addDependency(header)
@@ -95,4 +96,9 @@ class QuickFixMultiModuleTest : AbstractQuickFixMultiModuleTest() {
fun testSealed() {
doMultiPlatformTest(implName = "js", implKind = TargetPlatformKind.JavaScript)
}
@Test
fun testWithTest() {
doMultiPlatformTest(headerName = "common", withTests = true)
}
}