All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.load.java.AnnotationTypeQualifierResolver.kt Maven / Gradle / Ivy

There is a newer version: 2.0.0
Show newest version
/*
 * Copyright 2010-2017 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jetbrains.kotlin.load.java

import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.load.java.components.JavaAnnotationTargetMapper
import org.jetbrains.kotlin.resolve.constants.ArrayValue
import org.jetbrains.kotlin.resolve.constants.ConstantValue
import org.jetbrains.kotlin.resolve.constants.EnumValue
import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
import org.jetbrains.kotlin.resolve.descriptorUtil.firstArgument
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.utils.JavaTypeEnhancementState
import org.jetbrains.kotlin.utils.ReportLevel
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult

class AnnotationTypeQualifierResolver(storageManager: StorageManager, private val javaTypeEnhancementState: JavaTypeEnhancementState) {
    class TypeQualifierWithApplicability(
        private val typeQualifier: AnnotationDescriptor,
        private val applicability: Int
    ) {
        operator fun component1() = typeQualifier
        operator fun component2() = AnnotationQualifierApplicabilityType.values().filter(this::isApplicableTo)

        private fun isApplicableTo(elementType: AnnotationQualifierApplicabilityType): Boolean {
            if (isApplicableConsideringMask(elementType)) return true

            // We explicitly state that while JSR-305 TYPE_USE annotations effectively should be applied to every type
            // they are not applicable for type parameter bounds because it would be a breaking change otherwise.
            // Only defaulting annotations from jspecify are applicable
            return isApplicableConsideringMask(AnnotationQualifierApplicabilityType.TYPE_USE) &&
                    elementType != AnnotationQualifierApplicabilityType.TYPE_PARAMETER_BOUNDS
        }

        private fun isApplicableConsideringMask(elementType: AnnotationQualifierApplicabilityType) =
            (applicability and (1 shl elementType.ordinal)) != 0
    }

    private val resolvedNicknames =
        storageManager.createMemoizedFunctionWithNullableValues(this::computeTypeQualifierNickname)

    private fun computeTypeQualifierNickname(classDescriptor: ClassDescriptor): AnnotationDescriptor? {
        if (!classDescriptor.annotations.hasAnnotation(TYPE_QUALIFIER_NICKNAME_FQNAME)) return null

        return classDescriptor.annotations.firstNotNullResult(this::resolveTypeQualifierAnnotation)
    }

    private fun resolveTypeQualifierNickname(classDescriptor: ClassDescriptor): AnnotationDescriptor? {
        if (classDescriptor.kind != ClassKind.ANNOTATION_CLASS) return null

        return resolvedNicknames(classDescriptor)
    }

    fun resolveTypeQualifierAnnotation(annotationDescriptor: AnnotationDescriptor): AnnotationDescriptor? {
        if (javaTypeEnhancementState.disabledJsr305) {
            return null
        }

        val annotationClass = annotationDescriptor.annotationClass ?: return null
        if (annotationClass.isAnnotatedWithTypeQualifier) return annotationDescriptor

        return resolveTypeQualifierNickname(annotationClass)
    }

    fun resolveQualifierBuiltInDefaultAnnotation(annotationDescriptor: AnnotationDescriptor): JavaDefaultQualifiers? {
        if (javaTypeEnhancementState.disabledDefaultAnnotations) {
            return null
        }

        return BUILT_IN_TYPE_QUALIFIER_DEFAULT_ANNOTATIONS[annotationDescriptor.fqName]?.let { qualifierForDefaultingAnnotation ->
            val state = resolveDefaultAnnotationState(annotationDescriptor).takeIf { it != ReportLevel.IGNORE } ?: return null
            qualifierForDefaultingAnnotation.copy(
                nullabilityQualifier = qualifierForDefaultingAnnotation.nullabilityQualifier.copy(isForWarningOnly = state.isWarning)
            )
        }
    }

    private fun resolveDefaultAnnotationState(annotationDescriptor: AnnotationDescriptor): ReportLevel {
        if (annotationDescriptor.fqName in JSPECIFY_DEFAULT_ANNOTATIONS) {
            return javaTypeEnhancementState.jspecifyReportLevel
        }

        return resolveJsr305AnnotationState(annotationDescriptor)
    }

    fun resolveTypeQualifierDefaultAnnotation(annotationDescriptor: AnnotationDescriptor): TypeQualifierWithApplicability? {
        if (javaTypeEnhancementState.disabledJsr305) {
            return null
        }

        val typeQualifierDefaultAnnotatedClass =
            annotationDescriptor.annotationClass?.takeIf { it.annotations.hasAnnotation(TYPE_QUALIFIER_DEFAULT_FQNAME) }
                ?: return null

        val elementTypesMask =
            annotationDescriptor.annotationClass!!
                .annotations.findAnnotation(TYPE_QUALIFIER_DEFAULT_FQNAME)!!
                .allValueArguments
                .flatMap { (parameter, argument) ->
                    if (parameter == JvmAnnotationNames.DEFAULT_ANNOTATION_MEMBER_NAME)
                        argument.mapJavaConstantToQualifierApplicabilityTypes()
                    else
                        emptyList()
                }
                .fold(0) { acc: Int, applicabilityType -> acc or (1 shl applicabilityType.ordinal) }

        val typeQualifier = typeQualifierDefaultAnnotatedClass.annotations.firstOrNull { resolveTypeQualifierAnnotation(it) != null }
            ?: return null

        return TypeQualifierWithApplicability(typeQualifier, elementTypesMask)
    }

    fun resolveAnnotation(annotationDescriptor: AnnotationDescriptor): TypeQualifierWithApplicability? {
        val annotatedClass = annotationDescriptor.annotationClass ?: return null
        val target = annotatedClass.annotations.findAnnotation(JvmAnnotationNames.TARGET_ANNOTATION) ?: return null
        val elementTypesMask = target.allValueArguments
            .flatMap { (_, argument) -> argument.mapKotlinConstantToQualifierApplicabilityTypes() }
            .fold(0) { acc: Int, applicabilityType -> acc or (1 shl applicabilityType.ordinal) }

        return TypeQualifierWithApplicability(annotationDescriptor, elementTypesMask)
    }

    fun resolveJsr305AnnotationState(annotationDescriptor: AnnotationDescriptor): ReportLevel {
        resolveJsr305CustomState(annotationDescriptor)?.let { return it }
        return javaTypeEnhancementState.globalJsr305Level
    }

    fun resolveJsr305CustomState(annotationDescriptor: AnnotationDescriptor): ReportLevel? {
        javaTypeEnhancementState.userDefinedLevelForSpecificJsr305Annotation[annotationDescriptor.fqName?.asString()]?.let { return it }
        return annotationDescriptor.annotationClass?.migrationAnnotationStatus()
    }

    private fun ClassDescriptor.migrationAnnotationStatus(): ReportLevel? {
        val enumValue = annotations.findAnnotation(MIGRATION_ANNOTATION_FQNAME)?.firstArgument() as? EnumValue
            ?: return null

        javaTypeEnhancementState.migrationLevelForJsr305?.let { return it }

        return when (enumValue.enumEntryName.asString()) {
            "STRICT" -> ReportLevel.STRICT
            "WARN" -> ReportLevel.WARN
            "IGNORE" -> ReportLevel.IGNORE
            else -> null
        }
    }

    private fun String.toKotlinTargetNames() = JavaAnnotationTargetMapper.mapJavaTargetArgumentByName(this).map { it.name }

    private fun ConstantValue<*>.mapConstantToQualifierApplicabilityTypes(
        findPredicate: EnumValue.(AnnotationQualifierApplicabilityType) -> Boolean
    ): List =
        when (this) {
            is ArrayValue -> value.flatMap { it.mapConstantToQualifierApplicabilityTypes(findPredicate) }
            is EnumValue -> listOfNotNull(AnnotationQualifierApplicabilityType.values().find { findPredicate(it) })
            else -> emptyList()
        }

    private fun ConstantValue<*>.mapJavaConstantToQualifierApplicabilityTypes(): List =
        mapConstantToQualifierApplicabilityTypes { enumEntryName.identifier == it.javaTarget }

    private fun ConstantValue<*>.mapKotlinConstantToQualifierApplicabilityTypes(): List =
        mapConstantToQualifierApplicabilityTypes { enumEntryName.identifier in it.javaTarget.toKotlinTargetNames() }
}

private val ClassDescriptor.isAnnotatedWithTypeQualifier: Boolean
    get() = fqNameSafe in BUILT_IN_TYPE_QUALIFIER_FQ_NAMES || annotations.hasAnnotation(TYPE_QUALIFIER_FQNAME)




© 2015 - 2024 Weber Informatics LLC | Privacy Policy