From 5d7a07456dcadcdfa6f118b88035ed2c9d8b1160 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Tue, 23 Sep 2014 14:36:27 +0400 Subject: [PATCH] Create Function From Usage: Move type utilities to separate file --- .../CreateFunctionFromUsageFix.kt | 159 --------------- .../createFunction/typeUtils.kt | 182 ++++++++++++++++++ 2 files changed, 182 insertions(+), 159 deletions(-) create mode 100644 idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/typeUtils.kt diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/CreateFunctionFromUsageFix.kt b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/CreateFunctionFromUsageFix.kt index 6b80ee6a17c..8af44a3ea18 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/CreateFunctionFromUsageFix.kt +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/CreateFunctionFromUsageFix.kt @@ -320,53 +320,6 @@ private class TypeParameterListExpression(private val typeParameterNamesFromRece override fun calculateLookupItems(context: ExpressionContext?) = array() } -private fun JetType.contains(inner: JetType): Boolean { - return this == inner || getArguments().any { inner in it.getType() } -} - -private fun DeclarationDescriptor.render( - typeParameterNameMap: Map, - 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, 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) = render(typeParameterNameMap, false) -private fun JetType.renderLong(typeParameterNameMap: Map) = render(typeParameterNameMap, true) - -private fun getTypeParameterNamesNotInScope(typeParameters: Collection, scope: JetScope): List { - return typeParameters.filter { typeParameter -> - val classifier = scope.getClassifier(typeParameter.getName()) - classifier == null || classifier != typeParameter - } -} - -private fun JetType.getTypeParameters(): Set { - val typeParameters = LinkedHashSet() - 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 name, appended with a number if it is one of the existingNames or already exists in * scope. For example, given "foo", returns the next non-conflicting name in the list "foo", @@ -385,118 +338,6 @@ private fun isConflictingName(name: String, existingNames: Collection, s return name in existingNames || scope?.getClassifier(Name.identifier(name)) != null } -private fun JetExpression.guessTypes(context: BindingContext): Array { - - // 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 { - 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()) - - if (expectedTypes.isEmpty() || expectedTypes.any { expectedType -> ErrorUtils.containsErrorType(expectedType) }) { - return array() - } - val theType = TypeUtils.intersect(JetTypeChecker.DEFAULT, expectedTypes) - if (theType != null) { - return array(theType) - } - else { - // intersection doesn't exist; let user make an imperfect choice - return expectedTypes.copyToArray() - } -} - -/** - * Encapsulates a single type substitution of a JetType by another JetType. - */ -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, diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/typeUtils.kt b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/typeUtils.kt new file mode 100644 index 00000000000..6acea0c9c44 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/typeUtils.kt @@ -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, + 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, 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) = render(typeParameterNameMap, false) +private fun JetType.renderLong(typeParameterNameMap: Map) = render(typeParameterNameMap, true) + +private fun getTypeParameterNamesNotInScope(typeParameters: Collection, scope: JetScope): List { + return typeParameters.filter { typeParameter -> + val classifier = scope.getClassifier(typeParameter.getName()) + classifier == null || classifier != typeParameter + } +} + +private fun JetType.getTypeParameters(): Set { + val typeParameters = LinkedHashSet() + 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 { + // 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 { + 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()) + + if (expectedTypes.isEmpty() || expectedTypes.any { expectedType -> ErrorUtils.containsErrorType(expectedType) }) { + return array() + } + val theType = TypeUtils.intersect(JetTypeChecker.DEFAULT, expectedTypes) + if (theType != null) { + return array(theType) + } + else { + // intersection doesn't exist; let user make an imperfect choice + return expectedTypes.copyToArray() + } +} + +/** + * Encapsulates a single type substitution of a JetType by another JetType. + */ +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()) + } +} \ No newline at end of file