Skip to content

Commit

Permalink
Add experimental NullableTypeSpacingRule (#1426)
Browse files Browse the repository at this point in the history
* Add new experimental rule which prohibits whitespaces inside a nullable type

Required for implementing the signature rewrite rule (#1341)
  • Loading branch information
paul-dingemans authored Apr 26, 2022
1 parent 525dbc2 commit c82be37
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ An AssertJ style API for testing KtLint rules ([#1444](https://github.com/pinter
- 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))
- Add experimental rule for unexpected spaces in a nullable type (`nullable-type-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))

- Add experimental rule for consistent spacing before the start of the function body (`function-start-of-body-spacing`) ([#1341](https://github.com/pinterest/ktlint/issues/1341))
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ by passing the `--experimental` flag to `ktlint`.
- `experimental:function-start-of-body-spacing`: Consistent spacing before start of function body
- `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:nullable-type-spacing`: No spaces in a nullable type
- `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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public class ExperimentalRuleSetProvider : RuleSetProvider {
SpacingBetweenFunctionNameAndOpeningParenthesisRule(),
ParameterListSpacingRule(),
FunctionReturnTypeSpacingRule(),
FunctionStartOfBodySpacingRule()
FunctionStartOfBodySpacingRule(),
NullableTypeSpacingRule()
)
}
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.QUEST
import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE
import com.pinterest.ktlint.core.ast.prevLeaf
import org.jetbrains.kotlin.com.intellij.lang.ASTNode
import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement

public class NullableTypeSpacingRule : Rule("nullable-type-spacing") {
override fun visit(
node: ASTNode,
autoCorrect: Boolean,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit
) {
node
.takeIf { node.elementType == QUEST }
?.prevLeaf()
?.takeIf { it.elementType == WHITE_SPACE }
?.let { whiteSpaceBeforeQuest ->
emit(whiteSpaceBeforeQuest.startOffset, "Unexpected whitespace", true)
if (autoCorrect) {
(whiteSpaceBeforeQuest as LeafPsiElement).rawRemove()
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package com.pinterest.ktlint.ruleset.experimental

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

class NullableTypeSpacingRuleTest {
private val nullableTypeSpacingRuleAssertThat = NullableTypeSpacingRule().assertThat()

@Test
fun `Given a simple nullable type with a space before the quest then remove this space`() {
val code =
"""
val foo : String ? = null
""".trimIndent()
val formattedCode =
"""
val foo : String? = null
""".trimIndent()
Assertions.assertThat(NullableTypeSpacingRule().lint(code)).containsExactly(
LintError(1, 17, "nullable-type-spacing", "Unexpected whitespace")
)
Assertions.assertThat(NullableTypeSpacingRule().format(code)).isEqualTo(formattedCode)
}

@Test
fun `Given a non-nullable list of a nullable type with a space before the quest then remove this space`() {
val code =
"""
val foo : List<String ?> = listOf(null)
""".trimIndent()
val formattedCode =
"""
val foo : List<String?> = listOf(null)
""".trimIndent()
nullableTypeSpacingRuleAssertThat(code)
.hasLintViolation(1, 22, "Unexpected whitespace")
.isFormattedAs(formattedCode)
}

@Test
fun `Given a nullable list of a non-nullable type with a space before the quest then remove this space`() {
val code =
"""
val foo : List<String> ? = null
""".trimIndent()
val formattedCode =
"""
val foo : List<String>? = null
""".trimIndent()
nullableTypeSpacingRuleAssertThat(code)
.hasLintViolation(1, 23, "Unexpected whitespace")
.isFormattedAs(formattedCode)
}

@Test
fun `Given a type receiver of nullable simple type with a space before the quest then remove this space`() {
val code =
"""
fun String ?.foo() = "some-result"
""".trimIndent()
val formattedCode =
"""
fun String?.foo() = "some-result"
""".trimIndent()
nullableTypeSpacingRuleAssertThat(code)
.hasLintViolation(1, 11, "Unexpected whitespace")
.isFormattedAs(formattedCode)
}

@Test
fun `Given a type receiver of a non-nullable list of a nullable type with a space before the quest then remove this space`() {
val code =
"""
fun List<String ?>.foo() = "some-result"
""".trimIndent()
val formattedCode =
"""
fun List<String?>.foo() = "some-result"
""".trimIndent()
nullableTypeSpacingRuleAssertThat(code)
.hasLintViolation(1, 16, "Unexpected whitespace")
.isFormattedAs(formattedCode)
}

@Test
fun `Given a parameter of a nullable type with a space before the quest then remove this space`() {
val code =
"""
fun foo(string: String ?) = "some-result"
""".trimIndent()
val formattedCode =
"""
fun foo(string: String?) = "some-result"
""".trimIndent()
nullableTypeSpacingRuleAssertThat(code)
.hasLintViolation(1, 23, "Unexpected whitespace")
.isFormattedAs(formattedCode)
}

@Test
fun `Given a parameter of a list of a nullable type with a space before the quest then remove this space`() {
val code =
"""
fun foo(string: List<String ?>) = "some-result"
""".trimIndent()
val formattedCode =
"""
fun foo(string: List<String?>) = "some-result"
""".trimIndent()
nullableTypeSpacingRuleAssertThat(code)
.hasLintViolation(1, 28, "Unexpected whitespace")
.isFormattedAs(formattedCode)
}

@Test
fun `Given a nullable function return type with a space before the quest then remove this space`() {
val code =
"""
fun foo(): String ? = "some-result"
""".trimIndent()
val formattedCode =
"""
fun foo(): String? = "some-result"
""".trimIndent()
nullableTypeSpacingRuleAssertThat(code)
.hasLintViolation(1, 18, "Unexpected whitespace")
.isFormattedAs(formattedCode)
}

@Test
fun `Given a return type of a non-nullable list of a nullable type with a space before the quest then remove this space`() {
val code =
"""
fun foo(): List<String ?> = listOf("some-result", null)
""".trimIndent()
val formattedCode =
"""
fun foo(): List<String?> = listOf("some-result", null)
""".trimIndent()
nullableTypeSpacingRuleAssertThat(code)
.hasLintViolation(1, 23, "Unexpected whitespace")
.isFormattedAs(formattedCode)
}

@Test
fun `Given a return type of a nullable list with a space before the quest then remove this space`() {
val code =
"""
fun foo(): List<String> ? = null
""".trimIndent()
val formattedCode =
"""
fun foo(): List<String>? = null
""".trimIndent()
nullableTypeSpacingRuleAssertThat(code)
.hasLintViolation(1, 24, "Unexpected whitespace")
.isFormattedAs(formattedCode)
}
}

0 comments on commit c82be37

Please sign in to comment.