Refactor and improve code obtaining actual Java annotation parameter values
Extract Java-specific code into module 'frontend.java' and use already existing JavaAnnotationArgument facilities to determine the default parameter value in an actual Java annotation class. Annotation arguments are not yet supported because to create an instance of AnnotationValue, we need a descriptor, which is not available at this point. #KT-22704 In Progress #KT-28077 Open
This commit is contained in:
+49
-48
@@ -17,10 +17,7 @@
|
||||
package org.jetbrains.kotlin.resolve.checkers
|
||||
|
||||
import com.intellij.openapi.vfs.VfsUtilCore
|
||||
import com.intellij.psi.PsiAnnotationMethod
|
||||
import com.intellij.psi.PsiCall
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiLiteral
|
||||
import org.jetbrains.kotlin.config.AnalysisFlags
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
@@ -30,7 +27,7 @@ import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier
|
||||
import org.jetbrains.kotlin.resolve.*
|
||||
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant
|
||||
import org.jetbrains.kotlin.resolve.constants.ConstantValue
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isAnnotationConstructor
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isPrimaryConstructorOfInlineClass
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
@@ -39,12 +36,15 @@ import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver.Compati
|
||||
import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver.Compatibility.Compatible
|
||||
import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver.Compatibility.Incompatible
|
||||
import org.jetbrains.kotlin.resolve.source.PsiSourceFile
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import org.jetbrains.kotlin.utils.ifEmpty
|
||||
import java.io.File
|
||||
|
||||
object ExpectedActualDeclarationChecker : DeclarationChecker {
|
||||
val OPTIONAL_EXPECTATION_FQ_NAME = FqName("kotlin.OptionalExpectation")
|
||||
class ExpectedActualDeclarationChecker(val argumentExtractors: List<ActualAnnotationArgumentExtractor> = emptyList()) : DeclarationChecker {
|
||||
interface ActualAnnotationArgumentExtractor {
|
||||
fun extractActualValue(argument: PsiElement, expectedType: KotlinType): ConstantValue<*>?
|
||||
}
|
||||
|
||||
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
|
||||
if (!context.languageVersionSettings.supportsFeature(LanguageFeature.MultiPlatformProjects)) return
|
||||
@@ -96,27 +96,6 @@ object ExpectedActualDeclarationChecker : DeclarationChecker {
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun isOptionalAnnotationClass(descriptor: DeclarationDescriptor): Boolean =
|
||||
descriptor is ClassDescriptor &&
|
||||
descriptor.kind == ClassKind.ANNOTATION_CLASS &&
|
||||
descriptor.isExpect &&
|
||||
descriptor.annotations.hasAnnotation(OPTIONAL_EXPECTATION_FQ_NAME)
|
||||
|
||||
// TODO: move to some other place which is accessible both from backend-common and js.serializer
|
||||
@JvmStatic
|
||||
fun shouldGenerateExpectClass(descriptor: ClassDescriptor): Boolean {
|
||||
assert(descriptor.isExpect) { "Not an expected class: $descriptor" }
|
||||
|
||||
if (ExpectedActualDeclarationChecker.isOptionalAnnotationClass(descriptor)) {
|
||||
with(ExpectedActualResolver) {
|
||||
return descriptor.findCompatibleActualForExpected(descriptor.module).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun ExpectActualTracker.reportExpectActual(expected: MemberDescriptor, actualMembers: Sequence<MemberDescriptor>) {
|
||||
if (this is ExpectActualTracker.DoNothing) return
|
||||
|
||||
@@ -133,9 +112,6 @@ object ExpectedActualDeclarationChecker : DeclarationChecker {
|
||||
.safeAs<PsiSourceFile>()
|
||||
?.run { VfsUtilCore.virtualToIoFile(psiFile.virtualFile) }
|
||||
|
||||
fun Map<out Compatibility, Collection<MemberDescriptor>>.allStrongIncompatibilities(): Boolean =
|
||||
this.keys.all { it is Incompatible && it.kind == Compatibility.IncompatibilityKind.STRONG }
|
||||
|
||||
private fun checkActualDeclarationHasExpected(
|
||||
reportOn: KtNamedDeclaration, descriptor: MemberDescriptor, trace: BindingTrace, checkActual: Boolean
|
||||
) {
|
||||
@@ -257,12 +233,9 @@ object ExpectedActualDeclarationChecker : DeclarationChecker {
|
||||
val actualParameter = DescriptorToSourceUtils.descriptorToDeclaration(actualParameterDescriptor)
|
||||
|
||||
val expectedValue = trace.bindingContext.get(BindingContext.COMPILE_TIME_VALUE, expectedParameter.defaultValue)
|
||||
?.toConstantValue(expectedParameterDescriptor.type)
|
||||
|
||||
if (javaDefaultValueEqualsExpectedValue(actualParameter, trace, expectedParameter, expectedValue)) continue
|
||||
|
||||
val actualValue = (actualParameter as? KtParameter)?.let { parameter ->
|
||||
trace.bindingContext.get(BindingContext.COMPILE_TIME_VALUE, parameter.defaultValue)
|
||||
}
|
||||
val actualValue = getActualAnnotationParameterValue(actualParameter, trace.bindingContext, expectedParameterDescriptor.type)
|
||||
if (expectedValue != actualValue) {
|
||||
val target = (actualParameter as? KtParameter)?.defaultValue ?: (reportOn as? KtTypeAlias)?.nameIdentifier ?: reportOn
|
||||
trace.report(Errors.ACTUAL_ANNOTATION_CONFLICTING_DEFAULT_ARGUMENT_VALUE.on(target, actualParameterDescriptor))
|
||||
@@ -271,19 +244,47 @@ object ExpectedActualDeclarationChecker : DeclarationChecker {
|
||||
}
|
||||
}
|
||||
|
||||
private fun javaDefaultValueEqualsExpectedValue(
|
||||
actualParameter: PsiElement?, trace: BindingTrace, expectedParameter: KtParameter, expectedValue: CompileTimeConstant<*>?
|
||||
): Boolean {
|
||||
val actualPsi = (actualParameter as? PsiAnnotationMethod)?.defaultValue ?: return false
|
||||
val expectedType = trace.bindingContext.get(BindingContext.EXPECTED_EXPRESSION_TYPE, expectedParameter.defaultValue) ?: return false
|
||||
val actualValue = when (actualPsi) {
|
||||
is PsiLiteral -> actualPsi.value
|
||||
is PsiCall -> {
|
||||
//TODO: arrays and annotations
|
||||
return false
|
||||
}
|
||||
else -> return false
|
||||
private fun getActualAnnotationParameterValue(
|
||||
actualParameter: PsiElement?, bindingContext: BindingContext, expectedType: KotlinType
|
||||
): ConstantValue<*>? {
|
||||
if (actualParameter is KtParameter) {
|
||||
return bindingContext.get(BindingContext.COMPILE_TIME_VALUE, actualParameter.defaultValue)?.toConstantValue(expectedType)
|
||||
}
|
||||
return (actualValue == expectedValue?.getValue(expectedType))
|
||||
|
||||
if (actualParameter != null) {
|
||||
for (extractor in argumentExtractors) {
|
||||
extractor.extractActualValue(actualParameter, expectedType)?.let { return it }
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
companion object {
|
||||
val OPTIONAL_EXPECTATION_FQ_NAME = FqName("kotlin.OptionalExpectation")
|
||||
|
||||
@JvmStatic
|
||||
fun isOptionalAnnotationClass(descriptor: DeclarationDescriptor): Boolean =
|
||||
descriptor is ClassDescriptor &&
|
||||
descriptor.kind == ClassKind.ANNOTATION_CLASS &&
|
||||
descriptor.isExpect &&
|
||||
descriptor.annotations.hasAnnotation(OPTIONAL_EXPECTATION_FQ_NAME)
|
||||
|
||||
// TODO: move to some other place which is accessible both from backend-common and js.serializer
|
||||
@JvmStatic
|
||||
fun shouldGenerateExpectClass(descriptor: ClassDescriptor): Boolean {
|
||||
assert(descriptor.isExpect) { "Not an expected class: $descriptor" }
|
||||
|
||||
if (isOptionalAnnotationClass(descriptor)) {
|
||||
with(ExpectedActualResolver) {
|
||||
return descriptor.findCompatibleActualForExpected(descriptor.module).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
fun Map<out Compatibility, Collection<MemberDescriptor>>.allStrongIncompatibilities(): Boolean =
|
||||
this.keys.all { it is Incompatible && it.kind == Compatibility.IncompatibilityKind.STRONG }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user