Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new experimental rule ParameterListSpacingRule #1411

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 6 additions & 17 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -2,21 +2,7 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](https://semver.org/).

## Unreleased (0.46.0)

### API Changes & RuleSet providers

### Added
- Add experimental rule for unexpected spacing between function name and opening parenthesis (`spacing-between-function-name-and-opening-parenthesis`) ([#1341](https://github.com/pinterest/ktlint/issues/1341))

### Fixed

### Changed

### Removed


## Unreleased (0.45.0)
## Unreleased

### API Changes & RuleSet providers

@@ -27,16 +13,19 @@ If you are not an API user nor a RuleSet provider, then you can safely skip this
An AssertJ style API for testing KtLint rules ([#1444](https://github.com/pinterest/ktlint/issues/1444)) has been added. Usage of this API is encouraged in favor of using the old RuleExtension API. For more information, see [KtLintAssertThat API]( https://github.com/pinterest/ktlint/blob/master/ktlint-test/README.MD)

### Added
- Add experimental rule for unexpected spacing between function name and opening parenthesis (`spacing-between-function-name-and-opening-parenthesis`) ([#1341](https://github.com/pinterest/ktlint/issues/1341))
- Add experimental rule for unexpected spacing in the parameter list (`parameter-list-spacing`) ([#1341](https://github.com/pinterest/ktlint/issues/1341))
- Do not add a space after the typealias name (`type-parameter-list-spacing`) ([#1435](https://github.com/pinterest/ktlint/issues/1435))

### Fixed
- Do not add a space after the typealias name (`type-parameter-list-spacing`) ([#1435](https://github.com/pinterest/ktlint/issues/1435))

### Changed

* Set Kotlin development version to `1.6.21` and Kotlin version to `1.6.21`.

### Removed

### Removed

## [0.45.2] - 2022-04-06

### Fixed
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -82,6 +82,7 @@ by passing the `--experimental` flag to `ktlint`.
- `experimental:multiline-if-else`: Braces required for multiline if/else statements
- `experimental:no-empty-first-line-in-method-block`: No leading empty lines in method blocks
- `experimental:package-name`: No underscores in package names
- `experimental:parameter-list-spacing`: Consistent spacing inside the parameter list
- `experimental:unnecessary-parentheses-before-trailing-lambda`: An empty parentheses block before a lambda is redundant. For example `some-string".count() { it == '-' }`

### Spacing
Original file line number Diff line number Diff line change
@@ -31,6 +31,7 @@ public class ExperimentalRuleSetProvider : RuleSetProvider {
ModifierListSpacingRule(),
CommentWrappingRule(),
KdocWrappingRule(),
SpacingBetweenFunctionNameAndOpeningParenthesisRule()
SpacingBetweenFunctionNameAndOpeningParenthesisRule(),
ParameterListSpacingRule()
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
package com.pinterest.ktlint.ruleset.experimental

import com.pinterest.ktlint.core.Rule
import com.pinterest.ktlint.core.ast.ElementType.ANNOTATION_ENTRY
import com.pinterest.ktlint.core.ast.ElementType.COLON
import com.pinterest.ktlint.core.ast.ElementType.COMMA
import com.pinterest.ktlint.core.ast.ElementType.MODIFIER_LIST
import com.pinterest.ktlint.core.ast.ElementType.VALUE_PARAMETER
import com.pinterest.ktlint.core.ast.ElementType.VALUE_PARAMETER_LIST
import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE
import com.pinterest.ktlint.core.ast.children
import com.pinterest.ktlint.core.ast.nextCodeSibling
import com.pinterest.ktlint.core.ast.nextLeaf
import com.pinterest.ktlint.core.ast.nextSibling
import com.pinterest.ktlint.core.ast.prevCodeSibling
import com.pinterest.ktlint.core.ast.prevLeaf
import com.pinterest.ktlint.core.ast.upsertWhitespaceAfterMe
import org.jetbrains.kotlin.com.intellij.lang.ASTNode
import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafElement
import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement

/**
* Ensures consistent spacing inside the parameter list. This rule partly overlaps with other rules like spacing around
* comma's and colons. However, it does have a more complete view on the higher concept of the parameter-list without
* interfering of the parameter-list-wrapping rule.
*/
public class ParameterListSpacingRule : Rule("parameter-list-spacing") {
override fun visit(
node: ASTNode,
autoCorrect: Boolean,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit
) {
if (node.elementType == VALUE_PARAMETER_LIST) {
visitValueParameterList(node, emit, autoCorrect)
}
}

private fun visitValueParameterList(
node: ASTNode,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit,
autoCorrect: Boolean
) {
require(node.elementType == VALUE_PARAMETER_LIST)
val countValueParameters =
node
.children()
.count { it.elementType == VALUE_PARAMETER }
var valueParameterCount = 0
val iterator =
node
.children()
// Store elements in list before changing them as otherwise only one element is being changed
.toList()
.iterator()
while (iterator.hasNext()) {
val el = iterator.next()
when (el.elementType) {
WHITE_SPACE -> {
if (countValueParameters == 0) {
removeUnexpectedWhiteSpace(el, emit, autoCorrect)
} else if (valueParameterCount == 0 && el.isNotIndent()) {
// whitespace before first parameter
removeUnexpectedWhiteSpace(el, emit, autoCorrect)
} else if (valueParameterCount == countValueParameters && el.isNotIndent()) {
// whitespace after the last parameter
removeUnexpectedWhiteSpace(el, emit, autoCorrect)
} else if (el.nextCodeSibling()?.elementType == COMMA) {
// No whitespace between parameter name and comma allowed
removeUnexpectedWhiteSpace(el, emit, autoCorrect)
} else if (el.elementType == WHITE_SPACE && el.isNotIndent() && el.isNotSingleSpace()) {
require(el.prevCodeSibling()?.elementType == COMMA)
replaceWithSingleSpace(el, emit, autoCorrect)
}
}
COMMA -> {
// Comma must be followed by whitespace
val nextSibling =
el
.nextSibling { true }
?.elementType
if (nextSibling != WHITE_SPACE) {
addMissingWhiteSpaceAfterMe(el, emit, autoCorrect)
}
}
VALUE_PARAMETER -> {
valueParameterCount += 1
visitValueParameter(el, emit, autoCorrect)
}
}
}
}

private fun visitValueParameter(
node: ASTNode,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit,
autoCorrect: Boolean
) {
visitModifierList(node, emit, autoCorrect)
removeWhiteSpaceBetweenParameterIdentifierAndColon(node, emit, autoCorrect)
fixWhiteSpaceAfterColonInParameter(node, emit, autoCorrect)
}

private fun visitModifierList(
node: ASTNode,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit,
autoCorrect: Boolean
) {
val modifierList = node.findChildByType(MODIFIER_LIST) ?: return
removeWhiteSpaceBetweenModifiersInList(modifierList, emit, autoCorrect)
removeWhiteSpaceBetweenModifierListAndParameterIdentifier(modifierList, emit, autoCorrect)
}

private fun removeWhiteSpaceBetweenModifiersInList(
node: ASTNode,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit,
autoCorrect: Boolean
) {
require(node.elementType == MODIFIER_LIST)
node
.children()
.filter { it.elementType == WHITE_SPACE }
// Store elements in list before changing them as otherwise only the first whitespace is being changed
.toList()
.forEach { visitWhiteSpaceAfterModifier(it, emit, autoCorrect) }
}

private fun visitWhiteSpaceAfterModifier(
node: ASTNode,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit,
autoCorrect: Boolean
) {
node
.takeUnless {
// Ignore when the modifier is an annotation which is placed on a separate line
it.isIndent() && it.getPrecedingModifier()?.elementType == ANNOTATION_ENTRY
}
?.takeIf { it.isNotSingleSpace() }
?.let { replaceWithSingleSpace(it, emit, autoCorrect) }
}

private fun removeWhiteSpaceBetweenModifierListAndParameterIdentifier(
node: ASTNode,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit,
autoCorrect: Boolean
) {
require(node.elementType == MODIFIER_LIST)
node
.nextSibling { true }
?.takeIf { it.elementType == WHITE_SPACE }
?.let { visitWhiteSpaceAfterModifier(it, emit, autoCorrect) }
}

private fun removeWhiteSpaceBetweenParameterIdentifierAndColon(
node: ASTNode,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit,
autoCorrect: Boolean
) {
node
.findChildByType(COLON)
?.prevLeaf()
?.takeIf { it.elementType == WHITE_SPACE }
?.let { whiteSpaceBeforeColon ->
removeUnexpectedWhiteSpace(whiteSpaceBeforeColon, emit, autoCorrect)
}
}

private fun fixWhiteSpaceAfterColonInParameter(
node: ASTNode,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit,
autoCorrect: Boolean
) {
val colonNode = node.findChildByType(COLON) ?: return
colonNode
.nextLeaf()
?.takeIf { it.elementType == WHITE_SPACE }
.let { whiteSpaceAfterColon ->
if (whiteSpaceAfterColon == null) {
addMissingWhiteSpaceAfterMe(colonNode, emit, autoCorrect)
} else if (whiteSpaceAfterColon.isIndent() || whiteSpaceAfterColon.isNotSingleSpace()) {
replaceWithSingleSpace(whiteSpaceAfterColon, emit, autoCorrect)
}
}
}

private fun addMissingWhiteSpaceAfterMe(
node: ASTNode,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit,
autoCorrect: Boolean
) {
require(node.elementType == COLON || node.elementType == COMMA)
emit(node.startOffset, "Whitespace after '${node.text}' is missing", true)
if (autoCorrect) {
(node as LeafElement).upsertWhitespaceAfterMe(" ")
}
}

private fun ASTNode.isNotIndent(): Boolean = !isIndent()

private fun ASTNode.isIndent(): Boolean {
require(elementType == WHITE_SPACE)
return text.startsWith("\n")
}

private fun ASTNode.isNotSingleSpace(): Boolean {
require(elementType == WHITE_SPACE)
return text != " "
}

private fun removeUnexpectedWhiteSpace(
node: ASTNode,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit,
autoCorrect: Boolean
) {
emit(node.startOffset, "Unexpected whitespace", true)
if (autoCorrect) {
(node as LeafElement).rawRemove()
}
}

private fun replaceWithSingleSpace(
node: ASTNode,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit,
autoCorrect: Boolean
) {
emit(node.startOffset, "Expected a single space", true)
if (autoCorrect) {
(node as LeafPsiElement).replaceWithText(" ")
}
}

private fun ASTNode.getPrecedingModifier(): ASTNode? {
return prevCodeSibling()
?.let { prevCodeSibling ->
if (prevCodeSibling.elementType == MODIFIER_LIST) {
prevCodeSibling.lastChildNode
} else {
require(prevCodeSibling.treeParent.elementType == MODIFIER_LIST)
prevCodeSibling
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,406 @@
package com.pinterest.ktlint.ruleset.experimental

import com.pinterest.ktlint.test.KtLintAssertThat.Companion.assertThat
import com.pinterest.ktlint.test.LintViolation
import org.junit.jupiter.api.Test

class ParameterListSpacingRuleTest {
private val parameterListSpacingRuleAssertThat = ParameterListSpacingRule().assertThat()

@Test
fun `Given a function signature which does not contain redundant spaces then do no reformat`() {
val code =
"""
fun foo(a: Any, vararg b: Any) = "some-result"
""".trimIndent()
parameterListSpacingRuleAssertThat(code).hasNoLintViolations()
}

@Test
fun `Given a function signature without parameters but with at least one space between the parenthesis then reformat`() {
val code =
"""
fun foo( ) = "some-result"
""".trimIndent()
val formattedCode =
"""
fun foo() = "some-result"
""".trimIndent()
parameterListSpacingRuleAssertThat(code)
.hasLintViolation(1, 9, "Unexpected whitespace")
.isFormattedAs(formattedCode)
}

@Test
fun `Given a function signature without parameters but with at least one newline between the parenthesis then reformat`() {
val code =
"""
fun foo(
) = "some-result"
""".trimIndent()
val formattedCode =
"""
fun foo() = "some-result"
""".trimIndent()
parameterListSpacingRuleAssertThat(code)
.hasLintViolation(1, 9, "Unexpected whitespace")
.isFormattedAs(formattedCode)
}

@Test
fun `Given a function signature with at least one space before the first parameter then reformat`() {
val code =
"""
fun foo( a: Any) = "some-result"
""".trimIndent()
val formattedCode =
"""
fun foo(a: Any) = "some-result"
""".trimIndent()
parameterListSpacingRuleAssertThat(code)
.hasLintViolation(1, 9, "Unexpected whitespace")
.isFormattedAs(formattedCode)
}

@Test
fun `Given a function signature with a newline before the first parameter then do not reformat`() {
val code =
"""
fun foo(
a: Any
) = "some-result"
""".trimIndent()
parameterListSpacingRuleAssertThat(code).hasNoLintViolations()
}

@Test
fun `Given a function signature with at least one space after the last parameter then reformat`() {
val code =
"""
fun foo(a: Any ) = "some-result"
""".trimIndent()
val formattedCode =
"""
fun foo(a: Any) = "some-result"
""".trimIndent()
parameterListSpacingRuleAssertThat(code)
.hasLintViolation(1, 15, "Unexpected whitespace")
.isFormattedAs(formattedCode)
}

@Test
fun `Given a function signature with a newline after the last parameter then do not reformat`() {
val code =
"""
fun foo(
a: Any
) = "some-result"
""".trimIndent()
parameterListSpacingRuleAssertThat(code).hasNoLintViolations()
}

@Test
fun `Given a function signature with at least one space between the parameter name and the colon then reformat`() {
val code =
"""
fun foo(a : Any) = "some-result"
""".trimIndent()
val formattedCode =
"""
fun foo(a: Any) = "some-result"
""".trimIndent()
parameterListSpacingRuleAssertThat(code)
.hasLintViolation(1, 10, "Unexpected whitespace")
.isFormattedAs(formattedCode)
}

@Test
fun `Given a function signature with at least one newline between the parameter name and the colon then reformat`() {
val code =
"""
fun foo(a
: Any) = "some-result"
""".trimIndent()
val formattedCode =
"""
fun foo(a: Any) = "some-result"
""".trimIndent()
parameterListSpacingRuleAssertThat(code)
.hasLintViolation(1, 10, "Unexpected whitespace")
.isFormattedAs(formattedCode)
}

@Test
fun `Given a function signature without space between the colon and the parameter type then reformat`() {
val code =
"""
fun foo(a:Any) = "some-result"
""".trimIndent()
val formattedCode =
"""
fun foo(a: Any) = "some-result"
""".trimIndent()
parameterListSpacingRuleAssertThat(code)
.hasLintViolation(1, 10, "Whitespace after ':' is missing")
.isFormattedAs(formattedCode)
}

@Test
fun `Given a function signature with too many spaces between the colon and the parameter type then reformat`() {
val code =
"""
fun foo(a:${TOO_MANY_SPACES}Any) = "some-result"
""".trimIndent()
val formattedCode =
"""
fun foo(a: Any) = "some-result"
""".trimIndent()
parameterListSpacingRuleAssertThat(code)
.hasLintViolation(1, 11, "Expected a single space")
.isFormattedAs(formattedCode)
}

@Test
fun `Given a function signature with a new line between the colon and the parameter type then reformat`() {
val code =
"""
fun foo(a:
Any) = "some-result"
""".trimIndent()
val formattedCode =
"""
fun foo(a: Any) = "some-result"
""".trimIndent()
parameterListSpacingRuleAssertThat(code)
.hasLintViolation(1, 11, "Expected a single space")
.isFormattedAs(formattedCode)
}

@Test
fun `Given a function signature with too many spaces between the modifier list and parameter name then reformat`() {
val code =
"""
fun foo1(vararg${TOO_MANY_SPACES}a: Any) = "some-result"
inline fun foo2(noinline${TOO_MANY_SPACES}bar: () -> Unit) {
bar()
}
inline fun foo3(crossinline${TOO_MANY_SPACES}bar: () -> Unit) {
bar()
}
""".trimIndent()
val formattedCode =
"""
fun foo1(vararg a: Any) = "some-result"
inline fun foo2(noinline bar: () -> Unit) {
bar()
}
inline fun foo3(crossinline bar: () -> Unit) {
bar()
}
""".trimIndent()
parameterListSpacingRuleAssertThat(code)
.hasLintViolations(
LintViolation(1, 16, "Expected a single space"),
LintViolation(2, 25, "Expected a single space"),
LintViolation(5, 28, "Expected a single space")
).isFormattedAs(formattedCode)
}

@Test
fun `Given a function signature with at least one newline between the modifier list and parameter name then reformat`() {
val code =
"""
fun foo1(vararg
a: Any) = "some-result"
inline fun foo2(noinline
bar: () -> Unit) {
bar()
}
inline fun foo3(crossinline
bar: () -> Unit) {
bar()
}
""".trimIndent()
val formattedCode =
"""
fun foo1(vararg a: Any) = "some-result"
inline fun foo2(noinline bar: () -> Unit) {
bar()
}
inline fun foo3(crossinline bar: () -> Unit) {
bar()
}
""".trimIndent()
parameterListSpacingRuleAssertThat(code)
.hasLintViolations(
LintViolation(1, 16, "Expected a single space"),
LintViolation(3, 25, "Expected a single space"),
LintViolation(7, 28, "Expected a single space")
).isFormattedAs(formattedCode)
}

@Test
fun `Given a function signature with too many spaces between the modifiers in the modifier list then reformat`() {
val code =
"""
// The code example below not make sense. Its importance is that modifier list can contain multiple elements
fun foo(vararg noinline crossinline a: Any) = "some-result"
""".trimIndent()
val formattedCode =
"""
// The code example below not make sense. Its importance is that modifier list can contain multiple elements
fun foo(vararg noinline crossinline a: Any) = "some-result"
""".trimIndent()
parameterListSpacingRuleAssertThat(code)
.hasLintViolations(
LintViolation(2, 15, "Expected a single space"),
LintViolation(2, 25, "Expected a single space"),
LintViolation(2, 38, "Expected a single space")
).isFormattedAs(formattedCode)
}

@Test
fun `Given a function signature with newlines between the modifiers in the modifier list then reformat`() {
val code =
"""
// The code example below not make sense. Its importance is that modifier list can contain multiple elements
fun foo(
vararg
noinline
crossinline
a: Any
) = "some-result"
""".trimIndent()
val formattedCode =
"""
// The code example below not make sense. Its importance is that modifier list can contain multiple elements
fun foo(
vararg noinline crossinline a: Any
) = "some-result"
""".trimIndent()
parameterListSpacingRuleAssertThat(code)
.hasLintViolations(
LintViolation(3, 11, "Expected a single space"),
LintViolation(4, 13, "Expected a single space"),
LintViolation(5, 16, "Expected a single space")
).isFormattedAs(formattedCode)
}

@Test
fun `Given a function signature with an annotated parameter and annotations separated by newlines then do not reformat`() {
val code =
"""
fun foo(
@Bar1(value = "bar1")
@Bar2(value = "bar2")
a: Any
) = "some-result"
""".trimIndent()
parameterListSpacingRuleAssertThat(code).hasNoLintViolations()
}

@Test
fun `Given a function signature with multiple parameters and at least one space before the comma separating the parameters then reformat`() {
val code =
"""
fun foo(a: Any${TOO_MANY_SPACES}, b: Any) = "some-result"
""".trimIndent() // ktlint-disable string-template
val formattedCode =
"""
fun foo(a: Any, b: Any) = "some-result"
""".trimIndent()
parameterListSpacingRuleAssertThat(code)
.hasLintViolation(1, 15, "Unexpected whitespace")
.isFormattedAs(formattedCode)
}

@Test
fun `Given a function signature with multiple parameters and at least one newline before the comma separating the parameters then reformat`() {
val code =
"""
fun foo(a: Any
, b: Any) = "some-result"
""".trimIndent() // ktlint-disable string-template
val formattedCode =
"""
fun foo(a: Any, b: Any) = "some-result"
""".trimIndent()
parameterListSpacingRuleAssertThat(code)
.hasLintViolation(1, 15, "Unexpected whitespace")
.isFormattedAs(formattedCode)
}

@Test
fun `Given a function signature with multiple parameters and no whitespace after the comma separating the parameters then reformat`() {
val code =
"""
fun foo(a: Any,b: Any) = "some-result"
""".trimIndent()
val formattedCode =
"""
fun foo(a: Any, b: Any) = "some-result"
""".trimIndent()
parameterListSpacingRuleAssertThat(code)
.hasLintViolation(1, 15, "Whitespace after ',' is missing")
.isFormattedAs(formattedCode)
}

@Test
fun `Given a function signature with multiple parameters and too many spaces after the comma separating the parameters then reformat`() {
val code =
"""
fun foo(a: Any,${TOO_MANY_SPACES}b: Any) = "some-result"
""".trimIndent() // ktlint-disable string-template
val formattedCode =
"""
fun foo(a: Any, b: Any) = "some-result"
""".trimIndent()
parameterListSpacingRuleAssertThat(code)
.hasLintViolation(1, 16, "Expected a single space")
.isFormattedAs(formattedCode)
}

@Test
fun `Given a function signature with multiple parameters and a newline after the comma separating the parameters then do not reformat`() {
val code =
"""
fun foo(
a: Any,
b: Any
) = "some-result"
""".trimIndent() // ktlint-disable string-template
parameterListSpacingRuleAssertThat(code).hasNoLintViolations()
}

@Test
fun `Given a function signature with multiple spacing errors in the parameters then reformat all`() {
val code =
"""
fun foo(c :Any, d : Any, b : Any , vararg a : Any ) = "some-result"
""".trimIndent()
val formattedCode =
"""
fun foo(c: Any, d: Any, b: Any, vararg a: Any) = "some-result"
""".trimIndent()
parameterListSpacingRuleAssertThat(code)
.hasLintViolations(
LintViolation(1, 10, "Unexpected whitespace"),
LintViolation(1, 11, "Whitespace after ':' is missing"),
LintViolation(1, 18, "Unexpected whitespace"),
LintViolation(1, 20, "Expected a single space"),
LintViolation(1, 27, "Expected a single space"),
LintViolation(1, 31, "Unexpected whitespace"),
LintViolation(1, 33, "Expected a single space"),
LintViolation(1, 38, "Unexpected whitespace"),
LintViolation(1, 40, "Expected a single space"),
LintViolation(1, 48, "Expected a single space"),
LintViolation(1, 51, "Unexpected whitespace"),
LintViolation(1, 53, "Expected a single space"),
LintViolation(1, 58, "Unexpected whitespace")
).isFormattedAs(formattedCode)
}

private companion object {
const val TOO_MANY_SPACES = " "
}
}