<commit_before>package scala.meta.trees

import com.intellij.psi._
import org.jetbrains.plugins.scala.lang.psi.api.base._
import org.jetbrains.plugins.scala.lang.psi.api.base.types._
import org.jetbrains.plugins.scala.lang.psi.api.statements.params.ScParameter
import org.jetbrains.plugins.scala.lang.psi.api.statements.{ScFunction, ScFunctionDefinition, ScTypeAliasDeclaration}
import org.jetbrains.plugins.scala.lang.psi.api.toplevel._
import org.jetbrains.plugins.scala.lang.psi.types.api.TypeParameterType
import org.jetbrains.plugins.scala.lang.psi.types.result.{Failure, Success, TypeResult, TypingContext}
import org.jetbrains.plugins.scala.lang.psi.types.{Compatibility, ScSubstitutor, ScTypePsiTypeBridge}
import org.jetbrains.plugins.scala.lang.psi.{api => p, types => ptype}
import org.jetbrains.plugins.scala.lang.refactoring.util.ScTypeUtil.AliasType
import org.scalameta.collections._

import scala.collection.immutable.Seq
import scala.language.postfixOps
import scala.meta.internal.{ast => m, semantic => h}
import scala.meta.trees.error._
import scala.{Seq => _}

trait TypeAdapter {
  self: TreeConverter =>

  private val typeCache = TwoWayCache[ptype.ScType, m.Type]()
  private val typeElementCache = TwoWayCache[ScTypeElement, m.Type]()
  private val psiElementTypeChache = TwoWayCache[PsiElement, m.Type]()

  def toType(tp: ScTypeElement): m.Type = {
    typeElementCache.getOrElseUpdate(tp, {
      tp match {
        case t: ScSimpleTypeElement =>
          val s = new ScSubstitutor(ScSubstitutor.cache.toMap, Map(), None)
          toType(s.subst(t.calcType))
        case t: ScFunctionalTypeElement =>
          toType(t.paramTypeElement) match {
            case m.Type.Tuple(elements) => m.Type.Function(elements, toType(t.returnTypeElement.get))
            case param => m.Type.Function(Seq(param), toType(t.returnTypeElement.get))
          }
        case t: ScParameterizedTypeElement =>
          m.Type.Apply(toType(t.typeElement.calcType), t.typeArgList.typeArgs.toStream.map(toType))
        case t: ScTupleTypeElement =>
          m.Type.Tuple(Seq(t.components.map(toType): _*))
        case t: ScWildcardTypeElement =>
          m.Type.Placeholder(typeBounds(t))
        case t: ScParenthesisedTypeElement =>
          t.typeElement match {
            case Some(t: ScReferenceableInfixTypeElement) => m.Type.ApplyInfix(toType(t.leftTypeElement), toTypeName(t.reference), toType(t.rightTypeElement.get))
            case _ => unreachable
          }
        case t: ScTypeVariableTypeElement => die("i cannot into type variables")
        case t: ScExistentialTypeElement =>
          val clauses = Seq(t.clause.declarations map {
            _ match {
              case tp: ScTypeAliasDeclaration => toTypeDecl(tp)
              case other => other ?!
            }
          } : _*)
          val quantified = toType(t.quantified)
          m.Type.Existential(quantified, clauses)
        case other: ScTypeElement =>
          LOG.warn(s"Using slow type conversion of type element ${other.getClass}: ${other.getText}")
          toType(other.getType())
        case other => other ?!
      }
    })
  }

  def toType(tr: TypeResult[ptype.ScType]): m.Type = {
    import org.jetbrains.plugins.scala.lang.psi.types.result._
    tr match {
      case Success(res, _) => toType(res)
      case Failure(cause, place) => throw new ScalaMetaTypeResultFailure(place, cause)
    }
  }

