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

Do not wrap a context receiver in a function parameter type reference #2892

Merged
merged 1 commit into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
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
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()
}
}
Loading