Create Function From Usage: Move type utilities to separate file

This commit is contained in:
Alexey Sedunov
2014-09-23 14:36:27 +04:00
parent 34632f46f6
commit 5d7a07456d
2 changed files with 182 additions and 159 deletions
@@ -320,53 +320,6 @@ private class TypeParameterListExpression(private val typeParameterNamesFromRece
override fun calculateLookupItems(context: ExpressionContext?) = array<LookupElement>()
}
private fun JetType.contains(inner: JetType): Boolean {
return this == inner || getArguments().any { inner in it.getType() }
}
private fun DeclarationDescriptor.render(
typeParameterNameMap: Map<TypeParameterDescriptor, String>,
fq: Boolean
): String = when {
this is TypeParameterDescriptor -> typeParameterNameMap[this] ?: getName().asString()
fq -> DescriptorUtils.getFqName(this).asString()
else -> getName().asString()
}
private fun JetType.render(typeParameterNameMap: Map<TypeParameterDescriptor, String>, fq: Boolean): String {
val arguments = getArguments().map { it.getType().render(typeParameterNameMap, fq) }
val typeString = getConstructor().getDeclarationDescriptor()!!.render(typeParameterNameMap, fq)
val typeArgumentString = if (arguments.notEmpty) arguments.makeString(", ", "<", ">") else ""
return "$typeString$typeArgumentString"
}
private fun JetType.renderShort(typeParameterNameMap: Map<TypeParameterDescriptor, String>) = render(typeParameterNameMap, false)
private fun JetType.renderLong(typeParameterNameMap: Map<TypeParameterDescriptor, String>) = render(typeParameterNameMap, true)
private fun getTypeParameterNamesNotInScope(typeParameters: Collection<TypeParameterDescriptor>, scope: JetScope): List<TypeParameterDescriptor> {
return typeParameters.filter { typeParameter ->
val classifier = scope.getClassifier(typeParameter.getName())
classifier == null || classifier != typeParameter
}
}
private fun JetType.getTypeParameters(): Set<TypeParameterDescriptor> {
val typeParameters = LinkedHashSet<TypeParameterDescriptor>()
val arguments = getArguments()
if (arguments.empty) {
val descriptor = getConstructor().getDeclarationDescriptor()
if (descriptor is TypeParameterDescriptor) {
typeParameters.add(descriptor as TypeParameterDescriptor)
}
}
else {
arguments.flatMapTo(typeParameters) { projection ->
projection.getType().getTypeParameters()
}
}
return typeParameters
}
/**
* Returns the given <code>name</code>, appended with a number if it is one of the <code>existingNames</code> or already exists in
* <code>scope</code>. For example, given <code>"foo"</code>, returns the next non-conflicting name in the list <code>"foo"</code>,
@@ -385,118 +338,6 @@ private fun isConflictingName(name: String, existingNames: Collection<String>, s
return name in existingNames || scope?.getClassifier(Name.identifier(name)) != null
}
private fun JetExpression.guessTypes(context: BindingContext): Array<JetType> {
// if we know the actual type of the expression
val theType1 = context[BindingContext.EXPRESSION_TYPE, this]
if (theType1 != null) {
return array(theType1)
}
// expression has an expected type
val theType2 = context[BindingContext.EXPECTED_EXPRESSION_TYPE, this]
if (theType2 != null) {
return array(theType2)
}
return when {
this is JetTypeConstraint -> {
// expression itself is a type assertion
val constraint = (this as JetTypeConstraint)
array(context[BindingContext.TYPE, constraint.getBoundTypeReference()]!!)
}
getParent() is JetTypeConstraint -> {
// expression is on the left side of a type assertion
val constraint = (getParent() as JetTypeConstraint)
array(context[BindingContext.TYPE, constraint.getBoundTypeReference()]!!)
}
this is JetMultiDeclarationEntry -> {
// expression is on the lhs of a multi-declaration
val typeRef = getTypeRef()
if (typeRef != null) {
// and has a specified type
array(context[BindingContext.TYPE, typeRef]!!)
}
else {
// otherwise guess
guessType(context)
}
}
this is JetParameter -> {
// expression is a parameter (e.g. declared in a for-loop)
val typeRef = getTypeReference()
if (typeRef != null) {
// and has a specified type
array(context[BindingContext.TYPE, typeRef]!!)
}
else {
// otherwise guess
guessType(context)
}
}
getParent() is JetVariableDeclaration -> {
// the expression is the RHS of a variable assignment with a specified type
val variable = getParent() as JetVariableDeclaration
val typeRef = variable.getTypeRef()
if (typeRef != null) {
// and has a specified type
array(context[BindingContext.TYPE, typeRef]!!)
}
else {
// otherwise guess, based on LHS
variable.guessType(context)
}
}
else -> array() // can't infer anything
}
}
private fun JetNamedDeclaration.guessType(context: BindingContext): Array<JetType> {
val scope = getContainingFile()!!.getUseScope()
val expectedTypes = SearchUtils.findAllReferences(this, scope)!!.stream().map { ref ->
if (ref is JetSimpleNameReference) {
context[BindingContext.EXPECTED_EXPRESSION_TYPE, ref.expression]
}
else {
null
}
}.filterNotNullTo(HashSet<JetType>())
if (expectedTypes.isEmpty() || expectedTypes.any { expectedType -> ErrorUtils.containsErrorType(expectedType) }) {
return array<JetType>()
}
val theType = TypeUtils.intersect(JetTypeChecker.DEFAULT, expectedTypes)
if (theType != null) {
return array<JetType>(theType)
}
else {
// intersection doesn't exist; let user make an imperfect choice
return expectedTypes.copyToArray()
}
}
/**
* Encapsulates a single type substitution of a <code>JetType</code> by another <code>JetType</code>.
*/
private class JetTypeSubstitution(public val forType: JetType, public val byType: JetType)
private fun JetType.substitute(substitution: JetTypeSubstitution, variance: Variance): JetType {
if (when (variance) {
Variance.INVARIANT -> this == substitution.forType
Variance.IN_VARIANCE -> JetTypeChecker.DEFAULT.isSubtypeOf(this, substitution.forType)
Variance.OUT_VARIANCE -> JetTypeChecker.DEFAULT.isSubtypeOf(substitution.forType, this)
}) {
return substitution.byType
}
else {
val newArguments = getArguments().zip(getConstructor().getParameters()).map { pair ->
val (projection, typeParameter) = pair
TypeProjectionImpl(Variance.INVARIANT, projection.getType().substitute(substitution, typeParameter.getVariance()))
}
return JetTypeImpl(getAnnotations(), getConstructor(), isNullable(), newArguments, getMemberScope())
}
}
public class CreateFunctionFromUsageFix internal (
element: PsiElement,
private val ownerType: TypeOrExpressionThereof,
@@ -0,0 +1,182 @@
package org.jetbrains.jet.plugin.quickfix.createFromUsage.createFunction
import org.jetbrains.jet.lang.types.JetType
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor
import org.jetbrains.jet.lang.resolve.DescriptorUtils
import org.jetbrains.jet.lang.resolve.scopes.JetScope
import java.util.LinkedHashSet
import org.jetbrains.jet.lang.psi.JetExpression
import org.jetbrains.jet.lang.resolve.BindingContext
import org.jetbrains.jet.lang.psi.JetTypeConstraint
import org.jetbrains.jet.lang.psi.JetMultiDeclarationEntry
import org.jetbrains.jet.lang.psi.JetParameter
import org.jetbrains.jet.lang.psi.JetVariableDeclaration
import org.jetbrains.jet.lang.psi.JetNamedDeclaration
import com.intellij.refactoring.psi.SearchUtils
import org.jetbrains.jet.plugin.references.JetSimpleNameReference
import java.util.HashSet
import org.jetbrains.jet.lang.types.ErrorUtils
import org.jetbrains.jet.lang.types.TypeUtils
import org.jetbrains.jet.lang.types.checker.JetTypeChecker
import org.jetbrains.jet.lang.types.Variance
import org.jetbrains.jet.lang.types.TypeProjectionImpl
import org.jetbrains.jet.lang.types.JetTypeImpl
private fun JetType.contains(inner: JetType): Boolean {
return this == inner || getArguments().any { inner in it.getType() }
}
private fun DeclarationDescriptor.render(
typeParameterNameMap: Map<TypeParameterDescriptor, String>,
fq: Boolean
): String = when {
this is TypeParameterDescriptor -> typeParameterNameMap[this] ?: getName().asString()
fq -> DescriptorUtils.getFqName(this).asString()
else -> getName().asString()
}
private fun JetType.render(typeParameterNameMap: Map<TypeParameterDescriptor, String>, fq: Boolean): String {
val arguments = getArguments().map { it.getType().render(typeParameterNameMap, fq) }
val typeString = getConstructor().getDeclarationDescriptor()!!.render(typeParameterNameMap, fq)
val typeArgumentString = if (arguments.notEmpty) arguments.joinToString(", ", "<", ">") else ""
return "$typeString$typeArgumentString"
}
private fun JetType.renderShort(typeParameterNameMap: Map<TypeParameterDescriptor, String>) = render(typeParameterNameMap, false)
private fun JetType.renderLong(typeParameterNameMap: Map<TypeParameterDescriptor, String>) = render(typeParameterNameMap, true)
private fun getTypeParameterNamesNotInScope(typeParameters: Collection<TypeParameterDescriptor>, scope: JetScope): List<TypeParameterDescriptor> {
return typeParameters.filter { typeParameter ->
val classifier = scope.getClassifier(typeParameter.getName())
classifier == null || classifier != typeParameter
}
}
private fun JetType.getTypeParameters(): Set<TypeParameterDescriptor> {
val typeParameters = LinkedHashSet<TypeParameterDescriptor>()
val arguments = getArguments()
if (arguments.empty) {
val descriptor = getConstructor().getDeclarationDescriptor()
if (descriptor is TypeParameterDescriptor) {
typeParameters.add(descriptor as TypeParameterDescriptor)
}
}
else {
arguments.flatMapTo(typeParameters) { projection ->
projection.getType().getTypeParameters()
}
}
return typeParameters
}
private fun JetExpression.guessTypes(context: BindingContext): Array<JetType> {
// if we know the actual type of the expression
val theType1 = context[BindingContext.EXPRESSION_TYPE, this]
if (theType1 != null) {
return array(theType1)
}
// expression has an expected type
val theType2 = context[BindingContext.EXPECTED_EXPRESSION_TYPE, this]
if (theType2 != null) {
return array(theType2)
}
return when {
this is JetTypeConstraint -> {
// expression itself is a type assertion
val constraint = (this as JetTypeConstraint)
array(context[BindingContext.TYPE, constraint.getBoundTypeReference()]!!)
}
getParent() is JetTypeConstraint -> {
// expression is on the left side of a type assertion
val constraint = (getParent() as JetTypeConstraint)
array(context[BindingContext.TYPE, constraint.getBoundTypeReference()]!!)
}
this is JetMultiDeclarationEntry -> {
// expression is on the lhs of a multi-declaration
val typeRef = getTypeRef()
if (typeRef != null) {
// and has a specified type
array(context[BindingContext.TYPE, typeRef]!!)
}
else {
// otherwise guess
guessType(context)
}
}
this is JetParameter -> {
// expression is a parameter (e.g. declared in a for-loop)
val typeRef = getTypeReference()
if (typeRef != null) {
// and has a specified type
array(context[BindingContext.TYPE, typeRef]!!)
}
else {
// otherwise guess
guessType(context)
}
}
getParent() is JetVariableDeclaration -> {
// the expression is the RHS of a variable assignment with a specified type
val variable = getParent() as JetVariableDeclaration
val typeRef = variable.getTypeRef()
if (typeRef != null) {
// and has a specified type
array(context[BindingContext.TYPE, typeRef]!!)
}
else {
// otherwise guess, based on LHS
variable.guessType(context)
}
}
else -> array() // can't infer anything
}
}
private fun JetNamedDeclaration.guessType(context: BindingContext): Array<JetType> {
val scope = getContainingFile()!!.getUseScope()
val expectedTypes = SearchUtils.findAllReferences(this, scope)!!.stream().map { ref ->
if (ref is JetSimpleNameReference) {
context[BindingContext.EXPECTED_EXPRESSION_TYPE, ref.expression]
}
else {
null
}
}.filterNotNullTo(HashSet<JetType>())
if (expectedTypes.isEmpty() || expectedTypes.any { expectedType -> ErrorUtils.containsErrorType(expectedType) }) {
return array<JetType>()
}
val theType = TypeUtils.intersect(JetTypeChecker.DEFAULT, expectedTypes)
if (theType != null) {
return array<JetType>(theType)
}
else {
// intersection doesn't exist; let user make an imperfect choice
return expectedTypes.copyToArray()
}
}
/**
* Encapsulates a single type substitution of a <code>JetType</code> by another <code>JetType</code>.
*/
private class JetTypeSubstitution(public val forType: JetType, public val byType: JetType)
private fun JetType.substitute(substitution: JetTypeSubstitution, variance: Variance): JetType {
if (when (variance) {
Variance.INVARIANT -> this == substitution.forType
Variance.IN_VARIANCE -> JetTypeChecker.DEFAULT.isSubtypeOf(this, substitution.forType)
Variance.OUT_VARIANCE -> JetTypeChecker.DEFAULT.isSubtypeOf(substitution.forType, this)
}) {
return substitution.byType
}
else {
val newArguments = getArguments().zip(getConstructor().getParameters()).map { pair ->
val (projection, typeParameter) = pair
TypeProjectionImpl(Variance.INVARIANT, projection.getType().substitute(substitution, typeParameter.getVariance()))
}
return JetTypeImpl(getAnnotations(), getConstructor(), isNullable(), newArguments, getMemberScope())
}
}