Introduce OptionalExpectationInspection
It works at INFORMATION level only and is intended to form quick-fixes to add relevant actual declarations. #KT-25533 Fixed
This commit is contained in:
+2
-2
@@ -40,7 +40,7 @@ import org.jetbrains.kotlin.utils.ifEmpty
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
object ExpectedActualDeclarationChecker : DeclarationChecker {
|
object ExpectedActualDeclarationChecker : DeclarationChecker {
|
||||||
internal val OPTIONAL_EXPECTATION_FQ_NAME = FqName("kotlin.OptionalExpectation")
|
val OPTIONAL_EXPECTATION_FQ_NAME = FqName("kotlin.OptionalExpectation")
|
||||||
|
|
||||||
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
|
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
|
||||||
if (!context.languageVersionSettings.supportsFeature(LanguageFeature.MultiPlatformProjects)) return
|
if (!context.languageVersionSettings.supportsFeature(LanguageFeature.MultiPlatformProjects)) return
|
||||||
@@ -129,7 +129,7 @@ object ExpectedActualDeclarationChecker : DeclarationChecker {
|
|||||||
.safeAs<PsiSourceFile>()
|
.safeAs<PsiSourceFile>()
|
||||||
?.run { VfsUtilCore.virtualToIoFile(psiFile.virtualFile) }
|
?.run { VfsUtilCore.virtualToIoFile(psiFile.virtualFile) }
|
||||||
|
|
||||||
private fun Map<out Compatibility, Collection<MemberDescriptor>>.allStrongIncompatibilities(): Boolean =
|
fun Map<out Compatibility, Collection<MemberDescriptor>>.allStrongIncompatibilities(): Boolean =
|
||||||
this.keys.all { it is Incompatible && it.kind == Compatibility.IncompatibilityKind.STRONG }
|
this.keys.all { it is Incompatible && it.kind == Compatibility.IncompatibilityKind.STRONG }
|
||||||
|
|
||||||
private fun checkActualDeclarationHasExpected(
|
private fun checkActualDeclarationHasExpected(
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
This special inspection reports optionally expected annotations without actual annotation in some platform modules.
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -3057,6 +3057,15 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
|
|||||||
language="kotlin"
|
language="kotlin"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.OptionalExpectationInspection"
|
||||||
|
displayName="Optionally expected annotation has no actual annotation"
|
||||||
|
groupPath="Kotlin"
|
||||||
|
groupName="Style issues"
|
||||||
|
enabledByDefault="true"
|
||||||
|
level="INFORMATION"
|
||||||
|
language="kotlin"
|
||||||
|
/>
|
||||||
|
|
||||||
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
|
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
|
||||||
|
|
||||||
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
|
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
|
||||||
|
|||||||
@@ -3055,6 +3055,15 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
|
|||||||
language="kotlin"
|
language="kotlin"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.OptionalExpectationInspection"
|
||||||
|
displayName="Optionally expected annotation has no actual annotation"
|
||||||
|
groupPath="Kotlin"
|
||||||
|
groupName="Style issues"
|
||||||
|
enabledByDefault="true"
|
||||||
|
level="INFORMATION"
|
||||||
|
language="kotlin"
|
||||||
|
/>
|
||||||
|
|
||||||
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
|
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
|
||||||
|
|
||||||
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
|
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
|
||||||
|
|||||||
@@ -3056,6 +3056,15 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
|
|||||||
language="kotlin"
|
language="kotlin"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.OptionalExpectationInspection"
|
||||||
|
displayName="Optionally expected annotation has no actual annotation"
|
||||||
|
groupPath="Kotlin"
|
||||||
|
groupName="Style issues"
|
||||||
|
enabledByDefault="true"
|
||||||
|
level="INFORMATION"
|
||||||
|
language="kotlin"
|
||||||
|
/>
|
||||||
|
|
||||||
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
|
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
|
||||||
|
|
||||||
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
|
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
|
||||||
|
|||||||
@@ -3057,6 +3057,15 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
|
|||||||
language="kotlin"
|
language="kotlin"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.OptionalExpectationInspection"
|
||||||
|
displayName="Optionally expected annotation has no actual annotation"
|
||||||
|
groupPath="Kotlin"
|
||||||
|
groupName="Style issues"
|
||||||
|
enabledByDefault="true"
|
||||||
|
level="INFORMATION"
|
||||||
|
language="kotlin"
|
||||||
|
/>
|
||||||
|
|
||||||
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
|
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
|
||||||
|
|
||||||
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
|
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
|
||||||
|
|||||||
@@ -3055,6 +3055,15 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
|
|||||||
language="kotlin"
|
language="kotlin"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.OptionalExpectationInspection"
|
||||||
|
displayName="Optionally expected annotation has no actual annotation"
|
||||||
|
groupPath="Kotlin"
|
||||||
|
groupName="Style issues"
|
||||||
|
enabledByDefault="true"
|
||||||
|
level="INFORMATION"
|
||||||
|
language="kotlin"
|
||||||
|
/>
|
||||||
|
|
||||||
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
|
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
|
||||||
|
|
||||||
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
|
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
|
||||||
|
|||||||
@@ -3055,6 +3055,15 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
|
|||||||
language="kotlin"
|
language="kotlin"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.OptionalExpectationInspection"
|
||||||
|
displayName="Optionally expected annotation has no actual annotation"
|
||||||
|
groupPath="Kotlin"
|
||||||
|
groupName="Style issues"
|
||||||
|
enabledByDefault="true"
|
||||||
|
level="INFORMATION"
|
||||||
|
language="kotlin"
|
||||||
|
/>
|
||||||
|
|
||||||
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
|
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
|
||||||
|
|
||||||
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
|
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
|
||||||
|
|||||||
@@ -3057,6 +3057,15 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
|
|||||||
language="kotlin"
|
language="kotlin"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.OptionalExpectationInspection"
|
||||||
|
displayName="Optionally expected annotation has no actual annotation"
|
||||||
|
groupPath="Kotlin"
|
||||||
|
groupName="Style issues"
|
||||||
|
enabledByDefault="true"
|
||||||
|
level="INFORMATION"
|
||||||
|
language="kotlin"
|
||||||
|
/>
|
||||||
|
|
||||||
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
|
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
|
||||||
|
|
||||||
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
|
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||||
|
* that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.inspections
|
||||||
|
|
||||||
|
import com.intellij.codeInspection.IntentionWrapper
|
||||||
|
import com.intellij.codeInspection.LocalInspectionToolSession
|
||||||
|
import com.intellij.codeInspection.ProblemHighlightType
|
||||||
|
import com.intellij.codeInspection.ProblemsHolder
|
||||||
|
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||||
|
import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
|
||||||
|
import org.jetbrains.kotlin.idea.caches.project.implementingDescriptors
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.expectactual.CreateActualClassFix
|
||||||
|
import org.jetbrains.kotlin.psi.KtClass
|
||||||
|
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||||
|
import org.jetbrains.kotlin.psi.classOrObjectVisitor
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier
|
||||||
|
import org.jetbrains.kotlin.resolve.MultiTargetPlatform
|
||||||
|
import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker
|
||||||
|
import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker.allStrongIncompatibilities
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||||
|
import org.jetbrains.kotlin.resolve.getMultiTargetPlatform
|
||||||
|
import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver
|
||||||
|
|
||||||
|
class OptionalExpectationInspection : AbstractKotlinInspection() {
|
||||||
|
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) =
|
||||||
|
classOrObjectVisitor(fun(classOrObject: KtClassOrObject) {
|
||||||
|
if (classOrObject !is KtClass || !classOrObject.isAnnotation()) return
|
||||||
|
if (!classOrObject.hasExpectModifier()) return
|
||||||
|
|
||||||
|
val descriptor = classOrObject.resolveToDescriptorIfAny() ?: return
|
||||||
|
if (!descriptor.annotations.hasAnnotation(ExpectedActualDeclarationChecker.OPTIONAL_EXPECTATION_FQ_NAME)) return
|
||||||
|
|
||||||
|
val implementingModules = classOrObject.findModuleDescriptor().implementingDescriptors
|
||||||
|
if (implementingModules.isEmpty()) return
|
||||||
|
|
||||||
|
for (actualModuleDescriptor in implementingModules) {
|
||||||
|
val compatibility = ExpectedActualResolver.findActualForExpected(descriptor, actualModuleDescriptor) ?: continue
|
||||||
|
if (!compatibility.allStrongIncompatibilities() &&
|
||||||
|
(ExpectedActualResolver.Compatibility.Compatible in compatibility ||
|
||||||
|
!compatibility.values.flatMapTo(
|
||||||
|
hashSetOf()
|
||||||
|
) { it }.all { actual ->
|
||||||
|
val expectedOnes = ExpectedActualResolver.findExpectedForActual(actual, descriptor.module)
|
||||||
|
expectedOnes != null && ExpectedActualResolver.Compatibility.Compatible in expectedOnes.keys
|
||||||
|
})
|
||||||
|
) continue
|
||||||
|
val platform = (actualModuleDescriptor.getMultiTargetPlatform() as? MultiTargetPlatform.Specific) ?: continue
|
||||||
|
val displayedName = actualModuleDescriptor.getCapability(ModuleInfo.Capability)?.displayedName ?: ""
|
||||||
|
val actualModule = (actualModuleDescriptor.getCapability(ModuleInfo.Capability) as? ModuleSourceInfo)?.module ?: continue
|
||||||
|
holder.registerProblem(
|
||||||
|
classOrObject.nameIdentifier ?: classOrObject,
|
||||||
|
"Optionally expected annotation has no actual annotation in module $displayedName for platform ${platform.platform}",
|
||||||
|
// NB: some highlighting is not suggested for this inspection
|
||||||
|
ProblemHighlightType.INFORMATION,
|
||||||
|
IntentionWrapper(CreateActualClassFix(classOrObject, actualModule, platform), classOrObject.containingFile)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -43,10 +43,13 @@ import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
|||||||
import org.jetbrains.kotlin.idea.util.actualsForExpected
|
import org.jetbrains.kotlin.idea.util.actualsForExpected
|
||||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier
|
import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.MultiTargetPlatform
|
import org.jetbrains.kotlin.resolve.MultiTargetPlatform
|
||||||
|
import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker
|
||||||
|
import org.jetbrains.kotlin.resolve.checkers.ExperimentalUsageChecker
|
||||||
import org.jetbrains.kotlin.resolve.getMultiTargetPlatform
|
import org.jetbrains.kotlin.resolve.getMultiTargetPlatform
|
||||||
|
|
||||||
sealed class CreateActualFix<out D : KtNamedDeclaration>(
|
sealed class CreateActualFix<out D : KtNamedDeclaration>(
|
||||||
@@ -280,6 +283,14 @@ internal fun KtPsiFactory.generateClassOrObjectByExpectedClass(
|
|||||||
actualEntry.replace(createSuperTypeCallEntry("${actualEntry.typeReference!!.text}()"))
|
actualEntry.replace(createSuperTypeCallEntry("${actualEntry.typeReference!!.text}()"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (actualClass.isAnnotation()) {
|
||||||
|
actualClass.annotationEntries.zip(expectedClass.annotationEntries).forEach { (actualEntry, expectedEntry) ->
|
||||||
|
val annotationDescriptor = context.get(BindingContext.ANNOTATION, expectedEntry) ?: return@forEach
|
||||||
|
if (annotationDescriptor.fqName in forbiddenAnnotationFqNames) {
|
||||||
|
actualEntry.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
declLoop@ for (expectedDeclaration in expectedClass.declarations.filter { !it.exists() }) {
|
declLoop@ for (expectedDeclaration in expectedClass.declarations.filter { !it.exists() }) {
|
||||||
val descriptor = expectedDeclaration.toDescriptor() ?: continue
|
val descriptor = expectedDeclaration.toDescriptor() ?: continue
|
||||||
@@ -319,6 +330,12 @@ internal fun KtPsiFactory.generateClassOrObjectByExpectedClass(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val forbiddenAnnotationFqNames = setOf(
|
||||||
|
ExpectedActualDeclarationChecker.OPTIONAL_EXPECTATION_FQ_NAME,
|
||||||
|
FqName("kotlin.ExperimentalMultiplatform"),
|
||||||
|
ExperimentalUsageChecker.USE_EXPERIMENTAL_FQ_NAME
|
||||||
|
)
|
||||||
|
|
||||||
private fun KtPsiFactory.generateFunction(
|
private fun KtPsiFactory.generateFunction(
|
||||||
project: Project,
|
project: Project,
|
||||||
expectedFunction: KtFunction,
|
expectedFunction: KtFunction,
|
||||||
|
|||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
// "Create actual annotation class for module testModule_JVM (JVM)" "true"
|
||||||
|
// TOOL: org.jetbrains.kotlin.idea.inspections.OptionalExpectationInspection
|
||||||
|
|
||||||
|
package kotlin
|
||||||
|
|
||||||
|
annotation class ExperimentalMultiplatform
|
||||||
|
annotation class OptionalExpectation
|
||||||
|
|
||||||
|
@ExperimentalMultiplatform
|
||||||
|
@OptionalExpectation
|
||||||
|
expect annotation class <caret>Ann
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
// "Create actual annotation class for module testModule_JVM (JVM)" "true"
|
||||||
|
// TOOL: org.jetbrains.kotlin.idea.inspections.OptionalExpectationInspection
|
||||||
|
|
||||||
|
package kotlin
|
||||||
|
|
||||||
|
annotation class ExperimentalMultiplatform
|
||||||
|
annotation class OptionalExpectation
|
||||||
|
|
||||||
|
@ExperimentalMultiplatform
|
||||||
|
@OptionalExpectation
|
||||||
|
expect annotation class <caret>Ann
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
// Ann: to be implemented
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
package kotlin
|
||||||
|
|
||||||
|
// Ann: to be implemented
|
||||||
|
actual
|
||||||
|
annotation class Ann
|
||||||
+5
@@ -74,6 +74,11 @@ public class QuickFixMultiModuleTestGenerated extends AbstractQuickFixMultiModul
|
|||||||
runTest("idea/testData/multiModuleQuickFix/annotation/");
|
runTest("idea/testData/multiModuleQuickFix/annotation/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("annotationOptionalExpectation")
|
||||||
|
public void testAnnotationOptionalExpectation() throws Exception {
|
||||||
|
runTest("idea/testData/multiModuleQuickFix/annotationOptionalExpectation/");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("class")
|
@TestMetadata("class")
|
||||||
public void testClass() throws Exception {
|
public void testClass() throws Exception {
|
||||||
runTest("idea/testData/multiModuleQuickFix/class/");
|
runTest("idea/testData/multiModuleQuickFix/class/");
|
||||||
|
|||||||
Reference in New Issue
Block a user