  def toType(elem: PsiElement): m.Type = {
    psiElementTypeChache.getOrElseUpdate(elem, {
      elem match {
        case t: typedef.ScTemplateDefinition =>
          val s = new ScSubstitutor(ScSubstitutor.cache.toMap, Map(), None)
          toType(s.subst(t.getType(TypingContext.empty).get)) // FIXME: what about typing context?
        case t: packaging.ScPackaging =>
          m.Type.Singleton(toTermName(t.reference.get)).setTypechecked
        case t: ScConstructor =>
          m.Type.Method(toParams(Seq(t.arguments:_*)), toType(t.newTemplate.get.getType(TypingContext.empty))).setTypechecked
        case t: ScPrimaryConstructor =>
          m.Type.Method(Seq(t.parameterList.clauses.map(convertParamClause):_*), toType(t.containingClass)).setTypechecked
        case t: ScFunctionDefinition =>
          m.Type.Method(Seq(t.parameterList.clauses.map(convertParamClause):_*), toType(t.getTypeWithCachedSubst)).setTypechecked
        case t: ScFunction =>
          m.Type.Function(Seq(t.paramTypes.map(toType(_, t).asInstanceOf[m.Type.Arg]): _*), toType(t.returnType)).setTypechecked
        case t: ScParameter =>
          val s = new ScSubstitutor(ScSubstitutor.cache.toMap, Map(), None)
          toType(s.subst(t.typeElement.get.getType().get))
        case t: ScTypedDefinition =>
          t.getTypeWithCachedSubst match {
            case Success(res, place) => toType(res)
            case Failure(cause, place) => unresolved(cause, place)
          }
        case t: ScReferenceElement =>
          t.bind() match {
            case Some(result) => toType(result.element)
            case None => m.Type.Placeholder(m.Type.Bounds(None, None))
          }
        case t: PsiPackage if t.getName == null =>
          m.Type.Singleton(std.rootPackageName).setTypechecked
        case t: PsiPackage =>
          m.Type.Singleton(toTermName(t)).setTypechecked
        case t: PsiClass =>
          m.Type.Name(t.getName).withAttrsFor(t).setTypechecked
        case t: PsiMethod =>
          m.Type.Method(Seq(t.getParameterList.getParameters
            .map(Compatibility.toParameter)
            .map(i=> convertParam(i.paramInCode.get))
            .toStream),
            toType(ScTypePsiTypeBridge.toScType(t.getReturnType, t.getProject))).setTypechecked
        case other => other ?!
      }
    })
  }

  def toType(tp: ptype.ScType, pivot: PsiElement = null): m.Type = {
    typeCache.getOrElseUpdate(tp, {
      tp.isAliasType match {
        case Some(AliasType(ta, lower, upper)) => return toTypeName(ta)
        case _ =>
      }
      tp match {
        case t: ptype.ScParameterizedType =>
          m.Type.Apply(toType(t.designator), Seq(t.typeArguments.map(toType(_)): _*)).setTypechecked
        case t: ptype.api.designator.ScThisType =>
          toTypeName(t.element).setTypechecked
        case t: ptype.api.designator.ScProjectionType =>
          t.projected match {
            case tt: ptype.api.designator.ScThisType =>
              m.Type.Select(toTermName(tt.element), toTypeName(t.actualElement)).setTypechecked
            case _ =>
              m.Type.Project(toType(t.projected), toTypeName(t.actualElement)).setTypechecked
          }
        case t: ptype.api.designator.ScDesignatorType =>
          if (t.element.isSingletonType)
            toSingletonType(t.element)
          else
            toTypeName(t.element)
        case t: ptype.ScCompoundType =>
          m.Type.Compound(Seq(t.components.map(toType(_)):_*), Seq.empty)
        case t: ptype.ScExistentialType =>
          val wcards = t.wildcards.map {wc =>
//            val (name, args, lower, upper) = wc
            val ubound = if (wc.upper == ptype.api.Any)      None else Some(toType(wc.upper))
            val lbound = if (wc.lower == ptype.api.Nothing)  None else Some(toType(wc.lower))
            m.Decl.Type(Nil, m.Type.Name(wc.name)
                //FIXME: pass actual prefix, when solution for recursive prefix computation is ready
                .withAttrs(h.Denotation.Single(h.Prefix.Zero, toSymbolWtihParent(wc.name, pivot, h.Signature.Type)))
                .setTypechecked,
              Nil, m.Type.Bounds(lbound, ubound)).setTypechecked
          }
          m.Type.Existential(toType(t.quantified), wcards).setTypechecked
        case t: ptype.api.StdType =>
          toStdTypeName(t)
        case t: TypeParameterType =>
          m.Type.Name(t.name).withAttrsFor(t.nameAndId._2).setTypechecked
        case t: ptype.ScType =>
          LOG.warn(s"Unknown type: ${t.getClass} - ${t.canonicalText}")
          m.Type.Name(t.canonicalText).withAttrs(h.Denotation.Zero)
      }
    })
  }

