Skip to content

Commit

Permalink
Bugfix for incorrect movement of expressions in init block (#1133)
Browse files Browse the repository at this point in the history
### What's done:
 * Fixed bug
 * Added a test

(#1132)
  • Loading branch information
sanyavertolet authored Dec 10, 2021
1 parent 4eaf14f commit d55ce53
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,10 @@ class SingleConstructorRule(configRules: List<RulesConfig>) : DiktatRule(
* - Create init block with other statements from the secondary constructor, including initialization of properties that require local variables or complex calls.
* - Finally, remove the secondary constructor.
*/
@Suppress("GENERIC_VARIABLE_WRONG_DECLARATION")
@Suppress(
"GENERIC_VARIABLE_WRONG_DECLARATION",
"TOO_LONG_FUNCTION"
)
private fun convertConstructorToPrimary(classNode: ASTNode, secondaryCtor: ASTNode) {
val secondaryCtorArguments = (secondaryCtor.psi as KtSecondaryConstructor).valueParameters

Expand Down Expand Up @@ -122,7 +125,15 @@ class SingleConstructorRule(configRules: List<RulesConfig>) : DiktatRule(
}
.distinct()

classNode.convertSecondaryConstructorToPrimary(secondaryCtor, declarationsAssignedInCtor, nonTrivialAssignments, otherStatements, comments)
// future init body
val expressions = (secondaryCtor.psi as KtSecondaryConstructor)
.bodyBlockExpression
?.statements
?.map { it.text }
?.filter { expr -> expr in otherStatements.map { it.text } || expr in nonTrivialAssignments.keys.map { it.text } }
?: emptyList()

classNode.convertSecondaryConstructorToPrimary(secondaryCtor, declarationsAssignedInCtor, nonTrivialAssignments, otherStatements, comments, expressions)
}

@Suppress("UnsafeCallOnNullableType")
Expand Down Expand Up @@ -152,14 +163,17 @@ class SingleConstructorRule(configRules: List<RulesConfig>) : DiktatRule(
@Suppress(
"NestedBlockDepth",
"GENERIC_VARIABLE_WRONG_DECLARATION",
"TOO_LONG_FUNCTION"
"TOO_LONG_FUNCTION",
"TOO_MANY_PARAMETERS",
"LongParameterList",
)
private fun ASTNode.convertSecondaryConstructorToPrimary(
secondaryCtor: ASTNode,
declarationsAssignedInCtor: List<KtProperty>,
nonTrivialAssignments: Map<KtBinaryExpression, KtNameReferenceExpression>,
otherStatements: List<KtExpression>,
comments: Map<String, ASTNode?>?
comments: Map<String, ASTNode?>?,
initBody: List<String>
) {
require(elementType == CLASS)

Expand All @@ -169,10 +183,6 @@ class SingleConstructorRule(configRules: List<RulesConfig>) : DiktatRule(

val primaryCtorNode = createPrimaryCtor(secondaryCtor, declarationsAssignedInCtor, nonTrivialSecondaryCtorParameters)

val initBody: MutableList<String> = mutableListOf()
initBody.addAll(otherStatements.map { it.text })
initBody.addAll(nonTrivialAssignments.keys.map { it.text })

val newArgumentListOfSecondaryCtor: List<KtParameter> = (secondaryCtor.psi as KtSecondaryConstructor)
.valueParameters
.filter { arg -> arg.name !in nonTrivialSecondaryCtorParameters.map { it.name } } // get rid of ctor arguments
Expand All @@ -191,17 +201,19 @@ class SingleConstructorRule(configRules: List<RulesConfig>) : DiktatRule(
}
}

// adding comments to init body
val initBodyWithComments = initBody.toMutableList()
comments?.forEach { (comment, nextExpression) ->
if (initBody.indexOf(nextExpression?.text) != -1) {
initBody.add(initBody.indexOf(nextExpression?.text), comment)
if (initBodyWithComments.indexOf(nextExpression?.text) != -1) {
initBodyWithComments.add(initBodyWithComments.indexOf(nextExpression?.text), comment)
}
}

if (otherStatements.isNotEmpty() || nonTrivialAssignments.isNotEmpty()) {
findChildByType(CLASS_BODY)?.run {
val classInitializer = kotlinParser.createNodeForInit(
"""|init {
| ${initBody.joinToString("\n")}
| ${initBodyWithComments.joinToString("\n")}
|}
""".trimMargin())
addChild(classInitializer, secondaryCtor)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,10 @@ class SingleConstructorRuleFixTest : FixTestBase("test/chapter6/classes", ::Sing
fun `should not replace constructor with init block`() {
fixAndCompare("ConstructorWithComplexAssignmentsExpected.kt", "ConstructorWithComplexAssignmentsTest.kt")
}

@Test
@Tag(WarningNames.SINGLE_CONSTRUCTOR_SHOULD_BE_PRIMARY)
fun `should keep expression order`() {
fixAndCompare("ConstructorShouldKeepExpressionsOrderExpected.kt", "ConstructorShouldKeepExpressionsOrderTest.kt")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package test.chapter6.classes

class Test (){
init {
str = "ABC"
println(str)
str = str.reversed()
println(str)
}
var str: String
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package test.chapter6.classes

class Test {
constructor() {
str = "ABC"
println(str)
str = str.reversed()
println(str)
}
var str: String
}

0 comments on commit d55ce53

Please sign in to comment.