mirror of
https://github.com/jlengrand/detekt.git
synced 2026-03-10 08:11:23 +00:00
Add CascadingCallWrapping style rule (#4979)
Add a new rule CascadingCallWrapping which requires that if a chained call is placed on a newline then all subsequent calls must be as well, improving readability of long chains.
This commit is contained in:
@@ -155,6 +155,8 @@ potential-bugs:
|
|||||||
style:
|
style:
|
||||||
CanBeNonNullable:
|
CanBeNonNullable:
|
||||||
active: true
|
active: true
|
||||||
|
CascadingCallWrapping:
|
||||||
|
active: true
|
||||||
ClassOrdering:
|
ClassOrdering:
|
||||||
active: true
|
active: true
|
||||||
CollapsibleIfStatements:
|
CollapsibleIfStatements:
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ open class SplitPattern(
|
|||||||
.mapIf(removeTrailingAsterisks) { seq ->
|
.mapIf(removeTrailingAsterisks) { seq ->
|
||||||
seq.map { it.removePrefix("*") }
|
seq.map { it.removePrefix("*") }
|
||||||
.map { it.removeSuffix("*") }
|
.map { it.removeSuffix("*") }
|
||||||
}.toList()
|
}
|
||||||
|
.toList()
|
||||||
|
|
||||||
private fun <T> Sequence<T>.mapIf(
|
private fun <T> Sequence<T>.mapIf(
|
||||||
condition: Boolean,
|
condition: Boolean,
|
||||||
|
|||||||
@@ -490,6 +490,9 @@ style:
|
|||||||
active: true
|
active: true
|
||||||
CanBeNonNullable:
|
CanBeNonNullable:
|
||||||
active: false
|
active: false
|
||||||
|
CascadingCallWrapping:
|
||||||
|
active: false
|
||||||
|
includeElvis: true
|
||||||
ClassOrdering:
|
ClassOrdering:
|
||||||
active: false
|
active: false
|
||||||
CollapsibleIfStatements:
|
CollapsibleIfStatements:
|
||||||
|
|||||||
@@ -171,7 +171,9 @@ class ConfigurationCollector {
|
|||||||
|
|
||||||
private fun KtValueArgument.getReferenceIdentifierOrNull(): String? =
|
private fun KtValueArgument.getReferenceIdentifierOrNull(): String? =
|
||||||
(getArgumentExpression() as? KtCallableReferenceExpression)
|
(getArgumentExpression() as? KtCallableReferenceExpression)
|
||||||
?.callableReference?.getIdentifier()?.text
|
?.callableReference
|
||||||
|
?.getIdentifier()
|
||||||
|
?.text
|
||||||
}
|
}
|
||||||
|
|
||||||
private object ConfigWithAndroidVariantsSupport {
|
private object ConfigWithAndroidVariantsSupport {
|
||||||
|
|||||||
@@ -64,7 +64,8 @@ class MultiRuleVisitor : DetektVisitor() {
|
|||||||
override fun visitSuperTypeList(list: KtSuperTypeList) {
|
override fun visitSuperTypeList(list: KtSuperTypeList) {
|
||||||
val isMultiRule = list.entries
|
val isMultiRule = list.entries
|
||||||
?.mapNotNull { it.typeAsUserType?.referencedName }
|
?.mapNotNull { it.typeAsUserType?.referencedName }
|
||||||
?.any { it == multiRule } ?: false
|
?.any { it == multiRule }
|
||||||
|
?: false
|
||||||
|
|
||||||
val containingClass = list.containingClass()
|
val containingClass = list.containingClass()
|
||||||
val className = containingClass?.name
|
val className = containingClass?.name
|
||||||
|
|||||||
@@ -63,7 +63,8 @@ internal class RuleVisitor : DetektVisitor() {
|
|||||||
val isRule = list.entries
|
val isRule = list.entries
|
||||||
?.asSequence()
|
?.asSequence()
|
||||||
?.map { it.typeAsUserType?.referencedName }
|
?.map { it.typeAsUserType?.referencedName }
|
||||||
?.any { ruleClasses.contains(it) } ?: false
|
?.any { ruleClasses.contains(it) }
|
||||||
|
?: false
|
||||||
|
|
||||||
val containingClass = list.containingClass()
|
val containingClass = list.containingClass()
|
||||||
val className = containingClass?.name
|
val className = containingClass?.name
|
||||||
@@ -138,7 +139,8 @@ internal class RuleVisitor : DetektVisitor() {
|
|||||||
.singleOrNull { it.name == "issue" }
|
.singleOrNull { it.name == "issue" }
|
||||||
?.initializer as? KtCallExpression
|
?.initializer as? KtCallExpression
|
||||||
)
|
)
|
||||||
?.valueArguments.orEmpty()
|
?.valueArguments
|
||||||
|
.orEmpty()
|
||||||
|
|
||||||
if (arguments.size >= ISSUE_ARGUMENT_SIZE) {
|
if (arguments.size >= ISSUE_ARGUMENT_SIZE) {
|
||||||
severity = getArgument(arguments[1], "Severity")
|
severity = getArgument(arguments[1], "Severity")
|
||||||
|
|||||||
@@ -47,7 +47,8 @@ open class DetektExtension @Inject constructor(objects: ObjectFactory) : CodeQua
|
|||||||
|
|
||||||
var baseline: File? = objects.fileProperty()
|
var baseline: File? = objects.fileProperty()
|
||||||
.fileValue(File("detekt-baseline.xml"))
|
.fileValue(File("detekt-baseline.xml"))
|
||||||
.get().asFile
|
.get()
|
||||||
|
.asFile
|
||||||
|
|
||||||
var basePath: String? = null
|
var basePath: String? = null
|
||||||
|
|
||||||
|
|||||||
@@ -156,7 +156,8 @@ class TooManyFunctions(config: Config = Config.empty) : Rule(config) {
|
|||||||
declarations
|
declarations
|
||||||
.filterIsInstance<KtNamedFunction>()
|
.filterIsInstance<KtNamedFunction>()
|
||||||
.count { !isIgnoredFunction(it) }
|
.count { !isIgnoredFunction(it) }
|
||||||
} ?: 0
|
}
|
||||||
|
?: 0
|
||||||
|
|
||||||
private fun isIgnoredFunction(function: KtNamedFunction): Boolean = when {
|
private fun isIgnoredFunction(function: KtNamedFunction): Boolean = when {
|
||||||
ignoreDeprecated && function.hasAnnotation(DEPRECATED) -> true
|
ignoreDeprecated && function.hasAnnotation(DEPRECATED) -> true
|
||||||
|
|||||||
@@ -67,7 +67,10 @@ class SuspendFunWithCoroutineScopeReceiver(config: Config) : Rule(config) {
|
|||||||
private fun checkReceiver(function: KtNamedFunction) {
|
private fun checkReceiver(function: KtNamedFunction) {
|
||||||
val suspendModifier = function.modifierList?.getModifier(KtTokens.SUSPEND_KEYWORD) ?: return
|
val suspendModifier = function.modifierList?.getModifier(KtTokens.SUSPEND_KEYWORD) ?: return
|
||||||
val receiver = bindingContext[BindingContext.FUNCTION, function]
|
val receiver = bindingContext[BindingContext.FUNCTION, function]
|
||||||
?.extensionReceiverParameter?.value?.type ?: return
|
?.extensionReceiverParameter
|
||||||
|
?.value
|
||||||
|
?.type
|
||||||
|
?: return
|
||||||
if (receiver.isCoroutineScope()) {
|
if (receiver.isCoroutineScope()) {
|
||||||
report(
|
report(
|
||||||
CodeSmell(
|
CodeSmell(
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import io.gitlab.arturbosch.detekt.rules.isNonNullCheck
|
|||||||
import io.gitlab.arturbosch.detekt.rules.isNullCheck
|
import io.gitlab.arturbosch.detekt.rules.isNullCheck
|
||||||
import io.gitlab.arturbosch.detekt.rules.isOpen
|
import io.gitlab.arturbosch.detekt.rules.isOpen
|
||||||
import io.gitlab.arturbosch.detekt.rules.isOverride
|
import io.gitlab.arturbosch.detekt.rules.isOverride
|
||||||
import org.jetbrains.kotlin.com.intellij.codeInsight.NullableNotNullManager.isNullable
|
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
@@ -137,11 +136,13 @@ class CanBeNonNullable(config: Config = Config.empty) : Rule(config) {
|
|||||||
function.valueParameters.asSequence()
|
function.valueParameters.asSequence()
|
||||||
.filter {
|
.filter {
|
||||||
it.typeReference?.typeElement is KtNullableType
|
it.typeReference?.typeElement is KtNullableType
|
||||||
}.mapNotNull { parameter ->
|
}
|
||||||
|
.mapNotNull { parameter ->
|
||||||
bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, parameter]?.let {
|
bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, parameter]?.let {
|
||||||
it to parameter
|
it to parameter
|
||||||
}
|
}
|
||||||
}.forEach { (descriptor, param) ->
|
}
|
||||||
|
.forEach { (descriptor, param) ->
|
||||||
candidateDescriptors.add(descriptor)
|
candidateDescriptors.add(descriptor)
|
||||||
nullableParams[descriptor] = NullableParam(param)
|
nullableParams[descriptor] = NullableParam(param)
|
||||||
}
|
}
|
||||||
@@ -175,7 +176,8 @@ class CanBeNonNullable(config: Config = Config.empty) : Rule(config) {
|
|||||||
.filter {
|
.filter {
|
||||||
val onlyNonNullCheck = validSingleChildExpression && it.isNonNullChecked && !it.isNullChecked
|
val onlyNonNullCheck = validSingleChildExpression && it.isNonNullChecked && !it.isNullChecked
|
||||||
it.isNonNullForced || onlyNonNullCheck
|
it.isNonNullForced || onlyNonNullCheck
|
||||||
}.forEach { nullableParam ->
|
}
|
||||||
|
.forEach { nullableParam ->
|
||||||
report(
|
report(
|
||||||
CodeSmell(
|
CodeSmell(
|
||||||
issue,
|
issue,
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
package io.gitlab.arturbosch.detekt.rules.style
|
||||||
|
|
||||||
|
import io.gitlab.arturbosch.detekt.api.CodeSmell
|
||||||
|
import io.gitlab.arturbosch.detekt.api.Config
|
||||||
|
import io.gitlab.arturbosch.detekt.api.Debt
|
||||||
|
import io.gitlab.arturbosch.detekt.api.Entity
|
||||||
|
import io.gitlab.arturbosch.detekt.api.Issue
|
||||||
|
import io.gitlab.arturbosch.detekt.api.Rule
|
||||||
|
import io.gitlab.arturbosch.detekt.api.Severity
|
||||||
|
import io.gitlab.arturbosch.detekt.api.config
|
||||||
|
import io.gitlab.arturbosch.detekt.api.internal.Configuration
|
||||||
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
|
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
||||||
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
|
import org.jetbrains.kotlin.psi.KtQualifiedExpression
|
||||||
|
import org.jetbrains.kotlin.psi.KtUnaryExpression
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requires that all chained calls are placed on a new line if a preceding one is.
|
||||||
|
*
|
||||||
|
* <noncompliant>
|
||||||
|
* foo()
|
||||||
|
* .bar().baz()
|
||||||
|
* </noncompliant>
|
||||||
|
*
|
||||||
|
* <compliant>
|
||||||
|
* foo().bar().baz()
|
||||||
|
*
|
||||||
|
* foo()
|
||||||
|
* .bar()
|
||||||
|
* .baz()
|
||||||
|
* </compliant>
|
||||||
|
*/
|
||||||
|
class CascadingCallWrapping(config: Config = Config.empty) : Rule(config) {
|
||||||
|
override val issue = Issue(
|
||||||
|
id = javaClass.simpleName,
|
||||||
|
severity = Severity.Style,
|
||||||
|
description = "If a chained call is wrapped to a new line, subsequent chained calls should be as well.",
|
||||||
|
debt = Debt.FIVE_MINS,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Configuration("require trailing elvis expressions to be wrapped on a new line")
|
||||||
|
private val includeElvis: Boolean by config(true)
|
||||||
|
|
||||||
|
override fun visitQualifiedExpression(expression: KtQualifiedExpression) {
|
||||||
|
super.visitQualifiedExpression(expression)
|
||||||
|
|
||||||
|
checkExpression(expression, callExpression = expression.selectorExpression)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitBinaryExpression(expression: KtBinaryExpression) {
|
||||||
|
super.visitBinaryExpression(expression)
|
||||||
|
|
||||||
|
if (includeElvis && expression.operationToken == KtTokens.ELVIS) {
|
||||||
|
checkExpression(expression, callExpression = expression.right)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkExpression(expression: KtExpression, callExpression: KtExpression?) {
|
||||||
|
if (!expression.containsNewline() && expression.receiverContainsNewline()) {
|
||||||
|
val callTextOrEmpty = callExpression?.text?.let { " `$it`" }.orEmpty()
|
||||||
|
report(
|
||||||
|
CodeSmell(
|
||||||
|
issue = issue,
|
||||||
|
entity = Entity.from(expression),
|
||||||
|
message = "Chained call$callTextOrEmpty should be wrapped to a new line since preceding calls were."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("ReturnCount")
|
||||||
|
private fun KtExpression.containsNewline(): Boolean {
|
||||||
|
val lhs: KtExpression
|
||||||
|
val rhs: KtExpression
|
||||||
|
|
||||||
|
when (this) {
|
||||||
|
is KtQualifiedExpression -> {
|
||||||
|
lhs = receiverExpression
|
||||||
|
rhs = selectorExpression ?: return false
|
||||||
|
}
|
||||||
|
is KtBinaryExpression -> {
|
||||||
|
if (operationToken != KtTokens.ELVIS) return false
|
||||||
|
lhs = left ?: return false
|
||||||
|
rhs = right ?: return false
|
||||||
|
}
|
||||||
|
else -> return false
|
||||||
|
}
|
||||||
|
|
||||||
|
val receiverEnd = lhs.startOffsetInParent + lhs.textLength
|
||||||
|
val selectorStart = rhs.startOffsetInParent
|
||||||
|
|
||||||
|
return (receiverEnd until selectorStart).any { text[it] == '\n' }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtExpression.receiverContainsNewline(): Boolean {
|
||||||
|
val lhs = when (this) {
|
||||||
|
is KtQualifiedExpression -> receiverExpression
|
||||||
|
is KtBinaryExpression -> left ?: return false
|
||||||
|
else -> return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return when (lhs) {
|
||||||
|
is KtQualifiedExpression -> lhs.containsNewline()
|
||||||
|
is KtUnaryExpression -> (lhs.baseExpression as? KtQualifiedExpression)?.containsNewline() == true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -80,7 +80,8 @@ class ForbiddenVoid(config: Config = Config.empty) : Rule(config) {
|
|||||||
private fun KtTypeReference.isPartOfReturnTypeOfFunction() =
|
private fun KtTypeReference.isPartOfReturnTypeOfFunction() =
|
||||||
getStrictParentOfType<KtNamedFunction>()
|
getStrictParentOfType<KtNamedFunction>()
|
||||||
?.typeReference
|
?.typeReference
|
||||||
?.anyDescendantOfType<KtTypeReference> { it == this } ?: false
|
?.anyDescendantOfType<KtTypeReference> { it == this }
|
||||||
|
?: false
|
||||||
|
|
||||||
private fun KtTypeReference.isParameterTypeOfFunction() =
|
private fun KtTypeReference.isParameterTypeOfFunction() =
|
||||||
getStrictParentOfType<KtParameter>() != null
|
getStrictParentOfType<KtParameter>() != null
|
||||||
|
|||||||
@@ -102,7 +102,8 @@ class ObjectLiteralToLambda(config: Config = Config.empty) : Rule(config) {
|
|||||||
if (
|
if (
|
||||||
declaration.name == null &&
|
declaration.name == null &&
|
||||||
bindingContext.getType(expression)
|
bindingContext.getType(expression)
|
||||||
?.singleSuperTypeOrNull()?.couldBeSamInterface == true &&
|
?.singleSuperTypeOrNull()
|
||||||
|
?.couldBeSamInterface == true &&
|
||||||
declaration.hasConvertibleMethod()
|
declaration.hasConvertibleMethod()
|
||||||
) {
|
) {
|
||||||
report(CodeSmell(issue, Entity.from(expression), issue.description))
|
report(CodeSmell(issue, Entity.from(expression), issue.description))
|
||||||
|
|||||||
@@ -100,7 +100,8 @@ class SerialVersionUIDInSerializableClass(config: Config = Config.empty) : Rule(
|
|||||||
|
|
||||||
private fun hasLongAssignment(property: KtProperty): Boolean {
|
private fun hasLongAssignment(property: KtProperty): Boolean {
|
||||||
val assignmentText = property.children
|
val assignmentText = property.children
|
||||||
.singleOrNull { it is KtConstantExpression || it is KtPrefixExpression }?.text
|
.singleOrNull { it is KtConstantExpression || it is KtPrefixExpression }
|
||||||
|
?.text
|
||||||
return assignmentText != null && assignmentText.last() == 'L' &&
|
return assignmentText != null && assignmentText.last() == 'L' &&
|
||||||
assignmentText.substring(0, assignmentText.length - 1).toLongOrNull() != null
|
assignmentText.substring(0, assignmentText.length - 1).toLongOrNull() != null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ class StyleGuideProvider : DefaultRuleSetProvider {
|
|||||||
ruleSetId,
|
ruleSetId,
|
||||||
listOf(
|
listOf(
|
||||||
CanBeNonNullable(config),
|
CanBeNonNullable(config),
|
||||||
|
CascadingCallWrapping(config),
|
||||||
ClassOrdering(config),
|
ClassOrdering(config),
|
||||||
CollapsibleIfStatements(config),
|
CollapsibleIfStatements(config),
|
||||||
DestructuringDeclarationWithTooManyEntries(config),
|
DestructuringDeclarationWithTooManyEntries(config),
|
||||||
|
|||||||
@@ -0,0 +1,178 @@
|
|||||||
|
package io.gitlab.arturbosch.detekt.rules.style
|
||||||
|
|
||||||
|
import io.gitlab.arturbosch.detekt.api.Config
|
||||||
|
import io.gitlab.arturbosch.detekt.test.TestConfig
|
||||||
|
import io.gitlab.arturbosch.detekt.test.assertThat
|
||||||
|
import io.gitlab.arturbosch.detekt.test.compileAndLint
|
||||||
|
import org.junit.jupiter.api.Nested
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
|
class CascadingCallWrappingSpec {
|
||||||
|
private val subject = CascadingCallWrapping(Config.empty)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `reports missing wrapping`() {
|
||||||
|
val code = """
|
||||||
|
val a = 0
|
||||||
|
.plus(0).plus(0).plus(0)
|
||||||
|
"""
|
||||||
|
|
||||||
|
assertThat(subject.compileAndLint(code))
|
||||||
|
.hasSize(1)
|
||||||
|
.hasTextLocations(8 to 30)
|
||||||
|
.first()
|
||||||
|
.hasMessage("Chained call `plus(0)` should be wrapped to a new line since preceding calls were.")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `does not report when chained calls are on a single line`() {
|
||||||
|
val code = """
|
||||||
|
val a = 0.plus(0).plus(0)
|
||||||
|
"""
|
||||||
|
|
||||||
|
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `does not report wrapped calls`() {
|
||||||
|
val code = """
|
||||||
|
val a = 0
|
||||||
|
.plus(0)
|
||||||
|
.plus(0)
|
||||||
|
"""
|
||||||
|
|
||||||
|
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `does not report unwrapped initial calls`() {
|
||||||
|
val code = """
|
||||||
|
val a = 0.plus(0).plus(0)
|
||||||
|
.plus(0)
|
||||||
|
.plus(0)
|
||||||
|
"""
|
||||||
|
|
||||||
|
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `reports missing wrapping for safe qualified calls`() {
|
||||||
|
val code = """
|
||||||
|
val a = 0
|
||||||
|
?.plus(0)?.plus(0)
|
||||||
|
"""
|
||||||
|
|
||||||
|
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `reports missing wrapping for calls with non-null assertions`() {
|
||||||
|
val code = """
|
||||||
|
val a = 0!!
|
||||||
|
.plus(0)!!.plus(0)
|
||||||
|
"""
|
||||||
|
|
||||||
|
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `reports missing wrapping for properties`() {
|
||||||
|
val code = """
|
||||||
|
val a = ""
|
||||||
|
.plus("").length
|
||||||
|
|
||||||
|
val b = ""
|
||||||
|
.length.plus(0)
|
||||||
|
"""
|
||||||
|
|
||||||
|
assertThat(subject.compileAndLint(code)).hasSize(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
inner class `with multiline calls` {
|
||||||
|
@Test
|
||||||
|
fun `does not report with wrapping`() {
|
||||||
|
val code = """
|
||||||
|
val a = 0
|
||||||
|
.plus(
|
||||||
|
0
|
||||||
|
)
|
||||||
|
.let {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `reports missing wrapping`() {
|
||||||
|
val code = """
|
||||||
|
val a = 0
|
||||||
|
.plus(
|
||||||
|
0
|
||||||
|
)
|
||||||
|
.let {
|
||||||
|
0
|
||||||
|
}.plus(
|
||||||
|
0
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
|
||||||
|
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `does not report when calls are multiline but never wrapped`() {
|
||||||
|
val code = """
|
||||||
|
val a = 0.plus(
|
||||||
|
0
|
||||||
|
).let {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `does not report for single multiline call`() {
|
||||||
|
val code = """
|
||||||
|
val a = 0.plus(
|
||||||
|
0
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
|
||||||
|
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
inner class `with elvis operators` {
|
||||||
|
private val subjectIncludingElvis = CascadingCallWrapping(TestConfig(mapOf("includeElvis" to true)))
|
||||||
|
private val subjectExcludingElvis = CascadingCallWrapping(TestConfig(mapOf("includeElvis" to false)))
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `does not report with wrapping`() {
|
||||||
|
val code = """
|
||||||
|
val a = 0
|
||||||
|
.plus(0)
|
||||||
|
?: 0
|
||||||
|
"""
|
||||||
|
|
||||||
|
assertThat(subjectIncludingElvis.compileAndLint(code)).isEmpty()
|
||||||
|
assertThat(subjectExcludingElvis.compileAndLint(code)).isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `reports missing wrapping`() {
|
||||||
|
val code = """
|
||||||
|
val a = 0
|
||||||
|
.plus(0) ?: 0
|
||||||
|
"""
|
||||||
|
|
||||||
|
assertThat(subjectIncludingElvis.compileAndLint(code)).hasSize(1)
|
||||||
|
assertThat(subjectExcludingElvis.compileAndLint(code)).isEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user