Skip to content

Commit

Permalink
Add new experimental rule to check consistent spacing around the retu…
Browse files Browse the repository at this point in the history
…rn type of a function (#1422)

* Add new experimental rule to check consistent spacing around the return type of a function

This rule is required for implementing #1341.
  • Loading branch information
paul-dingemans authored Apr 26, 2022
1 parent 07e1828 commit 216f0bb
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ An AssertJ style API for testing KtLint rules ([#1444](https://github.com/pinter
### 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))
- Add experimental rule for incorrect spacing around the function return type (`function-return-type-spacing`) ([#1341](https://github.com/pinterest/ktlint/pull/1341))
- Do not add a space after the typealias name (`type-parameter-list-spacing`) ([#1435](https://github.com/pinterest/ktlint/issues/1435))

### Fixed
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,13 @@ by passing the `--experimental` flag to `ktlint`.
- `experimental:annotation-spacing`: Annotations should be separated by the annotated declaration by a single line break
- `experimental:double-colon-spacing`: No spaces around `::`
- `experimental:fun-keyword-spacing`: Consistent spacing after the fun keyword
- `experimental:spacing-between-function-name-and-opening-parenthesis`: Consistent spacing between function name and opening parenthesis
- `experimental:function-return-type-spacing`: Consistent spacing around the function return type
- `experimental:function-type-reference-spacing`: Consistent spacing in the type reference before a function
- `experimental:modifier-list-spacing`: Consistent spacing between modifiers in and after the last modifier in a modifier list
- `experimental:spacing-around-angle-brackets`: No spaces around angle brackets
- `experimental:spacing-between-declarations-with-annotations`: Declarations with annotations should be separated by a blank line
- `experimental:spacing-between-declarations-with-comments`: Declarations with comments should be separated by a blank line
- `experimental:spacing-between-function-name-and-opening-parenthesis`: Consistent spacing between function name and opening parenthesis
- `experimental:type-parameter-list-spacing`: Spacing after a type parameter list in function and class declarations
- `experimental:unary-op-spacing`: No spaces around unary operators
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public class ExperimentalRuleSetProvider : RuleSetProvider {
CommentWrappingRule(),
KdocWrappingRule(),
SpacingBetweenFunctionNameAndOpeningParenthesisRule(),
ParameterListSpacingRule()
ParameterListSpacingRule(),
FunctionReturnTypeSpacingRule()
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.pinterest.ktlint.ruleset.experimental

import com.pinterest.ktlint.core.Rule
import com.pinterest.ktlint.core.ast.ElementType.COLON
import com.pinterest.ktlint.core.ast.ElementType.FUN
import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE
import com.pinterest.ktlint.core.ast.nextLeaf
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

public class FunctionReturnTypeSpacingRule : Rule("function-return-type-spacing") {
override fun visit(
node: ASTNode,
autoCorrect: Boolean,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit
) {
node
.takeIf { node.elementType == FUN }
?.let { node.findChildByType(COLON) }
?.let { colonNode ->
removeWhiteSpaceBetweenClosingParenthesisAndColon(colonNode, emit, autoCorrect)
fixWhiteSpaceBetweenColonAndReturnType(colonNode, emit, autoCorrect)
}
}

private fun removeWhiteSpaceBetweenClosingParenthesisAndColon(
node: ASTNode,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit,
autoCorrect: Boolean
) {
require(node.elementType == COLON)
node
.prevLeaf()
?.takeIf { it.elementType == WHITE_SPACE }
?.let { whitespaceBeforeColonNode ->
emit(whitespaceBeforeColonNode.startOffset, "Unexpected whitespace", true)
if (autoCorrect) {
whitespaceBeforeColonNode.treeParent?.removeChild(whitespaceBeforeColonNode)
}
}
}

private fun fixWhiteSpaceBetweenColonAndReturnType(
node: ASTNode,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit,
autoCorrect: Boolean
) {
require(node.elementType == COLON)
node
.nextLeaf()
?.takeIf { it.elementType == WHITE_SPACE }
.let { whiteSpaceAfterColon ->
if (whiteSpaceAfterColon == null) {
emit(node.startOffset, "Single space expected between colon and return type", true)
if (autoCorrect) {
(node as LeafElement).upsertWhitespaceAfterMe(" ")
}
} else if (whiteSpaceAfterColon.text != " ") {
emit(node.startOffset, "Unexpected whitespace", true)
if (autoCorrect) {
(whiteSpaceAfterColon as LeafElement).rawReplaceWithText(" ")
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package com.pinterest.ktlint.ruleset.experimental

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

class FunctionReturnTypeSpacingRuleTest {
private val functionReturnTypeSpacingRuleAssertThat = FunctionReturnTypeSpacingRule().assertThat()

@Test
fun `Given a function signature without whitespace between the closing parenthesis and the colon of the return type then do not reformat`() {
val code =
"""
fun foo(): String = "some-result"
""".trimIndent()
functionReturnTypeSpacingRuleAssertThat(code).hasNoLintViolations()
}

@Test
fun `Given a function signature with at least one space between the closing parenthesis and the colon of the return type then reformat`() {
val code =
"""
fun foo() : String = "some-result"
""".trimIndent()
val formattedCode =
"""
fun foo(): String = "some-result"
""".trimIndent()
functionReturnTypeSpacingRuleAssertThat(code)
.hasLintViolation(1, 10, "Unexpected whitespace")
.isFormattedAs(formattedCode)
}

@Test
fun `Given a function signature with a newline between the closing parenthesis and the colon of the return type then reformat`() {
val code =
"""
fun foo()
: String = "some-result"
""".trimIndent()
val formattedCode =
"""
fun foo(): String = "some-result"
""".trimIndent()
functionReturnTypeSpacingRuleAssertThat(code)
.hasLintViolation(1, 10, "Unexpected whitespace")
.isFormattedAs(formattedCode)
}

@Test
fun `Given a function signature without space between the colon and the return type then reformat`() {
val code =
"""
fun foo():String = "some-result"
""".trimIndent()
val formattedCode =
"""
fun foo(): String = "some-result"
""".trimIndent()
functionReturnTypeSpacingRuleAssertThat(code)
.hasLintViolation(1, 10, "Single space expected between colon and return type")
.isFormattedAs(formattedCode)
}

@Test
fun `Given a function signature with multiple spaces between the colon and the return type then reformat`() {
val code =
"""
fun foo(): String = "some-result"
""".trimIndent()
val formattedCode =
"""
fun foo(): String = "some-result"
""".trimIndent()
functionReturnTypeSpacingRuleAssertThat(code)
.hasLintViolation(1, 10, "Unexpected whitespace")
.isFormattedAs(formattedCode)
}

@Test
fun `Given a function signature with a new line between the colon and the return type then reformat`() {
val code =
"""
fun foo():
String = "some-result"
""".trimIndent()
val formattedCode =
"""
fun foo(): String = "some-result"
""".trimIndent()
functionReturnTypeSpacingRuleAssertThat(code)
.hasLintViolation(1, 10, "Unexpected whitespace")
.isFormattedAs(formattedCode)
}
}

0 comments on commit 216f0bb

Please sign in to comment.