diff --git a/CHANGELOG.md b/CHANGELOG.md index 86173c6c50..dcfbf5c3cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Fix "filename" rule will not work when '.editorconfig' file is not found ([#997](https://github.com/pinterest/ktlint/issues/1004)) - EditorConfig generation for `import-ordering` ([#1011](https://github.com/pinterest/ktlint/pull/1011)) - Internal error (`no-unused-imports`) ([#996](https://github.com/pinterest/ktlint/issues/996)) +- Fix false positive when argument list is after multiline dot-qualified expression (`argument-list-wrapping`) ([#893](https://github.com/pinterest/ktlint/issues/893)) ### Changed - Update Gradle shadow plugin to `6.1.0` version diff --git a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRule.kt b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRule.kt index ea0366b045..aa29ff9fb5 100644 --- a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRule.kt +++ b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRule.kt @@ -3,8 +3,11 @@ package com.pinterest.ktlint.ruleset.experimental import com.pinterest.ktlint.core.KtLint import com.pinterest.ktlint.core.Rule import com.pinterest.ktlint.core.ast.ElementType +import com.pinterest.ktlint.core.ast.ElementType.DOT_QUALIFIED_EXPRESSION +import com.pinterest.ktlint.core.ast.ElementType.TYPE_ARGUMENT_LIST import com.pinterest.ktlint.core.ast.children import com.pinterest.ktlint.core.ast.column +import com.pinterest.ktlint.core.ast.isPartOf import com.pinterest.ktlint.core.ast.isPartOfComment import com.pinterest.ktlint.core.ast.isRoot import com.pinterest.ktlint.core.ast.isWhiteSpace @@ -71,21 +74,38 @@ class ArgumentListWrappingRule : Rule("argument-list-wrapping") { // max_line_length exceeded maxLineLength > -1 && (node.column - 1 + node.textLength) > maxLineLength && !node.textContains('\n') if (putArgumentsOnSeparateLines) { - // IDEA quirk: - // generic< - // T, - // R>( - // 1, - // 2 - // ) - // instead of - // generic< - // T, - // R>( - // 1, - // 2 - // ) - val adjustedIndent = if (node.hasTypeParameterListInFront()) indentSize else 0 + val prevWhitespaceWithNewline = node.prevLeaf { it.isWhiteSpaceWithNewline() } + val adjustedIndent = when { + // IDEA quirk: + // generic< + // T, + // R>( + // 1, + // 2 + // ) + // instead of + // generic< + // T, + // R>( + // 1, + // 2 + // ) + prevWhitespaceWithNewline?.isPartOf(TYPE_ARGUMENT_LIST) == true -> indentSize + // IDEA quirk: + // foo + // .bar( + // 1, + // 2 + // ) + // instead of + // foo + // .bar( + // 1, + // 2 + // ) + prevWhitespaceWithNewline?.isPartOf(DOT_QUALIFIED_EXPRESSION) == true -> indentSize + else -> 0 + } // aiming for // ... LPAR @@ -199,12 +219,6 @@ class ArgumentListWrappingRule : Rule("argument-list-wrapping") { } } - private fun ASTNode.hasTypeParameterListInFront(): Boolean = - treeParent.children() - .firstOrNull { it.elementType == ElementType.TYPE_PARAMETER_LIST } - ?.children() - ?.any { it.isWhiteSpaceWithNewline() } == true - private fun ASTNode.prevWhiteSpaceWithNewLine(): ASTNode? { var prev = prevLeaf() while (prev != null && (prev.isWhiteSpace() || prev.isPartOfComment())) { diff --git a/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRuleTest.kt b/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRuleTest.kt index f6bcbbbf55..e4d1078bf4 100644 --- a/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRuleTest.kt +++ b/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/ArgumentListWrappingRuleTest.kt @@ -528,4 +528,47 @@ class ArgumentListWrappingRuleTest { ) ).isEmpty() } + + @Test + fun `lint argument list after multiline dot qualified expression`() { + assertThat( + ArgumentListWrappingRule().lint( + """ + class Logging(mode: Any, appInstanceIdentity: String, org: String) + + class StateManager { + var firebaseLogger: Logging? = null + } + + private fun replaceLogger(deviceId: String, orgName: String) { + val stateManager: StateManager = StateManager() + stateManager + .firebaseLogger( + mode = 0, + appInstanceIdentity = deviceId, + org = orgName + ) + } + """.trimIndent() + ) + ).isEmpty() + } + + @Test + fun `lint argument list after multiline type argument list`() { + assertThat( + ArgumentListWrappingRule().lint( + """ + fun test() { + generic< + Int, + Int>( + 1, + 2 + ) + } + """.trimIndent() + ) + ).isEmpty() + } }