  def toSingletonType(elem: PsiElement): m.Type.Singleton = {
    m.Type.Singleton(toTermName(elem)).setTypechecked
  }

  def toTypeParams(tp: TypeParameterType): m.Type.Param = {
    val ubound = if (tp.upperType.v == ptype.api.Any)      None else Some(toType(tp.upperType.v))
    val lbound = if (tp.lowerType.v == ptype.api.Nothing)  None else Some(toType(tp.lowerType.v))
    m.Type.Param(
      if(tp.isCovariant) m.Mod.Covariant() :: Nil else if(tp.isContravariant) m.Mod.Contravariant() :: Nil else Nil,
      if (tp.name != "_") m.Type.Name(tp.name) else m.Name.Anonymous().withAttrs(h.Denotation.Zero).setTypechecked,<commit_msg>disable unsupported features in TypeAdapter<commit_after>package scala.meta.trees

import com.intellij.psi._
import org.jetbrains.plugins.scala.lang.psi.api.base._
import org.jetbrains.plugins.scala.lang.psi.api.base.types._
import org.jetbrains.plugins.scala.lang.psi.api.statements.params.ScParameter
import org.jetbrains.plugins.scala.lang.psi.api.statements.{ScFunction, ScFunctionDefinition, ScTypeAliasDeclaration}
import org.jetbrains.plugins.scala.lang.psi.api.toplevel._
import org.jetbrains.plugins.scala.lang.psi.types.ScSubstitutor
import org.jetbrains.plugins.scala.lang.psi.types.api.TypeParameterType
import org.jetbrains.plugins.scala.lang.psi.types.result.{Failure, Success, TypeResult, TypingContext}
import org.jetbrains.plugins.scala.lang.psi.{api => p, types => ptype}
import org.jetbrains.plugins.scala.lang.refactoring.util.ScTypeUtil.AliasType

import scala.collection.immutable.Seq
import scala.language.postfixOps
import scala.meta.collections._
import scala.meta.internal.{semantic => h}
import scala.meta.trees.error._
import scala.{meta => m, Seq => _}

trait TypeAdapter {
  self: TreeConverter =>

  private val typeCache = TwoWayCache[ptype.ScType, m.Type]()
  private val typeElementCache = TwoWayCache[ScTypeElement, m.Type]()
  private val psiElementTypeChache = TwoWayCache[PsiElement, m.Type]()

  def toType(tp: ScTypeElement): m.Type = {
    typeElementCache.getOrElseUpdate(tp, {
      tp match {
        case t: ScSimpleTypeElement =>
          val s = new ScSubstitutor(ScSubstitutor.cache.toMap, Map(), None)
          toType(s.subst(t.calcType))
        case t: ScFunctionalTypeElement =>
          toType(t.paramTypeElement) match {
            case m.Type.Tuple(elements) => m.Type.Function(elements, toType(t.returnTypeElement.get))
            case param => m.Type.Function(Seq(param), toType(t.returnTypeElement.get))
          }
        case t: ScParameterizedTypeElement =>
          m.Type.Apply(toType(t.typeElement.calcType), t.typeArgList.typeArgs.toStream.map(toType))
        case t: ScTupleTypeElement =>
          m.Type.Tuple(Seq(t.components.map(toType): _*))
        case t: ScWildcardTypeElement =>
          m.Type.Placeholder(typeBounds(t))
        case t: ScParenthesisedTypeElement =>
          t.typeElement match {
            case Some(t: ScReferenceableInfixTypeElement) => m.Type.ApplyInfix(toType(t.leftTypeElement), toTypeName(t.reference), toType(t.rightTypeElement.get))
            case _ => unreachable
          }
        case t: ScTypeVariableTypeElement => die("i cannot into type variables")
        case t: ScExistentialTypeElement =>
          val clauses = Seq(t.clause.declarations map {
            _ match {
              case tp: ScTypeAliasDeclaration => toTypeDecl(tp)
              case other => other ?!
            }
          } : _*)
          val quantified = toType(t.quantified)
          m.Type.Existential(quantified, clauses)
        case other: ScTypeElement =>
          LOG.warn(s"Using slow type conversion of type element ${other.getClass}: ${other.getText}")
          toType(other.getType())
        case other => other ?!
      }
    })
  }

  def toType(tr: TypeResult[ptype.ScType]): m.Type = {
    import org.jetbrains.plugins.scala.lang.psi.types.result._
    tr match {
      case Success(res, _) => toType(res)
      case Failure(cause, place) => throw new ScalaMetaTypeResultFailure(place, cause)
    }
  }

  def toType(elem: PsiElement): m.Type = {
    psiElementTypeChache.getOrElseUpdate(elem, {
      elem match {
        case t: typedef.ScTemplateDefinition =>
          val s = new ScSubstitutor(ScSubstitutor.cache.toMap, Map(), None)
          toType(s.subst(t.getType(TypingContext.empty).get)) // FIXME: what about typing context?
        case t: packaging.ScPackaging =>
          m.Type.Singleton(toTermName(t.reference.get)).setTypechecked
        case t: ScConstructor => ???
//          m.Type.Method(toParams(Seq(t.arguments:_*)), toType(t.newTemplate.get.getType(TypingContext.empty))).setTypechecked
        case t: ScPrimaryConstructor => ???
//          m.Type.Method(Seq(t.parameterList.clauses.map(convertParamClause):_*), toType(t.containingClass)).setTypechecked
        case t: ScFunctionDefinition => ???
//          m.Type.Method(Seq(t.parameterList.clauses.map(convertParamClause):_*), toType(t.getTypeWithCachedSubst)).setTypechecked
        case t: ScFunction =>
          m.Type.Function(Seq(t.paramTypes.map(toType(_, t).asInstanceOf[m.Type.Arg]): _*), toType(t.returnType)).setTypechecked
        case t: ScParameter =>
          val s = new ScSubstitutor(ScSubstitutor.cache.toMap, Map(), None)
          toType(s.subst(t.typeElement.get.getType().get))
        case t: ScTypedDefinition =>
          t.getTypeWithCachedSubst match {
            case Success(res, place) => toType(res)
            case Failure(cause, place) => unresolved(cause, place)
          }
        case t: ScReferenceElement =>
          t.bind() match {
            case Some(result) => toType(result.element)
            case None => m.Type.Placeholder(m.Type.Bounds(None, None))
          }
        case t: PsiPackage if t.getName == null =>
          m.Type.Singleton(std.rootPackageName).setTypechecked
        case t: PsiPackage =>
          m.Type.Singleton(toTermName(t)).setTypechecked
        case t: PsiClass =>
          m.Type.Name(t.getName).withAttrsFor(t).setTypechecked
        case t: PsiMethod => ???
//          m.Type.Method(Seq(t.getParameterList.getParameters
//            .map(Compatibility.toParameter)
//            .map(i=> convertParam(i.paramInCode.get))
//            .toStream),
//            toType(ScTypePsiTypeBridge.toScType(t.getReturnType, t.getProject))).setTypechecked
        case other => other ?!
      }
    })
  }

  def toType(tp: ptype.ScType, pivot: PsiElement = null): m.Type = {
    typeCache.getOrElseUpdate(tp, {
      tp.isAliasType match {
        case Some(AliasType(ta, lower, upper)) => return toTypeName(ta)
        case _ =>
      }
      tp match {
        case t: ptype.ScParameterizedType =>
          m.Type.Apply(toType(t.designator), Seq(t.typeArguments.map(toType(_)): _*)).setTypechecked
        case t: ptype.api.designator.ScThisType =>
          toTypeName(t.element).setTypechecked
        case t: ptype.api.designator.ScProjectionType =>
          t.projected match {
            case tt: ptype.api.designator.ScThisType =>
              m.Type.Select(toTermName(tt.element), toTypeName(t.actualElement)).setTypechecked
            case _ =>
              m.Type.Project(toType(t.projected), toTypeName(t.actualElement)).setTypechecked
          }
        case t: ptype.api.designator.ScDesignatorType =>
          if (t.element.isSingletonType)
            toSingletonType(t.element)
          else
            toTypeName(t.element)
        case t: ptype.ScCompoundType =>
          m.Type.Compound(Seq(t.components.map(toType(_)):_*), Seq.empty)
        case t: ptype.ScExistentialType =>
          val wcards = t.wildcards.map {wc =>
//            val (name, args, lower, upper) = wc
            val ubound = if (wc.upper == ptype.api.Any)      None else Some(toType(wc.upper))
            val lbound = if (wc.lower == ptype.api.Nothing)  None else Some(toType(wc.lower))
            m.Decl.Type(Nil, m.Type.Name(wc.name)
                //FIXME: pass actual prefix, when solution for recursive prefix computation is ready
                .withAttrs(h.Denotation.Single(h.Prefix.None, toSymbolWtihParent(wc.name, pivot, h.ScalaSig.Type(wc.name))))
                .setTypechecked,
              Nil, m.Type.Bounds(lbound, ubound)).setTypechecked
          }
          m.Type.Existential(toType(t.quantified), wcards).setTypechecked
        case t: ptype.api.StdType =>
          toStdTypeName(t)
        case t: TypeParameterType =>
          m.Type.Name(t.name).withAttrsFor(t.nameAndId._2).setTypechecked
        case t: ptype.ScType =>
          LOG.warn(s"Unknown type: ${t.getClass} - ${t.canonicalText}")
          m.Type.Name(t.canonicalText).withAttrs(h.Denotation.None)
      }
    })
  }

  def toSingletonType(elem: PsiElement): m.Type.Singleton = {
    m.Type.Singleton(toTermName(elem)).setTypechecked
  }

  def toTypeParams(tp: TypeParameterType): m.Type.Param = {
    val ubound = if (tp.upperType.v == ptype.api.Any)      None else Some(toType(tp.upperType.v))
    val lbound = if (tp.lowerType.v == ptype.api.Nothing)  None else Some(toType(tp.lowerType.v))
    m.Type.Param(
      if(tp.isCovariant) m.Mod.Covariant() :: Nil else if(tp.isContravariant) m.Mod.Contravariant() :: Nil else Nil,
      if (tp.name != "_") m.Type.Name(tp.name) else m.Name.Anonymous().withAttrs(h.Denotation.None).setTypechecked,
{'fraction_non_alphanumeric': 0.12586088154269973, 'fraction_numerical': 0.0001147842056932966, 'mean_word_length': 2.7831089882761617, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 117, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7305739', 'n_tokens_mistral': 5615, 'n_tokens_neox': 5593, 'n_words': 925}