Java to Kotlin converter: refactored code moving dealing with constructors from Converter to ConstructorConverter
This commit is contained in:
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
* Copyright 2010-2014 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.jet.j2k
|
||||
|
||||
import com.intellij.psi.*
|
||||
import org.jetbrains.jet.j2k.ast.*
|
||||
import org.jetbrains.jet.j2k.visitors.ExpressionVisitor
|
||||
import com.intellij.psi.util.PsiUtil
|
||||
import java.util.HashMap
|
||||
import java.util.ArrayList
|
||||
import java.util.HashSet
|
||||
|
||||
class ConstructorConverter(private val converter: Converter) {
|
||||
private val typeConverter = converter.typeConverter
|
||||
|
||||
public fun convertConstructor(constructor: PsiMethod,
|
||||
annotations: Annotations,
|
||||
modifiers: Modifiers,
|
||||
membersToRemove: MutableSet<PsiMember>,
|
||||
postProcessBody: (Block) -> Block): Member {
|
||||
if (constructor.isPrimaryConstructor()) {
|
||||
return convertPrimaryConstructor(constructor, annotations, modifiers, membersToRemove, postProcessBody)
|
||||
}
|
||||
else {
|
||||
val params = converter.convertParameterList(constructor.getParameterList())
|
||||
var body = postProcessBody(converter.convertBlock(constructor.getBody()))
|
||||
val containingClass = constructor.getContainingClass()
|
||||
val typeParameterList = converter.convertTypeParameterList(containingClass?.getTypeParameterList())
|
||||
val factoryFunctionType = ClassType(containingClass?.declarationIdentifier() ?: Identifier.Empty,
|
||||
typeParameterList.parameters,
|
||||
Nullability.NotNull,
|
||||
converter.settings).assignNoPrototype()
|
||||
return FactoryFunction(annotations, modifiers, factoryFunctionType, params, typeParameterList, body)
|
||||
}
|
||||
}
|
||||
|
||||
private fun convertPrimaryConstructor(constructor: PsiMethod,
|
||||
annotations: Annotations,
|
||||
modifiers: Modifiers,
|
||||
membersToRemove: MutableSet<PsiMember>,
|
||||
postProcessBody: (Block) -> Block): PrimaryConstructor {
|
||||
val params = constructor.getParameterList().getParameters()
|
||||
val parameterToField = HashMap<PsiParameter, Pair<PsiField, Type>>()
|
||||
val body = constructor.getBody()
|
||||
val block = if (body != null) {
|
||||
val statementsToRemove = HashSet<PsiStatement>()
|
||||
val usageReplacementMap = HashMap<PsiVariable, String>()
|
||||
for (parameter in params) {
|
||||
val (field, initializationStatement) = findBackingFieldForConstructorParameter(parameter, constructor) ?: continue
|
||||
|
||||
val fieldType = typeConverter.convertVariableType(field)
|
||||
val parameterType = typeConverter.convertVariableType(parameter)
|
||||
// types can be different only in nullability
|
||||
val `type` = if (fieldType == parameterType) {
|
||||
fieldType
|
||||
}
|
||||
else if (fieldType.toNotNullType() == parameterType.toNotNullType()) {
|
||||
if (fieldType.isNullable) fieldType else parameterType // prefer nullable one
|
||||
}
|
||||
else {
|
||||
continue
|
||||
}
|
||||
|
||||
parameterToField.put(parameter, field to `type`)
|
||||
statementsToRemove.add(initializationStatement)
|
||||
membersToRemove.add(field)
|
||||
|
||||
if (field.getName() != parameter.getName()) {
|
||||
usageReplacementMap.put(parameter, field.getName()!!)
|
||||
}
|
||||
}
|
||||
|
||||
val correctedConverter = converter.withExpressionVisitor { ExpressionVisitor(it, usageReplacementMap) }
|
||||
postProcessBody(correctedConverter.convertBlock(body, false, { !statementsToRemove.contains(it) }))
|
||||
}
|
||||
else {
|
||||
Block.Empty
|
||||
}
|
||||
|
||||
val parameterList = ParameterList(params.map { parameter ->
|
||||
if (!parameterToField.containsKey(parameter)) {
|
||||
converter.convertParameter(parameter)
|
||||
}
|
||||
else {
|
||||
val (field, `type`) = parameterToField[parameter]!!
|
||||
Parameter(field.declarationIdentifier(),
|
||||
`type`,
|
||||
if (converter.isVal(field)) Parameter.VarValModifier.Val else Parameter.VarValModifier.Var,
|
||||
converter.convertAnnotations(parameter) + converter.convertAnnotations(field),
|
||||
converter.convertModifiers(field).filter { it in ACCESS_MODIFIERS }).assignPrototypes(listOf(parameter, field), CommentsAndSpacesInheritance(blankLinesBefore = false))
|
||||
}
|
||||
}).assignPrototype(constructor.getParameterList())
|
||||
return PrimaryConstructor(annotations, modifiers, parameterList, block).assignPrototype(constructor)
|
||||
}
|
||||
|
||||
private fun findBackingFieldForConstructorParameter(parameter: PsiParameter, constructor: PsiMethod): Pair<PsiField, PsiStatement>? {
|
||||
val body = constructor.getBody() ?: return null
|
||||
|
||||
val refs = findVariableUsages(parameter, body)
|
||||
|
||||
if (refs.any { PsiUtil.isAccessedForWriting(it) }) return null
|
||||
|
||||
for (ref in refs) {
|
||||
val assignment = ref.getParent() as? PsiAssignmentExpression ?: continue
|
||||
if (assignment.getOperationSign().getTokenType() != JavaTokenType.EQ) continue
|
||||
val assignee = assignment.getLExpression() as? PsiReferenceExpression ?: continue
|
||||
if (!isQualifierEmptyOrThis(assignee)) continue
|
||||
val field = assignee.resolve() as? PsiField ?: continue
|
||||
if (field.getContainingClass() != constructor.getContainingClass()) continue
|
||||
if (field.hasModifierProperty(PsiModifier.STATIC)) continue
|
||||
if (field.getInitializer() != null) continue
|
||||
|
||||
// assignment should be a top-level statement
|
||||
val statement = assignment.getParent() as? PsiExpressionStatement ?: continue
|
||||
if (statement.getParent() != body) continue
|
||||
|
||||
// and no other assignments to field should exist in the constructor
|
||||
if (findVariableUsages(field, body).any { it != assignee && PsiUtil.isAccessedForWriting(it) && isQualifierEmptyOrThis(it) }) continue
|
||||
//TODO: check access to field before assignment
|
||||
|
||||
return field to statement
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
public fun postProcessConstructors(classBody: ClassBody, psiClass: PsiClass): ClassBody {
|
||||
if (psiClass.getPrimaryConstructor() == null && psiClass.getConstructors().size > 1) {
|
||||
return generateArtificialPrimaryConstructor(psiClass.declarationIdentifier(), classBody)
|
||||
}
|
||||
else {
|
||||
correctFactoryFunctions(classBody, psiClass.getName()!!)
|
||||
return classBody
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateArtificialPrimaryConstructor(className: Identifier, classBody: ClassBody): ClassBody {
|
||||
assert(classBody.primaryConstructorSignature == null)
|
||||
|
||||
val finalOrWithEmptyInitializerFields = classBody.members.filterIsInstance(javaClass<Field>()).filter { it.isVal || it.initializer.isEmpty }
|
||||
val initializers = HashMap<Field, Expression>()
|
||||
for (factoryFunction in classBody.factoryFunctions()) {
|
||||
for (field in finalOrWithEmptyInitializerFields) {
|
||||
initializers.put(field, getDefaultInitializer(field))
|
||||
}
|
||||
|
||||
val statements = ArrayList<Statement>()
|
||||
for (statement in factoryFunction.body!!.statements) {
|
||||
var keepStatement = true
|
||||
if (statement is AssignmentExpression) {
|
||||
val assignee = statement.left
|
||||
if (assignee is QualifiedExpression && (assignee.qualifier as? Identifier)?.name == FactoryFunction.tempValName) {
|
||||
val name = (assignee.identifier as Identifier).name
|
||||
for (field in finalOrWithEmptyInitializerFields) {
|
||||
if (name == field.identifier.name) {
|
||||
initializers.put(field, statement.right)
|
||||
keepStatement = false
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (keepStatement) {
|
||||
statements.add(statement)
|
||||
}
|
||||
}
|
||||
|
||||
val initializer = MethodCallExpression.buildNotNull(null,
|
||||
className.name,
|
||||
finalOrWithEmptyInitializerFields.map { initializers[it]!! }).assignNoPrototype()
|
||||
if (statements.isNotEmpty()) {
|
||||
val localVar = LocalVariable(FactoryFunction.tempValIdentifier(),
|
||||
Annotations.Empty,
|
||||
Modifiers.Empty,
|
||||
null,
|
||||
initializer,
|
||||
true).assignNoPrototype()
|
||||
statements.add(0, DeclarationStatement(listOf(localVar)).assignNoPrototype())
|
||||
statements.add(ReturnStatement(FactoryFunction.tempValIdentifier()).assignNoPrototype())
|
||||
}
|
||||
else {
|
||||
statements.add(ReturnStatement(initializer).assignNoPrototype())
|
||||
}
|
||||
|
||||
factoryFunction.body = Block(statements, LBrace().assignNoPrototype(), RBrace().assignNoPrototype()).assignNoPrototype()
|
||||
}
|
||||
|
||||
val parameters = finalOrWithEmptyInitializerFields.map { field ->
|
||||
val varValModifier = if (field.isVal) Parameter.VarValModifier.Val else Parameter.VarValModifier.Var
|
||||
Parameter(field.identifier, field.`type`, varValModifier, field.annotations, field.modifiers.filter { it in ACCESS_MODIFIERS }).assignPrototypesFrom(field)
|
||||
}
|
||||
|
||||
val modifiers = Modifiers(listOf(Modifier.PRIVATE)).assignNoPrototype()
|
||||
val parameterList = ParameterList(parameters).assignNoPrototype()
|
||||
val constructorSignature = PrimaryConstructorSignature(modifiers, parameterList).assignNoPrototype()
|
||||
val updatedMembers = classBody.members.filter { !finalOrWithEmptyInitializerFields.contains(it) }
|
||||
return ClassBody(constructorSignature, updatedMembers, classBody.classObjectMembers, classBody.lBrace, classBody.rBrace)
|
||||
}
|
||||
|
||||
private fun correctFactoryFunctions(classBody: ClassBody, className: String) {
|
||||
for (factoryFunction in classBody.factoryFunctions()) {
|
||||
val body = factoryFunction.body!!
|
||||
val statements = correctFactoryFunctionStatements(body, className)
|
||||
factoryFunction.body = Block(statements, body.lBrace, body.rBrace).assignPrototypesFrom(body)
|
||||
}
|
||||
}
|
||||
|
||||
private fun correctFactoryFunctionStatements(body: Block, className: String): List<Statement> {
|
||||
val statements = ArrayList(body.statements)
|
||||
|
||||
// searching for other constructor call in form "this(...)"
|
||||
// it's not necessary the first statement because of statements inserted for writable parameters
|
||||
for (i in statements.indices) {
|
||||
val statement = statements[i]
|
||||
if (statement is MethodCallExpression) {
|
||||
if ((statement.methodExpression as? Identifier)?.name == "this") {
|
||||
val constructorCall = MethodCallExpression.buildNotNull(null, className, statement.arguments).assignPrototypesFrom(statement)
|
||||
if (i == statements.lastIndex) { // constructor call is the last statement - no intermediate variable needed
|
||||
statements[i] = ReturnStatement(constructorCall).assignNoPrototype()
|
||||
return statements
|
||||
}
|
||||
val localVar = LocalVariable(FactoryFunction.tempValIdentifier(), Annotations.Empty, Modifiers.Empty, null, constructorCall, true).assignNoPrototype()
|
||||
statements[i] = DeclarationStatement(listOf(localVar)).assignNoPrototype()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
statements.add(ReturnStatement(FactoryFunction.tempValIdentifier()).assignNoPrototype())
|
||||
return statements
|
||||
}
|
||||
|
||||
private fun ClassBody.factoryFunctions() = classObjectMembers.filterIsInstance(javaClass<FactoryFunction>())
|
||||
}
|
||||
@@ -38,10 +38,10 @@ public class Converter private(val project: Project, val settings: ConverterSett
|
||||
val methodReturnType: PsiType?,
|
||||
val expressionVisitorFactory: (Converter) -> ExpressionVisitor,
|
||||
val statementVisitorFactory: (Converter) -> StatementVisitor)
|
||||
val typeConverter: TypeConverter = state.typeConverter
|
||||
val methodReturnType: PsiType? = state.methodReturnType
|
||||
|
||||
val typeConverter: TypeConverter get() = state.typeConverter
|
||||
val methodReturnType: PsiType? get() = state.methodReturnType
|
||||
|
||||
private val constructorConverter = ConstructorConverter(this)
|
||||
private val expressionVisitor = state.expressionVisitorFactory(this)
|
||||
private val statementVisitor = state.statementVisitorFactory(this)
|
||||
|
||||
@@ -187,12 +187,7 @@ public class Converter private(val project: Project, val settings: ConverterSett
|
||||
psiClass.isEnum() -> Enum(name, annotations, modifiers, typeParameters, listOf(), listOf(), implementsTypes, classBody)
|
||||
|
||||
else -> {
|
||||
if (psiClass.getPrimaryConstructor() == null && psiClass.getConstructors().size > 1) {
|
||||
classBody = generateArtificialPrimaryConstructor(name, classBody)
|
||||
}
|
||||
else {
|
||||
correctFactoryFunctions(classBody, psiClass.getName()!!)
|
||||
}
|
||||
classBody = constructorConverter.postProcessConstructors(classBody, psiClass)
|
||||
|
||||
val baseClassParams: List<Expression> = run {
|
||||
val superVisitor = SuperVisitor()
|
||||
@@ -219,106 +214,6 @@ public class Converter private(val project: Project, val settings: ConverterSett
|
||||
}.assignPrototype(psiClass)
|
||||
}
|
||||
|
||||
private fun ClassBody.factoryFunctions() = classObjectMembers.filterIsInstance(javaClass<FactoryFunction>())
|
||||
|
||||
private fun generateArtificialPrimaryConstructor(className: Identifier, classBody: ClassBody): ClassBody {
|
||||
assert(classBody.primaryConstructorSignature == null)
|
||||
|
||||
val finalOrWithEmptyInitializerFields = classBody.members.filterIsInstance(javaClass<Field>()).filter { it.isVal || it.initializer.isEmpty }
|
||||
val initializers = HashMap<Field, Expression>()
|
||||
for (factoryFunction in classBody.factoryFunctions()) {
|
||||
for (field in finalOrWithEmptyInitializerFields) {
|
||||
initializers.put(field, getDefaultInitializer(field))
|
||||
}
|
||||
|
||||
val statements = ArrayList<Statement>()
|
||||
for (statement in factoryFunction.body!!.statements) {
|
||||
var keepStatement = true
|
||||
if (statement is AssignmentExpression) {
|
||||
val assignee = statement.left
|
||||
if (assignee is QualifiedExpression && (assignee.qualifier as? Identifier)?.name == FactoryFunction.tempValName) {
|
||||
val name = (assignee.identifier as Identifier).name
|
||||
for (field in finalOrWithEmptyInitializerFields) {
|
||||
if (name == field.identifier.name) {
|
||||
initializers.put(field, statement.right)
|
||||
keepStatement = false
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (keepStatement) {
|
||||
statements.add(statement)
|
||||
}
|
||||
}
|
||||
|
||||
val initializer = MethodCallExpression.buildNotNull(null,
|
||||
className.name,
|
||||
finalOrWithEmptyInitializerFields.map { initializers[it]!! }).assignNoPrototype()
|
||||
if (statements.isNotEmpty()) {
|
||||
val localVar = LocalVariable(FactoryFunction.tempValIdentifier(),
|
||||
Annotations.Empty,
|
||||
Modifiers.Empty,
|
||||
null,
|
||||
initializer,
|
||||
true).assignNoPrototype()
|
||||
statements.add(0, DeclarationStatement(listOf(localVar)).assignNoPrototype())
|
||||
statements.add(ReturnStatement(FactoryFunction.tempValIdentifier()).assignNoPrototype())
|
||||
}
|
||||
else {
|
||||
statements.add(ReturnStatement(initializer).assignNoPrototype())
|
||||
}
|
||||
|
||||
factoryFunction.body = Block(statements, LBrace().assignNoPrototype(), RBrace().assignNoPrototype()).assignNoPrototype()
|
||||
}
|
||||
|
||||
val parameters = finalOrWithEmptyInitializerFields.map { field ->
|
||||
val varValModifier = if (field.isVal) Parameter.VarValModifier.Val else Parameter.VarValModifier.Var
|
||||
Parameter(field.identifier, field.`type`, varValModifier, field.annotations, field.modifiers.filter { it in ACCESS_MODIFIERS }).assignPrototypesFrom(field)
|
||||
}
|
||||
|
||||
val modifiers = Modifiers(listOf(Modifier.PRIVATE)).assignNoPrototype()
|
||||
val parameterList = ParameterList(parameters).assignNoPrototype()
|
||||
val constructorSignature = PrimaryConstructorSignature(modifiers, parameterList).assignNoPrototype()
|
||||
val updatedMembers = classBody.members.filter { !finalOrWithEmptyInitializerFields.contains(it) }
|
||||
return ClassBody(constructorSignature, updatedMembers, classBody.classObjectMembers, classBody.lBrace, classBody.rBrace)
|
||||
}
|
||||
|
||||
private fun correctFactoryFunctions(classBody: ClassBody, className: String) {
|
||||
for (factoryFunction in classBody.factoryFunctions()) {
|
||||
val body = factoryFunction.body!!
|
||||
val statements = correctFactoryFunctionStatements(body, className)
|
||||
factoryFunction.body = Block(statements, body.lBrace, body.rBrace).assignPrototypesFrom(body)
|
||||
}
|
||||
}
|
||||
|
||||
private fun correctFactoryFunctionStatements(body: Block, className: String): List<Statement> {
|
||||
val statements = ArrayList(body.statements)
|
||||
|
||||
// searching for other constructor call in form "this(...)"
|
||||
// it's not necessary the first statement because of statements inserted for writable parameters
|
||||
for (i in statements.indices) {
|
||||
val statement = statements[i]
|
||||
if (statement is MethodCallExpression) {
|
||||
if ((statement.methodExpression as? Identifier)?.name == "this") {
|
||||
val constructorCall = MethodCallExpression.buildNotNull(null, className, statement.arguments).assignPrototypesFrom(statement)
|
||||
if (i == statements.lastIndex) { // constructor call is the last statement - no intermediate variable needed
|
||||
statements[i] = ReturnStatement(constructorCall).assignNoPrototype()
|
||||
return statements
|
||||
}
|
||||
val localVar = LocalVariable(FactoryFunction.tempValIdentifier(), Annotations.Empty, Modifiers.Empty, null, constructorCall, true).assignNoPrototype()
|
||||
statements[i] = DeclarationStatement(listOf(localVar)).assignNoPrototype()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
statements.add(ReturnStatement(FactoryFunction.tempValIdentifier()).assignNoPrototype())
|
||||
return statements
|
||||
}
|
||||
|
||||
private fun convertInitializer(initializer: PsiClassInitializer): Initializer {
|
||||
return Initializer(convertBlock(initializer.getBody()), convertModifiers(initializer)).assignPrototype(initializer)
|
||||
}
|
||||
@@ -368,7 +263,7 @@ public class Converter private(val project: Project, val settings: ConverterSett
|
||||
return if (convertedType == initializerType) null else convertedType
|
||||
}
|
||||
|
||||
private fun isVal(field: PsiField): Boolean {
|
||||
fun isVal(field: PsiField): Boolean {
|
||||
if (field.hasModifierProperty(PsiModifier.FINAL)) return true
|
||||
if (!field.hasModifierProperty(PsiModifier.PRIVATE)) return false
|
||||
val containingClass = field.getContainingClass() ?: return false
|
||||
@@ -421,20 +316,7 @@ public class Converter private(val project: Project, val settings: ConverterSett
|
||||
}
|
||||
|
||||
if (method.isConstructor()) {
|
||||
if (method.isPrimaryConstructor()) {
|
||||
return convertPrimaryConstructor(method, annotations, modifiers, membersToRemove, postProcessBody)
|
||||
}
|
||||
else {
|
||||
val params = convertParameterList(method.getParameterList())
|
||||
var body = postProcessBody(convertBlock(method.getBody()))
|
||||
val containingClass = method.getContainingClass()
|
||||
val typeParameterList = convertTypeParameterList(containingClass?.getTypeParameterList())
|
||||
val factoryFunctionType = ClassType(containingClass?.declarationIdentifier() ?: Identifier.Empty,
|
||||
typeParameterList.parameters,
|
||||
Nullability.NotNull,
|
||||
settings).assignNoPrototype()
|
||||
return FactoryFunction(annotations, modifiers, factoryFunctionType, params, typeParameterList, body)
|
||||
}
|
||||
return constructorConverter.convertConstructor(method, annotations, modifiers, membersToRemove, postProcessBody)
|
||||
}
|
||||
else {
|
||||
val isOverride = isOverride(method)
|
||||
@@ -494,96 +376,6 @@ public class Converter private(val project: Project, val settings: ConverterSett
|
||||
return false
|
||||
}
|
||||
|
||||
private fun convertPrimaryConstructor(constructor: PsiMethod,
|
||||
annotations: Annotations,
|
||||
modifiers: Modifiers,
|
||||
membersToRemove: MutableSet<PsiMember>,
|
||||
postProcessBody: (Block) -> Block): PrimaryConstructor {
|
||||
val params = constructor.getParameterList().getParameters()
|
||||
val parameterToField = HashMap<PsiParameter, Pair<PsiField, Type>>()
|
||||
val body = constructor.getBody()
|
||||
val block = if (body != null) {
|
||||
val statementsToRemove = HashSet<PsiStatement>()
|
||||
val usageReplacementMap = HashMap<PsiVariable, String>()
|
||||
for (parameter in params) {
|
||||
val (field, initializationStatement) = findBackingFieldForConstructorParameter(parameter, constructor) ?: continue
|
||||
|
||||
val fieldType = typeConverter.convertVariableType(field)
|
||||
val parameterType = typeConverter.convertVariableType(parameter)
|
||||
// types can be different only in nullability
|
||||
val `type` = if (fieldType == parameterType) {
|
||||
fieldType
|
||||
}
|
||||
else if (fieldType.toNotNullType() == parameterType.toNotNullType()) {
|
||||
if (fieldType.isNullable) fieldType else parameterType // prefer nullable one
|
||||
}
|
||||
else {
|
||||
continue
|
||||
}
|
||||
|
||||
parameterToField.put(parameter, field to `type`)
|
||||
statementsToRemove.add(initializationStatement)
|
||||
membersToRemove.add(field)
|
||||
|
||||
if (field.getName() != parameter.getName()) {
|
||||
usageReplacementMap.put(parameter, field.getName()!!)
|
||||
}
|
||||
}
|
||||
|
||||
val correctedConverter = withExpressionVisitor { ExpressionVisitor(it, usageReplacementMap) }
|
||||
postProcessBody(correctedConverter.convertBlock(body, false, { !statementsToRemove.contains(it) }))
|
||||
}
|
||||
else {
|
||||
Block.Empty
|
||||
}
|
||||
|
||||
val parameterList = ParameterList(params.map { parameter ->
|
||||
if (!parameterToField.containsKey(parameter)) {
|
||||
convertParameter(parameter)
|
||||
}
|
||||
else {
|
||||
val (field, `type`) = parameterToField[parameter]!!
|
||||
Parameter(field.declarationIdentifier(),
|
||||
`type`,
|
||||
if (isVal(field)) Parameter.VarValModifier.Val else Parameter.VarValModifier.Var,
|
||||
convertAnnotations(parameter) + convertAnnotations(field),
|
||||
convertModifiers(field).filter { it in ACCESS_MODIFIERS }).assignPrototypes(listOf(parameter, field), CommentsAndSpacesInheritance(blankLinesBefore = false))
|
||||
}
|
||||
}).assignPrototype(constructor.getParameterList())
|
||||
return PrimaryConstructor(annotations, modifiers, parameterList, block).assignPrototype(constructor)
|
||||
}
|
||||
|
||||
private fun findBackingFieldForConstructorParameter(parameter: PsiParameter, constructor: PsiMethod): Pair<PsiField, PsiStatement>? {
|
||||
val body = constructor.getBody() ?: return null
|
||||
|
||||
val refs = findVariableUsages(parameter, body)
|
||||
|
||||
if (refs.any { PsiUtil.isAccessedForWriting(it) }) return null
|
||||
|
||||
for (ref in refs) {
|
||||
val assignment = ref.getParent() as? PsiAssignmentExpression ?: continue
|
||||
if (assignment.getOperationSign().getTokenType() != JavaTokenType.EQ) continue
|
||||
val assignee = assignment.getLExpression() as? PsiReferenceExpression ?: continue
|
||||
if (!isQualifierEmptyOrThis(assignee)) continue
|
||||
val field = assignee.resolve() as? PsiField ?: continue
|
||||
if (field.getContainingClass() != constructor.getContainingClass()) continue
|
||||
if (field.hasModifierProperty(PsiModifier.STATIC)) continue
|
||||
if (field.getInitializer() != null) continue
|
||||
|
||||
// assignment should be a top-level statement
|
||||
val statement = assignment.getParent() as? PsiExpressionStatement ?: continue
|
||||
if (statement.getParent() != body) continue
|
||||
|
||||
// and no other assignments to field should exist in the constructor
|
||||
if (findVariableUsages(field, body).any { it != assignee && PsiUtil.isAccessedForWriting(it) && isQualifierEmptyOrThis(it) }) continue
|
||||
//TODO: check access to field before assignment
|
||||
|
||||
return field to statement
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun convertBlock(block: PsiCodeBlock?, notEmpty: Boolean = true, statementFilter: (PsiStatement) -> Boolean = { true }): Block {
|
||||
if (block == null) return Block.Empty
|
||||
|
||||
|
||||
Reference in New Issue
Block a user