Skip to content

Commit

Permalink
Enforce consistent spacing (e.g. no whitespace element at all) betwee…
Browse files Browse the repository at this point in the history
…n function name and the opening parenthesis

Includes cleanup of AnnotationSpacingRuleTest as it broke due to
existence of test which was not related to annotation spacing.
Also removed the unneeded blank final line in code examples
which are linted and formatted.

This rule is required for implementing the function signature rewrite rule pinterest#1341
  • Loading branch information
paul-dingemans committed Mar 13, 2022
1 parent 4646371 commit 5b9587d
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 96 deletions.
16 changes: 15 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,21 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](https://semver.org/).

## Unreleased
## 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)

### API Changes & RuleSet providers

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ 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-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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public class ExperimentalRuleSetProvider : RuleSetProvider {
FunctionTypeReferenceSpacingRule(),
ModifierListSpacingRule(),
CommentWrappingRule(),
KdocWrappingRule()
KdocWrappingRule(),
SpacingBetweenFunctionNameAndOpeningParenthesisRule()
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.pinterest.ktlint.ruleset.experimental

import com.pinterest.ktlint.core.Rule
import com.pinterest.ktlint.core.ast.ElementType
import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE
import com.pinterest.ktlint.core.ast.nextSibling
import org.jetbrains.kotlin.com.intellij.lang.ASTNode

public class SpacingBetweenFunctionNameAndOpeningParenthesisRule : Rule("spacing-between-function-name-and-opening-parenthesis") {
override fun visit(
node: ASTNode,
autoCorrect: Boolean,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit
) {
node
.takeIf { node.elementType == ElementType.FUN }
?.findChildByType(ElementType.IDENTIFIER)
?.nextSibling { true }
?.takeIf { it.elementType == WHITE_SPACE }
?.let { whiteSpace ->
emit(whiteSpace.startOffset, "Unexpected whitespace", true)
if (autoCorrect) {
whiteSpace.treeParent.removeChild(whiteSpace)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
package com.pinterest.ktlint.ruleset.experimental

import com.pinterest.ktlint.core.KtLint
import com.pinterest.ktlint.core.LintError
import com.pinterest.ktlint.test.format
import com.pinterest.ktlint.test.lint
import java.util.ArrayList
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test

class AnnotationSpacingRuleTest {

@Test
fun `lint no empty lines between an annotation and object`() {
assertThat(
AnnotationSpacingRule().lint(
"""
@JvmField
fun foo() {}
""".trimIndent()
)
).isEmpty()
Expand All @@ -31,7 +27,6 @@ class AnnotationSpacingRuleTest {
@JvmField
fun foo() {}
""".trimIndent()
)
).isEqualTo(
Expand All @@ -48,7 +43,6 @@ class AnnotationSpacingRuleTest {
@JvmField
fun foo() {}
""".trimIndent()

assertThat(
Expand All @@ -57,7 +51,6 @@ class AnnotationSpacingRuleTest {
"""
@JvmField
fun foo() {}
""".trimIndent()
)
}
Expand All @@ -72,7 +65,6 @@ class AnnotationSpacingRuleTest {
@JvmStatic
val r = A()
}
""".trimIndent()

assertThat(
Expand All @@ -84,7 +76,6 @@ class AnnotationSpacingRuleTest {
@JvmStatic
val r = A()
}
""".trimIndent()
)
}
Expand All @@ -98,7 +89,6 @@ class AnnotationSpacingRuleTest {
fun foo() {}
""".trimIndent()

assertThat(
Expand All @@ -107,7 +97,6 @@ class AnnotationSpacingRuleTest {
"""
@JvmField
fun foo() {}
""".trimIndent()
)
}
Expand All @@ -117,15 +106,13 @@ class AnnotationSpacingRuleTest {
val code =
"""
@JvmField fun foo() {}
""".trimIndent()

assertThat(
AnnotationSpacingRule().format(code)
).isEqualTo(
"""
@JvmField fun foo() {}
""".trimIndent()
)
}
Expand All @@ -137,7 +124,6 @@ class AnnotationSpacingRuleTest {
@JvmField @JvmStatic
fun foo() = Unit
""".trimIndent()

assertThat(
Expand All @@ -146,7 +132,6 @@ class AnnotationSpacingRuleTest {
"""
@JvmField @JvmStatic
fun foo() = Unit
""".trimIndent()
)
}
Expand All @@ -159,7 +144,6 @@ class AnnotationSpacingRuleTest {
@JvmStatic
fun foo() = Unit
""".trimIndent()

assertThat(
Expand All @@ -169,7 +153,6 @@ class AnnotationSpacingRuleTest {
@JvmField
@JvmStatic
fun foo() = Unit
""".trimIndent()
)
}
Expand All @@ -183,7 +166,6 @@ class AnnotationSpacingRuleTest {
@JvmName
@JvmStatic fun foo() = Unit
""".trimIndent()

assertThat(
Expand All @@ -193,7 +175,6 @@ class AnnotationSpacingRuleTest {
@JvmField
@JvmName
@JvmStatic fun foo() = Unit
""".trimIndent()
)
}
Expand All @@ -211,7 +192,6 @@ class AnnotationSpacingRuleTest {
fun foo() = Unit
""".trimIndent()

assertThat(
Expand All @@ -222,7 +202,6 @@ class AnnotationSpacingRuleTest {
@JvmName
@JvmStatic
fun foo() = Unit
""".trimIndent()
)
}
Expand All @@ -240,7 +219,6 @@ class AnnotationSpacingRuleTest {
val foo = Foo()
}
""".trimIndent()

assertThat(
Expand All @@ -252,82 +230,10 @@ class AnnotationSpacingRuleTest {
@JvmStatic
val foo = Foo()
}
""".trimIndent()
)
}

@Test
fun `lint there should not be an error on multiple lines assertion while additional formatting ongoing`() {
val code =
"""
package a.b.c
class Test {
fun bloop() {
asdfadsf(asdfadsf, asdfasdf, asdfasdfasdfads,
asdfasdf, asdfasdf, asdfasdf)
}
@Blah
val test: Int
}
""".trimIndent()
assertThat(
AnnotationSpacingRule().format(code)
).isEqualTo(
"""
package a.b.c
class Test {
fun bloop() {
asdfadsf(asdfadsf, asdfasdf, asdfasdfasdfads,
asdfasdf, asdfasdf, asdfasdf)
}
@Blah
val test: Int
}
""".trimIndent()
)
}

@Test
fun `lint there should not be an error on multiline assertion while additional formatting ongoing from file`() {
val code =
"""
package a.b.c
class Test {
fun bloop() {
asdfadsf(asdfadsf, asdfasdf, asdfasdfasdfads,
asdfasdf, asdfasdf, asdfasdf)
}
@Blah
val test: Int
}
""".trimIndent()
assertThat(
ArrayList<LintError>().apply {
KtLint.lint(
KtLint.Params(
text = code,
ruleSets = mutableListOf(
ExperimentalRuleSetProvider().get()
),
cb = { e, _ -> add(e) }
)
)
}
).allMatch {
it.ruleId == "experimental:argument-list-wrapping"
}
}

@Test
fun `annotations should not be separated by comments from the annotated construct`() {
val code =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.pinterest.ktlint.ruleset.experimental

import com.pinterest.ktlint.core.LintError
import com.pinterest.ktlint.test.format
import com.pinterest.ktlint.test.lint
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test

class SpacingBetweenFunctionNameAndOpeningParenthesisRuleTest {
@Test
fun `Given a function signature without whitespace between function name and opening parenthesis then do not reformat`() {
val code =
"""
fun foo() = "foo"
""".trimIndent()
assertThat(SpacingBetweenFunctionNameAndOpeningParenthesisRule().lint(code)).isEmpty()
assertThat(SpacingBetweenFunctionNameAndOpeningParenthesisRule().format(code)).isEqualTo(code)
}

@Test
fun `Given a function signature with one or more spaces between function name and opening parenthesis then do not reformat`() {
val code =
"""
fun foo () = "foo"
""".trimIndent()
val formattedCode =
"""
fun foo() = "foo"
""".trimIndent()
assertThat(SpacingBetweenFunctionNameAndOpeningParenthesisRule().lint(code)).containsExactly(
LintError(1, 8, "spacing-between-function-name-and-opening-parenthesis", "Unexpected whitespace")
)
assertThat(SpacingBetweenFunctionNameAndOpeningParenthesisRule().format(code)).isEqualTo(formattedCode)
}

@Test
fun `Given a function signature with one or more new lines between function name and opening parenthesis then do not reformat`() {
val code =
"""
fun foo
() = "foo"
""".trimIndent()
val formattedCode =
"""
fun foo() = "foo"
""".trimIndent()
assertThat(SpacingBetweenFunctionNameAndOpeningParenthesisRule().lint(code)).containsExactly(
LintError(1, 8, "spacing-between-function-name-and-opening-parenthesis", "Unexpected whitespace")
)
assertThat(SpacingBetweenFunctionNameAndOpeningParenthesisRule().format(code)).isEqualTo(formattedCode)
}
}

0 comments on commit 5b9587d

Please sign in to comment.