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

arrow.meta.phases.analysis.KtUtils.kt Maven / Gradle / Ivy

There is a newer version: 1.6.3-alpha.2
Show newest version
package arrow.meta.phases.analysis

import arrow.meta.ArrowMetaConfigurationKeys
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType
import org.jetbrains.kotlin.builtins.isBuiltinFunctionalType
import org.jetbrains.kotlin.com.intellij.psi.PsiElement
import org.jetbrains.kotlin.com.intellij.psi.SyntaxTraverser
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
import org.jetbrains.kotlin.platform.CommonPlatforms
import org.jetbrains.kotlin.psi.KtAnnotated
import org.jetbrains.kotlin.psi.KtBlockExpression
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtDeclarationWithBody
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtObjectDeclaration
import org.jetbrains.kotlin.psi.KtReturnExpression
import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
import org.jetbrains.kotlin.psi.psiUtil.astReplace
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.platform
import org.jetbrains.kotlin.resolve.multiplatform.isCommonSource
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeConstructor
import org.jetbrains.kotlin.types.TypeProjection
import org.jetbrains.kotlin.utils.addToStdlib.safeAs

fun KtDeclarationWithBody.body(): KtExpression? = bodyExpression ?: bodyBlockExpression

fun KtExpression.bodySourceAsExpression(): String? =
  when (this) {
    is KtBlockExpression ->
      statements
        .map {
          when (it) {
            is KtReturnExpression -> it.returnedExpression?.text
            else -> text
          }
        }
        .joinToString("\n")
        .drop(1)
        .dropLast(1)
    else -> text
  }

fun KtElement.transform(f: (KtElement) -> KtElement?): KtElement {
  accept(
    object : KtTreeVisitorVoid() {
      override fun visitKtElement(element: KtElement) {
        val result = f(element)
        if (result != null) {
          element.astReplace(result)
        }
        super.visitKtElement(element)
      }
    }
  )
  return this
}

fun KtElement.dfs(f: (KtElement) -> Boolean): List {
  val found = arrayListOf()
  accept(
    object : KtTreeVisitorVoid() {
      override fun visitKtElement(element: KtElement) {
        val result = f(element)
        if (result) found.add(element)
        super.visitKtElement(element)
      }
    }
  )
  return found
}

/**
 * collects all distinct witnesses of [f] from the receiver, where the return type contains pairs of
 * [A] and a list of other corresponding elements that full fill f
 */
fun  List.exists(f: (A, A) -> Boolean): List>> =
  fold(emptyList()) { acc: List>>, a: A ->
    acc + (a to filter { b: A -> if (a != b) f(a, b) else false })
  }

/**
 * traverse and filters starting from the root node [this] down to all it's children and applying
 * [f]
 */
fun  PsiElement.traverseFilter(on: Class, f: (A) -> B?): List =
  SyntaxTraverser.psiTraverser(this).filter(on).mapNotNull(f).toList()

/**
 * a convenient function that collects all child nodes [A] starting from [this] it applies
 * [traverseFilter] with the identity function
 */
fun  PsiElement.sequence(on: Class): List = traverseFilter(on) { it }

interface Eq { // from arrow
  fun A.eqv(other: A): Boolean
  fun A.neqv(other: A): Boolean = !eqv(other)

  companion object {
    inline operator fun  invoke(crossinline feqv: (A, A) -> Boolean): Eq =
      object : Eq {
        override fun A.eqv(other: A): Boolean = feqv(this, other)
      }
  }
}

/** defines Equality on the type constructor */
fun typeConstructorEq(): Eq = Eq { t1, t2 -> t1.constructor == t2.constructor }

/** defines Equality on types, where FunctionTypes are reduced to their return type */
fun resolveFunctionTypeEq(): Eq = Eq { t1, t2 ->
  resolveFunctionType(t1) == resolveFunctionType(t2)
}

/**
 * Given [eq] this function returns [KotlinType]s that [intersect] with the returnType from the list
 * in [types]. One concrete example for equality on [TypeConstructor] may look like this:
 * ```kotlin:ank
 * import org.jetbrains.kotlin.descriptors.CallableDescriptor
 * import org.jetbrains.kotlin.builtins.KotlinBuiltIns
 * import org.jetbrains.kotlin.types.KotlinType
 * import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
 * import arrow.meta.phases.analysis.Eq
 * import arrow.meta.phases.analysis.intersect
 * //sampleStart
 *
 * fun typeConstructorEq(): Eq =
 *  Eq { t1, t2 ->
 *   t1.constructor == t2.constructor
 *  }
 *
 * /**
 * * this function yields true if the type constructor - short [TC] - of the returnType in F is equal to one TC in [types]
 * */
 * fun  F.returns( //
 *   types: KotlinBuiltIns.() -> List
 * ): Boolean =
 *   intersect(typeConstructorEq(), types).isNotEmpty()
 * //sampleEnd
 * ```
 * @see [org.jetbrains.kotlin.types.TypeUtils] for more abstractions
 * @param eq can be define for e.g.: [TypeConstructor], [MemberScope] or typeArguments List<
 * [TypeProjection]>, etc.
 * @see functionTypeEq
 */
fun  C.intersect(
  eq: Eq,
  types: KotlinBuiltIns.() -> List
): List =
  eq.run {
    returnType?.let { result: KotlinType -> builtIns.types().filter { it.eqv(result) } }
      ?: emptyList()
  }

/**
 * given [eq] this function returns a List of [KotlinType] that are contained by both [list] and
 * [other]
 * @param eq can be defined for [TypeConstructor], [MemberScope] or typeArguments List<
 * [TypeProjection]>, etc.
 * @see intersect
 */
fun  D.intersect(
  eq: Eq,
  list: List,
  other: KotlinBuiltIns.() -> List
): List =
  eq.run {
    val set = list.toMutableList()
    set.retainAll { t1 -> builtIns.other().any { t2 -> t1.eqv(t2) } }
    set.toList()
  }

/** resolves FunctionType to it's returnType */
val resolveFunctionType: (KotlinType) -> KotlinType
  get() = { if (it.isBuiltinFunctionalType) it.getReturnTypeFromFunctionType() else it }

/** naive type equality where function types are reduced to their return type */
val returnTypeEq: Eq
  get() = Eq { a, b -> resolveFunctionType(a) == resolveFunctionType(b) }

fun KtAnnotated.isAnnotatedWith(regex: Regex): Boolean =
  annotationEntries.any { it.text.matches(regex) }

val KtClass.companionObject: KtObjectDeclaration?
  get() =
    declarations
      .singleOrNull { it.safeAs()?.isCompanion() == true }
      .safeAs()

fun DeclarationDescriptor.skipGeneration(): Boolean =
  platform != CommonPlatforms.defaultCommonPlatform &&
    (findPsi()?.containingFile as? KtFile)?.isCommonSource == true

fun getOrCreateBaseDirectory(configuration: CompilerConfiguration?): java.io.File {
  val parentBuildPath: String? =
    configuration?.get(ArrowMetaConfigurationKeys.GENERATED_SRC_OUTPUT_DIR)?.firstOrNull()
      ?: System.getProperty("arrow.meta.generate.source.dir")
  checkNotNull(parentBuildPath) {
    "Generated sources output dir is not found: ${configuration?.get(ArrowMetaConfigurationKeys.GENERATED_SRC_OUTPUT_DIR)} ${System.getProperty("arrow.meta.generate.source.dir")}"
  }
  val directory = java.io.File("$parentBuildPath")
  directory.mkdirs()
  return directory
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy