Skip to content

Commit 4766835

Browse files
Properly format expressions wrapped *before* a binary operator (#1486)
### What's done: * Now, the indentation in binary expressions wrapped *before* a binary operator or an infix function is also controlled with `extendedIndentAfterOperators`. * The above is also true for `as` and `as?` operators. * The only exclusion is the Elvis operator (`?:`). * Fixes #1340.
1 parent d354d88 commit 4766835

7 files changed

Lines changed: 634 additions & 6 deletions

File tree

diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/kdoc/KdocMethods.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ class KdocMethods(configRules: List<RulesConfig>) : DiktatRule(
170170
val isReferenceExpressionWithSameName = node.findAllDescendantsWithSpecificType(REFERENCE_EXPRESSION).map { it.text }.contains((node.psi as KtFunction).name)
171171
val hasReturnKdoc = kdocTags != null && kdocTags.hasKnownKdocTag(KDocKnownTag.RETURN)
172172
return (hasExplicitNotUnitReturnType || isFunWithExpressionBody && !hasExplicitUnitReturnType && hasNotExpressionBodyTypes)
173-
&& !hasReturnKdoc && !isReferenceExpressionWithSameName
173+
&& !hasReturnKdoc && !isReferenceExpressionWithSameName
174174
}
175175

176176
private fun getExplicitlyThrownExceptions(node: ASTNode): Set<String> {

diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter4/SmartCastRule.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ class SmartCastRule(configRules: List<RulesConfig>) : DiktatRule(
126126
val list: MutableList<KtNameReferenceExpression> = mutableListOf()
127127
asExpr.forEach { asCall ->
128128
if (asCall.node.findParentNodeWithSpecificType(IF)
129-
== it.node.findParentNodeWithSpecificType(IF)) {
129+
== it.node.findParentNodeWithSpecificType(IF)) {
130130
list.add(asCall)
131131
}
132132
}

diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -930,7 +930,7 @@ private fun ASTNode.hasExplicitIt(): Boolean {
930930
val parameterList = findChildByType(ElementType.FUNCTION_LITERAL)
931931
?.findChildByType(ElementType.VALUE_PARAMETER_LIST)
932932
?.psi
933-
as KtParameterList?
933+
as KtParameterList?
934934
return parameterList?.parameters
935935
?.any { it.name == "it" }
936936
?: false

diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/indentation/Checkers.kt

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import com.pinterest.ktlint.core.ast.ElementType.ARROW
1414
import com.pinterest.ktlint.core.ast.ElementType.AS_KEYWORD
1515
import com.pinterest.ktlint.core.ast.ElementType.AS_SAFE
1616
import com.pinterest.ktlint.core.ast.ElementType.BINARY_EXPRESSION
17+
import com.pinterest.ktlint.core.ast.ElementType.BINARY_WITH_TYPE
1718
import com.pinterest.ktlint.core.ast.ElementType.BLOCK_COMMENT
1819
import com.pinterest.ktlint.core.ast.ElementType.BODY
1920
import com.pinterest.ktlint.core.ast.ElementType.CALL_EXPRESSION
@@ -39,6 +40,7 @@ import com.pinterest.ktlint.core.ast.ElementType.VALUE_ARGUMENT_LIST
3940
import com.pinterest.ktlint.core.ast.ElementType.VALUE_PARAMETER
4041
import com.pinterest.ktlint.core.ast.ElementType.VALUE_PARAMETER_LIST
4142
import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE
43+
import com.pinterest.ktlint.core.ast.children
4244
import com.pinterest.ktlint.core.ast.nextCodeSibling
4345
import com.pinterest.ktlint.core.ast.prevSibling
4446
import org.jetbrains.kotlin.com.intellij.lang.ASTNode
@@ -138,7 +140,17 @@ internal class ValueParameterListChecker(configuration: IndentationConfig) : Cus
138140
internal class ExpressionIndentationChecker(configuration: IndentationConfig) : CustomIndentationChecker(configuration) {
139141
override fun checkNode(whiteSpace: PsiWhiteSpace, indentError: IndentationError): CheckResult? =
140142
when {
141-
whiteSpace.parent.node.elementType == BINARY_EXPRESSION && whiteSpace.prevSibling.node.elementType == OPERATION_REFERENCE -> {
143+
whiteSpace.parent.node.elementType in sequenceOf(BINARY_EXPRESSION, BINARY_WITH_TYPE) &&
144+
whiteSpace.immediateSiblings().any { sibling ->
145+
/*
146+
* We're looking for an operation reference, including
147+
* `as` and `as?` (`AS_SAFE`), but excluding `?:` (`ELVIS`),
148+
* because there's a separate flag for Elvis expressions
149+
* in IDEA (`CONTINUATION_INDENT_IN_ELVIS`).
150+
*/
151+
sibling.node.elementType == OPERATION_REFERENCE &&
152+
sibling.node.children().firstOrNull()?.elementType != ELVIS
153+
} -> {
142154
val parentIndent = whiteSpace.parentIndent() ?: indentError.expected
143155
val expectedIndent = parentIndent + IndentationAmount.valueOf(configuration.extendedIndentAfterOperators)
144156
CheckResult.from(indentError.actual, expectedIndent, true)
@@ -308,3 +320,10 @@ internal fun PsiElement.parentIndent(): Int? = parentsWithSelf
308320
.firstOrNull()
309321
?.text
310322
?.lastIndent()
323+
324+
/**
325+
* @return the sequence of immediate siblings (the previous and the next one),
326+
* excluding `null`'s.
327+
*/
328+
private fun PsiElement.immediateSiblings(): Sequence<PsiElement> =
329+
sequenceOf(prevSibling, nextSibling).filterNotNull()

diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/IndentationRuleFixTest.kt

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import org.cqfn.diktat.ruleset.chapter3.spaces.IndentationRuleTestMixin.withCust
1111
import org.cqfn.diktat.ruleset.chapter3.spaces.IndentationRuleTestResources.dotQualifiedExpressions
1212
import org.cqfn.diktat.ruleset.chapter3.spaces.IndentationRuleTestResources.expressionBodyFunctions
1313
import org.cqfn.diktat.ruleset.chapter3.spaces.IndentationRuleTestResources.expressionsWrappedAfterOperator
14+
import org.cqfn.diktat.ruleset.chapter3.spaces.IndentationRuleTestResources.expressionsWrappedBeforeOperator
1415
import org.cqfn.diktat.ruleset.chapter3.spaces.IndentationRuleTestResources.parenthesesSurroundedInfixExpressions
1516
import org.cqfn.diktat.ruleset.chapter3.spaces.IndentationRuleTestResources.whitespaceInStringLiterals
1617
import org.cqfn.diktat.ruleset.constants.Warnings.WRONG_INDENTATION
@@ -225,6 +226,40 @@ class IndentationRuleFixTest : FixTestBase("test/paragraph3/indentation",
225226
}
226227
}
227228

229+
/**
230+
* See [#1340](https://github.com/saveourtool/diktat/issues/1340).
231+
*/
232+
@Nested
233+
@TestMethodOrder(DisplayName::class)
234+
inner class `Expressions wrapped before operator` {
235+
@ParameterizedTest(name = "$EXTENDED_INDENT_AFTER_OPERATORS = {0}")
236+
@ValueSource(booleans = [false, true])
237+
@Tag(WarningNames.WRONG_INDENTATION)
238+
fun `should be properly indented`(extendedIndentAfterOperators: Boolean, @TempDir tempDir: Path) {
239+
val defaultConfig = IndentationConfig(NEWLINE_AT_END to false)
240+
val customConfig = defaultConfig.withCustomParameters(EXTENDED_INDENT_AFTER_OPERATORS to extendedIndentAfterOperators)
241+
242+
lintMultipleMethods(
243+
expressionsWrappedBeforeOperator[extendedIndentAfterOperators].assertNotNull(),
244+
tempDir = tempDir,
245+
rulesConfigList = customConfig.asRulesConfigList())
246+
}
247+
248+
@ParameterizedTest(name = "$EXTENDED_INDENT_AFTER_OPERATORS = {0}")
249+
@ValueSource(booleans = [false, true])
250+
@Tag(WarningNames.WRONG_INDENTATION)
251+
fun `should be reformatted if mis-indented`(extendedIndentAfterOperators: Boolean, @TempDir tempDir: Path) {
252+
val defaultConfig = IndentationConfig(NEWLINE_AT_END to false)
253+
val customConfig = defaultConfig.withCustomParameters(EXTENDED_INDENT_AFTER_OPERATORS to extendedIndentAfterOperators)
254+
255+
lintMultipleMethods(
256+
actualContent = expressionsWrappedBeforeOperator[!extendedIndentAfterOperators].assertNotNull(),
257+
expectedContent = expressionsWrappedBeforeOperator[extendedIndentAfterOperators].assertNotNull(),
258+
tempDir = tempDir,
259+
rulesConfigList = customConfig.asRulesConfigList())
260+
}
261+
}
262+
228263
/**
229264
* See [#1409](https://github.com/saveourtool/diktat/issues/1409).
230265
*/

0 commit comments

Comments
 (0)