Skip to content

Commit

Permalink
Do not wrap a context receiver in a function parameter type reference (
Browse files Browse the repository at this point in the history
…#2892)

Closes #2854
  • Loading branch information
paul-dingemans authored Dec 3, 2024
1 parent 1bc924c commit 52ecb3f
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 7 deletions.
4 changes: 2 additions & 2 deletions documentation/release-latest/docs/rules/standard.md
Original file line number Diff line number Diff line change
Expand Up @@ -4343,9 +4343,9 @@ Suppress or disable rule (1)
ktlint_standard_condition-wrapping = disabled
```

### Content receiver wrapping
### Context receiver wrapping

Wraps the content receiver list to a separate line regardless of maximum line length. If the maximum line length is configured and is exceeded, wrap the context receivers and if needed its projection types to separate lines.
Wraps the context receiver list to a separate line regardless of maximum line length. If the maximum line length is configured and is exceeded, wrap the context receivers and if needed its projection types to separate lines.

=== "[:material-heart:](#) Ktlint"

Expand Down
4 changes: 2 additions & 2 deletions documentation/snapshot/docs/rules/standard.md
Original file line number Diff line number Diff line change
Expand Up @@ -4343,9 +4343,9 @@ Suppress or disable rule (1)
ktlint_standard_condition-wrapping = disabled
```

### Content receiver wrapping
### Context receiver wrapping

Wraps the content receiver list to a separate line regardless of maximum line length. If the maximum line length is configured and is exceeded, wrap the context receivers and if needed its projection types to separate lines.
Wraps the context receiver list of a function to a separate line regardless of maximum line length. If the maximum line length is configured and is exceeded, wrap the context receivers and if needed its projection types to separate lines.

=== "[:material-heart:](#) Ktlint"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@ package com.pinterest.ktlint.ruleset.standard.rules
import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision
import com.pinterest.ktlint.rule.engine.core.api.ElementType.CONTEXT_RECEIVER
import com.pinterest.ktlint.rule.engine.core.api.ElementType.CONTEXT_RECEIVER_LIST
import com.pinterest.ktlint.rule.engine.core.api.ElementType.FUN
import com.pinterest.ktlint.rule.engine.core.api.ElementType.FUNCTION_TYPE
import com.pinterest.ktlint.rule.engine.core.api.ElementType.GT
import com.pinterest.ktlint.rule.engine.core.api.ElementType.RPAR
import com.pinterest.ktlint.rule.engine.core.api.ElementType.TYPE_ARGUMENT_LIST
import com.pinterest.ktlint.rule.engine.core.api.ElementType.TYPE_PROJECTION
import com.pinterest.ktlint.rule.engine.core.api.ElementType.TYPE_REFERENCE
import com.pinterest.ktlint.rule.engine.core.api.ElementType.VALUE_PARAMETER
import com.pinterest.ktlint.rule.engine.core.api.ElementType.VALUE_PARAMETER_LIST
import com.pinterest.ktlint.rule.engine.core.api.IndentConfig
import com.pinterest.ktlint.rule.engine.core.api.IndentConfig.Companion.DEFAULT_INDENT_CONFIG
import com.pinterest.ktlint.rule.engine.core.api.RuleId
Expand Down Expand Up @@ -80,10 +85,11 @@ public class ContextReceiverWrappingRule :
node: ASTNode,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision,
) {
// Context receiver must be followed by new line or comment
// Context receiver must be followed by new line or comment unless it is a type reference of a parameter
node
.lastChildLeafOrSelf()
.nextLeaf { !it.isWhiteSpaceWithoutNewline() && !it.isPartOfComment() }
.takeUnless { it.isTypeReferenceParameterInFunction() }
?.lastChildLeafOrSelf()
?.nextLeaf { !it.isWhiteSpaceWithoutNewline() && !it.isPartOfComment() }
?.takeIf { !it.isWhiteSpaceWithNewline() }
?.let { nodeAfterContextReceiver ->
emit(nodeAfterContextReceiver.startOffset, "Expected a newline after the context receiver", true)
Expand Down Expand Up @@ -126,6 +132,20 @@ public class ContextReceiverWrappingRule :
}
}

private fun ASTNode.isTypeReferenceParameterInFunction() =
takeIf { it.elementType == CONTEXT_RECEIVER_LIST }
?.treeParent
?.takeIf { it.elementType == FUNCTION_TYPE }
?.treeParent
?.takeIf { it.elementType == TYPE_REFERENCE }
?.treeParent
?.takeIf { it.elementType == VALUE_PARAMETER }
?.treeParent
?.takeIf { it.elementType == VALUE_PARAMETER_LIST }
?.treeParent
?.let { it.elementType == FUN }
?: false

private fun visitContextReceiverTypeArgumentList(
node: ASTNode,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,4 +199,16 @@ class ContextReceiverWrappingRuleTest {
LintViolation(2, 36, "Newline expected before closing parenthesis as max line length is violated"),
).isFormattedAs(formattedCode)
}

@Test
fun `Issue 2854 - Given a function parameter with a context receiver then do not wrap after the context receiver`() {
val code =
"""
fun bar1(foo: context(Foo) () -> Unit = { foobar() }) {}
fun bar2(
foo: context(Foo) () -> Unit = { foobar() }
) {}
""".trimIndent()
contextReceiverWrappingRuleAssertThat(code).hasNoLintViolations()
}
}

0 comments on commit 52ecb3f

Please # to comment.