J2K: replace overloads with optional parameters not only for constructors but for methods too

This commit is contained in:
Valentin Kipyatkov
2015-04-08 16:22:15 +03:00
parent ff4ff78307
commit f6e363abb8
23 changed files with 721 additions and 184 deletions
@@ -49,8 +49,12 @@ class ClassBodyConverter(private val psiClass: PsiClass,
public fun convertBody(): ClassBody {
processAccessorsToDrop()
val overloadReducer = OverloadReducer(psiClass.getMethods().filter { it !in membersToRemove } /* do not allow OverloadReducer to use accessors converted to properties */,
isOpenClass,
converter.referenceSearcher)
val constructorConverter = if (psiClass.getName() != null && !isObject)
ConstructorConverter(psiClass, converter, fieldCorrections)
ConstructorConverter(psiClass, converter, fieldCorrections, overloadReducer)
else
null
@@ -59,8 +63,9 @@ class ClassBodyConverter(private val psiClass: PsiClass,
if (element is PsiMember) {
if (element is PsiAnnotationMethod) continue // converted in convertAnnotationType()
if (isObject && element.isConstructor()) continue // no constructor in object
if (element is PsiMethod && overloadReducer.shouldDropMethod(element)) continue
val converted = converter.convertMember(element, membersToRemove, constructorConverter)
val converted = converter.convertMember(element, membersToRemove, constructorConverter, overloadReducer)
if (converted != null) {
convertedMembers.put(element, converted)
}
@@ -88,7 +93,7 @@ class ClassBodyConverter(private val psiClass: PsiClass,
}
}
return ClassBody(null, null, convertedMembers.values().toList(), emptyList(), lBrace, rBrace)
return ClassBody(null, null, convertedMembers.values().toList(), emptyList(), lBrace, rBrace, overloadReducer)
}
val useCompanionObject = shouldGenerateCompanionObject(convertedMembers)
@@ -123,14 +128,15 @@ class ClassBodyConverter(private val psiClass: PsiClass,
primaryConstructorSignature = null // no "()" after class name is needed in this case
}
return ClassBody(primaryConstructorSignature, constructorConverter?.baseClassParams, members, companionObjectMembers, lBrace, rBrace)
return ClassBody(primaryConstructorSignature, constructorConverter?.baseClassParams, members, companionObjectMembers, lBrace, rBrace, overloadReducer)
}
private fun Converter.convertMember(member: PsiMember,
membersToRemove: MutableSet<PsiMember>,
constructorConverter: ConstructorConverter?): Member? {
constructorConverter: ConstructorConverter?,
overloadReducer: OverloadReducer): Member? {
return when (member) {
is PsiMethod -> convertMethod(member, membersToRemove, constructorConverter, isOpenClass)
is PsiMethod -> convertMethod(member, membersToRemove, constructorConverter, overloadReducer, isOpenClass)
is PsiField -> convertField(member, fieldCorrections[member])
is PsiClass -> convertClass(member)
is PsiClassInitializer -> convertInitializer(member)
@@ -27,9 +27,10 @@ import java.util.HashSet
class ConstructorConverter(
private val psiClass: PsiClass,
private val converter: Converter,
private val fieldCorrections: Map<PsiField, FieldCorrectionInfo>
private val fieldCorrections: Map<PsiField, FieldCorrectionInfo>,
private val overloadReducer: OverloadReducer
) {
private val constructors = psiClass.getConstructors()
private val constructors = psiClass.getConstructors().asList()
private val toTargetConstructorMap = buildToTargetConstructorMap()
@@ -43,29 +44,12 @@ class ConstructorConverter(
val candidates = constructors.filter { it !in toTargetConstructorMap }
if (candidates.size() != 1) return null // there should be only one constructor which does not call other constructor
val primary = candidates.single()
if (toTargetConstructorMap.values().any() { it.constructor != primary }) return null // all other constructors call our candidate (directly or indirectly)
if (toTargetConstructorMap.values().any() { it != primary }) return null // all other constructors call our candidate (directly or indirectly)
return primary
}
private class TargetConstructorInfo(
/**
* Target constructor (one which is finally invoked by this one)
*/
val constructor: PsiMethod,
/**
* Is not null if this constructor is equivalent to the target constructor with a few last parameters having default values
*/
val parameterDefaults: List<PsiExpression>?)
private val constructorsToDrop = HashSet<PsiMethod>()
private val lastParamDefaults = HashMap<PsiMethod, ArrayList<PsiExpression>>() // defaults for a few last parameters in reverse order
init {
dropConstructorsForDefaultValues()
}
private fun buildToTargetConstructorMap(): Map<PsiMethod, TargetConstructorInfo> {
val toTargetConstructorMap = HashMap<PsiMethod, TargetConstructorInfo>()
private fun buildToTargetConstructorMap(): Map<PsiMethod, PsiMethod> {
val toTargetConstructorMap = HashMap<PsiMethod, PsiMethod>()
for (constructor in constructors) {
val firstStatement = constructor.getBody()?.getStatements()?.firstOrNull()
val methodCall = (firstStatement as? PsiExpressionStatement)?.getExpression() as? PsiMethodCallExpression
@@ -74,25 +58,11 @@ class ConstructorConverter(
if (refExpr.getCanonicalText() == "this") {
val target = refExpr.resolve() as? PsiMethod
if (target != null && target.isConstructor()) {
var parameterDefaults = calcTargetParameterDefaults(constructor, target, methodCall)
val finalTargetInfo = toTargetConstructorMap[target]
if (finalTargetInfo != null && parameterDefaults != null) {
parameterDefaults = if (finalTargetInfo.parameterDefaults != null)
parameterDefaults!! + finalTargetInfo.parameterDefaults
else
null
}
val finalTarget = finalTargetInfo?.constructor ?: target
toTargetConstructorMap[constructor] = TargetConstructorInfo(finalTarget, parameterDefaults)
val finalTarget = toTargetConstructorMap[target] ?: target
toTargetConstructorMap[constructor] = finalTarget
for (entry in toTargetConstructorMap.entrySet()) {
if (entry.value.constructor == constructor) {
val newParameterDefaults = if (parameterDefaults != null)
entry.value.parameterDefaults?.plus(parameterDefaults!!)
else
null
entry.setValue(TargetConstructorInfo(finalTarget, newParameterDefaults))
if (entry.value == constructor) {
entry.setValue(finalTarget)
}
}
}
@@ -102,61 +72,6 @@ class ConstructorConverter(
return toTargetConstructorMap
}
private fun calcTargetParameterDefaults(constructor: PsiMethod, target: PsiMethod, targetCall: PsiMethodCallExpression): List<PsiExpression>? {
if (constructor.getBody()!!.getStatements().size() != 1) return null // constructor's body should consist of only "this(...)"
val parameters = constructor.getParameterList().getParameters()
val targetParameters = target.getParameterList().getParameters()
if (parameters.size() >= targetParameters.size()) return null
val args = targetCall.getArgumentList().getExpressions()
if (args.size() != targetParameters.size()) return null // incorrect code
for (i in parameters.indices) {
val parameter = parameters[i]
val targetParameter = targetParameters[i]
if (parameter.getName() != targetParameter.getName() || parameter.getType() != targetParameter.getType()) return null
val arg = args[i]
if (arg !is PsiReferenceExpression || arg.resolve() != parameter) return null
}
return args.drop(parameters.size())
}
private fun dropConstructorsForDefaultValues() {
val dropCandidates = toTargetConstructorMap
.filter {
it.value.parameterDefaults != null
&& it.key.accessModifier() == it.value.constructor.accessModifier()
&& it.key.getModifierList().getAnnotations().isEmpty() /* do not drop constructors with annotations */
}
.map { it.key }
.sortBy { -it.getParameterList().getParametersCount() } // we will try to drop them starting from ones with more parameters
@DropCandidatesLoop
for (constructor in dropCandidates) {
val paramCount = constructor.getParameterList().getParametersCount()
val targetInfo = toTargetConstructorMap[constructor]!!
val targetConstructor = targetInfo.constructor
val targetParamCount = targetConstructor.getParameterList().getParametersCount()
assert(paramCount < targetParamCount)
val defaults = targetInfo.parameterDefaults!!
assert(defaults.size() == targetParamCount - paramCount)
val targetDefaults = lastParamDefaults.getOrPut(targetConstructor, { ArrayList() })
for (i in defaults.indices) {
val default = defaults[defaults.size() - i - 1]
if (i < targetDefaults.size()) { // default for this parameter has already been assigned
if (targetDefaults[i].getText() != default.getText()) continue@DropCandidatesLoop
}
else {
targetDefaults.add(default)
}
}
constructorsToDrop.add(constructor)
}
}
public var baseClassParams: List<DeferredElement<Expression>>? = if (constructors.isEmpty()) emptyList() else null
private set
@@ -169,10 +84,9 @@ class ConstructorConverter(
convertPrimaryConstructor(annotations, modifiers, membersToRemove, postProcessBody)
}
else {
if (constructor in constructorsToDrop) return null
if (overloadReducer.shouldDropMethod(constructor)) return null
val params = convertParameterList(constructor, converter,
{ parameter, default -> converter.convertParameter(parameter, defaultValue = default) })
val params = converter.convertParameterList(constructor, overloadReducer)
val thisOrSuper = findThisOrSuperCall(constructor)
val thisOrSuperDeferred = if (thisOrSuper != null)
@@ -193,14 +107,6 @@ class ConstructorConverter(
SecondaryConstructor(annotations, modifiers, params, converter.deferredElement(::convertBody), thisOrSuperDeferred)
}
if (result.parameterList.parameters.any { it.defaultValue != null }) {
result.annotations += Annotations(listOf(Annotation(Identifier("overloads").assignNoPrototype(),
listOf(),
brackets = result is PrimaryConstructor,
newLineAfter = false).assignNoPrototype())).assignNoPrototype()
}
return result
}
@@ -288,53 +194,34 @@ class ConstructorConverter(
baseClassParams = emptyList()
}
val parameterList = convertParameterList(primaryConstructor,
correctedConverter,
{ parameter, default ->
if (!parameterToField.containsKey(parameter)) {
correctedConverter.convertParameter(parameter, defaultValue = default)
}
else {
val (field, type) = parameterToField[parameter]!!
val fieldCorrection = fieldCorrections[field]
val name = fieldCorrection?.identifier ?: field.declarationIdentifier()
val accessModifiers = if (fieldCorrection != null)
Modifiers(listOf()).with(fieldCorrection.access).assignNoPrototype()
else
converter.convertModifiers(field).filter { it in ACCESS_MODIFIERS }
Parameter(name,
type,
if (isVal(converter.referenceSearcher, field)) Parameter.VarValModifier.Val else Parameter.VarValModifier.Var,
converter.convertAnnotations(parameter) + converter.convertAnnotations(field),
accessModifiers,
default).assignPrototypes(listOf(parameter, field), CommentsAndSpacesInheritance(blankLinesBefore = false))
}
},
correctCodeConverter = { correct() })
val parameterList = correctedConverter.convertParameterList(
primaryConstructor,
overloadReducer,
{ parameter, default ->
if (!parameterToField.containsKey(parameter)) {
correctedConverter.convertParameter(parameter, defaultValue = default)
}
else {
val (field, type) = parameterToField[parameter]!!
val fieldCorrection = fieldCorrections[field]
val name = fieldCorrection?.identifier ?: field.declarationIdentifier()
val accessModifiers = if (fieldCorrection != null)
Modifiers(listOf()).with(fieldCorrection.access).assignNoPrototype()
else
converter.convertModifiers(field).filter { it in ACCESS_MODIFIERS }
Parameter(name,
type,
if (isVal(converter.referenceSearcher, field)) Parameter.VarValModifier.Val else Parameter.VarValModifier.Var,
converter.convertAnnotations(parameter) + converter.convertAnnotations(field),
accessModifiers,
default).assignPrototypes(listOf(parameter, field), CommentsAndSpacesInheritance(blankLinesBefore = false))
}
},
correctCodeConverter = { correct() })
return PrimaryConstructor(annotations, modifiers, parameterList, converter.deferredElement(bodyGenerator)).assignPrototype(primaryConstructor)
}
private fun convertParameterList(
constructor: PsiMethod,
converter: Converter,
convertParameter: (parameter: PsiParameter, default: DeferredElement<Expression>?) -> Parameter,
correctCodeConverter: CodeConverter.() -> CodeConverter = { this }
): ParameterList {
val parameterList = constructor.getParameterList()
val params = parameterList.getParameters()
val defaults = lastParamDefaults[constructor] ?: emptyList<PsiExpression>()
return ParameterList(params.indices.map { i ->
val parameter = params[i]
val indexFromEnd = params.size() - i - 1
val defaultValue = if (indexFromEnd < defaults.size())
converter.deferredElement { codeConverter -> codeConverter.correctCodeConverter().convertExpression(defaults[indexFromEnd], parameter.getType()) }
else
null
convertParameter(parameter, defaultValue)
}).assignPrototype(parameterList)
}
private fun findBackingFieldForConstructorParameter(parameter: PsiParameter, constructor: PsiMethod): Pair<PsiField, PsiStatement>? {
val body = constructor.getBody() ?: return null
+55 -18
View File
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.j2k
import com.google.common.collect.Multimap
import com.intellij.openapi.project.Project
import com.intellij.psi.*
import com.intellij.psi.CommonClassNames.*
@@ -32,6 +31,7 @@ import org.jetbrains.kotlin.j2k.usageProcessing.UsageProcessing
import org.jetbrains.kotlin.j2k.usageProcessing.UsageProcessingExpressionConverter
import org.jetbrains.kotlin.types.expressions.OperatorConventions.*
import java.util.ArrayList
import java.util.HashMap
class Converter private(
private val elementToConvert: PsiElement,
@@ -62,7 +62,9 @@ class Converter private(
public fun create(elementToConvert: PsiElement, settings: ConverterSettings, inConversionScope: (PsiElement) -> Boolean,
referenceSearcher: ReferenceSearcher, resolverForConverter: ResolverForConverter,
usageProcessingsCollector: (UsageProcessing) -> Unit): Converter {
return Converter(elementToConvert, settings, inConversionScope, referenceSearcher, resolverForConverter, CommonState(usageProcessingsCollector), PersonalState(null))
return Converter(elementToConvert, settings, inConversionScope,
CachingReferenceSearcher(referenceSearcher),
resolverForConverter, CommonState(usageProcessingsCollector), PersonalState(null))
}
}
@@ -98,7 +100,7 @@ class Converter private(
private fun convertTopElement(element: PsiElement): Element? = when (element) {
is PsiJavaFile -> convertFile(element)
is PsiClass -> convertClass(element)
is PsiMethod -> convertMethod(element, null, null, false)
is PsiMethod -> convertMethod(element, null, null, null, false)
is PsiField -> convertField(element, null)
is PsiStatement -> createDefaultCodeConverter().convertStatement(element)
is PsiExpression -> createDefaultCodeConverter().convertExpression(element)
@@ -274,7 +276,8 @@ class Converter private(
// to convert fields and nested types - they are not allowed in Kotlin but we convert them and let user refactor code
var classBody = ClassBodyConverter(psiClass, this, isOpenClass = false, isObject = false).convertBody()
classBody = ClassBody(constructorSignature, classBody.baseClassParams, classBody.members, classBody.companionObjectMembers, classBody.lBrace, classBody.rBrace)
classBody = ClassBody(constructorSignature, classBody.baseClassParams, classBody.members,
classBody.companionObjectMembers, classBody.lBrace, classBody.rBrace, classBody.overloadReducer)
val annotationAnnotation = Annotation(Identifier("annotation").assignNoPrototype(), listOf(), false, false).assignNoPrototype()
return Class(psiClass.declarationIdentifier(),
@@ -348,10 +351,13 @@ class Converter private(
return if (convertedType == initializerType) null else convertedType
}
public fun convertMethod(method: PsiMethod,
membersToRemove: MutableSet<PsiMember>?,
constructorConverter: ConstructorConverter?,
isInOpenClass: Boolean): Member? {
public fun convertMethod(
method: PsiMethod,
membersToRemove: MutableSet<PsiMember>?,
constructorConverter: ConstructorConverter?,
overloadReducer: OverloadReducer?,
isInOpenClass: Boolean
): FunctionLike? {
val returnType = typeConverter.convertMethodReturnType(method)
val annotations = convertAnnotations(method) + convertThrows(method)
@@ -401,7 +407,8 @@ class Converter private(
}
}
var params = convertParameterList(method.getParameterList())
var params = convertParameterList(method, overloadReducer)
val typeParameterList = convertTypeParameterList(method.getTypeParameterList())
var body = deferredElement { codeConverter: CodeConverter ->
val body = codeConverter.withMethodReturnType(method.getReturnType()).convertBlock(method.getBody())
@@ -410,7 +417,17 @@ class Converter private(
Function(method.declarationIdentifier(), annotations, modifiers, returnType, typeParameterList, params, body, containingClass?.isInterface() ?: false)
}
return function?.assignPrototype(method)
if (function == null) return null
if (function.parameterList.parameters.any { it.defaultValue != null }) {
function.annotations += Annotations(
listOf(Annotation(Identifier("overloads").assignNoPrototype(),
listOf(),
brackets = function is PrimaryConstructor,
newLineAfter = false).assignNoPrototype())).assignNoPrototype()
}
return function.assignPrototype(method)
}
/**
@@ -507,14 +524,13 @@ class Converter private(
private fun convertToNotNullableTypes(types: Array<out PsiType?>): List<Type>
= types.map { typeConverter.convertType(it, Nullability.NotNull) }
private fun convertParameterList(parameterList: PsiParameterList): ParameterList
= ParameterList(parameterList.getParameters().map { convertParameter(it) }).assignPrototype(parameterList)
public fun convertParameter(parameter: PsiParameter,
nullability: Nullability = Nullability.Default,
varValModifier: Parameter.VarValModifier = Parameter.VarValModifier.None,
modifiers: Modifiers = Modifiers.Empty,
defaultValue: DeferredElement<Expression>? = null): Parameter {
public fun convertParameter(
parameter: PsiParameter,
nullability: Nullability = Nullability.Default,
varValModifier: Parameter.VarValModifier = Parameter.VarValModifier.None,
modifiers: Modifiers = Modifiers.Empty,
defaultValue: DeferredElement<Expression>? = null
): Parameter {
var type = typeConverter.convertVariableType(parameter)
when (nullability) {
Nullability.NotNull -> type = type.toNotNullType()
@@ -555,6 +571,27 @@ class Converter private(
val annotation = Annotation(Identifier("throws").assignNoPrototype(), arguments, false, true)
return Annotations(listOf(annotation.assignPrototype(throwsList))).assignPrototype(throwsList)
}
private class CachingReferenceSearcher(private val searcher: ReferenceSearcher) : ReferenceSearcher by searcher {
private val hasInheritorsCached = HashMap<PsiClass, Boolean>()
private val hasOverridesCached = HashMap<PsiMethod, Boolean>()
override fun hasInheritors(`class`: PsiClass): Boolean {
val cached = hasInheritorsCached[`class`]
if (cached != null) return cached
val result = searcher.hasInheritors(`class`)
hasInheritorsCached[`class`] = result
return result
}
override fun hasOverrides(method: PsiMethod): Boolean {
val cached = hasOverridesCached[method]
if (cached != null) return cached
val result = searcher.hasOverrides(method)
hasOverridesCached[method] = result
return result
}
}
}
val PRIMITIVE_TYPE_CONVERSIONS: Map<String, String> = mapOf(
@@ -0,0 +1,195 @@
/*
* Copyright 2010-2015 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.j2k
import com.intellij.psi.*
import org.jetbrains.kotlin.j2k.ast.*
import java.util.ArrayList
import java.util.HashMap
import java.util.HashSet
class OverloadReducer(
private val methods: Collection<PsiMethod>,
private val isOpenClass: Boolean,
private val referenceSearcher: ReferenceSearcher
) {
private val methodToLastParameterDefaults = HashMap<PsiMethod, ArrayList<PsiExpression>>()
private val methodsToDrop = HashSet<PsiMethod>()
init {
val map = buildOverloadEquivalenceMap()
dropOverloadsForDefaultValues(map)
}
public fun shouldDropMethod(method: PsiMethod): Boolean = method in methodsToDrop
public fun parameterDefault(method: PsiMethod, parameterIndex: Int): PsiExpression? {
val defaults = methodToLastParameterDefaults[method] ?: return null
val index = method.getParameterList().getParametersCount() - parameterIndex - 1
return if (index < defaults.size()) defaults[index] else null
}
private class EquivalentOverloadInfo(
/**
* Target method (one which is finally invoked by this one)
*/
val method: PsiMethod,
/**
* This method is equivalent to the target method with a few last parameters having default values
*/
val parameterDefaults: List<PsiExpression>)
private fun buildOverloadEquivalenceMap(): Map<PsiMethod, EquivalentOverloadInfo> {
val overloadGroups = methods
.groupBy { listOf(if (it.isConstructor()) null else it.getName(),
it.accessModifier(),
it.getReturnType(),
it.hasModifierProperty(PsiModifier.STATIC),
getAnnotationsFingerprint(it)) }
.values()
.filter { it.size() > 1 }
.map { it.filterNot { shouldSkipOverload(it) } }
.filter { it.size() > 1 }
val map = HashMap<PsiMethod, EquivalentOverloadInfo>()
for (group in overloadGroups) {
for (method in group) {
val overloadInfo = findEquivalentOverload(method, group) ?: continue
val furtherOverloadInfo = map[overloadInfo.method]
val resultOverloadInfo = if (furtherOverloadInfo != null) {
EquivalentOverloadInfo(furtherOverloadInfo.method, overloadInfo.parameterDefaults + furtherOverloadInfo.parameterDefaults)
}
else {
overloadInfo
}
map[method] = resultOverloadInfo
for (entry in map.entrySet()) {
if (entry.value.method == method) {
val newParameterDefaults = entry.value.parameterDefaults + resultOverloadInfo.parameterDefaults
entry.setValue(EquivalentOverloadInfo(resultOverloadInfo.method, newParameterDefaults))
}
}
}
}
return map
}
private fun shouldSkipOverload(method: PsiMethod): Boolean {
if (method.isConstructor()) return false
if (method.hasModifierProperty(PsiModifier.ABSTRACT)) return true
if (method.hasModifierProperty(PsiModifier.NATIVE)) return true
if (method.hasModifierProperty(PsiModifier.SYNCHRONIZED)) return true
if (method.getHierarchicalMethodSignature().getSuperSignatures().isNotEmpty()) return true
if (isOpenClass && referenceSearcher.hasOverrides(method)) return true
return false
}
private fun findEquivalentOverload(method: PsiMethod, overloads: Collection<PsiMethod>): EquivalentOverloadInfo? {
val statement = method.getBody()?.getStatements()?.singleOrNull() ?: return null
val methodCall = when (statement) {
is PsiExpressionStatement -> statement.getExpression()
is PsiReturnStatement -> statement.getReturnValue()
else -> null
} as? PsiMethodCallExpression ?: return null
val expectedMethodName = if (method.isConstructor()) "this" else method.getName()
val refExpr = methodCall.getMethodExpression()
if (refExpr.isQualified() || refExpr.getReferenceName() != expectedMethodName) return null
val target = refExpr.resolve() as? PsiMethod ?: return null
if (target !in overloads) return null
val parameterDefaults = calcTargetParameterDefaults(method, target, methodCall) ?: return null
return EquivalentOverloadInfo(target, parameterDefaults)
}
private fun calcTargetParameterDefaults(method: PsiMethod, target: PsiMethod, targetCall: PsiMethodCallExpression): List<PsiExpression>? {
val parameters = method.getParameterList().getParameters()
val targetParameters = target.getParameterList().getParameters()
if (parameters.size() >= targetParameters.size()) return null
val args = targetCall.getArgumentList().getExpressions()
if (args.size() != targetParameters.size()) return null // incorrect code
for (i in parameters.indices) {
val parameter = parameters[i]
val targetParameter = targetParameters[i]
if (parameter.getName() != targetParameter.getName() || parameter.getType() != targetParameter.getType()) return null
val arg = args[i]
if (arg !is PsiReferenceExpression || arg.resolve() != parameter) return null
}
return args.drop(parameters.size())
}
private fun dropOverloadsForDefaultValues(equivalenceMap: Map<PsiMethod, EquivalentOverloadInfo>) {
val dropCandidates = equivalenceMap
.map { it.key }
.sortBy { -it.getParameterList().getParametersCount() } // we will try to drop them starting from ones with more parameters
@DropCandidatesLoop
for (method in dropCandidates) {
val paramCount = method.getParameterList().getParametersCount()
val targetInfo = equivalenceMap[method]!!
val targetParamCount = targetInfo.method.getParameterList().getParametersCount()
assert(paramCount < targetParamCount)
val defaults = targetInfo.parameterDefaults
assert(defaults.size() == targetParamCount - paramCount)
val targetDefaults = methodToLastParameterDefaults.getOrPut(targetInfo.method, { ArrayList() })
for (i in defaults.indices) {
val default = defaults[defaults.size() - i - 1]
if (i < targetDefaults.size()) { // default for this parameter has already been assigned
if (targetDefaults[i].getText() != default.getText()) continue@DropCandidatesLoop
}
else {
targetDefaults.add(default)
}
}
methodsToDrop.add(method)
}
}
private fun getAnnotationsFingerprint(method: PsiMethod): Any {
return method.getModifierList().getAnnotations().map { it.getText() }
}
}
public fun Converter.convertParameterList(
method: PsiMethod,
overloadReducer: OverloadReducer?,
convertParameter: (parameter: PsiParameter, default: DeferredElement<Expression>?) -> Parameter = { parameter, default -> convertParameter(parameter, defaultValue = default) },
correctCodeConverter: CodeConverter.() -> CodeConverter = { this }
): ParameterList {
val parameterList = method.getParameterList()
val params = parameterList.getParameters()
return ParameterList(params.indices.map { i ->
val parameter = params[i]
val defaultValue = overloadReducer?.parameterDefault(method, i)
val defaultValueConverted = if (defaultValue != null)
deferredElement { codeConverter -> codeConverter.correctCodeConverter().convertExpression(defaultValue, parameter.getType()) }
else
null
convertParameter(parameter, defaultValueConverted)
}).assignPrototype(parameterList)
}
@@ -16,7 +16,11 @@
package org.jetbrains.kotlin.j2k.ast
import org.jetbrains.kotlin.j2k.*
import com.intellij.psi.PsiExpression
import com.intellij.psi.PsiMethod
import org.jetbrains.kotlin.j2k.CodeBuilder
import org.jetbrains.kotlin.j2k.OverloadReducer
import org.jetbrains.kotlin.j2k.append
abstract class Member(var annotations: Annotations, val modifiers: Modifiers) : Element()
@@ -26,7 +30,8 @@ class ClassBody (
val members: List<Member>,
val companionObjectMembers: List<Member>,
val lBrace: LBrace,
val rBrace: RBrace) {
val rBrace: RBrace,
val overloadReducer: OverloadReducer) {
fun append(builder: CodeBuilder) {
val membersFiltered = members.filter { !it.isEmpty }
@@ -22,9 +22,9 @@ import com.intellij.util.IncorrectOperationException
abstract class Constructor(
annotations: Annotations,
modifiers: Modifiers,
public val parameterList: ParameterList,
protected val body: DeferredElement<Block>
) : Member(annotations, modifiers)
parameterList: ParameterList,
body: DeferredElement<Block>
) : FunctionLike(annotations, modifiers, parameterList, body)
class PrimaryConstructor(
annotations: Annotations,
@@ -36,14 +36,14 @@ class PrimaryConstructor(
override fun generateCode(builder: CodeBuilder) { throw IncorrectOperationException() }
public fun initializer(): Initializer
= Initializer(body, Modifiers.Empty).assignPrototypesFrom(this, CommentsAndSpacesInheritance(commentsBefore = false))
= Initializer(body!!, Modifiers.Empty).assignPrototypesFrom(this, CommentsAndSpacesInheritance(commentsBefore = false))
public fun createSignature(converter: Converter): PrimaryConstructorSignature {
val signature = PrimaryConstructorSignature(annotations, modifiers, parameterList)
// assign prototypes later because we don't know yet whether the body is empty or not
converter.addPostUnfoldDeferredElementsAction {
val inheritance = CommentsAndSpacesInheritance(blankLinesBefore = false, commentsAfter = body.isEmpty, commentsInside = body.isEmpty)
val inheritance = CommentsAndSpacesInheritance(blankLinesBefore = false, commentsAfter = body!!.isEmpty, commentsInside = body.isEmpty)
signature.assignPrototypesFrom(this, inheritance)
}
@@ -89,7 +89,7 @@ class SecondaryConstructor(
builder append " : " append thisOrSuperCall
}
builder append " " append body
builder append " " append body!!
}
}
@@ -18,16 +18,23 @@ package org.jetbrains.kotlin.j2k.ast
import org.jetbrains.kotlin.j2k.*
abstract class FunctionLike(
annotations: Annotations,
modifiers: Modifiers,
val parameterList: ParameterList,
val body: DeferredElement<Block>?
) : Member(annotations, modifiers)
class Function(
val name: Identifier,
annotations: Annotations,
modifiers: Modifiers,
val returnType: Type,
val typeParameterList: TypeParameterList,
val parameterList: ParameterList,
val body: DeferredElement<Block>?,
parameterList: ParameterList,
body: DeferredElement<Block>?,
val isInTrait: Boolean
) : Member(annotations, modifiers) {
) : FunctionLike(annotations, modifiers, parameterList, body) {
private fun presentationModifiers(): Modifiers {
var modifiers = this.modifiers
@@ -0,0 +1,17 @@
class A {
private String s = "";
private int x = 0;
public A() {
}
public A(int p, String s){
this(p, s, 1);
}
public A(int p, String s, int x) {
this();
this.s = s;
this.x = x;
}
}
@@ -0,0 +1,11 @@
// ERROR: Val cannot be reassigned
// ERROR: Val cannot be reassigned
class A() {
private val s = ""
private val x = 0
overloads public constructor(p: Int, s: String, x: Int = 1) : this() {
this.s = s
this.x = x
}
}
@@ -0,0 +1,7 @@
abstract class A {
public abstract void foo(int p);
public void foo(){
foo(1);
}
}
@@ -0,0 +1,7 @@
abstract class A {
public abstract fun foo(p: Int)
public fun foo() {
foo(1)
}
}
@@ -0,0 +1,15 @@
class A {
private int field = 0;
public int getField() {
return field;
}
public void setField(int field) {
this.field = field;
}
public void setField() {
setField(0);
}
}
@@ -0,0 +1,7 @@
class A {
public var field: Int = 0
public fun setField() {
field = 0
}
}
@@ -0,0 +1,44 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
class A {
@NotNull
Object foo(String s) {
System.out.println("s = " + s);
return "";
}
@NotNull
Object foo() {
return foo(null);
}
@Nullable
Object bar(String s) {
System.out.println("s = " + s);
return s == null ? "" : null;
}
@NotNull
Object bar() {
return bar(null);
}
public @Nullable Object bar1(String s) {
System.out.println("s = " + s);
return s == null ? "" : null;
}
public @NotNull Object bar1() {
return bar1(null);
}
@Deprecated
public void f() {
f(1);
}
public void f(int p) {
System.out.println("p = " + p);
}
}
@@ -0,0 +1,35 @@
// ERROR: Type mismatch: inferred type is kotlin.Any? but kotlin.Any was expected
// ERROR: Type mismatch: inferred type is kotlin.Any? but kotlin.Any was expected
class A {
overloads fun foo(s: String? = null): Any {
println("s = " + s!!)
return ""
}
fun bar(s: String?): Any? {
println("s = " + s!!)
return if (s == null) "" else null
}
fun bar(): Any {
return bar(null)
}
public fun bar1(s: String?): Any? {
println("s = " + s!!)
return if (s == null) "" else null
}
public fun bar1(): Any {
return bar1(null)
}
deprecated("")
public fun f() {
f(1)
}
public fun f(p: Int) {
println("p = " + p)
}
}
@@ -0,0 +1,46 @@
interface I {
void foo(int i, char c, String s);
void bar();
}
class A implements I{
public void foo(int i, char c, String s) {
System.out.println("foo" + i + c + s);
}
public void foo(int i, char c) {
foo(i, c, "");
}
public void foo(int i) {
foo(i, 'a', "");
}
public void bar() {
bar(1);
}
public void bar(int i) {}
public void x() {
x(1);
}
public void x(int i) {}
public void y() {
y(1);
}
public void y(int i) {}
}
class B extends A {
public void x() {
super.x();
}
public void y(int i) {
super.y(i);
}
}
@@ -0,0 +1,49 @@
trait I {
public fun foo(i: Int, c: Char, s: String)
public fun bar()
}
open class A : I {
override fun foo(i: Int, c: Char, s: String) {
println("foo" + i + c + s)
}
public fun foo(i: Int, c: Char) {
foo(i, c, "")
}
public fun foo(i: Int) {
foo(i, 'a', "")
}
override fun bar() {
bar(1)
}
public fun bar(i: Int) {
}
public open fun x() {
x(1)
}
public fun x(i: Int) {
}
public fun y() {
y(1)
}
public open fun y(i: Int) {
}
}
class B : A() {
override fun x() {
super.x()
}
override fun y(i: Int) {
super.y(i)
}
}
@@ -0,0 +1,22 @@
class A {
void foo(int i, char c, String s) {
System.out.println("foo" + i + c + s);
}
void foo(int i, char c) {
foo(i, c, "");
}
void foo(int i) {
foo(i, 'a', "");
}
int bar(String s) {
System.out.println("s = " + s);
return 0;
}
int bar() {
return bar(null);
}
}
@@ -0,0 +1,10 @@
class A {
overloads fun foo(i: Int, c: Char = 'a', s: String = "") {
println("foo" + i + c + s)
}
overloads fun bar(s: String? = null): Int {
println("s = " + s!!)
return 0
}
}
@@ -0,0 +1,12 @@
class A {
public void foo(int p) {
System.out.println("p = [" + p + "]");
}
public synchronized void foo(){
foo(calcSomething());
}
// this method should be invoked under synchronized block!
private int calcSomething() { return 0; }
}
@@ -0,0 +1,14 @@
class A {
public fun foo(p: Int) {
println("p = [" + p + "]")
}
synchronized public fun foo() {
foo(calcSomething())
}
// this method should be invoked under synchronized block!
private fun calcSomething(): Int {
return 0
}
}
@@ -78,6 +78,7 @@ import java.util.regex.Pattern;
JavaToKotlinConverterForWebDemoTestGenerated.NewClassExpression.class,
JavaToKotlinConverterForWebDemoTestGenerated.Nullability.class,
JavaToKotlinConverterForWebDemoTestGenerated.ObjectLiteral.class,
JavaToKotlinConverterForWebDemoTestGenerated.Overloads.class,
JavaToKotlinConverterForWebDemoTestGenerated.PackageStatement.class,
JavaToKotlinConverterForWebDemoTestGenerated.ParenthesizedExpression.class,
JavaToKotlinConverterForWebDemoTestGenerated.PolyadicExpression.class,
@@ -1313,6 +1314,12 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo
doTest(fileName);
}
@TestMetadata("secondaryConstructorWithDefaultValues2.java")
public void testSecondaryConstructorWithDefaultValues2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/constructors/secondaryConstructorWithDefaultValues2.java");
doTest(fileName);
}
@TestMetadata("secondaryConstructorsInInnerClass.java")
public void testSecondaryConstructorsInInnerClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/constructors/secondaryConstructorsInInnerClass.java");
@@ -3489,6 +3496,51 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo
}
}
@TestMetadata("j2k/testData/fileOrElement/overloads")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Overloads extends AbstractJavaToKotlinConverterForWebDemoTest {
@TestMetadata("Abstract.java")
public void testAbstract() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/overloads/Abstract.java");
doTest(fileName);
}
@TestMetadata("AccessorToDrop.java")
public void testAccessorToDrop() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/overloads/AccessorToDrop.java");
doTest(fileName);
}
public void testAllFilesPresentInOverloads() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("j2k/testData/fileOrElement/overloads"), Pattern.compile("^(.+)\\.java$"), true);
}
@TestMetadata("Annotations.java")
public void testAnnotations() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/overloads/Annotations.java");
doTest(fileName);
}
@TestMetadata("Override.java")
public void testOverride() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/overloads/Override.java");
doTest(fileName);
}
@TestMetadata("Simple.java")
public void testSimple() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/overloads/Simple.java");
doTest(fileName);
}
@TestMetadata("Synchronized.java")
public void testSynchronized() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/overloads/Synchronized.java");
doTest(fileName);
}
}
@TestMetadata("j2k/testData/fileOrElement/packageStatement")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -78,6 +78,7 @@ import java.util.regex.Pattern;
JavaToKotlinConverterSingleFileTestGenerated.NewClassExpression.class,
JavaToKotlinConverterSingleFileTestGenerated.Nullability.class,
JavaToKotlinConverterSingleFileTestGenerated.ObjectLiteral.class,
JavaToKotlinConverterSingleFileTestGenerated.Overloads.class,
JavaToKotlinConverterSingleFileTestGenerated.PackageStatement.class,
JavaToKotlinConverterSingleFileTestGenerated.ParenthesizedExpression.class,
JavaToKotlinConverterSingleFileTestGenerated.PolyadicExpression.class,
@@ -1313,6 +1314,12 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo
doTest(fileName);
}
@TestMetadata("secondaryConstructorWithDefaultValues2.java")
public void testSecondaryConstructorWithDefaultValues2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/constructors/secondaryConstructorWithDefaultValues2.java");
doTest(fileName);
}
@TestMetadata("secondaryConstructorsInInnerClass.java")
public void testSecondaryConstructorsInInnerClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/constructors/secondaryConstructorsInInnerClass.java");
@@ -3489,6 +3496,51 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo
}
}
@TestMetadata("j2k/testData/fileOrElement/overloads")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Overloads extends AbstractJavaToKotlinConverterSingleFileTest {
@TestMetadata("Abstract.java")
public void testAbstract() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/overloads/Abstract.java");
doTest(fileName);
}
@TestMetadata("AccessorToDrop.java")
public void testAccessorToDrop() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/overloads/AccessorToDrop.java");
doTest(fileName);
}
public void testAllFilesPresentInOverloads() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("j2k/testData/fileOrElement/overloads"), Pattern.compile("^(.+)\\.java$"), true);
}
@TestMetadata("Annotations.java")
public void testAnnotations() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/overloads/Annotations.java");
doTest(fileName);
}
@TestMetadata("Override.java")
public void testOverride() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/overloads/Override.java");
doTest(fileName);
}
@TestMetadata("Simple.java")
public void testSimple() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/overloads/Simple.java");
doTest(fileName);
}
@TestMetadata("Synchronized.java")
public void testSynchronized() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/overloads/Synchronized.java");
doTest(fileName);
}
}
@TestMetadata("j2k/testData/fileOrElement/packageStatement")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)