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
|
||||
|
||||
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) {
|
||||
if (!context.languageVersionSettings.supportsFeature(LanguageFeature.MultiPlatformProjects)) return
|
||||
@@ -129,7 +129,7 @@ object ExpectedActualDeclarationChecker : DeclarationChecker {
|
||||
.safeAs<PsiSourceFile>()
|
||||
?.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 }
|
||||
|
||||
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"
|
||||
/>
|
||||
|
||||
<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"/>
|
||||
|
||||
<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"
|
||||
/>
|
||||
|
||||
<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"/>
|
||||
|
||||
<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"
|
||||
/>
|
||||
|
||||
<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"/>
|
||||
|
||||
<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"
|
||||
/>
|
||||
|
||||
<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"/>
|
||||
|
||||
<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"
|
||||
/>
|
||||
|
||||
<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"/>
|
||||
|
||||
<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"
|
||||
/>
|
||||
|
||||
<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"/>
|
||||
|
||||
<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"
|
||||
/>
|
||||
|
||||
<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"/>
|
||||
|
||||
<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.application.runWriteAction
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
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
|
||||
|
||||
sealed class CreateActualFix<out D : KtNamedDeclaration>(
|
||||
@@ -280,6 +283,14 @@ internal fun KtPsiFactory.generateClassOrObjectByExpectedClass(
|
||||
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() }) {
|
||||
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(
|
||||
project: Project,
|
||||
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/");
|
||||
}
|
||||
|
||||
@TestMetadata("annotationOptionalExpectation")
|
||||
public void testAnnotationOptionalExpectation() throws Exception {
|
||||
runTest("idea/testData/multiModuleQuickFix/annotationOptionalExpectation/");
|
||||
}
|
||||
|
||||
@TestMetadata("class")
|
||||
public void testClass() throws Exception {
|
||||
runTest("idea/testData/multiModuleQuickFix/class/");
|
||||
|
||||
Reference in New Issue
Block a user