From 60023f30a5262532345ad234adf77198ddc547d3 Mon Sep 17 00:00:00 2001 From: Markus Schwarz Date: Thu, 20 Jan 2022 20:35:49 +0100 Subject: [PATCH 01/18] implement first idea on values with reasons --- .../arturbosch/detekt/api/ConfigProperty.kt | 45 +++++++++++++++---- .../arturbosch/detekt/api/ExplainedValues.kt | 19 ++++++++ .../detekt/api/ConfigPropertySpec.kt | 41 +++++++++++++++++ .../detekt/core/config/YamlConfigSpec.kt | 26 +++++++++++ .../src/test/resources/explained-values.yml | 13 ++++++ 5 files changed, 136 insertions(+), 8 deletions(-) create mode 100644 detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ExplainedValues.kt create mode 100644 detekt-core/src/test/resources/explained-values.yml diff --git a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt index b519b819e43..d115b198dc6 100644 --- a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt +++ b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt @@ -111,14 +111,8 @@ fun configWithAndroidVariants( private fun getValueOrDefault(configAware: ConfigAware, propertyName: String, defaultValue: T): T { @Suppress("UNCHECKED_CAST") return when (defaultValue) { - is List<*> -> { - if (defaultValue.all { it is String }) { - val defaultValueAsListOfStrings = defaultValue as List - configAware.valueOrDefaultCommaSeparated(propertyName, defaultValueAsListOfStrings) as T - } else { - error("Only lists of strings are supported. '$propertyName' is invalid. ") - } - } + is List<*> -> configAware.getListOrDefault(propertyName, defaultValue) as T + is ExplainedValues -> configAware.getExplainedValuesOrDefault(propertyName, defaultValue) as T is String, is Boolean, is Int -> configAware.valueOrDefault(propertyName, defaultValue) @@ -129,6 +123,41 @@ private fun getValueOrDefault(configAware: ConfigAware, propertyName: } } +private fun ConfigAware.getListOrDefault(propertyName: String, defaultValue: List<*>): List { + return if (defaultValue.all { it is String }) { + val defaultValueAsListOfStrings = defaultValue as List + valueOrDefaultCommaSeparated(propertyName, defaultValueAsListOfStrings) + } else { + error("Only lists of strings are supported. '$propertyName' is invalid. ") + } +} + +private fun ConfigAware.getExplainedValuesOrDefault( + propertyName: String, + defaultValue: ExplainedValues +): ExplainedValues { + val valuesAsListOrNull: List<*>? = valueOrNull(propertyName) + if (valuesAsListOrNull != null) { + if (valuesAsListOrNull.all { it is String }) { + return ExplainedValuesImpl(valuesAsListOrNull.map { ExplainedValueImpl(it.toString()) }) + } + if (valuesAsListOrNull.all { it is Map<*, *> }) { + val explainedValues = valuesAsListOrNull + .map { it as Map<*, *> } + .map { dict -> + ExplainedValueImpl( + value = dict["value"].toString(), + reason = dict["reason"]?.toString() + ) + } + return ExplainedValuesImpl(explainedValues) + } else { + error("Only lists of strings are supported. '$propertyName' is invalid. ") + } + } + return defaultValue +} + private abstract class MemoizedConfigProperty : ReadOnlyProperty { private var value: U? = null diff --git a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ExplainedValues.kt b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ExplainedValues.kt new file mode 100644 index 00000000000..e486c26831b --- /dev/null +++ b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ExplainedValues.kt @@ -0,0 +1,19 @@ +package io.gitlab.arturbosch.detekt.api + +interface ExplainedValues : Iterable { + val values: List + override operator fun iterator(): Iterator = values.iterator() +} + +interface ExplainedValue { + val value: String + val reason: String? +} + +fun explainedValues(vararg values: Pair): ExplainedValues { + return ExplainedValuesImpl(values = values.map { ExplainedValueImpl(it.first, it.second) }) +} + +internal data class ExplainedValuesImpl(override val values: List) : ExplainedValues + +internal data class ExplainedValueImpl(override val value: String, override val reason: String? = null) : ExplainedValue diff --git a/detekt-api/src/test/kotlin/io/gitlab/arturbosch/detekt/api/ConfigPropertySpec.kt b/detekt-api/src/test/kotlin/io/gitlab/arturbosch/detekt/api/ConfigPropertySpec.kt index 9ee51433d86..3621ecaed94 100644 --- a/detekt-api/src/test/kotlin/io/gitlab/arturbosch/detekt/api/ConfigPropertySpec.kt +++ b/detekt-api/src/test/kotlin/io/gitlab/arturbosch/detekt/api/ConfigPropertySpec.kt @@ -3,6 +3,7 @@ package io.gitlab.arturbosch.detekt.api import io.gitlab.arturbosch.detekt.test.TestConfig import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy +import org.assertj.core.api.Assertions.tuple import org.spekframework.spek2.Spek import org.spekframework.spek2.style.specification.describe import java.util.concurrent.atomic.AtomicInteger @@ -116,6 +117,46 @@ class ConfigPropertySpec : Spek({ } } } + context("ExplainedValues property") { + val defaultValue by memoized { explainedValues("aValue" to "aReason") } + context("value defined as list") { + val subject by memoized { + object : TestConfigAware("present" to listOf("a", "b", "c")) { + val present: ExplainedValues by config(defaultValue) + val notPresent: ExplainedValues by config(defaultValue) + } + } + it("uses the value provided in config if present") { + assertThat(subject.present) + .extracting(ExplainedValue::value, ExplainedValue::reason) + .containsExactly(tuple("a", null), tuple("b", null), tuple("c", null)) + } + it("uses the default value if not present") { + assertThat(subject.notPresent).isEqualTo(defaultValue) + } + } + context("value defined as list of maps") { + val subject by memoized { + object : TestConfigAware( + "present" to listOf( + mapOf("value" to "a", "reason" to "reasonA"), + mapOf("value" to "b", "reason" to null) + ) + ) { + val present: ExplainedValues by config(defaultValue) + val notPresent: ExplainedValues by config(defaultValue) + } + } + it("uses the value provided in config if present") { + assertThat(subject.present) + .extracting(ExplainedValue::value, ExplainedValue::reason) + .containsExactly(tuple("a", "reasonA"), tuple("b", null)) + } + it("uses the default value if not present") { + assertThat(subject.notPresent).isEqualTo(defaultValue) + } + } + } } context("invalid type") { context("Long") { diff --git a/detekt-core/src/test/kotlin/io/gitlab/arturbosch/detekt/core/config/YamlConfigSpec.kt b/detekt-core/src/test/kotlin/io/gitlab/arturbosch/detekt/core/config/YamlConfigSpec.kt index 68e9435ce8d..9065a70c149 100644 --- a/detekt-core/src/test/kotlin/io/gitlab/arturbosch/detekt/core/config/YamlConfigSpec.kt +++ b/detekt-core/src/test/kotlin/io/gitlab/arturbosch/detekt/core/config/YamlConfigSpec.kt @@ -109,6 +109,32 @@ class YamlConfigSpec : Spek({ assertThat(config).isNotNull } + describe("explained values") { + val config by memoized { YamlConfig.load(resourceAsPath("explained-values.yml")) } + + it("can be parsed") { + println(config) + assertThat(config).isNotNull + } + + it("supports lists") { + println(config) + val actualAsList: List<*>? = config + .subConfig("style") + .subConfig("AsList") + .valueOrNull("values") + assertThat(actualAsList).isNotNull + } + + it("supports maps") { + val actualAsMap: List>? = config + .subConfig("style") + .subConfig("AsMap") + .valueOrNull("values") + assertThat(actualAsMap).isNotNull + } + } + it("throws an exception on an non-existing file") { val path = Paths.get("doesNotExist.yml") assertThatIllegalArgumentException() diff --git a/detekt-core/src/test/resources/explained-values.yml b/detekt-core/src/test/resources/explained-values.yml new file mode 100644 index 00000000000..a0075e73969 --- /dev/null +++ b/detekt-core/src/test/resources/explained-values.yml @@ -0,0 +1,13 @@ +style: + AsList: + values: + - a + - b + - c + AsMap: + values: + - value: a + reason: reason A + - value: b + - value: c + reason: reason C From a7368793541f32dece2015665037c94398de8fc5 Mon Sep 17 00:00:00 2001 From: Markus Schwarz Date: Sun, 23 Jan 2022 15:34:03 +0100 Subject: [PATCH 02/18] have ExplainedValues implement List by delegation --- .../arturbosch/detekt/api/ConfigProperty.kt | 25 ++++++++----------- .../arturbosch/detekt/api/ExplainedValues.kt | 18 +++---------- 2 files changed, 15 insertions(+), 28 deletions(-) diff --git a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt index d115b198dc6..676d2b1727e 100644 --- a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt +++ b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt @@ -111,8 +111,8 @@ fun configWithAndroidVariants( private fun getValueOrDefault(configAware: ConfigAware, propertyName: String, defaultValue: T): T { @Suppress("UNCHECKED_CAST") return when (defaultValue) { - is List<*> -> configAware.getListOrDefault(propertyName, defaultValue) as T is ExplainedValues -> configAware.getExplainedValuesOrDefault(propertyName, defaultValue) as T + is List<*> -> configAware.getListOrDefault(propertyName, defaultValue) as T is String, is Boolean, is Int -> configAware.valueOrDefault(propertyName, defaultValue) @@ -136,26 +136,23 @@ private fun ConfigAware.getExplainedValuesOrDefault( propertyName: String, defaultValue: ExplainedValues ): ExplainedValues { - val valuesAsListOrNull: List<*>? = valueOrNull(propertyName) - if (valuesAsListOrNull != null) { - if (valuesAsListOrNull.all { it is String }) { - return ExplainedValuesImpl(valuesAsListOrNull.map { ExplainedValueImpl(it.toString()) }) - } - if (valuesAsListOrNull.all { it is Map<*, *> }) { - val explainedValues = valuesAsListOrNull + val valuesAsList: List<*> = valueOrNull(propertyName) ?: return defaultValue + if (valuesAsList.all { it is String }) { + return ExplainedValues(values = valuesAsList.map { ExplainedValue(it.toString()) }) + } + if (valuesAsList.all { it is Map<*, *> }) { + return ExplainedValues( + valuesAsList .map { it as Map<*, *> } .map { dict -> - ExplainedValueImpl( + ExplainedValue( value = dict["value"].toString(), reason = dict["reason"]?.toString() ) } - return ExplainedValuesImpl(explainedValues) - } else { - error("Only lists of strings are supported. '$propertyName' is invalid. ") - } + ) } - return defaultValue + error("Only lists of strings are supported. '$propertyName' is invalid. ") } private abstract class MemoizedConfigProperty : ReadOnlyProperty { diff --git a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ExplainedValues.kt b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ExplainedValues.kt index e486c26831b..9dbac086e7c 100644 --- a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ExplainedValues.kt +++ b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ExplainedValues.kt @@ -1,19 +1,9 @@ package io.gitlab.arturbosch.detekt.api -interface ExplainedValues : Iterable { - val values: List - override operator fun iterator(): Iterator = values.iterator() +fun explainedValues(vararg values: Pair): ExplainedValues { + return ExplainedValues(values.map { ExplainedValue(it.first, it.second) }) } -interface ExplainedValue { - val value: String - val reason: String? -} - -fun explainedValues(vararg values: Pair): ExplainedValues { - return ExplainedValuesImpl(values = values.map { ExplainedValueImpl(it.first, it.second) }) -} - -internal data class ExplainedValuesImpl(override val values: List) : ExplainedValues +data class ExplainedValues(val values: List) : List by values -internal data class ExplainedValueImpl(override val value: String, override val reason: String? = null) : ExplainedValue +data class ExplainedValue(val value: String, val reason: String? = null) From 3f14934be48816435ab945d1356907ec885c7d5e Mon Sep 17 00:00:00 2001 From: Markus Schwarz Date: Wed, 9 Feb 2022 20:47:22 +0100 Subject: [PATCH 03/18] Parse explained value defaults --- .../collection/ConfigurationCollector.kt | 96 ++++++--- .../generator/collection/DefaultValue.kt | 5 + .../generator/collection/RuleCollectorSpec.kt | 186 ++++++++++++------ .../generator/util/CollectorTestExtensions.kt | 3 +- 4 files changed, 205 insertions(+), 85 deletions(-) diff --git a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/ConfigurationCollector.kt b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/ConfigurationCollector.kt index 680d37b9809..3ed99a0c601 100644 --- a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/ConfigurationCollector.kt +++ b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/ConfigurationCollector.kt @@ -1,5 +1,7 @@ package io.gitlab.arturbosch.detekt.generator.collection +import io.gitlab.arturbosch.detekt.api.ExplainedValue +import io.gitlab.arturbosch.detekt.api.ExplainedValues import io.gitlab.arturbosch.detekt.generator.collection.ConfigurationCollector.ConfigWithAndroidVariantsSupport.ANDROID_VARIANTS_DELEGATE_NAME import io.gitlab.arturbosch.detekt.generator.collection.ConfigurationCollector.ConfigWithAndroidVariantsSupport.DEFAULT_ANDROID_VALUE_ARGUMENT_NAME import io.gitlab.arturbosch.detekt.generator.collection.ConfigurationCollector.ConfigWithAndroidVariantsSupport.isAndroidVariantConfigDelegate @@ -9,7 +11,12 @@ import io.gitlab.arturbosch.detekt.generator.collection.ConfigurationCollector.C import io.gitlab.arturbosch.detekt.generator.collection.ConfigurationCollector.DefaultValueSupport.getAndroidDefaultValue import io.gitlab.arturbosch.detekt.generator.collection.ConfigurationCollector.DefaultValueSupport.getDefaultValue import io.gitlab.arturbosch.detekt.generator.collection.ConfigurationCollector.DefaultValueSupport.toDefaultValueIfLiteral +import io.gitlab.arturbosch.detekt.generator.collection.ConfigurationCollector.ExplainedValuesSupport.getExplainedValuesDefaultOrNull +import io.gitlab.arturbosch.detekt.generator.collection.ConfigurationCollector.ExplainedValuesSupport.hasExplainedValueDeclaration +import io.gitlab.arturbosch.detekt.generator.collection.ConfigurationCollector.StringListSupport.getListDefaultOrNull +import io.gitlab.arturbosch.detekt.generator.collection.ConfigurationCollector.StringListSupport.hasListDeclaration import io.gitlab.arturbosch.detekt.generator.collection.exception.InvalidDocumentationException +import org.jetbrains.kotlin.psi.KtBinaryExpression import org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.KtCallableReferenceExpression import org.jetbrains.kotlin.psi.KtConstantExpression @@ -56,8 +63,13 @@ class ConfigurationCollector { } private fun KtProperty.getConstantValue(): DefaultValue? { + if (hasExplainedValueDeclaration()) { + return getExplainedValuesDefaultOrNull() + ?: invalidDocumentation { "Invalid declaration of explained values default for property '$text'" } + } if (hasListDeclaration()) { - return DefaultValue.of(getListDeclaration().valueArguments.map { it.text.withoutQuotes() }) + return getListDefaultOrNull(emptyMap()) + ?: invalidDocumentation { "Invalid declaration of string list default for property '$text'" } } return findDescendantOfType()?.toDefaultValueIfLiteral() @@ -125,15 +137,9 @@ class ConfigurationCollector { } fun KtExpression.toDefaultValue(constantsByName: Map): DefaultValue { - val listDeclarationForDefault = getListDeclarationOrNull() - if (listDeclarationForDefault != null) { - val listValues = listDeclarationForDefault.valueArguments.map { - (constantsByName[it.text]?.getAsPlainString() ?: it.text.withoutQuotes()) - } - return DefaultValue.of(listValues) - } - - return toDefaultValueIfLiteral() + return getExplainedValuesDefaultOrNull() + ?: getListDefaultOrNull(constantsByName) + ?: toDefaultValueIfLiteral() ?: constantsByName[text.withoutQuotes()] ?: error("$text is neither a literal nor a constant") } @@ -176,6 +182,61 @@ class ConfigurationCollector { delegate?.expression?.referenceExpression()?.text == ANDROID_VARIANTS_DELEGATE_NAME } + private object ExplainedValuesSupport { + private const val EXPLAINED_VALUE_FACTORY_METHOD = "explainedValues" + + fun KtElement.getExplainedValuesDefaultOrNull(): DefaultValue? { + return getExplainedValueDeclarationOrNull() + ?.valueArguments + ?.map(::toExplainedValue) + ?.let { DefaultValue.of(ExplainedValues(it)) } + } + + fun KtElement.getExplainedValueDeclarationOrNull(): KtCallExpression? = + findDescendantOfType { it.isExplainedValueDeclaration() } + + fun KtCallExpression.isExplainedValueDeclaration(): Boolean { + return referenceExpression()?.text == EXPLAINED_VALUE_FACTORY_METHOD + } + + fun KtProperty.hasExplainedValueDeclaration(): Boolean = + anyDescendantOfType { it.isExplainedValueDeclaration() } + + private fun toExplainedValue(arg: KtValueArgument): ExplainedValue { + val keyToValue = arg.children.first() as? KtBinaryExpression + return keyToValue?.let { + ExplainedValue( + value = it.left!!.text.withoutQuotes(), + reason = it.right!!.text.withoutQuotes() + ) + } ?: error("invalid value argument '${arg.text}'") + } + } + + private object StringListSupport { + private const val LIST_OF = "listOf" + private const val EMPTY_LIST = "emptyList" + private val LIST_CREATORS = setOf(LIST_OF, EMPTY_LIST) + + fun KtElement.getListDefaultOrNull(constantsByName: Map): DefaultValue? { + return getListDeclarationOrNull()?.valueArguments?.map { + (constantsByName[it.text]?.getAsPlainString() ?: it.text.withoutQuotes()) + }?.let { DefaultValue.of(it) } + } + + fun KtElement.getListDeclarationOrNull(): KtCallExpression? = + findDescendantOfType { it.isListDeclaration() } + + fun KtProperty.hasListDeclaration(): Boolean = + anyDescendantOfType { it.isListDeclaration() } + + fun KtElement.getListDeclaration(): KtCallExpression = + checkNotNull(getListDeclarationOrNull()) + + fun KtCallExpression.isListDeclaration() = + referenceExpression()?.text in LIST_CREATORS + } + companion object { private const val SIMPLE_DELEGATE_NAME = "config" private val DELEGATE_NAMES = listOf( @@ -184,25 +245,10 @@ class ConfigurationCollector { ANDROID_VARIANTS_DELEGATE_NAME ) private const val DEFAULT_VALUE_ARGUMENT_NAME = "defaultValue" - private const val LIST_OF = "listOf" - private const val EMPTY_LIST = "emptyList" - private val LIST_CREATORS = setOf(LIST_OF, EMPTY_LIST) - - private fun KtElement.getListDeclaration(): KtCallExpression = - checkNotNull(getListDeclarationOrNull()) - - private fun KtElement.getListDeclarationOrNull(): KtCallExpression? = - findDescendantOfType { it.isListDeclaration() } private fun KtProperty.isInitializedWithConfigDelegate(): Boolean = delegate?.expression?.referenceExpression()?.text in DELEGATE_NAMES - private fun KtProperty.hasListDeclaration(): Boolean = - anyDescendantOfType { it.isListDeclaration() } - - private fun KtCallExpression.isListDeclaration() = - referenceExpression()?.text in LIST_CREATORS - private fun KtElement.invalidDocumentation(message: () -> String): Nothing { throw InvalidDocumentationException("[${containingFile.name}] ${message.invoke()}") } diff --git a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/DefaultValue.kt b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/DefaultValue.kt index d04ba305563..32baf373ff5 100644 --- a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/DefaultValue.kt +++ b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/DefaultValue.kt @@ -1,5 +1,7 @@ package io.gitlab.arturbosch.detekt.generator.collection +import io.gitlab.arturbosch.detekt.api.ExplainedValues + sealed interface DefaultValue { fun isNonEmptyList(): Boolean = false fun getAsList(): List = error("default value is not a list") @@ -11,6 +13,7 @@ sealed interface DefaultValue { fun of(defaultValue: Boolean): DefaultValue = BooleanDefault(defaultValue) fun of(defaultValue: Int): DefaultValue = IntegerDefault(defaultValue) fun of(defaultValue: List): DefaultValue = StringListDefault(defaultValue) + fun of(defaultValue: ExplainedValues): DefaultValue = ExplainedValuesDefault(defaultValue) } } @@ -36,3 +39,5 @@ private data class StringListDefault(private val defaultValue: List) : D override fun getAsPlainString(): String = defaultValue.toString() override fun getQuotedIfNecessary(): String = quoted } + +private data class ExplainedValuesDefault(private val defaultValue: ExplainedValues) : DefaultValue diff --git a/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/collection/RuleCollectorSpec.kt b/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/collection/RuleCollectorSpec.kt index e2b55942213..b0a4b60ed85 100644 --- a/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/collection/RuleCollectorSpec.kt +++ b/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/collection/RuleCollectorSpec.kt @@ -1,5 +1,6 @@ package io.gitlab.arturbosch.detekt.generator.collection +import io.gitlab.arturbosch.detekt.api.explainedValues import io.gitlab.arturbosch.detekt.generator.collection.DefaultValue.Companion.of import io.gitlab.arturbosch.detekt.generator.collection.exception.InvalidAliasesDeclaration import io.gitlab.arturbosch.detekt.generator.collection.exception.InvalidCodeExampleDocumentationException @@ -176,7 +177,7 @@ class RuleCollectorSpec { @Nested inner class `collects configuration options` { @Nested - inner class `using annotation` { + inner class `config` { @Test fun `contains no configuration options by default`() { val code = """ @@ -228,6 +229,73 @@ class RuleCollectorSpec { assertThat(items[0].configuration[0].defaultValue).isEqualTo(of(1_999_000)) } + @Nested + inner class ExplainedValues { + private val code = """ + /** + * description + */ + class SomeRandomClass() : Rule { + @Configuration("description") + private val singleWithPositionalParam: ExplainedValues by config(explainedValues("value" to "reason")) + @Configuration("description") + private val singleWithNamedParam by config(defaultValue = explainedValues("value" to "reason")) + @Configuration("description") + private val singleWithConstant by config(DEFAULT_VALUE) + @Configuration("description") + private val noValues by config(explainedValues()) + @Configuration("description") + private val multipleValues by config(explainedValues("a" to "A and A", "b" to "B and B")) + @Configuration("description") + private val multipleLines by config(explainedValues( + "a" to "A " + + "and A", + "b" to ""${"\""}B and B""${"\""})) + + companion object { + private val DEFAULT_VALUE = explainedValues("value" to "reason") + } + } + """ + private val rule = subject.run(code)[0] + + @Test + fun `parse options of type ExplainedValues`() { + assertThat(rule.configuration).hasSize(6) + } + + @Test + fun `no values`() { + val config = rule.configuration.first { it.name == "noValues" } + assertThat(config.defaultValue).isEqualTo(of(explainedValues())) + } + + @Test + fun `single value`() { + val expected = of(explainedValues("value" to "reason")) + val singleValueConfigs = rule.configuration.filter { it.name.startsWith("single") } + assertThat(singleValueConfigs) + .hasSize(3) + .extracting("defaultValue") + .containsOnly(expected) + } + + @Test + fun `multiple values`() { + val configs = rule.configuration.filter { it.name.startsWith("multiple") } + val expected = of( + explainedValues( + "a" to "A and A", + "b" to "B and B" + ) + ) + assertThat(configs) + .hasSize(2) + .extracting("defaultValue") + .containsOnly(expected) + } + } + @Test fun `extracts default value when it is a multi line string`() { val code = """ @@ -511,12 +579,13 @@ class RuleCollectorSpec { """ assertThatExceptionOfType(InvalidDocumentationException::class.java).isThrownBy { subject.run(code) } } + } - @Nested - inner class `android variants` { - @Test - fun `extracts values with android variants`() { - val code = """ + @Nested + inner class `android variants` { + @Test + fun `extracts values with android variants`() { + val code = """ /** * description */ @@ -525,21 +594,21 @@ class RuleCollectorSpec { private val maxLineLength: Int by configWithAndroidVariants(120, 100) } """ - val items = subject.run(code) - assertThat(items[0].configuration[0]).isEqualTo( - Configuration( - name = "maxLineLength", - description = "description", - defaultValue = of(120), - defaultAndroidValue = of(100), - deprecated = null - ) + val items = subject.run(code) + assertThat(items[0].configuration[0]).isEqualTo( + Configuration( + name = "maxLineLength", + description = "description", + defaultValue = of(120), + defaultAndroidValue = of(100), + deprecated = null ) - } + ) + } - @Test - fun `extracts values with android variants as named arguments`() { - val code = """ + @Test + fun `extracts values with android variants as named arguments`() { + val code = """ /** * description */ @@ -549,24 +618,24 @@ class RuleCollectorSpec { configWithAndroidVariants(defaultValue = 120, defaultAndroidValue = 100) } """ - val items = subject.run(code) - assertThat(items[0].configuration[0]).isEqualTo( - Configuration( - name = "maxLineLength", - description = "description", - defaultValue = of(120), - defaultAndroidValue = of(100), - deprecated = null - ) + val items = subject.run(code) + assertThat(items[0].configuration[0]).isEqualTo( + Configuration( + name = "maxLineLength", + description = "description", + defaultValue = of(120), + defaultAndroidValue = of(100), + deprecated = null ) - } + ) } + } - @Nested - inner class `fallback property` { - @Test - fun `extracts default value`() { - val code = """ + @Nested + inner class `fallback property` { + @Test + fun `extracts default value`() { + val code = """ /** * description */ @@ -581,15 +650,15 @@ class RuleCollectorSpec { private val config3: Int by configWithFallback(defaultValue = 99, fallbackProperty = ::prop) } """ - val items = subject.run(code) - val fallbackProperties = items[0].configuration.filter { it.name.startsWith("config") } - assertThat(fallbackProperties).hasSize(3) - assertThat(fallbackProperties.map { it.defaultValue }).containsOnly(of(99)) - } + val items = subject.run(code) + val fallbackProperties = items[0].configuration.filter { it.name.startsWith("config") } + assertThat(fallbackProperties).hasSize(3) + assertThat(fallbackProperties.map { it.defaultValue }).containsOnly(of(99)) + } - @Test - fun `reports an error if the property to fallback on exists but is not a config property`() { - val code = """ + @Test + fun `reports an error if the property to fallback on exists but is not a config property`() { + val code = """ /** * description */ @@ -599,15 +668,15 @@ class RuleCollectorSpec { private val config: Int by configWithFallback(::prop, 99) } """ - assertThatThrownBy { subject.run(code) } - .isInstanceOf(InvalidDocumentationException::class.java) - .hasMessageContaining("delegate") - } + assertThatThrownBy { subject.run(code) } + .isInstanceOf(InvalidDocumentationException::class.java) + .hasMessageContaining("delegate") } + } - @Nested - inner class `transformed property` { - val code = """ + @Nested + inner class `transformed property` { + val code = """ /** * description */ @@ -619,17 +688,16 @@ class RuleCollectorSpec { } """ - @Test - fun `extracts default value with transformer function`() { - val items = subject.run(code) - assertThat(items[0].configuration[0].defaultValue).isEqualTo(of("[a-z]+")) - } + @Test + fun `extracts default value with transformer function`() { + val items = subject.run(code) + assertThat(items[0].configuration[0].defaultValue).isEqualTo(of("[a-z]+")) + } - @Test - fun `extracts default value with method reference`() { - val items = subject.run(code) - assertThat(items[0].configuration[1].defaultValue).isEqualTo(of(false)) - } + @Test + fun `extracts default value with method reference`() { + val items = subject.run(code) + assertThat(items[0].configuration[1].defaultValue).isEqualTo(of(false)) } } } diff --git a/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/util/CollectorTestExtensions.kt b/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/util/CollectorTestExtensions.kt index 9e6c6e8ff06..419a62a8cd1 100644 --- a/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/util/CollectorTestExtensions.kt +++ b/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/util/CollectorTestExtensions.kt @@ -2,8 +2,9 @@ package io.gitlab.arturbosch.detekt.generator.util import io.github.detekt.test.utils.compileContentForTest import io.gitlab.arturbosch.detekt.generator.collection.Collector +import org.intellij.lang.annotations.Language -fun Collector.run(code: String): List { +fun Collector.run(@Language("kotlin") code: String): List { val ktFile = compileContentForTest(code.trimIndent()) visit(ktFile) return items From 1de89d6d1f4dbfda3c84bca2c51fe0fcf36307b8 Mon Sep 17 00:00:00 2001 From: Markus Schwarz Date: Wed, 9 Feb 2022 21:07:58 +0100 Subject: [PATCH 04/18] fix error message --- .../kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt index 676d2b1727e..6534be14bd6 100644 --- a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt +++ b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt @@ -152,7 +152,7 @@ private fun ConfigAware.getExplainedValuesOrDefault( } ) } - error("Only lists of strings are supported. '$propertyName' is invalid. ") + error("Only lists of strings or maps with keys 'value' and 'reason' are supported. '$propertyName' is invalid. ") } private abstract class MemoizedConfigProperty : ReadOnlyProperty { From 0bbcdc68c5eee0e0e117f265f3ce02d5712fa9b3 Mon Sep 17 00:00:00 2001 From: Markus Schwarz Date: Fri, 18 Feb 2022 21:35:21 +0100 Subject: [PATCH 05/18] [WIP] support ExplainedValues in yaml --- .../detekt/core/config/YamlConfigSpec.kt | 22 +++- .../src/test/resources/explained-values.yml | 4 +- .../generator/collection/DefaultValue.kt | 27 +++- .../arturbosch/detekt/generator/out/Yaml.kt | 37 +++++- .../defaultconfig/RuleSetConfigPrinter.kt | 6 +- .../detekt/generator/out/YamlSpec.kt | 124 +++++++++++++++++- .../defaultconfig/RuleSetConfigPrinterTest.kt | 26 ++++ 7 files changed, 228 insertions(+), 18 deletions(-) diff --git a/detekt-core/src/test/kotlin/io/gitlab/arturbosch/detekt/core/config/YamlConfigSpec.kt b/detekt-core/src/test/kotlin/io/gitlab/arturbosch/detekt/core/config/YamlConfigSpec.kt index a7a280838bc..72a1f3c089a 100644 --- a/detekt-core/src/test/kotlin/io/gitlab/arturbosch/detekt/core/config/YamlConfigSpec.kt +++ b/detekt-core/src/test/kotlin/io/gitlab/arturbosch/detekt/core/config/YamlConfigSpec.kt @@ -132,27 +132,37 @@ class YamlConfigSpec { @Test fun `can be parsed`() { - println(config) assertThat(config).isNotNull } @Test fun `supports lists`() { - println(config) val actualAsList: List<*>? = config .subConfig("style") .subConfig("AsList") .valueOrNull("values") - assertThat(actualAsList).isNotNull + assertThat(actualAsList).hasSize(3) } @Test - fun `supports maps`() { + fun `supports dictionaries`() { val actualAsMap: List>? = config .subConfig("style") - .subConfig("AsMap") + .subConfig("AsListOfMaps") .valueOrNull("values") - assertThat(actualAsMap).isNotNull + assertThat(actualAsMap) + .hasSize(3) + } + + @Test + fun `supports empty dictionaries`() { + val actualAsMap: List>? = config + .subConfig("style") + .subConfig("EmptyListOfMaps") + .valueOrNull("values") + assertThat(actualAsMap) + .isNotNull + .isEmpty() } } diff --git a/detekt-core/src/test/resources/explained-values.yml b/detekt-core/src/test/resources/explained-values.yml index a0075e73969..ea8fecd04c4 100644 --- a/detekt-core/src/test/resources/explained-values.yml +++ b/detekt-core/src/test/resources/explained-values.yml @@ -4,10 +4,12 @@ style: - a - b - c - AsMap: + AsListOfMaps: values: - value: a reason: reason A - value: b - value: c reason: reason C + EmptyListOfMaps: + values: [] diff --git a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/DefaultValue.kt b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/DefaultValue.kt index 32baf373ff5..da80714bcb1 100644 --- a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/DefaultValue.kt +++ b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/DefaultValue.kt @@ -1,8 +1,15 @@ package io.gitlab.arturbosch.detekt.generator.collection import io.gitlab.arturbosch.detekt.api.ExplainedValues +import io.gitlab.arturbosch.detekt.generator.out.YamlNode +import io.gitlab.arturbosch.detekt.generator.out.keyValue +import io.gitlab.arturbosch.detekt.generator.out.list +import io.gitlab.arturbosch.detekt.generator.out.listOfMaps sealed interface DefaultValue { + + fun printAsYaml(name: String, yaml: YamlNode) + fun isNonEmptyList(): Boolean = false fun getAsList(): List = error("default value is not a list") fun getAsPlainString(): String = toString() @@ -19,20 +26,33 @@ sealed interface DefaultValue { private data class StringDefault(private val defaultValue: String) : DefaultValue { private val quoted = "'$defaultValue'" + override fun printAsYaml(name: String, yaml: YamlNode) { + yaml.keyValue { name to quoted } + } + override fun getAsPlainString(): String = defaultValue override fun getQuotedIfNecessary(): String = quoted } private data class BooleanDefault(private val defaultValue: Boolean) : DefaultValue { override fun getAsPlainString(): String = defaultValue.toString() + override fun printAsYaml(name: String, yaml: YamlNode) { + yaml.keyValue { name to defaultValue.toString() } + } } private data class IntegerDefault(private val defaultValue: Int) : DefaultValue { override fun getAsPlainString(): String = defaultValue.toString() + override fun printAsYaml(name: String, yaml: YamlNode) { + yaml.keyValue { name to defaultValue.toString() } + } } private data class StringListDefault(private val defaultValue: List) : DefaultValue { private val quoted: String = defaultValue.map { "'$it'" }.toString() + override fun printAsYaml(name: String, yaml: YamlNode) { + yaml.list(name, defaultValue) + } override fun isNonEmptyList(): Boolean = defaultValue.isNotEmpty() override fun getAsList(): List = defaultValue.ifEmpty { error("default value is an empty list") } @@ -40,4 +60,9 @@ private data class StringListDefault(private val defaultValue: List) : D override fun getQuotedIfNecessary(): String = quoted } -private data class ExplainedValuesDefault(private val defaultValue: ExplainedValues) : DefaultValue +private data class ExplainedValuesDefault(private val defaultValue: ExplainedValues) : DefaultValue { + override fun printAsYaml(name: String, yaml: YamlNode) { + val asMap: List> = defaultValue.values.map { mapOf("value" to it.value, "reason" to it.reason) } + yaml.listOfMaps(name, asMap) + } +} diff --git a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/out/Yaml.kt b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/out/Yaml.kt index 6807507c7a9..902527c47ee 100644 --- a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/out/Yaml.kt +++ b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/out/Yaml.kt @@ -55,12 +55,41 @@ inline fun YamlNode.keyValue(comment: String = "", keyValue: () -> Pair) { - append("$name:") - list.forEach { - append("$SINGLE_INDENT- ${it.quotedForList()}") + if (list.isEmpty()) { + keyValue { name to EMPTY_LIST } + } else { + append("$name:") + list.forEach { + append("${SINGLE_INDENT}${LIST_PREFIX}${it.quotedForList()}") + } + } +} + +fun YamlNode.listOfMaps(name: String, maps: List>) { + val noneEmptyMaps = maps.filter { it.isNotEmpty() } + if (noneEmptyMaps.isEmpty()) { + list(name, emptyList()) + } else { + node(name) { + maps.forEach { map(it) } + } } } +private fun YamlNode.map(map: Map) { + map.entries + .filter { it.value != null } + .sortedBy { it.key } + .forEachIndexed { index, (key, value) -> + val prefix = if (index == 0) { + LIST_PREFIX + } else { + SINGLE_INDENT + } + keyValue { "$prefix$key" to value.orEmpty() } + } +} + inline fun YamlNode.yaml(yaml: () -> String): Unit = append(yaml()) private fun String.quotedForList(): String { @@ -77,3 +106,5 @@ private fun String.quoted() = "'$this'" private const val SINGLE_INDENT = " " private const val SINGLE_QUOTE = "'" private const val DOUBLE_QUOTE = "\"" +private const val EMPTY_LIST = "[]" +private const val LIST_PREFIX = "- " diff --git a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/printer/defaultconfig/RuleSetConfigPrinter.kt b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/printer/defaultconfig/RuleSetConfigPrinter.kt index 9e4c896053b..2e4f54abfe6 100644 --- a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/printer/defaultconfig/RuleSetConfigPrinter.kt +++ b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/printer/defaultconfig/RuleSetConfigPrinter.kt @@ -47,9 +47,5 @@ internal fun YamlNode.printRule(rule: Rule) { internal fun YamlNode.printConfiguration(configuration: Configuration) { if (configuration.isDeprecated()) return - if (configuration.isDefaultValueNonEmptyList()) { - list(configuration.name, configuration.getDefaultValueAsList()) - } else { - keyValue { configuration.name to configuration.defaultValue.getQuotedIfNecessary() } - } + configuration.defaultValue.printAsYaml(configuration.name, this) } diff --git a/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/out/YamlSpec.kt b/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/out/YamlSpec.kt index 839d0ba5250..4f92068294f 100644 --- a/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/out/YamlSpec.kt +++ b/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/out/YamlSpec.kt @@ -7,7 +7,7 @@ import org.junit.jupiter.api.Test class YamlSpec { @Nested - inner class `keyValue` { + inner class KeyValue { @Test fun `renders key and value as provided`() { val result = yaml { keyValue() { "key" to "value" } } @@ -16,7 +16,15 @@ class YamlSpec { } @Nested - inner class `list` { + inner class ListOfStrings { + + @Test + fun `renders an empty list`() { + val given = emptyList() + val result = yaml { list("key", given) } + val expected = "key: []" + assertThat(result).isEqualTo(expected) + } @Test fun `renders single element`() { @@ -74,4 +82,116 @@ class YamlSpec { assertThat(result).isEqualTo(expected) } } + + @Nested + inner class ListOfMaps { + + @Test + fun `renders an empty list of maps`() { + val given = listOf>() + val result = yaml { listOfMaps("key", given) } + val expected = "key: []" + assertThat(result).isEqualTo(expected) + } + + @Test + fun `renders an list of empty maps`() { + val given = listOf>(emptyMap(), emptyMap()) + val result = yaml { listOfMaps("key", given) } + val expected = "key: []" + assertThat(result).isEqualTo(expected) + } + + @Test + fun `renders single map with single element`() { + val given = listOf(mapOf("name" to "value")) + val result = yaml { listOfMaps("key", given) } + val expected = """key: + | - name: value + """.trimMargin() + assertThat(result).isEqualTo(expected) + } + + @Test + fun `renders single map with multiple elements`() { + val given = listOf( + mapOf( + "name1" to "value 1", + "name2" to "value 2", + "name3" to "value 3" + ) + ) + val result = yaml { listOfMaps("key", given) } + val expected = """key: + | - name1: value 1 + | name2: value 2 + | name3: value 3 + """.trimMargin() + assertThat(result).isEqualTo(expected) + } + + @Test + fun `renders multiple maps with multiple elements omitting empty maps`() { + val given = listOf( + mapOf( + "name1" to "value 1", + "name2" to "value 2" + ), + emptyMap(), + mapOf( + "name3" to "value 3" + ), + mapOf( + "name4" to "value 4", + "name5" to "value 5" + ) + ) + val result = yaml { listOfMaps("key", given) } + val expected = """key: + | - name1: value 1 + | name2: value 2 + | - name3: value 3 + | - name4: value 4 + | name5: value 5 + """.trimMargin() + assertThat(result).isEqualTo(expected) + } + + @Test + fun `sorts entries by key name`() { + val given = listOf( + mapOf( + "z" to "value", + "a" to "value", + "x" to "value", + "b" to "value", + ), + ) + val result = yaml { listOfMaps("key", given) } + val expected = """key: + | - a: value + | b: value + | x: value + | z: value + """.trimMargin() + assertThat(result).isEqualTo(expected) + } + + @Test + fun `omits entries with null value`() { + val given = listOf( + mapOf( + "a" to "value", + "b" to null, + "c" to "value", + ), + ) + val result = yaml { listOfMaps("key", given) } + val expected = """key: + | - a: value + | c: value + """.trimMargin() + assertThat(result).isEqualTo(expected) + } + } } diff --git a/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/printer/defaultconfig/RuleSetConfigPrinterTest.kt b/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/printer/defaultconfig/RuleSetConfigPrinterTest.kt index 0cfa1fc5e4d..b6a0d79a4d7 100644 --- a/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/printer/defaultconfig/RuleSetConfigPrinterTest.kt +++ b/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/printer/defaultconfig/RuleSetConfigPrinterTest.kt @@ -1,6 +1,7 @@ package io.gitlab.arturbosch.detekt.generator.printer.defaultconfig import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.api.explainedValues import io.gitlab.arturbosch.detekt.generator.collection.Active import io.gitlab.arturbosch.detekt.generator.collection.Configuration import io.gitlab.arturbosch.detekt.generator.collection.DefaultValue @@ -200,6 +201,31 @@ internal class RuleSetConfigPrinterTest { """.trimMargin() assertThat(actual).isEqualTo(expected) } + + @Test + fun `empty ExplainedValues default value uses empty list syntax`() { + val given = configurationTemplate.copy(defaultValue = DefaultValue.of(explainedValues())) + val actual = yaml { printConfiguration(given) } + assertThat(actual).isEqualTo("name: []") + } + + @Test + fun `ExplainedValues default value block syntax`() { + val given = configurationTemplate.copy(defaultValue = DefaultValue.of(explainedValues( + "a" to "reason a", + "b" to null, + "c" to "reason c", + ))) + val actual = yaml { printConfiguration(given) } + val expected = """name: + | - reason: reason a + | value: a + | - value: b + | - reason: reason c + | value: c + """.trimMargin() + assertThat(actual).isEqualTo(expected) + } } } } From f19b593a54ec34bdf030728c1625722616835834 Mon Sep 17 00:00:00 2001 From: Markus Schwarz Date: Sat, 5 Mar 2022 22:26:23 +0100 Subject: [PATCH 06/18] print markdown value in DefaultValue directly --- .../generator/collection/Configuration.kt | 4 - .../collection/ConfigurationCollector.kt | 2 +- .../generator/collection/DefaultValue.kt | 39 +++++---- .../printer/RuleConfigurationPrinter.kt | 4 +- .../generator/collection/ConfigurationSpec.kt | 81 ------------------- .../printer/RuleConfigurationPrinterTest.kt | 23 ++++++ 6 files changed, 51 insertions(+), 102 deletions(-) delete mode 100644 detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/collection/ConfigurationSpec.kt diff --git a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/Configuration.kt b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/Configuration.kt index 4773e3e25c0..7b77f8a184e 100644 --- a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/Configuration.kt +++ b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/Configuration.kt @@ -8,8 +8,4 @@ data class Configuration( val deprecated: String? ) { fun isDeprecated() = deprecated != null - - fun isDefaultValueNonEmptyList() = defaultValue.isNonEmptyList() - - fun getDefaultValueAsList(): List = defaultValue.getAsList() } diff --git a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/ConfigurationCollector.kt b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/ConfigurationCollector.kt index 3ed99a0c601..6a52ecde4ef 100644 --- a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/ConfigurationCollector.kt +++ b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/ConfigurationCollector.kt @@ -220,7 +220,7 @@ class ConfigurationCollector { fun KtElement.getListDefaultOrNull(constantsByName: Map): DefaultValue? { return getListDeclarationOrNull()?.valueArguments?.map { - (constantsByName[it.text]?.getAsPlainString() ?: it.text.withoutQuotes()) + (constantsByName[it.text]?.getPlainValue() ?: it.text.withoutQuotes()) }?.let { DefaultValue.of(it) } } diff --git a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/DefaultValue.kt b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/DefaultValue.kt index da80714bcb1..cf870517066 100644 --- a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/DefaultValue.kt +++ b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/DefaultValue.kt @@ -8,12 +8,9 @@ import io.gitlab.arturbosch.detekt.generator.out.listOfMaps sealed interface DefaultValue { + fun getPlainValue(): String fun printAsYaml(name: String, yaml: YamlNode) - - fun isNonEmptyList(): Boolean = false - fun getAsList(): List = error("default value is not a list") - fun getAsPlainString(): String = toString() - fun getQuotedIfNecessary(): String = getAsPlainString() + fun printAsMarkdownCode(): String companion object { fun of(defaultValue: String): DefaultValue = StringDefault(defaultValue) @@ -30,22 +27,27 @@ private data class StringDefault(private val defaultValue: String) : DefaultValu yaml.keyValue { name to quoted } } - override fun getAsPlainString(): String = defaultValue - override fun getQuotedIfNecessary(): String = quoted + override fun printAsMarkdownCode(): String = quoted + + override fun getPlainValue(): String = defaultValue } private data class BooleanDefault(private val defaultValue: Boolean) : DefaultValue { - override fun getAsPlainString(): String = defaultValue.toString() + override fun getPlainValue(): String = defaultValue.toString() override fun printAsYaml(name: String, yaml: YamlNode) { yaml.keyValue { name to defaultValue.toString() } } + + override fun printAsMarkdownCode(): String = defaultValue.toString() } private data class IntegerDefault(private val defaultValue: Int) : DefaultValue { - override fun getAsPlainString(): String = defaultValue.toString() + override fun getPlainValue(): String = defaultValue.toString() override fun printAsYaml(name: String, yaml: YamlNode) { yaml.keyValue { name to defaultValue.toString() } } + + override fun printAsMarkdownCode(): String = defaultValue.toString() } private data class StringListDefault(private val defaultValue: List) : DefaultValue { @@ -54,15 +56,24 @@ private data class StringListDefault(private val defaultValue: List) : D yaml.list(name, defaultValue) } - override fun isNonEmptyList(): Boolean = defaultValue.isNotEmpty() - override fun getAsList(): List = defaultValue.ifEmpty { error("default value is an empty list") } - override fun getAsPlainString(): String = defaultValue.toString() - override fun getQuotedIfNecessary(): String = quoted + override fun printAsMarkdownCode(): String = quoted + override fun getPlainValue(): String { + error("there is no plain string representation for list defaults") + } } private data class ExplainedValuesDefault(private val defaultValue: ExplainedValues) : DefaultValue { + override fun getPlainValue(): String { + error("there is no plain string representation for explained value defaults") + } + override fun printAsYaml(name: String, yaml: YamlNode) { - val asMap: List> = defaultValue.values.map { mapOf("value" to it.value, "reason" to it.reason) } + val asMap: List> = + defaultValue.values.map { mapOf("value" to it.value, "reason" to it.reason) } yaml.listOfMaps(name, asMap) } + + override fun printAsMarkdownCode(): String { + return defaultValue.values.map { "'${it.value}'" }.toString() + } } diff --git a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/printer/RuleConfigurationPrinter.kt b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/printer/RuleConfigurationPrinter.kt index 56f2ffe2c67..558ab6b62b4 100644 --- a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/printer/RuleConfigurationPrinter.kt +++ b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/printer/RuleConfigurationPrinter.kt @@ -18,8 +18,8 @@ internal object RuleConfigurationPrinter : DocumentationPrinter Date: Sun, 6 Mar 2022 19:03:50 +0100 Subject: [PATCH 07/18] quote values in yaml maps --- .../arturbosch/detekt/generator/out/Yaml.kt | 6 +-- .../detekt/generator/out/YamlSpec.kt | 48 +++++++++++++------ 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/out/Yaml.kt b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/out/Yaml.kt index 902527c47ee..7e72af6e796 100644 --- a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/out/Yaml.kt +++ b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/out/Yaml.kt @@ -60,7 +60,7 @@ fun YamlNode.list(name: String, list: List) { } else { append("$name:") list.forEach { - append("${SINGLE_INDENT}${LIST_PREFIX}${it.quotedForList()}") + append("${SINGLE_INDENT}${LIST_PREFIX}${it.ensureQuoted()}") } } } @@ -86,13 +86,13 @@ private fun YamlNode.map(map: Map) { } else { SINGLE_INDENT } - keyValue { "$prefix$key" to value.orEmpty() } + keyValue { "$prefix$key" to value!!.ensureQuoted() } } } inline fun YamlNode.yaml(yaml: () -> String): Unit = append(yaml()) -private fun String.quotedForList(): String { +private fun String.ensureQuoted(): String { return when { isBlank() -> quoted() startsWith(SINGLE_QUOTE) && endsWith(SINGLE_QUOTE) || diff --git a/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/out/YamlSpec.kt b/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/out/YamlSpec.kt index 4f92068294f..a2749abf0d8 100644 --- a/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/out/YamlSpec.kt +++ b/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/out/YamlSpec.kt @@ -107,7 +107,7 @@ class YamlSpec { val given = listOf(mapOf("name" to "value")) val result = yaml { listOfMaps("key", given) } val expected = """key: - | - name: value + | - name: 'value' """.trimMargin() assertThat(result).isEqualTo(expected) } @@ -123,9 +123,9 @@ class YamlSpec { ) val result = yaml { listOfMaps("key", given) } val expected = """key: - | - name1: value 1 - | name2: value 2 - | name3: value 3 + | - name1: 'value 1' + | name2: 'value 2' + | name3: 'value 3' """.trimMargin() assertThat(result).isEqualTo(expected) } @@ -148,11 +148,11 @@ class YamlSpec { ) val result = yaml { listOfMaps("key", given) } val expected = """key: - | - name1: value 1 - | name2: value 2 - | - name3: value 3 - | - name4: value 4 - | name5: value 5 + | - name1: 'value 1' + | name2: 'value 2' + | - name3: 'value 3' + | - name4: 'value 4' + | name5: 'value 5' """.trimMargin() assertThat(result).isEqualTo(expected) } @@ -169,10 +169,10 @@ class YamlSpec { ) val result = yaml { listOfMaps("key", given) } val expected = """key: - | - a: value - | b: value - | x: value - | z: value + | - a: 'value' + | b: 'value' + | x: 'value' + | z: 'value' """.trimMargin() assertThat(result).isEqualTo(expected) } @@ -188,8 +188,26 @@ class YamlSpec { ) val result = yaml { listOfMaps("key", given) } val expected = """key: - | - a: value - | c: value + | - a: 'value' + | c: 'value' + """.trimMargin() + assertThat(result).isEqualTo(expected) + } + + @Test + fun `quotes values if necessary`() { + val given = listOf( + mapOf( + "name1" to "'already quoted'", + "name2" to "\"also quoted\"", + "name3" to "should be quoted" + ) + ) + val result = yaml { listOfMaps("key", given) } + val expected = """key: + | - name1: 'already quoted' + | name2: "also quoted" + | name3: 'should be quoted' """.trimMargin() assertThat(result).isEqualTo(expected) } From 87f1ea58d6ebb27821202e01cd45634a127611dd Mon Sep 17 00:00:00 2001 From: marschwar Date: Mon, 7 Mar 2022 19:19:48 +0100 Subject: [PATCH 08/18] Update detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Brais Gabín --- .../kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt index 6534be14bd6..4829d0ec85e 100644 --- a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt +++ b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt @@ -152,7 +152,7 @@ private fun ConfigAware.getExplainedValuesOrDefault( } ) } - error("Only lists of strings or maps with keys 'value' and 'reason' are supported. '$propertyName' is invalid. ") + error("Only lists of strings or maps with keys 'value' and 'reason' are supported. '$propertyName' is invalid.") } private abstract class MemoizedConfigProperty : ReadOnlyProperty { From d8010b6415ec33d013b0ca94f37fd3b5b17a7000 Mon Sep 17 00:00:00 2001 From: marschwar Date: Mon, 7 Mar 2022 19:58:06 +0100 Subject: [PATCH 09/18] Update detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Brais Gabín --- .../kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt index 4829d0ec85e..12994df0d6c 100644 --- a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt +++ b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt @@ -138,7 +138,7 @@ private fun ConfigAware.getExplainedValuesOrDefault( ): ExplainedValues { val valuesAsList: List<*> = valueOrNull(propertyName) ?: return defaultValue if (valuesAsList.all { it is String }) { - return ExplainedValues(values = valuesAsList.map { ExplainedValue(it.toString()) }) + return ExplainedValues(values = valuesAsList.map { ExplainedValue(it as String) }) } if (valuesAsList.all { it is Map<*, *> }) { return ExplainedValues( From 8b24d419dcf18aa24cf4d3e08a3b7d3ed90b9a82 Mon Sep 17 00:00:00 2001 From: Markus Schwarz Date: Mon, 7 Mar 2022 19:28:21 +0100 Subject: [PATCH 10/18] add test case for missing reason --- .../gitlab/arturbosch/detekt/api/ConfigPropertySpec.kt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/detekt-api/src/test/kotlin/io/gitlab/arturbosch/detekt/api/ConfigPropertySpec.kt b/detekt-api/src/test/kotlin/io/gitlab/arturbosch/detekt/api/ConfigPropertySpec.kt index 0e8a68d1328..bf184ed9fff 100644 --- a/detekt-api/src/test/kotlin/io/gitlab/arturbosch/detekt/api/ConfigPropertySpec.kt +++ b/detekt-api/src/test/kotlin/io/gitlab/arturbosch/detekt/api/ConfigPropertySpec.kt @@ -176,7 +176,8 @@ class ConfigPropertySpec { private val subject = object : TestConfigAware( "present" to listOf( mapOf("value" to "a", "reason" to "reasonA"), - mapOf("value" to "b", "reason" to null) + mapOf("value" to "b", "reason" to null), + mapOf("value" to "c"), ) ) { val present: ExplainedValues by config(defaultValue) @@ -187,7 +188,11 @@ class ConfigPropertySpec { fun `uses the value provided in config if present`() { assertThat(subject.present) .extracting(ExplainedValue::value, ExplainedValue::reason) - .containsExactly(tuple("a", "reasonA"), tuple("b", null)) + .containsExactly( + tuple("a", "reasonA"), + tuple("b", null), + tuple("c", null) + ) } @Test From bd0261e65bd6613c75f2ee93685b86f889a80071 Mon Sep 17 00:00:00 2001 From: Markus Schwarz Date: Mon, 7 Mar 2022 19:58:56 +0100 Subject: [PATCH 11/18] fail fast in case of invalid configuration --- .../arturbosch/detekt/api/ConfigProperty.kt | 14 ++++-- .../detekt/api/ConfigPropertySpec.kt | 43 +++++++++++++++++++ 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt index 12994df0d6c..4f88259c942 100644 --- a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt +++ b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt @@ -145,10 +145,16 @@ private fun ConfigAware.getExplainedValuesOrDefault( valuesAsList .map { it as Map<*, *> } .map { dict -> - ExplainedValue( - value = dict["value"].toString(), - reason = dict["reason"]?.toString() - ) + try { + ExplainedValue( + value = dict["value"] as String, + reason = dict["reason"] as String? + ) + } catch (e: ClassCastException) { + throw Config.InvalidConfigurationError(e) + } catch (e: NullPointerException) { + throw Config.InvalidConfigurationError(e) + } } ) } diff --git a/detekt-api/src/test/kotlin/io/gitlab/arturbosch/detekt/api/ConfigPropertySpec.kt b/detekt-api/src/test/kotlin/io/gitlab/arturbosch/detekt/api/ConfigPropertySpec.kt index bf184ed9fff..6624fd6a18c 100644 --- a/detekt-api/src/test/kotlin/io/gitlab/arturbosch/detekt/api/ConfigPropertySpec.kt +++ b/detekt-api/src/test/kotlin/io/gitlab/arturbosch/detekt/api/ConfigPropertySpec.kt @@ -200,6 +200,49 @@ class ConfigPropertySpec { assertThat(subject.notPresent).isEqualTo(defaultValue) } } + + @Nested + inner class `value defined as list of maps with invalid data` { + + @Test + fun `value missing`() { + assertThatThrownBy { + object : TestConfigAware( + "present" to listOf( + mapOf("reason" to "reason") + ) + ) { + val present: ExplainedValues by config(defaultValue) + }.present + }.isInstanceOf(Config.InvalidConfigurationError::class.java) + } + + @Test + fun `value with an invalid type`() { + assertThatThrownBy { + object : TestConfigAware( + "present" to listOf( + mapOf("value" to 42, "reason" to "reason") + ) + ) { + val present: ExplainedValues by config(defaultValue) + }.present + }.isInstanceOf(Config.InvalidConfigurationError::class.java) + } + + @Test + fun `reason with an invalid type`() { + assertThatThrownBy { + object : TestConfigAware( + "present" to listOf( + mapOf("value" to "a", "reason" to 42) + ) + ) { + val present: ExplainedValues by config(defaultValue) + }.present + }.isInstanceOf(Config.InvalidConfigurationError::class.java) + } + } } } From 204aaec535bcdbd6ae2ebffd43e34d144dab916e Mon Sep 17 00:00:00 2001 From: Markus Schwarz Date: Mon, 21 Mar 2022 20:55:03 +0100 Subject: [PATCH 12/18] rename explainedValues to valuesWithReason --- .../arturbosch/detekt/api/ConfigProperty.kt | 14 +++--- .../arturbosch/detekt/api/ExplainedValues.kt | 9 ---- .../arturbosch/detekt/api/ValuesWithReason.kt | 9 ++++ .../detekt/api/ConfigPropertySpec.kt | 22 +++++----- .../detekt/core/config/YamlConfigSpec.kt | 4 +- ...ined-values.yml => values-with-reason.yml} | 0 .../collection/ConfigurationCollector.kt | 44 +++++++++---------- .../generator/collection/DefaultValue.kt | 8 ++-- .../generator/collection/RuleCollectorSpec.kt | 30 ++++++------- .../printer/RuleConfigurationPrinterTest.kt | 6 +-- .../defaultconfig/RuleSetConfigPrinterTest.kt | 32 ++++++++------ 11 files changed, 91 insertions(+), 87 deletions(-) delete mode 100644 detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ExplainedValues.kt create mode 100644 detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ValuesWithReason.kt rename detekt-core/src/test/resources/{explained-values.yml => values-with-reason.yml} (100%) diff --git a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt index 4f88259c942..5f45cd0f18f 100644 --- a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt +++ b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt @@ -111,7 +111,7 @@ fun configWithAndroidVariants( private fun getValueOrDefault(configAware: ConfigAware, propertyName: String, defaultValue: T): T { @Suppress("UNCHECKED_CAST") return when (defaultValue) { - is ExplainedValues -> configAware.getExplainedValuesOrDefault(propertyName, defaultValue) as T + is ValuesWithReason -> configAware.getValuesWithReasonOrDefault(propertyName, defaultValue) as T is List<*> -> configAware.getListOrDefault(propertyName, defaultValue) as T is String, is Boolean, @@ -132,21 +132,21 @@ private fun ConfigAware.getListOrDefault(propertyName: String, defaultValue: Lis } } -private fun ConfigAware.getExplainedValuesOrDefault( +private fun ConfigAware.getValuesWithReasonOrDefault( propertyName: String, - defaultValue: ExplainedValues -): ExplainedValues { + defaultValue: ValuesWithReason +): ValuesWithReason { val valuesAsList: List<*> = valueOrNull(propertyName) ?: return defaultValue if (valuesAsList.all { it is String }) { - return ExplainedValues(values = valuesAsList.map { ExplainedValue(it as String) }) + return ValuesWithReason(values = valuesAsList.map { ValueWithReason(it as String) }) } if (valuesAsList.all { it is Map<*, *> }) { - return ExplainedValues( + return ValuesWithReason( valuesAsList .map { it as Map<*, *> } .map { dict -> try { - ExplainedValue( + ValueWithReason( value = dict["value"] as String, reason = dict["reason"] as String? ) diff --git a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ExplainedValues.kt b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ExplainedValues.kt deleted file mode 100644 index 9dbac086e7c..00000000000 --- a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ExplainedValues.kt +++ /dev/null @@ -1,9 +0,0 @@ -package io.gitlab.arturbosch.detekt.api - -fun explainedValues(vararg values: Pair): ExplainedValues { - return ExplainedValues(values.map { ExplainedValue(it.first, it.second) }) -} - -data class ExplainedValues(val values: List) : List by values - -data class ExplainedValue(val value: String, val reason: String? = null) diff --git a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ValuesWithReason.kt b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ValuesWithReason.kt new file mode 100644 index 00000000000..e491537731d --- /dev/null +++ b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ValuesWithReason.kt @@ -0,0 +1,9 @@ +package io.gitlab.arturbosch.detekt.api + +fun valuesWithReason(vararg values: Pair): ValuesWithReason { + return ValuesWithReason(values.map { ValueWithReason(it.first, it.second) }) +} + +data class ValuesWithReason(val values: List) : List by values + +data class ValueWithReason(val value: String, val reason: String? = null) diff --git a/detekt-api/src/test/kotlin/io/gitlab/arturbosch/detekt/api/ConfigPropertySpec.kt b/detekt-api/src/test/kotlin/io/gitlab/arturbosch/detekt/api/ConfigPropertySpec.kt index 6624fd6a18c..6d9f414dbaf 100644 --- a/detekt-api/src/test/kotlin/io/gitlab/arturbosch/detekt/api/ConfigPropertySpec.kt +++ b/detekt-api/src/test/kotlin/io/gitlab/arturbosch/detekt/api/ConfigPropertySpec.kt @@ -148,20 +148,20 @@ class ConfigPropertySpec { } @Nested - inner class `ExplainedValues property` { - private val defaultValue = explainedValues("aValue" to "aReason") + inner class `ValuesWithReason property` { + private val defaultValue = valuesWithReason("aValue" to "aReason") @Nested inner class `value defined as list` { private val subject = object : TestConfigAware("present" to listOf("a", "b", "c")) { - val present: ExplainedValues by config(defaultValue) - val notPresent: ExplainedValues by config(defaultValue) + val present: ValuesWithReason by config(defaultValue) + val notPresent: ValuesWithReason by config(defaultValue) } @Test fun `uses the value provided in config if present`() { assertThat(subject.present) - .extracting(ExplainedValue::value, ExplainedValue::reason) + .extracting(ValueWithReason::value, ValueWithReason::reason) .containsExactly(tuple("a", null), tuple("b", null), tuple("c", null)) } @@ -180,14 +180,14 @@ class ConfigPropertySpec { mapOf("value" to "c"), ) ) { - val present: ExplainedValues by config(defaultValue) - val notPresent: ExplainedValues by config(defaultValue) + val present: ValuesWithReason by config(defaultValue) + val notPresent: ValuesWithReason by config(defaultValue) } @Test fun `uses the value provided in config if present`() { assertThat(subject.present) - .extracting(ExplainedValue::value, ExplainedValue::reason) + .extracting(ValueWithReason::value, ValueWithReason::reason) .containsExactly( tuple("a", "reasonA"), tuple("b", null), @@ -212,7 +212,7 @@ class ConfigPropertySpec { mapOf("reason" to "reason") ) ) { - val present: ExplainedValues by config(defaultValue) + val present: ValuesWithReason by config(defaultValue) }.present }.isInstanceOf(Config.InvalidConfigurationError::class.java) } @@ -225,7 +225,7 @@ class ConfigPropertySpec { mapOf("value" to 42, "reason" to "reason") ) ) { - val present: ExplainedValues by config(defaultValue) + val present: ValuesWithReason by config(defaultValue) }.present }.isInstanceOf(Config.InvalidConfigurationError::class.java) } @@ -238,7 +238,7 @@ class ConfigPropertySpec { mapOf("value" to "a", "reason" to 42) ) ) { - val present: ExplainedValues by config(defaultValue) + val present: ValuesWithReason by config(defaultValue) }.present }.isInstanceOf(Config.InvalidConfigurationError::class.java) } diff --git a/detekt-core/src/test/kotlin/io/gitlab/arturbosch/detekt/core/config/YamlConfigSpec.kt b/detekt-core/src/test/kotlin/io/gitlab/arturbosch/detekt/core/config/YamlConfigSpec.kt index 72a1f3c089a..8c45bc8c762 100644 --- a/detekt-core/src/test/kotlin/io/gitlab/arturbosch/detekt/core/config/YamlConfigSpec.kt +++ b/detekt-core/src/test/kotlin/io/gitlab/arturbosch/detekt/core/config/YamlConfigSpec.kt @@ -127,8 +127,8 @@ class YamlConfigSpec { } @Nested - inner class `explained values` { - private val config = YamlConfig.load(resourceAsPath("explained-values.yml")) + inner class `Values with reason` { + private val config = YamlConfig.load(resourceAsPath("values-with-reason.yml")) @Test fun `can be parsed`() { diff --git a/detekt-core/src/test/resources/explained-values.yml b/detekt-core/src/test/resources/values-with-reason.yml similarity index 100% rename from detekt-core/src/test/resources/explained-values.yml rename to detekt-core/src/test/resources/values-with-reason.yml diff --git a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/ConfigurationCollector.kt b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/ConfigurationCollector.kt index 6a52ecde4ef..7f6efc62722 100644 --- a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/ConfigurationCollector.kt +++ b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/ConfigurationCollector.kt @@ -1,7 +1,7 @@ package io.gitlab.arturbosch.detekt.generator.collection -import io.gitlab.arturbosch.detekt.api.ExplainedValue -import io.gitlab.arturbosch.detekt.api.ExplainedValues +import io.gitlab.arturbosch.detekt.api.ValueWithReason +import io.gitlab.arturbosch.detekt.api.ValuesWithReason import io.gitlab.arturbosch.detekt.generator.collection.ConfigurationCollector.ConfigWithAndroidVariantsSupport.ANDROID_VARIANTS_DELEGATE_NAME import io.gitlab.arturbosch.detekt.generator.collection.ConfigurationCollector.ConfigWithAndroidVariantsSupport.DEFAULT_ANDROID_VALUE_ARGUMENT_NAME import io.gitlab.arturbosch.detekt.generator.collection.ConfigurationCollector.ConfigWithAndroidVariantsSupport.isAndroidVariantConfigDelegate @@ -11,10 +11,10 @@ import io.gitlab.arturbosch.detekt.generator.collection.ConfigurationCollector.C import io.gitlab.arturbosch.detekt.generator.collection.ConfigurationCollector.DefaultValueSupport.getAndroidDefaultValue import io.gitlab.arturbosch.detekt.generator.collection.ConfigurationCollector.DefaultValueSupport.getDefaultValue import io.gitlab.arturbosch.detekt.generator.collection.ConfigurationCollector.DefaultValueSupport.toDefaultValueIfLiteral -import io.gitlab.arturbosch.detekt.generator.collection.ConfigurationCollector.ExplainedValuesSupport.getExplainedValuesDefaultOrNull -import io.gitlab.arturbosch.detekt.generator.collection.ConfigurationCollector.ExplainedValuesSupport.hasExplainedValueDeclaration import io.gitlab.arturbosch.detekt.generator.collection.ConfigurationCollector.StringListSupport.getListDefaultOrNull import io.gitlab.arturbosch.detekt.generator.collection.ConfigurationCollector.StringListSupport.hasListDeclaration +import io.gitlab.arturbosch.detekt.generator.collection.ConfigurationCollector.ValuesWithReasonSupport.getValuesWithReasonDefaultOrNull +import io.gitlab.arturbosch.detekt.generator.collection.ConfigurationCollector.ValuesWithReasonSupport.hasValuesWithReasonDeclaration import io.gitlab.arturbosch.detekt.generator.collection.exception.InvalidDocumentationException import org.jetbrains.kotlin.psi.KtBinaryExpression import org.jetbrains.kotlin.psi.KtCallExpression @@ -63,9 +63,9 @@ class ConfigurationCollector { } private fun KtProperty.getConstantValue(): DefaultValue? { - if (hasExplainedValueDeclaration()) { - return getExplainedValuesDefaultOrNull() - ?: invalidDocumentation { "Invalid declaration of explained values default for property '$text'" } + if (hasValuesWithReasonDeclaration()) { + return getValuesWithReasonDefaultOrNull() + ?: invalidDocumentation { "Invalid declaration of values with reasons default for property '$text'" } } if (hasListDeclaration()) { return getListDefaultOrNull(emptyMap()) @@ -137,7 +137,7 @@ class ConfigurationCollector { } fun KtExpression.toDefaultValue(constantsByName: Map): DefaultValue { - return getExplainedValuesDefaultOrNull() + return getValuesWithReasonDefaultOrNull() ?: getListDefaultOrNull(constantsByName) ?: toDefaultValueIfLiteral() ?: constantsByName[text.withoutQuotes()] @@ -182,30 +182,30 @@ class ConfigurationCollector { delegate?.expression?.referenceExpression()?.text == ANDROID_VARIANTS_DELEGATE_NAME } - private object ExplainedValuesSupport { - private const val EXPLAINED_VALUE_FACTORY_METHOD = "explainedValues" + private object ValuesWithReasonSupport { + private const val VALUES_WITH_REASON_FACTORY_METHOD = "valuesWithReason" - fun KtElement.getExplainedValuesDefaultOrNull(): DefaultValue? { - return getExplainedValueDeclarationOrNull() + fun KtElement.getValuesWithReasonDefaultOrNull(): DefaultValue? { + return getValuesWithReasonDeclarationOrNull() ?.valueArguments - ?.map(::toExplainedValue) - ?.let { DefaultValue.of(ExplainedValues(it)) } + ?.map(::toValueWithReason) + ?.let { DefaultValue.of(ValuesWithReason(it)) } } - fun KtElement.getExplainedValueDeclarationOrNull(): KtCallExpression? = - findDescendantOfType { it.isExplainedValueDeclaration() } + fun KtElement.getValuesWithReasonDeclarationOrNull(): KtCallExpression? = + findDescendantOfType { it.isValuesWithReasonDeclaration() } - fun KtCallExpression.isExplainedValueDeclaration(): Boolean { - return referenceExpression()?.text == EXPLAINED_VALUE_FACTORY_METHOD + fun KtCallExpression.isValuesWithReasonDeclaration(): Boolean { + return referenceExpression()?.text == VALUES_WITH_REASON_FACTORY_METHOD } - fun KtProperty.hasExplainedValueDeclaration(): Boolean = - anyDescendantOfType { it.isExplainedValueDeclaration() } + fun KtProperty.hasValuesWithReasonDeclaration(): Boolean = + anyDescendantOfType { it.isValuesWithReasonDeclaration() } - private fun toExplainedValue(arg: KtValueArgument): ExplainedValue { + private fun toValueWithReason(arg: KtValueArgument): ValueWithReason { val keyToValue = arg.children.first() as? KtBinaryExpression return keyToValue?.let { - ExplainedValue( + ValueWithReason( value = it.left!!.text.withoutQuotes(), reason = it.right!!.text.withoutQuotes() ) diff --git a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/DefaultValue.kt b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/DefaultValue.kt index cf870517066..ab5f2077de9 100644 --- a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/DefaultValue.kt +++ b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/DefaultValue.kt @@ -1,6 +1,6 @@ package io.gitlab.arturbosch.detekt.generator.collection -import io.gitlab.arturbosch.detekt.api.ExplainedValues +import io.gitlab.arturbosch.detekt.api.ValuesWithReason import io.gitlab.arturbosch.detekt.generator.out.YamlNode import io.gitlab.arturbosch.detekt.generator.out.keyValue import io.gitlab.arturbosch.detekt.generator.out.list @@ -17,7 +17,7 @@ sealed interface DefaultValue { fun of(defaultValue: Boolean): DefaultValue = BooleanDefault(defaultValue) fun of(defaultValue: Int): DefaultValue = IntegerDefault(defaultValue) fun of(defaultValue: List): DefaultValue = StringListDefault(defaultValue) - fun of(defaultValue: ExplainedValues): DefaultValue = ExplainedValuesDefault(defaultValue) + fun of(defaultValue: ValuesWithReason): DefaultValue = ValuesWithReasonDefault(defaultValue) } } @@ -62,9 +62,9 @@ private data class StringListDefault(private val defaultValue: List) : D } } -private data class ExplainedValuesDefault(private val defaultValue: ExplainedValues) : DefaultValue { +private data class ValuesWithReasonDefault(private val defaultValue: ValuesWithReason) : DefaultValue { override fun getPlainValue(): String { - error("there is no plain string representation for explained value defaults") + error("there is no plain string representation for values with reason defaults") } override fun printAsYaml(name: String, yaml: YamlNode) { diff --git a/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/collection/RuleCollectorSpec.kt b/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/collection/RuleCollectorSpec.kt index b0a4b60ed85..1ca9b604982 100644 --- a/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/collection/RuleCollectorSpec.kt +++ b/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/collection/RuleCollectorSpec.kt @@ -1,6 +1,6 @@ package io.gitlab.arturbosch.detekt.generator.collection -import io.gitlab.arturbosch.detekt.api.explainedValues +import io.gitlab.arturbosch.detekt.api.valuesWithReason import io.gitlab.arturbosch.detekt.generator.collection.DefaultValue.Companion.of import io.gitlab.arturbosch.detekt.generator.collection.exception.InvalidAliasesDeclaration import io.gitlab.arturbosch.detekt.generator.collection.exception.InvalidCodeExampleDocumentationException @@ -230,49 +230,49 @@ class RuleCollectorSpec { } @Nested - inner class ExplainedValues { + inner class ValuesWithReasonSpec { private val code = """ /** * description */ class SomeRandomClass() : Rule { @Configuration("description") - private val singleWithPositionalParam: ExplainedValues by config(explainedValues("value" to "reason")) + private val singleWithPositionalParam: ValuesWithReason by config(valuesWithReason("value" to "reason")) @Configuration("description") - private val singleWithNamedParam by config(defaultValue = explainedValues("value" to "reason")) + private val singleWithNamedParam by config(defaultValue = valuesWithReason("value" to "reason")) @Configuration("description") private val singleWithConstant by config(DEFAULT_VALUE) @Configuration("description") - private val noValues by config(explainedValues()) + private val noValues by config(valuesWithReason()) @Configuration("description") - private val multipleValues by config(explainedValues("a" to "A and A", "b" to "B and B")) + private val multipleValues by config(valuesWithReason("a" to "A and A", "b" to "B and B")) @Configuration("description") - private val multipleLines by config(explainedValues( + private val multipleLines by config(valuesWithReason( "a" to "A " + "and A", "b" to ""${"\""}B and B""${"\""})) companion object { - private val DEFAULT_VALUE = explainedValues("value" to "reason") + private val DEFAULT_VALUE = valuesWithReason("value" to "reason") } } """ private val rule = subject.run(code)[0] @Test - fun `parse options of type ExplainedValues`() { + fun `parse options of type ValuesWithReason`() { assertThat(rule.configuration).hasSize(6) } @Test fun `no values`() { val config = rule.configuration.first { it.name == "noValues" } - assertThat(config.defaultValue).isEqualTo(of(explainedValues())) + assertThat(config.defaultValue).isEqualTo(of(valuesWithReason())) } @Test fun `single value`() { - val expected = of(explainedValues("value" to "reason")) + val expected = of(valuesWithReason("value" to "reason")) val singleValueConfigs = rule.configuration.filter { it.name.startsWith("single") } assertThat(singleValueConfigs) .hasSize(3) @@ -284,7 +284,7 @@ class RuleCollectorSpec { fun `multiple values`() { val configs = rule.configuration.filter { it.name.startsWith("multiple") } val expected = of( - explainedValues( + valuesWithReason( "a" to "A and A", "b" to "B and B" ) @@ -593,7 +593,7 @@ class RuleCollectorSpec { @Configuration("description") private val maxLineLength: Int by configWithAndroidVariants(120, 100) } - """ + """ val items = subject.run(code) assertThat(items[0].configuration[0]).isEqualTo( Configuration( @@ -617,7 +617,7 @@ class RuleCollectorSpec { private val maxLineLength: Int by configWithAndroidVariants(defaultValue = 120, defaultAndroidValue = 100) } - """ + """ val items = subject.run(code) assertThat(items[0].configuration[0]).isEqualTo( Configuration( @@ -686,7 +686,7 @@ class RuleCollectorSpec { @Configuration("description") private val config2: String by config(false, Boolean::toString) } - """ + """ @Test fun `extracts default value with transformer function`() { diff --git a/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/printer/RuleConfigurationPrinterTest.kt b/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/printer/RuleConfigurationPrinterTest.kt index ffbad1534e2..f25819866a8 100644 --- a/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/printer/RuleConfigurationPrinterTest.kt +++ b/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/printer/RuleConfigurationPrinterTest.kt @@ -1,6 +1,6 @@ package io.gitlab.arturbosch.detekt.generator.printer -import io.gitlab.arturbosch.detekt.api.explainedValues +import io.gitlab.arturbosch.detekt.api.valuesWithReason import io.gitlab.arturbosch.detekt.generator.collection.Configuration import io.gitlab.arturbosch.detekt.generator.collection.DefaultValue import org.assertj.core.api.Assertions.assertThat @@ -61,10 +61,10 @@ internal class RuleConfigurationPrinterTest { } @Test - fun `explained values default`() { + fun `values with reason default`() { val config = configTemplate.copy( defaultValue = DefaultValue.of( - explainedValues( + valuesWithReason( "a" to "reason for a", "b" to "reason for b", "c" to null diff --git a/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/printer/defaultconfig/RuleSetConfigPrinterTest.kt b/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/printer/defaultconfig/RuleSetConfigPrinterTest.kt index b6a0d79a4d7..35532232259 100644 --- a/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/printer/defaultconfig/RuleSetConfigPrinterTest.kt +++ b/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/printer/defaultconfig/RuleSetConfigPrinterTest.kt @@ -1,7 +1,7 @@ package io.gitlab.arturbosch.detekt.generator.printer.defaultconfig import io.gitlab.arturbosch.detekt.api.Config -import io.gitlab.arturbosch.detekt.api.explainedValues +import io.gitlab.arturbosch.detekt.api.valuesWithReason import io.gitlab.arturbosch.detekt.generator.collection.Active import io.gitlab.arturbosch.detekt.generator.collection.Configuration import io.gitlab.arturbosch.detekt.generator.collection.DefaultValue @@ -203,26 +203,30 @@ internal class RuleSetConfigPrinterTest { } @Test - fun `empty ExplainedValues default value uses empty list syntax`() { - val given = configurationTemplate.copy(defaultValue = DefaultValue.of(explainedValues())) + fun `empty ValuesWithReason default value uses empty list syntax`() { + val given = configurationTemplate.copy(defaultValue = DefaultValue.of(valuesWithReason())) val actual = yaml { printConfiguration(given) } assertThat(actual).isEqualTo("name: []") } @Test - fun `ExplainedValues default value block syntax`() { - val given = configurationTemplate.copy(defaultValue = DefaultValue.of(explainedValues( - "a" to "reason a", - "b" to null, - "c" to "reason c", - ))) + fun `ValuesWithReason default value block syntax`() { + val given = configurationTemplate.copy( + defaultValue = DefaultValue.of( + valuesWithReason( + "a" to "reason a", + "b" to null, + "c" to "reason c", + ) + ) + ) val actual = yaml { printConfiguration(given) } val expected = """name: - | - reason: reason a - | value: a - | - value: b - | - reason: reason c - | value: c + | - reason: 'reason a' + | value: 'a' + | - value: 'b' + | - reason: 'reason c' + | value: 'c' """.trimMargin() assertThat(actual).isEqualTo(expected) } From c2eec92599126a7e09f3ede27cf3049e648b8da0 Mon Sep 17 00:00:00 2001 From: Markus Schwarz Date: Mon, 21 Mar 2022 22:04:48 +0100 Subject: [PATCH 13/18] add api documentation --- detekt-api/api/detekt-api.api | 28 ++++++++++++++ .../arturbosch/detekt/api/ValuesWithReason.kt | 37 ++++++++++++++++++- .../collection/ConfigurationCollector.kt | 4 +- .../generator/collection/DefaultValue.kt | 4 +- 4 files changed, 67 insertions(+), 6 deletions(-) diff --git a/detekt-api/api/detekt-api.api b/detekt-api/api/detekt-api.api index dabd9594cd3..8405d730980 100644 --- a/detekt-api/api/detekt-api.api +++ b/detekt-api/api/detekt-api.api @@ -616,3 +616,31 @@ public abstract interface annotation class io/gitlab/arturbosch/detekt/api/Unsta public abstract fun reason ()Ljava/lang/String; } +public final class io/gitlab/arturbosch/detekt/api/ValueWithReason { + public fun (Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;Ljava/lang/String;)Lio/gitlab/arturbosch/detekt/api/ValueWithReason; + public static synthetic fun copy$default (Lio/gitlab/arturbosch/detekt/api/ValueWithReason;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lio/gitlab/arturbosch/detekt/api/ValueWithReason; + public fun equals (Ljava/lang/Object;)Z + public final fun getReason ()Ljava/lang/String; + public final fun getValue ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class io/gitlab/arturbosch/detekt/api/ValuesWithReason : java/lang/Iterable, kotlin/jvm/internal/markers/KMappedMarker { + public final fun copy (Ljava/util/List;)Lio/gitlab/arturbosch/detekt/api/ValuesWithReason; + public static synthetic fun copy$default (Lio/gitlab/arturbosch/detekt/api/ValuesWithReason;Ljava/util/List;ILjava/lang/Object;)Lio/gitlab/arturbosch/detekt/api/ValuesWithReason; + public fun equals (Ljava/lang/Object;)Z + public fun hashCode ()I + public fun iterator ()Ljava/util/Iterator; + public fun toString ()Ljava/lang/String; +} + +public final class io/gitlab/arturbosch/detekt/api/ValuesWithReasonKt { + public static final fun valuesWithReason (Ljava/util/List;)Lio/gitlab/arturbosch/detekt/api/ValuesWithReason; + public static final fun valuesWithReason ([Lkotlin/Pair;)Lio/gitlab/arturbosch/detekt/api/ValuesWithReason; +} + diff --git a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ValuesWithReason.kt b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ValuesWithReason.kt index e491537731d..7fe929f2c17 100644 --- a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ValuesWithReason.kt +++ b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ValuesWithReason.kt @@ -1,9 +1,42 @@ package io.gitlab.arturbosch.detekt.api +/** + * This factory method can be used by rule authors to specify one or many configuration values along with an + * explanation for each value. For example: + * + * ```kotlin + * @Configuration("imports which should not be used") + * private val imports: ValuesWithReason by config( + * valuesWithReason("org.junit.Test" to "Do not use Junit4. Use org.junit.jupiter.api.Test instead.") + * ) + * ``` + * + * Note that the [config] property delegate only supports the factory methods when defining [ValuesWithReason]. + */ fun valuesWithReason(vararg values: Pair): ValuesWithReason { - return ValuesWithReason(values.map { ValueWithReason(it.first, it.second) }) + return valuesWithReason(values.map { ValueWithReason(it.first, it.second) }) } -data class ValuesWithReason(val values: List) : List by values +/** + * This factory method can be used by rule authors to specify one or many configuration values along with an + * explanation for each value. + * + * Note that the [config] property delegate only supports the factory methods when defining [ValuesWithReason]. + */ +fun valuesWithReason(values: List): ValuesWithReason { + return ValuesWithReason(values) +} + +/** + * [ValuesWithReason] is essentially the same as [List] of [ValueWithReason]. Due to type erasure we cannot use the + * list directly. Instances of this type should always created using the [valuesWithReason] factory method. + */ +data class ValuesWithReason internal constructor(private val values: List) : + Iterable by values +/** + * A ValueWithReason represents a single configuration value that may have an explanation as to why it is used. + * @property value the actual value that is configured + * @property reason an optional explanation for the configured value + */ data class ValueWithReason(val value: String, val reason: String? = null) diff --git a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/ConfigurationCollector.kt b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/ConfigurationCollector.kt index 7f6efc62722..23ce70079cf 100644 --- a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/ConfigurationCollector.kt +++ b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/ConfigurationCollector.kt @@ -1,7 +1,7 @@ package io.gitlab.arturbosch.detekt.generator.collection import io.gitlab.arturbosch.detekt.api.ValueWithReason -import io.gitlab.arturbosch.detekt.api.ValuesWithReason +import io.gitlab.arturbosch.detekt.api.valuesWithReason import io.gitlab.arturbosch.detekt.generator.collection.ConfigurationCollector.ConfigWithAndroidVariantsSupport.ANDROID_VARIANTS_DELEGATE_NAME import io.gitlab.arturbosch.detekt.generator.collection.ConfigurationCollector.ConfigWithAndroidVariantsSupport.DEFAULT_ANDROID_VALUE_ARGUMENT_NAME import io.gitlab.arturbosch.detekt.generator.collection.ConfigurationCollector.ConfigWithAndroidVariantsSupport.isAndroidVariantConfigDelegate @@ -189,7 +189,7 @@ class ConfigurationCollector { return getValuesWithReasonDeclarationOrNull() ?.valueArguments ?.map(::toValueWithReason) - ?.let { DefaultValue.of(ValuesWithReason(it)) } + ?.let { DefaultValue.of(valuesWithReason(it)) } } fun KtElement.getValuesWithReasonDeclarationOrNull(): KtCallExpression? = diff --git a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/DefaultValue.kt b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/DefaultValue.kt index ab5f2077de9..f3b481b2371 100644 --- a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/DefaultValue.kt +++ b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/DefaultValue.kt @@ -69,11 +69,11 @@ private data class ValuesWithReasonDefault(private val defaultValue: ValuesWithR override fun printAsYaml(name: String, yaml: YamlNode) { val asMap: List> = - defaultValue.values.map { mapOf("value" to it.value, "reason" to it.reason) } + defaultValue.map { mapOf("value" to it.value, "reason" to it.reason) } yaml.listOfMaps(name, asMap) } override fun printAsMarkdownCode(): String { - return defaultValue.values.map { "'${it.value}'" }.toString() + return defaultValue.map { "'${it.value}'" }.toString() } } From edcf8c3cd0bc1f79142520263ebbf41808a00608 Mon Sep 17 00:00:00 2001 From: Markus Schwarz Date: Mon, 21 Mar 2022 22:39:14 +0100 Subject: [PATCH 14/18] resolve and suppress code smells --- .../io/gitlab/arturbosch/detekt/api/ConfigProperty.kt | 2 +- .../detekt/generator/collection/ConfigurationCollector.kt | 6 ++++-- .../io/gitlab/arturbosch/detekt/generator/out/Yaml.kt | 2 +- .../generator/printer/defaultconfig/RuleSetConfigPrinter.kt | 1 - .../io/gitlab/arturbosch/detekt/generator/out/YamlSpec.kt | 2 +- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt index 5f45cd0f18f..fc70b1ec24e 100644 --- a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt +++ b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt @@ -152,7 +152,7 @@ private fun ConfigAware.getValuesWithReasonOrDefault( ) } catch (e: ClassCastException) { throw Config.InvalidConfigurationError(e) - } catch (e: NullPointerException) { + } catch (@Suppress("TooGenericExceptionCaught") e: NullPointerException) { throw Config.InvalidConfigurationError(e) } } diff --git a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/ConfigurationCollector.kt b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/ConfigurationCollector.kt index 23ce70079cf..86e36b87da4 100644 --- a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/ConfigurationCollector.kt +++ b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/ConfigurationCollector.kt @@ -206,8 +206,10 @@ class ConfigurationCollector { val keyToValue = arg.children.first() as? KtBinaryExpression return keyToValue?.let { ValueWithReason( - value = it.left!!.text.withoutQuotes(), - reason = it.right!!.text.withoutQuotes() + value = it.left?.text?.withoutQuotes() + ?: error("left side of value with reason argument is null"), + reason = it.right?.text?.withoutQuotes() + ?: error("right side of value with reason argument is null") ) } ?: error("invalid value argument '${arg.text}'") } diff --git a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/out/Yaml.kt b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/out/Yaml.kt index 7e72af6e796..100f1cbf7ba 100644 --- a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/out/Yaml.kt +++ b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/out/Yaml.kt @@ -86,7 +86,7 @@ private fun YamlNode.map(map: Map) { } else { SINGLE_INDENT } - keyValue { "$prefix$key" to value!!.ensureQuoted() } + keyValue { "$prefix$key" to (value?.ensureQuoted() ?: error("value cannot be null")) } } } diff --git a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/printer/defaultconfig/RuleSetConfigPrinter.kt b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/printer/defaultconfig/RuleSetConfigPrinter.kt index 2e4f54abfe6..3034cd417a1 100644 --- a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/printer/defaultconfig/RuleSetConfigPrinter.kt +++ b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/printer/defaultconfig/RuleSetConfigPrinter.kt @@ -7,7 +7,6 @@ import io.gitlab.arturbosch.detekt.generator.collection.RuleSetPage import io.gitlab.arturbosch.detekt.generator.collection.RuleSetProvider import io.gitlab.arturbosch.detekt.generator.out.YamlNode import io.gitlab.arturbosch.detekt.generator.out.keyValue -import io.gitlab.arturbosch.detekt.generator.out.list import io.gitlab.arturbosch.detekt.generator.out.node internal fun YamlNode.printRuleSetPage(ruleSetPage: RuleSetPage) { diff --git a/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/out/YamlSpec.kt b/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/out/YamlSpec.kt index a2749abf0d8..7dd481ba9a6 100644 --- a/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/out/YamlSpec.kt +++ b/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/out/YamlSpec.kt @@ -88,7 +88,7 @@ class YamlSpec { @Test fun `renders an empty list of maps`() { - val given = listOf>() + val given = emptyList>() val result = yaml { listOfMaps("key", given) } val expected = "key: []" assertThat(result).isEqualTo(expected) From ecbce9fe40ff096b2f1145a183ddeee6d55a02ca Mon Sep 17 00:00:00 2001 From: Markus Schwarz Date: Mon, 21 Mar 2022 22:45:16 +0100 Subject: [PATCH 15/18] suppress UNCHECKED_CAST --- .../kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt index fc70b1ec24e..056cd9f6eaa 100644 --- a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt +++ b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/ConfigProperty.kt @@ -125,6 +125,7 @@ private fun getValueOrDefault(configAware: ConfigAware, propertyName: private fun ConfigAware.getListOrDefault(propertyName: String, defaultValue: List<*>): List { return if (defaultValue.all { it is String }) { + @Suppress("UNCHECKED_CAST") val defaultValueAsListOfStrings = defaultValue as List valueOrDefaultCommaSeparated(propertyName, defaultValueAsListOfStrings) } else { From 8b4a23ceb23284967b4e0252886bbe4959563a67 Mon Sep 17 00:00:00 2001 From: Markus Schwarz Date: Tue, 24 May 2022 21:56:14 +0200 Subject: [PATCH 16/18] merge RuleCollectorSpec --- .../generator/collection/RuleCollectorSpec.kt | 918 +++++++++--------- 1 file changed, 457 insertions(+), 461 deletions(-) diff --git a/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/collection/RuleCollectorSpec.kt b/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/collection/RuleCollectorSpec.kt index 1ca9b604982..2990b09e8ea 100644 --- a/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/collection/RuleCollectorSpec.kt +++ b/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/collection/RuleCollectorSpec.kt @@ -23,176 +23,173 @@ class RuleCollectorSpec { subject = RuleCollector() } - @Nested - inner class `a RuleCollector` { - - @Test - fun `collects no rules when no class is extended`() { - val code = "class SomeRandomClass" - val items = subject.run(code) - assertThat(items).isEmpty() - } + @Test + fun `collects no rules when no class is extended`() { + val code = "class SomeRandomClass" + val items = subject.run(code) + assertThat(items).isEmpty() + } - @Test - fun `collects no rules when no rule class is extended`() { - val code = "class SomeRandomClass : SomeOtherClass" - val items = subject.run(code) - assertThat(items).isEmpty() - } + @Test + fun `collects no rules when no rule class is extended`() { + val code = "class SomeRandomClass : SomeOtherClass" + val items = subject.run(code) + assertThat(items).isEmpty() + } - @Test - fun `throws when a class extends Rule but has no valid documentation`() { - val rules = listOf("Rule", "FormattingRule", "ThresholdRule", "EmptyRule") - for (rule in rules) { - val code = "class SomeRandomClass : $rule" - assertThatExceptionOfType(InvalidDocumentationException::class.java).isThrownBy { subject.run(code) } - } + @Test + fun `throws when a class extends Rule but has no valid documentation`() { + val rules = listOf("Rule", "FormattingRule", "ThresholdRule", "EmptyRule") + for (rule in rules) { + val code = "class SomeRandomClass : $rule" + assertThatExceptionOfType(InvalidDocumentationException::class.java).isThrownBy { subject.run(code) } } + } - @Test - fun `collects the rule name`() { - val name = "SomeRandomClass" - val code = """ - /** - * description - */ - class $name : Rule - """ - val items = subject.run(code) - assertThat(items[0].name).isEqualTo(name) - } + @Test + fun `collects the rule name`() { + val name = "SomeRandomClass" + val code = """ + /** + * description + */ + class $name : Rule + """ + val items = subject.run(code) + assertThat(items[0].name).isEqualTo(name) + } - @Test - fun `collects the rule description`() { - val description = "description" - val code = """ - /** - * $description - */ - class SomeRandomClass : Rule - """ - val items = subject.run(code) - assertThat(items[0].description).isEqualTo(description) - } + @Test + fun `collects the rule description`() { + val description = "description" + val code = """ + /** + * $description + */ + class SomeRandomClass : Rule + """ + val items = subject.run(code) + assertThat(items[0].description).isEqualTo(description) + } - @Test - fun `has a multi paragraph description`() { - val description = "description" - val code = """ - /** - * $description - * - * more... - */ - class SomeRandomClass : Rule - """ - val items = subject.run(code) - assertThat(items[0].description).startsWith(description) - assertThat(items[0].description).contains("more...") - } + @Test + fun `has a multi paragraph description`() { + val description = "description" + val code = """ + /** + * $description + * + * more... + */ + class SomeRandomClass : Rule + """ + val items = subject.run(code) + assertThat(items[0].description).startsWith(description) + assertThat(items[0].description).contains("more...") + } - @Test - fun `is not active by default`() { - val code = """ - /** - * description - */ - class SomeRandomClass : Rule - """ - val items = subject.run(code) - assertThat(items[0].defaultActivationStatus.active).isFalse() - } + @Test + fun `is not active by default`() { + val code = """ + /** + * description + */ + class SomeRandomClass : Rule + """ + val items = subject.run(code) + assertThat(items[0].defaultActivationStatus.active).isFalse() + } - @Test - fun `is active by default with valid version`() { - val code = """ - /** - * description - */ - @ActiveByDefault("1.12.123") - class SomeRandomClass : Rule - """ - val items = subject.run(code) - val defaultActivationStatus = items[0].defaultActivationStatus as Active - assertThat(defaultActivationStatus.since).isEqualTo("1.12.123") - } + @Test + fun `is active by default with valid version`() { + val code = """ + /** + * description + */ + @ActiveByDefault("1.12.123") + class SomeRandomClass : Rule + """ + val items = subject.run(code) + val defaultActivationStatus = items[0].defaultActivationStatus as Active + assertThat(defaultActivationStatus.since).isEqualTo("1.12.123") + } - @Test - fun `is active by default with named since`() { - val code = """ - /** - * description - */ - @ActiveByDefault(since = "1.2.3") - class SomeRandomClass : Rule - """ - val items = subject.run(code) - val defaultActivationStatus = items[0].defaultActivationStatus as Active - assertThat(defaultActivationStatus.since).isEqualTo("1.2.3") - } + @Test + fun `is active by default with named since`() { + val code = """ + /** + * description + */ + @ActiveByDefault(since = "1.2.3") + class SomeRandomClass : Rule + """ + val items = subject.run(code) + val defaultActivationStatus = items[0].defaultActivationStatus as Active + assertThat(defaultActivationStatus.since).isEqualTo("1.2.3") + } - @Test - fun `is active by default with invalid version`() { - val code = """ - /** - * description - */ - @ActiveByDefault("1.2.x") - class SomeRandomClass : Rule - """ - assertThatExceptionOfType(InvalidDocumentationException::class.java).isThrownBy { subject.run(code) } - } + @Test + fun `is active by default with invalid version`() { + val code = """ + /** + * description + */ + @ActiveByDefault("1.2.x") + class SomeRandomClass : Rule + """ + assertThatExceptionOfType(InvalidDocumentationException::class.java).isThrownBy { subject.run(code) } + } - @Test - fun `is auto-correctable`() { - val code = """ - /** - * description - * - */ - @AutoCorrectable(since = "1.0.0") - class SomeRandomClass : Rule - """ - val items = subject.run(code) - assertThat(items[0].autoCorrect).isTrue() - } + @Test + fun `is auto-correctable`() { + val code = """ + /** + * description + * + */ + @AutoCorrectable(since = "1.0.0") + class SomeRandomClass : Rule + """ + val items = subject.run(code) + assertThat(items[0].autoCorrect).isTrue() + } - @Test - fun `collects the issue property`() { - val code = """ - /** - * description - */ - class SomeRandomClass : Rule { - override val defaultRuleIdAliases = setOf("RULE", "RULE2") - override val issue = Issue(javaClass.simpleName, Severity.Style, "", Debt.TEN_MINS) - } - """ - val items = subject.run(code) - assertThat(items[0].severity).isEqualTo("Style") - assertThat(items[0].debt).isEqualTo("10min") - assertThat(items[0].aliases).isEqualTo("RULE, RULE2") - } + @Test + fun `collects the issue property`() { + val code = """ + /** + * description + */ + class SomeRandomClass : Rule { + override val defaultRuleIdAliases = setOf("RULE", "RULE2") + override val issue = Issue(javaClass.simpleName, Severity.Style, "", Debt.TEN_MINS) + } + """ + val items = subject.run(code) + assertThat(items[0].severity).isEqualTo("Style") + assertThat(items[0].debt).isEqualTo("10min") + assertThat(items[0].aliases).isEqualTo("RULE, RULE2") + } + @Nested + inner class `collects configuration options using annotation` { @Nested - inner class `collects configuration options` { - @Nested - inner class `config` { - @Test - fun `contains no configuration options by default`() { - val code = """ + inner class `config` { + @Test + fun `contains no configuration options by default`() { + val code = """ /** * description */ class SomeRandomClass : Rule - """ - val items = subject.run(code) - assertThat(items[0].configuration).isEmpty() - } + """ + val items = subject.run(code) + assertThat(items[0].configuration).isEmpty() + } - @Test - fun `contains one configuration option with correct formatting`() { - val code = """ + @Test + fun `contains one configuration option with correct formatting`() { + val code = """ /** * description */ @@ -200,22 +197,22 @@ class RuleCollectorSpec { @Configuration("description") private val config: String by config("[A-Z$]") } - """ - val items = subject.run(code) - assertThat(items[0].configuration).hasSize(1) - val expectedConfiguration = Configuration( - name = "config", - description = "description", - defaultValue = of("[A-Z$]"), - defaultAndroidValue = null, - deprecated = null - ) - assertThat(items[0].configuration[0]).isEqualTo(expectedConfiguration) - } + """ + val items = subject.run(code) + assertThat(items[0].configuration).hasSize(1) + val expectedConfiguration = Configuration( + name = "config", + description = "description", + defaultValue = of("[A-Z$]"), + defaultAndroidValue = null, + deprecated = null + ) + assertThat(items[0].configuration[0]).isEqualTo(expectedConfiguration) + } - @Test - fun `contains one configuration option of type Int`() { - val code = """ + @Test + fun `contains one configuration option of type Int`() { + val code = """ /** * description */ @@ -223,15 +220,15 @@ class RuleCollectorSpec { @Configuration("description") private val config: Int by config(1_999_000) } - """ - val items = subject.run(code) - assertThat(items[0].configuration).hasSize(1) - assertThat(items[0].configuration[0].defaultValue).isEqualTo(of(1_999_000)) - } + """ + val items = subject.run(code) + assertThat(items[0].configuration).hasSize(1) + assertThat(items[0].configuration[0].defaultValue).isEqualTo(of(1_999_000)) + } - @Nested - inner class ValuesWithReasonSpec { - private val code = """ + @Nested + inner class ValuesWithReasonSpec { + private val code = """ /** * description */ @@ -256,49 +253,49 @@ class RuleCollectorSpec { private val DEFAULT_VALUE = valuesWithReason("value" to "reason") } } - """ - private val rule = subject.run(code)[0] - - @Test - fun `parse options of type ValuesWithReason`() { - assertThat(rule.configuration).hasSize(6) - } - - @Test - fun `no values`() { - val config = rule.configuration.first { it.name == "noValues" } - assertThat(config.defaultValue).isEqualTo(of(valuesWithReason())) - } - - @Test - fun `single value`() { - val expected = of(valuesWithReason("value" to "reason")) - val singleValueConfigs = rule.configuration.filter { it.name.startsWith("single") } - assertThat(singleValueConfigs) - .hasSize(3) - .extracting("defaultValue") - .containsOnly(expected) - } - - @Test - fun `multiple values`() { - val configs = rule.configuration.filter { it.name.startsWith("multiple") } - val expected = of( - valuesWithReason( - "a" to "A and A", - "b" to "B and B" - ) - ) - assertThat(configs) - .hasSize(2) - .extracting("defaultValue") - .containsOnly(expected) - } + """ + private val rule = subject.run(code)[0] + + @Test + fun `parse options of type ValuesWithReason`() { + assertThat(rule.configuration).hasSize(6) + } + + @Test + fun `no values`() { + val config = rule.configuration.first { it.name == "noValues" } + assertThat(config.defaultValue).isEqualTo(of(valuesWithReason())) + } + + @Test + fun `single value`() { + val expected = of(valuesWithReason("value" to "reason")) + val singleValueConfigs = rule.configuration.filter { it.name.startsWith("single") } + assertThat(singleValueConfigs) + .hasSize(3) + .extracting("defaultValue") + .containsOnly(expected) } @Test - fun `extracts default value when it is a multi line string`() { - val code = """ + fun `multiple values`() { + val configs = rule.configuration.filter { it.name.startsWith("multiple") } + val expected = of( + valuesWithReason( + "a" to "A and A", + "b" to "B and B" + ) + ) + assertThat(configs) + .hasSize(2) + .extracting("defaultValue") + .containsOnly(expected) + } + } + + @Test + fun `extracts default value when it is a multi line string`() { + val code = """ /** * description */ @@ -306,14 +303,14 @@ class RuleCollectorSpec { @Configuration("description") private val config: String by config(""${"\""}abcd""${"\""}) } - """ - val items = subject.run(code) - assertThat(items[0].configuration[0].defaultValue).isEqualTo(of("abcd")) - } + """ + val items = subject.run(code) + assertThat(items[0].configuration[0].defaultValue).isEqualTo(of("abcd")) + } - @Test - fun `extracts default value when defined with named parameter`() { - val code = """ + @Test + fun `extracts default value when defined with named parameter`() { + val code = """ /** * description */ @@ -321,14 +318,14 @@ class RuleCollectorSpec { @Configuration("description") private val config: Int by config(defaultValue = 99) } - """ - val items = subject.run(code) - assertThat(items[0].configuration[0].defaultValue).isEqualTo(of(99)) - } + """ + val items = subject.run(code) + assertThat(items[0].configuration[0].defaultValue).isEqualTo(of(99)) + } - @Test - fun `extracts default value for list of strings`() { - val code = """ + @Test + fun `extracts default value for list of strings`() { + val code = """ /** * description */ @@ -341,15 +338,15 @@ class RuleCollectorSpec { ) ) } - """ - val items = subject.run(code) - val expected = of(listOf("a", "b")) - assertThat(items[0].configuration[0].defaultValue).isEqualTo(expected) - } + """ + val items = subject.run(code) + val expected = of(listOf("a", "b")) + assertThat(items[0].configuration[0].defaultValue).isEqualTo(expected) + } - @Test - fun `contains multiple configuration options`() { - val code = """ + @Test + fun `contains multiple configuration options`() { + val code = """ /** * description */ @@ -360,14 +357,14 @@ class RuleCollectorSpec { @Configuration("description") private val config2: String by config("") } - """ - val items = subject.run(code) - assertThat(items[0].configuration).hasSize(2) - } + """ + val items = subject.run(code) + assertThat(items[0].configuration).hasSize(2) + } - @Test - fun `has description that is concatenated`() { - val code = """ + @Test + fun `has description that is concatenated`() { + val code = """ /** * description */ @@ -378,15 +375,15 @@ class RuleCollectorSpec { "description") private val config: String by config("a") } - """ - val items = subject.run(code) - assertThat(items[0].configuration[0].description).isEqualTo("This is a multi line description") - assertThat(items[0].configuration[0].defaultValue).isEqualTo(of("a")) - } + """ + val items = subject.run(code) + assertThat(items[0].configuration[0].description).isEqualTo("This is a multi line description") + assertThat(items[0].configuration[0].defaultValue).isEqualTo(of("a")) + } - @Test - fun `extracts default value when it is an Int constant`() { - val code = """ + @Test + fun `extracts default value when it is an Int constant`() { + val code = """ /** * description */ @@ -398,14 +395,14 @@ class RuleCollectorSpec { private const val DEFAULT_CONFIG_VALUE = 99 } } - """ - val items = subject.run(code) - assertThat(items[0].configuration[0].defaultValue).isEqualTo(of(99)) - } + """ + val items = subject.run(code) + assertThat(items[0].configuration[0].defaultValue).isEqualTo(of(99)) + } - @Test - fun `extracts default value when it is an Int constant as named parameter`() { - val code = """ + @Test + fun `extracts default value when it is an Int constant as named parameter`() { + val code = """ /** * description */ @@ -417,14 +414,14 @@ class RuleCollectorSpec { private const val DEFAULT_CONFIG_VALUE = 99 } } - """ - val items = subject.run(code) - assertThat(items[0].configuration[0].defaultValue).isEqualTo(of(99)) - } + """ + val items = subject.run(code) + assertThat(items[0].configuration[0].defaultValue).isEqualTo(of(99)) + } - @Test - fun `extracts default value when it is a String constant`() { - val code = """ + @Test + fun `extracts default value when it is a String constant`() { + val code = """ /** * description */ @@ -436,14 +433,14 @@ class RuleCollectorSpec { private const val DEFAULT_CONFIG_VALUE = "a" } } - """ - val items = subject.run(code) - assertThat(items[0].configuration[0].defaultValue).isEqualTo(of("a")) - } + """ + val items = subject.run(code) + assertThat(items[0].configuration[0].defaultValue).isEqualTo(of("a")) + } - @Test - fun `extracts default value for list of strings from constant`() { - val code = """ + @Test + fun `extracts default value for list of strings from constant`() { + val code = """ /** * description */ @@ -459,16 +456,16 @@ class RuleCollectorSpec { private val DEFAULT_CONFIG_VALUE_A = "a" } } - """ - val items = subject.run(code) - val expected = of(listOf("a", "b")) - assertThat(items[0].configuration[0].defaultValue).isEqualTo(expected) - assertThat(items[0].configuration[1].defaultValue).isEqualTo(expected) - } + """ + val items = subject.run(code) + val expected = of(listOf("a", "b")) + assertThat(items[0].configuration[0].defaultValue).isEqualTo(expected) + assertThat(items[0].configuration[1].defaultValue).isEqualTo(expected) + } - @Test - fun `extracts emptyList default value`() { - val code = """ + @Test + fun `extracts emptyList default value`() { + val code = """ /** * description */ @@ -479,16 +476,16 @@ class RuleCollectorSpec { @Configuration("description") private val config2: List by config(emptyList()) } - """ - val items = subject.run(code) - val expected = of(emptyList()) - assertThat(items[0].configuration[0].defaultValue).isEqualTo(expected) - assertThat(items[0].configuration[1].defaultValue).isEqualTo(expected) - } + """ + val items = subject.run(code) + val expected = of(emptyList()) + assertThat(items[0].configuration[0].defaultValue).isEqualTo(expected) + assertThat(items[0].configuration[1].defaultValue).isEqualTo(expected) + } - @Test - fun `extracts emptyList default value of transformed list`() { - val code = """ + @Test + fun `extracts emptyList default value of transformed list`() { + val code = """ /** * description */ @@ -503,16 +500,16 @@ class RuleCollectorSpec { private val DEFAULT_CONFIG_VALUE: List = emptyList() } } - """ - val items = subject.run(code) - val expected = of(emptyList()) - assertThat(items[0].configuration[0].defaultValue).isEqualTo(expected) - assertThat(items[0].configuration[1].defaultValue).isEqualTo(expected) - } + """ + val items = subject.run(code) + val expected = of(emptyList()) + assertThat(items[0].configuration[0].defaultValue).isEqualTo(expected) + assertThat(items[0].configuration[1].defaultValue).isEqualTo(expected) + } - @Test - fun `is marked as deprecated as well`() { - val code = """ + @Test + fun `is marked as deprecated as well`() { + val code = """ /** * description */ @@ -521,27 +518,27 @@ class RuleCollectorSpec { @Configuration("description") private val config: String by config("") } - """ - val items = subject.run(code) - assertThat(items[0].configuration[0].deprecated).isEqualTo("use config1 instead") - } + """ + val items = subject.run(code) + assertThat(items[0].configuration[0].deprecated).isEqualTo("use config1 instead") + } - @Test - fun `fails if kdoc is used to define configuration`() { - val code = """ + @Test + fun `fails if kdoc is used to define configuration`() { + val code = """ /** * description * @configuration config1 - description (default: `''`) */ class SomeRandomClass() : Rule { } - """ - assertThatExceptionOfType(InvalidDocumentationException::class.java).isThrownBy { subject.run(code) } - } + """ + assertThatExceptionOfType(InvalidDocumentationException::class.java).isThrownBy { subject.run(code) } + } - @Test - fun `fails if not used in combination with delegate`() { - val code = """ + @Test + fun `fails if not used in combination with delegate`() { + val code = """ /** * description */ @@ -549,13 +546,13 @@ class RuleCollectorSpec { @Configuration("description") private val config: String = "foo" } - """ - assertThatExceptionOfType(InvalidDocumentationException::class.java).isThrownBy { subject.run(code) } - } + """ + assertThatExceptionOfType(InvalidDocumentationException::class.java).isThrownBy { subject.run(code) } + } - @Test - fun `fails if not used in combination with config delegate`() { - val code = """ + @Test + fun `fails if not used in combination with config delegate`() { + val code = """ /** * description */ @@ -563,29 +560,29 @@ class RuleCollectorSpec { @Configuration("description") private val config: String by lazy { "foo" } } - """ - assertThatExceptionOfType(InvalidDocumentationException::class.java).isThrownBy { subject.run(code) } - } + """ + assertThatExceptionOfType(InvalidDocumentationException::class.java).isThrownBy { subject.run(code) } + } - @Test - fun `fails if config delegate is used without annotation`() { - val code = """ + @Test + fun `fails if config delegate is used without annotation`() { + val code = """ /** * description */ class SomeRandomClass() : Rule { private val config: String by config("") } - """ - assertThatExceptionOfType(InvalidDocumentationException::class.java).isThrownBy { subject.run(code) } - } + """ + assertThatExceptionOfType(InvalidDocumentationException::class.java).isThrownBy { subject.run(code) } } + } - @Nested - inner class `android variants` { - @Test - fun `extracts values with android variants`() { - val code = """ + @Nested + inner class `android variants` { + @Test + fun `extracts values with android variants`() { + val code = """ /** * description */ @@ -593,22 +590,22 @@ class RuleCollectorSpec { @Configuration("description") private val maxLineLength: Int by configWithAndroidVariants(120, 100) } - """ - val items = subject.run(code) - assertThat(items[0].configuration[0]).isEqualTo( - Configuration( - name = "maxLineLength", - description = "description", - defaultValue = of(120), - defaultAndroidValue = of(100), - deprecated = null - ) + """ + val items = subject.run(code) + assertThat(items[0].configuration[0]).isEqualTo( + Configuration( + name = "maxLineLength", + description = "description", + defaultValue = of(120), + defaultAndroidValue = of(100), + deprecated = null ) - } + ) + } - @Test - fun `extracts values with android variants as named arguments`() { - val code = """ + @Test + fun `extracts values with android variants as named arguments`() { + val code = """ /** * description */ @@ -617,25 +614,25 @@ class RuleCollectorSpec { private val maxLineLength: Int by configWithAndroidVariants(defaultValue = 120, defaultAndroidValue = 100) } - """ - val items = subject.run(code) - assertThat(items[0].configuration[0]).isEqualTo( - Configuration( - name = "maxLineLength", - description = "description", - defaultValue = of(120), - defaultAndroidValue = of(100), - deprecated = null - ) + """ + val items = subject.run(code) + assertThat(items[0].configuration[0]).isEqualTo( + Configuration( + name = "maxLineLength", + description = "description", + defaultValue = of(120), + defaultAndroidValue = of(100), + deprecated = null ) - } + ) } + } - @Nested - inner class `fallback property` { - @Test - fun `extracts default value`() { - val code = """ + @Nested + inner class `fallback property` { + @Test + fun `extracts default value`() { + val code = """ /** * description */ @@ -649,16 +646,16 @@ class RuleCollectorSpec { @Configuration("description") private val config3: Int by configWithFallback(defaultValue = 99, fallbackProperty = ::prop) } - """ - val items = subject.run(code) - val fallbackProperties = items[0].configuration.filter { it.name.startsWith("config") } - assertThat(fallbackProperties).hasSize(3) - assertThat(fallbackProperties.map { it.defaultValue }).containsOnly(of(99)) - } + """ + val items = subject.run(code) + val fallbackProperties = items[0].configuration.filter { it.name.startsWith("config") } + assertThat(fallbackProperties).hasSize(3) + assertThat(fallbackProperties.map { it.defaultValue }).containsOnly(of(99)) + } - @Test - fun `reports an error if the property to fallback on exists but is not a config property`() { - val code = """ + @Test + fun `reports an error if the property to fallback on exists but is not a config property`() { + val code = """ /** * description */ @@ -667,16 +664,16 @@ class RuleCollectorSpec { @Configuration("description") private val config: Int by configWithFallback(::prop, 99) } - """ - assertThatThrownBy { subject.run(code) } - .isInstanceOf(InvalidDocumentationException::class.java) - .hasMessageContaining("delegate") - } + """ + assertThatThrownBy { subject.run(code) } + .isInstanceOf(InvalidDocumentationException::class.java) + .hasMessageContaining("delegate") } + } - @Nested - inner class `transformed property` { - val code = """ + @Nested + inner class `transformed property` { + val code = """ /** * description */ @@ -686,66 +683,66 @@ class RuleCollectorSpec { @Configuration("description") private val config2: String by config(false, Boolean::toString) } - """ + """ - @Test - fun `extracts default value with transformer function`() { - val items = subject.run(code) - assertThat(items[0].configuration[0].defaultValue).isEqualTo(of("[a-z]+")) - } + @Test + fun `extracts default value with transformer function`() { + val items = subject.run(code) + assertThat(items[0].configuration[0].defaultValue).isEqualTo(of("[a-z]+")) + } - @Test - fun `extracts default value with method reference`() { - val items = subject.run(code) - assertThat(items[0].configuration[1].defaultValue).isEqualTo(of(false)) - } + @Test + fun `extracts default value with method reference`() { + val items = subject.run(code) + assertThat(items[0].configuration[1].defaultValue).isEqualTo(of(false)) } } + } - @Nested - inner class `collects type resolution information` { - @Test - fun `has no type resolution by default`() { - val code = """ + @Nested + inner class `collects type resolution information` { + @Test + fun `has no type resolution by default`() { + val code = """ /** * description */ class SomeRandomClass : Rule - """ - val items = subject.run(code) - assertThat(items[0].requiresTypeResolution).isFalse() - } + """ + val items = subject.run(code) + assertThat(items[0].requiresTypeResolution).isFalse() + } - @Test - fun `collects the flag that it requires type resolution`() { - val code = """ + @Test + fun `collects the flag that it requires type resolution`() { + val code = """ /** * description */ @RequiresTypeResolution class SomeRandomClass : Rule - """ - val items = subject.run(code) - assertThat(items[0].requiresTypeResolution).isTrue() - } + """ + val items = subject.run(code) + assertThat(items[0].requiresTypeResolution).isTrue() + } - @Test - fun `collects the flag that it requires type resolution from fully qualified annotation`() { - val code = """ + @Test + fun `collects the flag that it requires type resolution from fully qualified annotation`() { + val code = """ /** * description */ @io.gitlab.arturbosch.detekt.api.internal.RequiresTypeResolution class SomeRandomClass : Rule - """ - val items = subject.run(code) - assertThat(items[0].requiresTypeResolution).isTrue() - } + """ + val items = subject.run(code) + assertThat(items[0].requiresTypeResolution).isTrue() } + } - @Test - fun `contains compliant and noncompliant code examples`() { - val code = """ + @Test + fun `contains compliant and noncompliant code examples`() { + val code = """ /** * description * @@ -758,29 +755,29 @@ class RuleCollectorSpec { * */ class RandomClass : Rule - """ - val items = subject.run(code) - assertThat(items[0].nonCompliantCodeExample).isEqualTo("val one = 2") - assertThat(items[0].compliantCodeExample).isEqualTo("val one = 1") - } + """ + val items = subject.run(code) + assertThat(items[0].nonCompliantCodeExample).isEqualTo("val one = 2") + assertThat(items[0].compliantCodeExample).isEqualTo("val one = 1") + } - @Test - fun `has wrong noncompliant code example declaration`() { - val code = """ + @Test + fun `has wrong noncompliant code example declaration`() { + val code = """ /** * description * * */ class RandomClass : Rule - """ - assertThatExceptionOfType(InvalidCodeExampleDocumentationException::class.java) - .isThrownBy { subject.run(code) } - } + """ + assertThatExceptionOfType(InvalidCodeExampleDocumentationException::class.java) + .isThrownBy { subject.run(code) } + } - @Test - fun `has wrong compliant code example declaration`() { - val code = """ + @Test + fun `has wrong compliant code example declaration`() { + val code = """ /** * description * @@ -790,14 +787,14 @@ class RuleCollectorSpec { * */ class RandomClass : Rule - """ - assertThatExceptionOfType(InvalidCodeExampleDocumentationException::class.java) - .isThrownBy { subject.run(code) } - } + """ + assertThatExceptionOfType(InvalidCodeExampleDocumentationException::class.java) + .isThrownBy { subject.run(code) } + } - @Test - fun `has wrong compliant without noncompliant code example declaration`() { - val code = """ + @Test + fun `has wrong compliant without noncompliant code example declaration`() { + val code = """ /** * description * @@ -806,14 +803,14 @@ class RuleCollectorSpec { * */ class RandomClass : Rule - """ - assertThatExceptionOfType(InvalidCodeExampleDocumentationException::class.java) - .isThrownBy { subject.run(code) } - } + """ + assertThatExceptionOfType(InvalidCodeExampleDocumentationException::class.java) + .isThrownBy { subject.run(code) } + } - @Test - fun `has wrong issue style property`() { - val code = """ + @Test + fun `has wrong issue style property`() { + val code = """ /** * description */ @@ -825,13 +822,13 @@ class RuleCollectorSpec { "", debt = Debt.TEN_MINS) } - """ - assertThatExceptionOfType(InvalidIssueDeclaration::class.java).isThrownBy { subject.run(code) } - } + """ + assertThatExceptionOfType(InvalidIssueDeclaration::class.java).isThrownBy { subject.run(code) } + } - @Test - fun `has wrong aliases property structure`() { - val code = """ + @Test + fun `has wrong aliases property structure`() { + val code = """ /** * description */ @@ -844,20 +841,19 @@ class RuleCollectorSpec { "", debt = Debt.TEN_MINS) } - """ - assertThatExceptionOfType(InvalidAliasesDeclaration::class.java).isThrownBy { subject.run(code) } - } + """ + assertThatExceptionOfType(InvalidAliasesDeclaration::class.java).isThrownBy { subject.run(code) } + } - @Test - fun `contains tabs in KDoc`() { - val description = "\tdescription" - val code = """ + @Test + fun `contains tabs in KDoc`() { + val description = "\tdescription" + val code = """ /** * $description */ class SomeRandomClass : Rule - """ - assertThatExceptionOfType(InvalidDocumentationException::class.java).isThrownBy { subject.run(code) } - } + """ + assertThatExceptionOfType(InvalidDocumentationException::class.java).isThrownBy { subject.run(code) } } } From 23efa5abb4064decc0c69d87179267fec74d4ec6 Mon Sep 17 00:00:00 2001 From: Markus Schwarz Date: Tue, 24 May 2022 22:31:45 +0200 Subject: [PATCH 17/18] reformat spec --- .../generator/collection/RuleCollectorSpec.kt | 515 +++++++++--------- 1 file changed, 259 insertions(+), 256 deletions(-) diff --git a/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/collection/RuleCollectorSpec.kt b/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/collection/RuleCollectorSpec.kt index 2990b09e8ea..86a444aaab9 100644 --- a/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/collection/RuleCollectorSpec.kt +++ b/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/collection/RuleCollectorSpec.kt @@ -173,23 +173,21 @@ class RuleCollectorSpec { @Nested inner class `collects configuration options using annotation` { - @Nested - inner class `config` { - @Test - fun `contains no configuration options by default`() { - val code = """ + @Test + fun `contains no configuration options by default`() { + val code = """ /** * description */ class SomeRandomClass : Rule - """ - val items = subject.run(code) - assertThat(items[0].configuration).isEmpty() - } + """ + val items = subject.run(code) + assertThat(items[0].configuration).isEmpty() + } - @Test - fun `contains one configuration option with correct formatting`() { - val code = """ + @Test + fun `contains one configuration option with correct formatting`() { + val code = """ /** * description */ @@ -197,22 +195,22 @@ class RuleCollectorSpec { @Configuration("description") private val config: String by config("[A-Z$]") } - """ - val items = subject.run(code) - assertThat(items[0].configuration).hasSize(1) - val expectedConfiguration = Configuration( - name = "config", - description = "description", - defaultValue = of("[A-Z$]"), - defaultAndroidValue = null, - deprecated = null - ) - assertThat(items[0].configuration[0]).isEqualTo(expectedConfiguration) - } + """ + val items = subject.run(code) + assertThat(items[0].configuration).hasSize(1) + val expectedConfiguration = Configuration( + name = "config", + description = "description", + defaultValue = of("[A-Z$]"), + defaultAndroidValue = null, + deprecated = null + ) + assertThat(items[0].configuration[0]).isEqualTo(expectedConfiguration) + } - @Test - fun `contains one configuration option of type Int`() { - val code = """ + @Test + fun `contains one configuration option of type Int`() { + val code = """ /** * description */ @@ -220,97 +218,30 @@ class RuleCollectorSpec { @Configuration("description") private val config: Int by config(1_999_000) } - """ - val items = subject.run(code) - assertThat(items[0].configuration).hasSize(1) - assertThat(items[0].configuration[0].defaultValue).isEqualTo(of(1_999_000)) - } - - @Nested - inner class ValuesWithReasonSpec { - private val code = """ - /** - * description - */ - class SomeRandomClass() : Rule { - @Configuration("description") - private val singleWithPositionalParam: ValuesWithReason by config(valuesWithReason("value" to "reason")) - @Configuration("description") - private val singleWithNamedParam by config(defaultValue = valuesWithReason("value" to "reason")) - @Configuration("description") - private val singleWithConstant by config(DEFAULT_VALUE) - @Configuration("description") - private val noValues by config(valuesWithReason()) - @Configuration("description") - private val multipleValues by config(valuesWithReason("a" to "A and A", "b" to "B and B")) - @Configuration("description") - private val multipleLines by config(valuesWithReason( - "a" to "A " + - "and A", - "b" to ""${"\""}B and B""${"\""})) - - companion object { - private val DEFAULT_VALUE = valuesWithReason("value" to "reason") - } - } - """ - private val rule = subject.run(code)[0] - - @Test - fun `parse options of type ValuesWithReason`() { - assertThat(rule.configuration).hasSize(6) - } - - @Test - fun `no values`() { - val config = rule.configuration.first { it.name == "noValues" } - assertThat(config.defaultValue).isEqualTo(of(valuesWithReason())) - } - - @Test - fun `single value`() { - val expected = of(valuesWithReason("value" to "reason")) - val singleValueConfigs = rule.configuration.filter { it.name.startsWith("single") } - assertThat(singleValueConfigs) - .hasSize(3) - .extracting("defaultValue") - .containsOnly(expected) - } - - @Test - fun `multiple values`() { - val configs = rule.configuration.filter { it.name.startsWith("multiple") } - val expected = of( - valuesWithReason( - "a" to "A and A", - "b" to "B and B" - ) - ) - assertThat(configs) - .hasSize(2) - .extracting("defaultValue") - .containsOnly(expected) - } - } + """ + val items = subject.run(code) + assertThat(items[0].configuration).hasSize(1) + assertThat(items[0].configuration[0].defaultValue).isEqualTo(of(1_999_000)) + } - @Test - fun `extracts default value when it is a multi line string`() { - val code = """ - /** - * description - */ - class SomeRandomClass() : Rule { - @Configuration("description") - private val config: String by config(""${"\""}abcd""${"\""}) - } - """ - val items = subject.run(code) - assertThat(items[0].configuration[0].defaultValue).isEqualTo(of("abcd")) - } + @Test + fun `extracts default value when it is a multi line string`() { + val code = """ + /** + * description + */ + class SomeRandomClass() : Rule { + @Configuration("description") + private val config: String by config(""${"\""}abcd""${"\""}) + } + """ + val items = subject.run(code) + assertThat(items[0].configuration[0].defaultValue).isEqualTo(of("abcd")) + } - @Test - fun `extracts default value when defined with named parameter`() { - val code = """ + @Test + fun `extracts default value when defined with named parameter`() { + val code = """ /** * description */ @@ -318,14 +249,14 @@ class RuleCollectorSpec { @Configuration("description") private val config: Int by config(defaultValue = 99) } - """ - val items = subject.run(code) - assertThat(items[0].configuration[0].defaultValue).isEqualTo(of(99)) - } + """ + val items = subject.run(code) + assertThat(items[0].configuration[0].defaultValue).isEqualTo(of(99)) + } - @Test - fun `extracts default value for list of strings`() { - val code = """ + @Test + fun `extracts default value for list of strings`() { + val code = """ /** * description */ @@ -338,15 +269,15 @@ class RuleCollectorSpec { ) ) } - """ - val items = subject.run(code) - val expected = of(listOf("a", "b")) - assertThat(items[0].configuration[0].defaultValue).isEqualTo(expected) - } + """ + val items = subject.run(code) + val expected = of(listOf("a", "b")) + assertThat(items[0].configuration[0].defaultValue).isEqualTo(expected) + } - @Test - fun `contains multiple configuration options`() { - val code = """ + @Test + fun `contains multiple configuration options`() { + val code = """ /** * description */ @@ -357,14 +288,14 @@ class RuleCollectorSpec { @Configuration("description") private val config2: String by config("") } - """ - val items = subject.run(code) - assertThat(items[0].configuration).hasSize(2) - } + """ + val items = subject.run(code) + assertThat(items[0].configuration).hasSize(2) + } - @Test - fun `has description that is concatenated`() { - val code = """ + @Test + fun `has description that is concatenated`() { + val code = """ /** * description */ @@ -375,15 +306,15 @@ class RuleCollectorSpec { "description") private val config: String by config("a") } - """ - val items = subject.run(code) - assertThat(items[0].configuration[0].description).isEqualTo("This is a multi line description") - assertThat(items[0].configuration[0].defaultValue).isEqualTo(of("a")) - } + """ + val items = subject.run(code) + assertThat(items[0].configuration[0].description).isEqualTo("This is a multi line description") + assertThat(items[0].configuration[0].defaultValue).isEqualTo(of("a")) + } - @Test - fun `extracts default value when it is an Int constant`() { - val code = """ + @Test + fun `extracts default value when it is an Int constant`() { + val code = """ /** * description */ @@ -395,14 +326,14 @@ class RuleCollectorSpec { private const val DEFAULT_CONFIG_VALUE = 99 } } - """ - val items = subject.run(code) - assertThat(items[0].configuration[0].defaultValue).isEqualTo(of(99)) - } + """ + val items = subject.run(code) + assertThat(items[0].configuration[0].defaultValue).isEqualTo(of(99)) + } - @Test - fun `extracts default value when it is an Int constant as named parameter`() { - val code = """ + @Test + fun `extracts default value when it is an Int constant as named parameter`() { + val code = """ /** * description */ @@ -414,14 +345,14 @@ class RuleCollectorSpec { private const val DEFAULT_CONFIG_VALUE = 99 } } - """ - val items = subject.run(code) - assertThat(items[0].configuration[0].defaultValue).isEqualTo(of(99)) - } + """ + val items = subject.run(code) + assertThat(items[0].configuration[0].defaultValue).isEqualTo(of(99)) + } - @Test - fun `extracts default value when it is a String constant`() { - val code = """ + @Test + fun `extracts default value when it is a String constant`() { + val code = """ /** * description */ @@ -433,14 +364,14 @@ class RuleCollectorSpec { private const val DEFAULT_CONFIG_VALUE = "a" } } - """ - val items = subject.run(code) - assertThat(items[0].configuration[0].defaultValue).isEqualTo(of("a")) - } + """ + val items = subject.run(code) + assertThat(items[0].configuration[0].defaultValue).isEqualTo(of("a")) + } - @Test - fun `extracts default value for list of strings from constant`() { - val code = """ + @Test + fun `extracts default value for list of strings from constant`() { + val code = """ /** * description */ @@ -456,16 +387,16 @@ class RuleCollectorSpec { private val DEFAULT_CONFIG_VALUE_A = "a" } } - """ - val items = subject.run(code) - val expected = of(listOf("a", "b")) - assertThat(items[0].configuration[0].defaultValue).isEqualTo(expected) - assertThat(items[0].configuration[1].defaultValue).isEqualTo(expected) - } + """ + val items = subject.run(code) + val expected = of(listOf("a", "b")) + assertThat(items[0].configuration[0].defaultValue).isEqualTo(expected) + assertThat(items[0].configuration[1].defaultValue).isEqualTo(expected) + } - @Test - fun `extracts emptyList default value`() { - val code = """ + @Test + fun `extracts emptyList default value`() { + val code = """ /** * description */ @@ -476,16 +407,16 @@ class RuleCollectorSpec { @Configuration("description") private val config2: List by config(emptyList()) } - """ - val items = subject.run(code) - val expected = of(emptyList()) - assertThat(items[0].configuration[0].defaultValue).isEqualTo(expected) - assertThat(items[0].configuration[1].defaultValue).isEqualTo(expected) - } + """ + val items = subject.run(code) + val expected = of(emptyList()) + assertThat(items[0].configuration[0].defaultValue).isEqualTo(expected) + assertThat(items[0].configuration[1].defaultValue).isEqualTo(expected) + } - @Test - fun `extracts emptyList default value of transformed list`() { - val code = """ + @Test + fun `extracts emptyList default value of transformed list`() { + val code = """ /** * description */ @@ -500,16 +431,16 @@ class RuleCollectorSpec { private val DEFAULT_CONFIG_VALUE: List = emptyList() } } - """ - val items = subject.run(code) - val expected = of(emptyList()) - assertThat(items[0].configuration[0].defaultValue).isEqualTo(expected) - assertThat(items[0].configuration[1].defaultValue).isEqualTo(expected) - } + """ + val items = subject.run(code) + val expected = of(emptyList()) + assertThat(items[0].configuration[0].defaultValue).isEqualTo(expected) + assertThat(items[0].configuration[1].defaultValue).isEqualTo(expected) + } - @Test - fun `is marked as deprecated as well`() { - val code = """ + @Test + fun `is marked as deprecated as well`() { + val code = """ /** * description */ @@ -518,27 +449,27 @@ class RuleCollectorSpec { @Configuration("description") private val config: String by config("") } - """ - val items = subject.run(code) - assertThat(items[0].configuration[0].deprecated).isEqualTo("use config1 instead") - } + """ + val items = subject.run(code) + assertThat(items[0].configuration[0].deprecated).isEqualTo("use config1 instead") + } - @Test - fun `fails if kdoc is used to define configuration`() { - val code = """ + @Test + fun `fails if kdoc is used to define configuration`() { + val code = """ /** * description * @configuration config1 - description (default: `''`) */ class SomeRandomClass() : Rule { } - """ - assertThatExceptionOfType(InvalidDocumentationException::class.java).isThrownBy { subject.run(code) } - } + """ + assertThatExceptionOfType(InvalidDocumentationException::class.java).isThrownBy { subject.run(code) } + } - @Test - fun `fails if not used in combination with delegate`() { - val code = """ + @Test + fun `fails if not used in combination with delegate`() { + val code = """ /** * description */ @@ -546,13 +477,13 @@ class RuleCollectorSpec { @Configuration("description") private val config: String = "foo" } - """ - assertThatExceptionOfType(InvalidDocumentationException::class.java).isThrownBy { subject.run(code) } - } + """ + assertThatExceptionOfType(InvalidDocumentationException::class.java).isThrownBy { subject.run(code) } + } - @Test - fun `fails if not used in combination with config delegate`() { - val code = """ + @Test + fun `fails if not used in combination with config delegate`() { + val code = """ /** * description */ @@ -560,22 +491,21 @@ class RuleCollectorSpec { @Configuration("description") private val config: String by lazy { "foo" } } - """ - assertThatExceptionOfType(InvalidDocumentationException::class.java).isThrownBy { subject.run(code) } - } + """ + assertThatExceptionOfType(InvalidDocumentationException::class.java).isThrownBy { subject.run(code) } + } - @Test - fun `fails if config delegate is used without annotation`() { - val code = """ - /** - * description - */ - class SomeRandomClass() : Rule { - private val config: String by config("") - } - """ - assertThatExceptionOfType(InvalidDocumentationException::class.java).isThrownBy { subject.run(code) } - } + @Test + fun `fails if config delegate is used without annotation`() { + val code = """ + /** + * description + */ + class SomeRandomClass() : Rule { + private val config: String by config("") + } + """ + assertThatExceptionOfType(InvalidDocumentationException::class.java).isThrownBy { subject.run(code) } } @Nested @@ -583,13 +513,13 @@ class RuleCollectorSpec { @Test fun `extracts values with android variants`() { val code = """ - /** - * description - */ - class SomeRandomClass() : Rule { - @Configuration("description") - private val maxLineLength: Int by configWithAndroidVariants(120, 100) - } + /** + * description + */ + class SomeRandomClass() : Rule { + @Configuration("description") + private val maxLineLength: Int by configWithAndroidVariants(120, 100) + } """ val items = subject.run(code) assertThat(items[0].configuration[0]).isEqualTo( @@ -606,14 +536,14 @@ class RuleCollectorSpec { @Test fun `extracts values with android variants as named arguments`() { val code = """ - /** - * description - */ - class SomeRandomClass() : Rule { - @Configuration("description") - private val maxLineLength: Int by - configWithAndroidVariants(defaultValue = 120, defaultAndroidValue = 100) - } + /** + * description + */ + class SomeRandomClass() : Rule { + @Configuration("description") + private val maxLineLength: Int by + configWithAndroidVariants(defaultValue = 120, defaultAndroidValue = 100) + } """ val items = subject.run(code) assertThat(items[0].configuration[0]).isEqualTo( @@ -633,19 +563,19 @@ class RuleCollectorSpec { @Test fun `extracts default value`() { val code = """ - /** - * description - */ - class SomeRandomClass() : Rule { - @Configuration("description") - val prop: Int by config(1) - @Configuration("description") - private val config1: Int by configWithFallback(this::prop, 99) - @Configuration("description") - private val config2: Int by configWithFallback(fallbackProperty = ::prop, defaultValue = 99) - @Configuration("description") - private val config3: Int by configWithFallback(defaultValue = 99, fallbackProperty = ::prop) - } + /** + * description + */ + class SomeRandomClass() : Rule { + @Configuration("description") + val prop: Int by config(1) + @Configuration("description") + private val config1: Int by configWithFallback(this::prop, 99) + @Configuration("description") + private val config2: Int by configWithFallback(fallbackProperty = ::prop, defaultValue = 99) + @Configuration("description") + private val config3: Int by configWithFallback(defaultValue = 99, fallbackProperty = ::prop) + } """ val items = subject.run(code) val fallbackProperties = items[0].configuration.filter { it.name.startsWith("config") } @@ -697,6 +627,79 @@ class RuleCollectorSpec { assertThat(items[0].configuration[1].defaultValue).isEqualTo(of(false)) } } + + @Nested + inner class `values with reason property` { + private val code = """ + /** + * description + */ + class SomeRandomClass() : Rule { + @Configuration("description") + private val singleWithPositionalParam: ValuesWithReason + by config(valuesWithReason("value" to "reason")) + @Configuration("description") + private val singleWithNamedParam + by config(defaultValue = valuesWithReason("value" to "reason")) + @Configuration("description") + private val singleWithConstant by config(DEFAULT_VALUE) + @Configuration("description") + private val noValues by config(valuesWithReason()) + @Configuration("description") + private val multipleValues by config(valuesWithReason("a" to "A and A", "b" to "B and B")) + @Configuration("description") + private val multipleLines + by config(valuesWithReason( + "a" to "A " + + "and A", + "b" to ""${"\""}B and B""${"\""})) + + companion object { + private val DEFAULT_VALUE = valuesWithReason("value" to "reason") + } + } + """ + + @Test + fun `parse options of type ValuesWithReason`() { + val rule = subject.run(code)[0] + assertThat(rule.configuration).hasSize(6) + } + + @Test + fun `no values`() { + val rule = subject.run(code)[0] + val config = rule.configuration.first { it.name == "noValues" } + assertThat(config.defaultValue).isEqualTo(of(valuesWithReason())) + } + + @Test + fun `single value`() { + val rule = subject.run(code)[0] + val expected = of(valuesWithReason("value" to "reason")) + val singleValueConfigs = rule.configuration.filter { it.name.startsWith("single") } + assertThat(singleValueConfigs) + .hasSize(3) + .extracting("defaultValue") + .containsOnly(expected) + } + + @Test + fun `multiple values`() { + val rule = subject.run(code)[0] + val configs = rule.configuration.filter { it.name.startsWith("multiple") } + val expected = of( + valuesWithReason( + "a" to "A and A", + "b" to "B and B" + ) + ) + assertThat(configs) + .hasSize(2) + .extracting("defaultValue") + .containsOnly(expected) + } + } } @Nested From 4d2d279d0fbbdabf7272a71adb73f17c0801165f Mon Sep 17 00:00:00 2001 From: Markus Schwarz Date: Sun, 29 May 2022 21:29:49 +0200 Subject: [PATCH 18/18] fix indentation of test code --- .../generator/collection/RuleCollectorSpec.kt | 530 +++++++++--------- 1 file changed, 265 insertions(+), 265 deletions(-) diff --git a/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/collection/RuleCollectorSpec.kt b/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/collection/RuleCollectorSpec.kt index 86a444aaab9..d7d5ce6e37e 100644 --- a/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/collection/RuleCollectorSpec.kt +++ b/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/collection/RuleCollectorSpec.kt @@ -176,10 +176,10 @@ class RuleCollectorSpec { @Test fun `contains no configuration options by default`() { val code = """ - /** - * description - */ - class SomeRandomClass : Rule + /** + * description + */ + class SomeRandomClass : Rule """ val items = subject.run(code) assertThat(items[0].configuration).isEmpty() @@ -188,13 +188,13 @@ class RuleCollectorSpec { @Test fun `contains one configuration option with correct formatting`() { val code = """ - /** - * description - */ - class SomeRandomClass() : Rule { - @Configuration("description") - private val config: String by config("[A-Z$]") - } + /** + * description + */ + class SomeRandomClass() : Rule { + @Configuration("description") + private val config: String by config("[A-Z$]") + } """ val items = subject.run(code) assertThat(items[0].configuration).hasSize(1) @@ -211,13 +211,13 @@ class RuleCollectorSpec { @Test fun `contains one configuration option of type Int`() { val code = """ - /** - * description - */ - class SomeRandomClass() : Rule { - @Configuration("description") - private val config: Int by config(1_999_000) - } + /** + * description + */ + class SomeRandomClass() : Rule { + @Configuration("description") + private val config: Int by config(1_999_000) + } """ val items = subject.run(code) assertThat(items[0].configuration).hasSize(1) @@ -227,13 +227,13 @@ class RuleCollectorSpec { @Test fun `extracts default value when it is a multi line string`() { val code = """ - /** - * description - */ - class SomeRandomClass() : Rule { - @Configuration("description") - private val config: String by config(""${"\""}abcd""${"\""}) - } + /** + * description + */ + class SomeRandomClass() : Rule { + @Configuration("description") + private val config: String by config(""${"\""}abcd""${"\""}) + } """ val items = subject.run(code) assertThat(items[0].configuration[0].defaultValue).isEqualTo(of("abcd")) @@ -242,13 +242,13 @@ class RuleCollectorSpec { @Test fun `extracts default value when defined with named parameter`() { val code = """ - /** - * description - */ - class SomeRandomClass() : Rule { - @Configuration("description") - private val config: Int by config(defaultValue = 99) - } + /** + * description + */ + class SomeRandomClass() : Rule { + @Configuration("description") + private val config: Int by config(defaultValue = 99) + } """ val items = subject.run(code) assertThat(items[0].configuration[0].defaultValue).isEqualTo(of(99)) @@ -257,18 +257,18 @@ class RuleCollectorSpec { @Test fun `extracts default value for list of strings`() { val code = """ - /** - * description - */ - class SomeRandomClass() : Rule { - @Configuration("description") - private val config: List by config( - listOf( - "a", - "b" - ) - ) - } + /** + * description + */ + class SomeRandomClass() : Rule { + @Configuration("description") + private val config: List by config( + listOf( + "a", + "b" + ) + ) + } """ val items = subject.run(code) val expected = of(listOf("a", "b")) @@ -278,16 +278,16 @@ class RuleCollectorSpec { @Test fun `contains multiple configuration options`() { val code = """ - /** - * description - */ - class SomeRandomClass() : Rule { - @Configuration("description") - private val config: String by config("") - - @Configuration("description") - private val config2: String by config("") - } + /** + * description + */ + class SomeRandomClass() : Rule { + @Configuration("description") + private val config: String by config("") + + @Configuration("description") + private val config2: String by config("") + } """ val items = subject.run(code) assertThat(items[0].configuration).hasSize(2) @@ -296,16 +296,16 @@ class RuleCollectorSpec { @Test fun `has description that is concatenated`() { val code = """ - /** - * description - */ - class SomeRandomClass() : Rule { - @Configuration( - "This is a " + - "multi line " + - "description") - private val config: String by config("a") - } + /** + * description + */ + class SomeRandomClass() : Rule { + @Configuration( + "This is a " + + "multi line " + + "description") + private val config: String by config("a") + } """ val items = subject.run(code) assertThat(items[0].configuration[0].description).isEqualTo("This is a multi line description") @@ -315,17 +315,17 @@ class RuleCollectorSpec { @Test fun `extracts default value when it is an Int constant`() { val code = """ - /** - * description - */ - class SomeRandomClass() : Rule { - @Configuration("description") - private val config: Int by config(DEFAULT_CONFIG_VALUE) - - companion object { - private const val DEFAULT_CONFIG_VALUE = 99 - } - } + /** + * description + */ + class SomeRandomClass() : Rule { + @Configuration("description") + private val config: Int by config(DEFAULT_CONFIG_VALUE) + + companion object { + private const val DEFAULT_CONFIG_VALUE = 99 + } + } """ val items = subject.run(code) assertThat(items[0].configuration[0].defaultValue).isEqualTo(of(99)) @@ -334,17 +334,17 @@ class RuleCollectorSpec { @Test fun `extracts default value when it is an Int constant as named parameter`() { val code = """ - /** - * description - */ - class SomeRandomClass() : Rule { - @Configuration("description") - private val config: Int by config(defaultValue = DEFAULT_CONFIG_VALUE) - - companion object { - private const val DEFAULT_CONFIG_VALUE = 99 - } - } + /** + * description + */ + class SomeRandomClass() : Rule { + @Configuration("description") + private val config: Int by config(defaultValue = DEFAULT_CONFIG_VALUE) + + companion object { + private const val DEFAULT_CONFIG_VALUE = 99 + } + } """ val items = subject.run(code) assertThat(items[0].configuration[0].defaultValue).isEqualTo(of(99)) @@ -353,17 +353,17 @@ class RuleCollectorSpec { @Test fun `extracts default value when it is a String constant`() { val code = """ - /** - * description - */ - class SomeRandomClass() : Rule { - @Configuration("description") - private val config: String by config(DEFAULT_CONFIG_VALUE) - - companion object { - private const val DEFAULT_CONFIG_VALUE = "a" - } - } + /** + * description + */ + class SomeRandomClass() : Rule { + @Configuration("description") + private val config: String by config(DEFAULT_CONFIG_VALUE) + + companion object { + private const val DEFAULT_CONFIG_VALUE = "a" + } + } """ val items = subject.run(code) assertThat(items[0].configuration[0].defaultValue).isEqualTo(of("a")) @@ -372,21 +372,21 @@ class RuleCollectorSpec { @Test fun `extracts default value for list of strings from constant`() { val code = """ - /** - * description - */ - class SomeRandomClass() : Rule { - @Configuration("description") - private val config1: List by config(DEFAULT_CONFIG_VALUE) + /** + * description + */ + class SomeRandomClass() : Rule { + @Configuration("description") + private val config1: List by config(DEFAULT_CONFIG_VALUE) - @Configuration("description") - private val config2: List by config(listOf(DEFAULT_CONFIG_VALUE_A, "b")) + @Configuration("description") + private val config2: List by config(listOf(DEFAULT_CONFIG_VALUE_A, "b")) - companion object { - private val DEFAULT_CONFIG_VALUE = listOf("a", "b") - private val DEFAULT_CONFIG_VALUE_A = "a" - } - } + companion object { + private val DEFAULT_CONFIG_VALUE = listOf("a", "b") + private val DEFAULT_CONFIG_VALUE_A = "a" + } + } """ val items = subject.run(code) val expected = of(listOf("a", "b")) @@ -397,16 +397,16 @@ class RuleCollectorSpec { @Test fun `extracts emptyList default value`() { val code = """ - /** - * description - */ - class SomeRandomClass() : Rule { - @Configuration("description") - private val config1: List by config(listOf()) + /** + * description + */ + class SomeRandomClass() : Rule { + @Configuration("description") + private val config1: List by config(listOf()) - @Configuration("description") - private val config2: List by config(emptyList()) - } + @Configuration("description") + private val config2: List by config(emptyList()) + } """ val items = subject.run(code) val expected = of(emptyList()) @@ -417,20 +417,20 @@ class RuleCollectorSpec { @Test fun `extracts emptyList default value of transformed list`() { val code = """ - /** - * description - */ - class SomeRandomClass() : Rule { - @Configuration("description") - private val config1: List by config(listOf()) { it.map(String::toInt) } + /** + * description + */ + class SomeRandomClass() : Rule { + @Configuration("description") + private val config1: List by config(listOf()) { it.map(String::toInt) } - @Configuration("description") - private val config2: List by config(DEFAULT_CONFIG_VALUE) { it.map(String::toInt) } + @Configuration("description") + private val config2: List by config(DEFAULT_CONFIG_VALUE) { it.map(String::toInt) } - companion object { - private val DEFAULT_CONFIG_VALUE: List = emptyList() - } - } + companion object { + private val DEFAULT_CONFIG_VALUE: List = emptyList() + } + } """ val items = subject.run(code) val expected = of(emptyList()) @@ -441,14 +441,14 @@ class RuleCollectorSpec { @Test fun `is marked as deprecated as well`() { val code = """ - /** - * description - */ - class SomeRandomClass() : Rule { - @Deprecated("use config1 instead") - @Configuration("description") - private val config: String by config("") - } + /** + * description + */ + class SomeRandomClass() : Rule { + @Deprecated("use config1 instead") + @Configuration("description") + private val config: String by config("") + } """ val items = subject.run(code) assertThat(items[0].configuration[0].deprecated).isEqualTo("use config1 instead") @@ -457,12 +457,12 @@ class RuleCollectorSpec { @Test fun `fails if kdoc is used to define configuration`() { val code = """ - /** - * description - * @configuration config1 - description (default: `''`) - */ - class SomeRandomClass() : Rule { - } + /** + * description + * @configuration config1 - description (default: `''`) + */ + class SomeRandomClass() : Rule { + } """ assertThatExceptionOfType(InvalidDocumentationException::class.java).isThrownBy { subject.run(code) } } @@ -470,13 +470,13 @@ class RuleCollectorSpec { @Test fun `fails if not used in combination with delegate`() { val code = """ - /** - * description - */ - class SomeRandomClass() : Rule { - @Configuration("description") - private val config: String = "foo" - } + /** + * description + */ + class SomeRandomClass() : Rule { + @Configuration("description") + private val config: String = "foo" + } """ assertThatExceptionOfType(InvalidDocumentationException::class.java).isThrownBy { subject.run(code) } } @@ -484,13 +484,13 @@ class RuleCollectorSpec { @Test fun `fails if not used in combination with config delegate`() { val code = """ - /** - * description - */ - class SomeRandomClass() : Rule { - @Configuration("description") - private val config: String by lazy { "foo" } - } + /** + * description + */ + class SomeRandomClass() : Rule { + @Configuration("description") + private val config: String by lazy { "foo" } + } """ assertThatExceptionOfType(InvalidDocumentationException::class.java).isThrownBy { subject.run(code) } } @@ -563,19 +563,19 @@ class RuleCollectorSpec { @Test fun `extracts default value`() { val code = """ - /** - * description - */ - class SomeRandomClass() : Rule { - @Configuration("description") - val prop: Int by config(1) - @Configuration("description") - private val config1: Int by configWithFallback(this::prop, 99) - @Configuration("description") - private val config2: Int by configWithFallback(fallbackProperty = ::prop, defaultValue = 99) - @Configuration("description") - private val config3: Int by configWithFallback(defaultValue = 99, fallbackProperty = ::prop) - } + /** + * description + */ + class SomeRandomClass() : Rule { + @Configuration("description") + val prop: Int by config(1) + @Configuration("description") + private val config1: Int by configWithFallback(this::prop, 99) + @Configuration("description") + private val config2: Int by configWithFallback(fallbackProperty = ::prop, defaultValue = 99) + @Configuration("description") + private val config3: Int by configWithFallback(defaultValue = 99, fallbackProperty = ::prop) + } """ val items = subject.run(code) val fallbackProperties = items[0].configuration.filter { it.name.startsWith("config") } @@ -586,14 +586,14 @@ class RuleCollectorSpec { @Test fun `reports an error if the property to fallback on exists but is not a config property`() { val code = """ - /** - * description - */ - class SomeRandomClass() : Rule { - val prop: Int = 1 - @Configuration("description") - private val config: Int by configWithFallback(::prop, 99) - } + /** + * description + */ + class SomeRandomClass() : Rule { + val prop: Int = 1 + @Configuration("description") + private val config: Int by configWithFallback(::prop, 99) + } """ assertThatThrownBy { subject.run(code) } .isInstanceOf(InvalidDocumentationException::class.java) @@ -604,15 +604,15 @@ class RuleCollectorSpec { @Nested inner class `transformed property` { val code = """ - /** - * description - */ - class SomeRandomClass() : Rule { - @Configuration("description") - private val config1: Regex by config("[a-z]+") { it.toRegex() } - @Configuration("description") - private val config2: String by config(false, Boolean::toString) - } + /** + * description + */ + class SomeRandomClass() : Rule { + @Configuration("description") + private val config1: Regex by config("[a-z]+") { it.toRegex() } + @Configuration("description") + private val config2: String by config(false, Boolean::toString) + } """ @Test @@ -707,10 +707,10 @@ class RuleCollectorSpec { @Test fun `has no type resolution by default`() { val code = """ - /** - * description - */ - class SomeRandomClass : Rule + /** + * description + */ + class SomeRandomClass : Rule """ val items = subject.run(code) assertThat(items[0].requiresTypeResolution).isFalse() @@ -719,11 +719,11 @@ class RuleCollectorSpec { @Test fun `collects the flag that it requires type resolution`() { val code = """ - /** - * description - */ - @RequiresTypeResolution - class SomeRandomClass : Rule + /** + * description + */ + @RequiresTypeResolution + class SomeRandomClass : Rule """ val items = subject.run(code) assertThat(items[0].requiresTypeResolution).isTrue() @@ -732,11 +732,11 @@ class RuleCollectorSpec { @Test fun `collects the flag that it requires type resolution from fully qualified annotation`() { val code = """ - /** - * description - */ - @io.gitlab.arturbosch.detekt.api.internal.RequiresTypeResolution - class SomeRandomClass : Rule + /** + * description + */ + @io.gitlab.arturbosch.detekt.api.internal.RequiresTypeResolution + class SomeRandomClass : Rule """ val items = subject.run(code) assertThat(items[0].requiresTypeResolution).isTrue() @@ -746,18 +746,18 @@ class RuleCollectorSpec { @Test fun `contains compliant and noncompliant code examples`() { val code = """ - /** - * description - * - * - * val one = 2 - * - * - * - * val one = 1 - * - */ - class RandomClass : Rule + /** + * description + * + * + * val one = 2 + * + * + * + * val one = 1 + * + */ + class RandomClass : Rule """ val items = subject.run(code) assertThat(items[0].nonCompliantCodeExample).isEqualTo("val one = 2") @@ -767,12 +767,12 @@ class RuleCollectorSpec { @Test fun `has wrong noncompliant code example declaration`() { val code = """ - /** - * description - * - * - */ - class RandomClass : Rule + /** + * description + * + * + */ + class RandomClass : Rule """ assertThatExceptionOfType(InvalidCodeExampleDocumentationException::class.java) .isThrownBy { subject.run(code) } @@ -781,15 +781,15 @@ class RuleCollectorSpec { @Test fun `has wrong compliant code example declaration`() { val code = """ - /** - * description - * - * - * val one = 2 - * - * - */ - class RandomClass : Rule + /** + * description + * + * + * val one = 2 + * + * + */ + class RandomClass : Rule """ assertThatExceptionOfType(InvalidCodeExampleDocumentationException::class.java) .isThrownBy { subject.run(code) } @@ -798,14 +798,14 @@ class RuleCollectorSpec { @Test fun `has wrong compliant without noncompliant code example declaration`() { val code = """ - /** - * description - * - * - * val one = 1 - * - */ - class RandomClass : Rule + /** + * description + * + * + * val one = 1 + * + */ + class RandomClass : Rule """ assertThatExceptionOfType(InvalidCodeExampleDocumentationException::class.java) .isThrownBy { subject.run(code) } @@ -814,17 +814,17 @@ class RuleCollectorSpec { @Test fun `has wrong issue style property`() { val code = """ - /** - * description - */ - class SomeRandomClass : Rule { - - val style = Severity.Style - override val issue = Issue(javaClass.simpleName, - style, - "", - debt = Debt.TEN_MINS) - } + /** + * description + */ + class SomeRandomClass : Rule { + + val style = Severity.Style + override val issue = Issue(javaClass.simpleName, + style, + "", + debt = Debt.TEN_MINS) + } """ assertThatExceptionOfType(InvalidIssueDeclaration::class.java).isThrownBy { subject.run(code) } } @@ -832,18 +832,18 @@ class RuleCollectorSpec { @Test fun `has wrong aliases property structure`() { val code = """ - /** - * description - */ - class SomeRandomClass : Rule { - - val a = setOf("UNUSED_VARIABLE") - override val defaultRuleIdAliases = a - override val issue = Issue(javaClass.simpleName, - Severity.Style, - "", - debt = Debt.TEN_MINS) - } + /** + * description + */ + class SomeRandomClass : Rule { + + val a = setOf("UNUSED_VARIABLE") + override val defaultRuleIdAliases = a + override val issue = Issue(javaClass.simpleName, + Severity.Style, + "", + debt = Debt.TEN_MINS) + } """ assertThatExceptionOfType(InvalidAliasesDeclaration::class.java).isThrownBy { subject.run(code) } } @@ -852,10 +852,10 @@ class RuleCollectorSpec { fun `contains tabs in KDoc`() { val description = "\tdescription" val code = """ - /** - * $description - */ - class SomeRandomClass : Rule + /** + * $description + */ + class SomeRandomClass : Rule """ assertThatExceptionOfType(InvalidDocumentationException::class.java).isThrownBy { subject.run(code) } }