Skip to content

Commit

Permalink
Function type in nullable type exceeding max line length (#2060)
Browse files Browse the repository at this point in the history
* Prevent incorrect reporting of violations in case a nullable function type reference exceeds the maximum line length `parameter-list-wrapping`

Closes #1324
  • Loading branch information
paul-dingemans authored May 28, 2023
1 parent 441a9c9 commit 2af5f12
Show file tree
Hide file tree
Showing 3 changed files with 207 additions and 90 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ At this point in time, it is not yet decided what the next steps will be. Ktlint
* Do not flag a (potential) mutable extension property in case the getter is annotated or prefixed with a modifier `property-naming` ([#2024](https://github.com/pinterest/ktlint/issues/2024))
* Do not merge an annotated expression body with the function signature even if it fits on a single line ([#2043](https://github.com/pinterest/ktlint/issues/2043))
* Ignore property with name `serialVersionUID` in `property-naming` ([#2045](https://github.com/pinterest/ktlint/issues/2045))
* Prevent incorrect reporting of violations in case a nullable function type reference exceeds the maximum line length `parameter-list-wrapping` ([#1324](https://github.com/pinterest/ktlint/issues/1324))

### Changed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import com.pinterest.ktlint.rule.engine.core.api.editorconfig.MAX_LINE_LENGTH_PR
import com.pinterest.ktlint.rule.engine.core.api.firstChildLeafOrSelf
import com.pinterest.ktlint.rule.engine.core.api.indent
import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithNewline
import com.pinterest.ktlint.rule.engine.core.api.leavesIncludingSelf
import com.pinterest.ktlint.rule.engine.core.api.nextLeaf
import com.pinterest.ktlint.rule.engine.core.api.prevCodeLeaf
import com.pinterest.ktlint.rule.engine.core.api.prevLeaf
import com.pinterest.ktlint.rule.engine.core.api.prevSibling
Expand Down Expand Up @@ -88,48 +90,55 @@ public class ParameterListWrappingRule :
require(node.elementType == NULLABLE_TYPE)
node
.takeUnless {
// skip when max line length is not exceedd
// skip when max line length is not exceeded
(node.column - 1 + node.textLength) <= maxLineLength
}?.findChildByType(FUNCTION_TYPE)
?.findChildByType(VALUE_PARAMETER_LIST)
?.takeIf { it.findChildByType(VALUE_PARAMETER) != null }
?.takeUnless { it.textContains('\n') }
?.let {
node
.children()
.forEach {
when (it.elementType) {
LPAR -> {
emit(
it.startOffset,
"Parameter of nullable type should be on a separate line (unless the type fits on a single line)",
true,
)
if (autoCorrect) {
it.upsertWhitespaceAfterMe(indentConfig.childIndentOf(node))
}
}
RPAR -> {
emit(it.startOffset, errorMessage(it), true)
if (autoCorrect) {
it.upsertWhitespaceBeforeMe(indentConfig.parentIndentOf(node))
}
}
}?.takeUnless { it.textContains('\n') }
?.takeIf { it.isFunctionTypeWithNonEmptyValueParameterList() }
?.let { nullableType ->
nullableType
.findChildByType(LPAR)
?.takeUnless { it.nextLeaf()?.isWhiteSpaceWithNewline() == true }
?.let { lpar ->
emit(
lpar.startOffset + 1,
"Expected new line before function type as it does not fit on a single line",
true,
)
if (autoCorrect) {
lpar.upsertWhitespaceAfterMe(indentConfig.childIndentOf(node))
}
}
nullableType
.findChildByType(RPAR)
?.takeUnless { it.prevLeaf()?.isWhiteSpaceWithNewline() == true }
?.let { rpar ->
emit(
rpar.startOffset,
"Expected new line after function type as it does not fit on a single line",
true,
)
if (autoCorrect) {
rpar.upsertWhitespaceBeforeMe(indentConfig.parentIndentOf(node))
}
}
}
}

private fun ASTNode.isFunctionTypeWithNonEmptyValueParameterList() =
null !=
findChildByType(FUNCTION_TYPE)
?.findChildByType(VALUE_PARAMETER_LIST)
?.findChildByType(VALUE_PARAMETER)

private fun ASTNode.needToWrapParameterList() =
when {
hasNoParameters() -> false
codeStyle != ktlint_official && isPartOfFunctionLiteralInNonKtlintOfficialCodeStyle() -> false
codeStyle == ktlint_official && isPartOfFunctionLiteralStartingOnSameLineAsClosingParenthesisOfPrecedingReferenceExpression() ->
false
isFunctionTypeWrappedInNullableType() -> false
textContains('\n') -> true
codeStyle == ktlint_official && containsAnnotatedParameter() -> true
exceedsMaxLineLength() -> true
isOnLineExceedingMaxLineLength() -> true
else -> false
}

Expand Down Expand Up @@ -159,11 +168,6 @@ public class ParameterListWrappingRule :
}
}

private fun ASTNode.isFunctionTypeWrappedInNullableType(): Boolean {
require(elementType == VALUE_PARAMETER_LIST)
return treeParent.elementType == FUNCTION_TYPE && treeParent?.treeParent?.elementType == NULLABLE_TYPE
}

private fun ASTNode.containsAnnotatedParameter(): Boolean {
require(elementType == VALUE_PARAMETER_LIST)
return this.children()
Expand Down Expand Up @@ -228,10 +232,12 @@ public class ParameterListWrappingRule :
when (child.elementType) {
LPAR -> {
val prevLeaf = child.prevLeaf()
if (prevLeaf is PsiWhiteSpace && prevLeaf.textContains('\n')) {
if (!child.treeParent.isValueParameterListInFunctionType() &&
prevLeaf.isWhiteSpaceWithNewline()
) {
emit(child.startOffset, errorMessage(child), true)
if (autoCorrect) {
prevLeaf.delete()
(prevLeaf as PsiWhiteSpace).delete()
}
}
}
Expand Down Expand Up @@ -272,7 +278,24 @@ public class ParameterListWrappingRule :
}
}

private fun ASTNode.exceedsMaxLineLength() = (column - 1 + textLength) > maxLineLength && !textContains('\n')
private fun ASTNode.isValueParameterListInFunctionType() =
FUNCTION_TYPE ==
takeIf { it.elementType == VALUE_PARAMETER_LIST }
?.treeParent
?.elementType

private fun ASTNode.isOnLineExceedingMaxLineLength(): Boolean {
val stopLeaf = nextLeaf { it.textContains('\n') }?.nextLeaf()
val lineContent =
prevLeaf { it.textContains('\n') }
?.leavesIncludingSelf()
?.takeWhile { it.prevLeaf() != stopLeaf }
?.joinToString(separator = "") { it.text }
?.substringAfter('\n')
?.substringBefore('\n')
.orEmpty()
return lineContent.length > maxLineLength
}

private fun errorMessage(node: ASTNode) =
when (node.elementType) {
Expand Down
Loading

0 comments on commit 2af5f12

Please sign in to comment.