From 8a69676534fb1ca43526b960a668e3cd5e4aaa1b Mon Sep 17 00:00:00 2001 From: marschwar Date: Sun, 23 Jan 2022 06:45:19 +0100 Subject: [PATCH] Introduce DefaultValue type (#3928) * Introduce DefaultValue type * reuse createDefaultValueIfLiteral in RuleSetProviderCollector.kt * use DefaultValue.of() explicitly * default getAsPlainString to toString * rename ListDefault to StringListDefault Co-authored-by: Markus Schwarz --- config/detekt/detekt.yml | 4 +- .../generator/collection/Configuration.kt | 24 +--- .../collection/ConfigurationCollector.kt | 114 +++++++++--------- .../generator/collection/DefaultValue.kt | 38 ++++++ .../generator/collection/DefaultValues.kt | 22 ++++ .../collection/RuleSetProviderCollector.kt | 20 +-- .../generator/printer/RuleSetPagePrinter.kt | 8 +- .../printer/defaultconfig/ConfigPrinter.kt | 2 +- .../generator/collection/ConfigurationSpec.kt | 11 +- .../generator/collection/RuleCollectorSpec.kt | 48 ++++---- .../RuleSetProviderCollectorSpec.kt | 42 ++++++- .../generator/util/RuleSetPageCreator.kt | 21 ++-- .../src/test/resources/RuleSet.md | 2 +- .../src/test/resources/RuleSetConfig.yml | 2 +- 14 files changed, 214 insertions(+), 144 deletions(-) create mode 100644 detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/DefaultValue.kt create mode 100644 detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/DefaultValues.kt diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index f394727ff..73c28d19f 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -104,12 +104,12 @@ formatting: naming: ClassNaming: - excludes: [ '**/*Spec.kt' ] + excludes: ['**/*Spec.kt'] TopLevelPropertyNaming: constantPattern: '[a-z][_A-Za-z0-9]*|[A-Z][_A-Z0-9]*' InvalidPackageDeclaration: active: true - excludes: [ '**/build-logic/**/*.kt', '**/*.kts' ] + excludes: ['**/build-logic/**/*.kt', '**/*.kts'] NoNameShadowing: active: true NonBooleanPropertyPrefixedWithIs: 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 f1dd86631..4773e3e25 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 @@ -3,31 +3,13 @@ package io.gitlab.arturbosch.detekt.generator.collection data class Configuration( val name: String, val description: String, - val defaultValue: String, - val defaultAndroidValue: String?, + val defaultValue: DefaultValue, + val defaultAndroidValue: DefaultValue?, val deprecated: String? ) { fun isDeprecated() = deprecated != null fun isDefaultValueNonEmptyList() = defaultValue.isNonEmptyList() - fun getDefaultValueAsList(): List { - if (defaultValue.isNonEmptyList()) { - return defaultValue.toList() - } - error("default value '$defaultValue' is not a list") - } - - private fun String.isNonEmptyList(): Boolean = NON_EMPTY_LIST_REGEX.matchEntire(this) != null - - private fun String.toList(): List = - trim() - .removePrefix("[") - .removeSuffix("]") - .split(",") - .map { it.trim().removeSurrounding("'") } - - companion object { - private val NON_EMPTY_LIST_REGEX = Regex("""\[.*[\S]+.*]""") - } + 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 1f4fa5edc..680d37b98 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 @@ -6,6 +6,9 @@ import io.gitlab.arturbosch.detekt.generator.collection.ConfigurationCollector.C import io.gitlab.arturbosch.detekt.generator.collection.ConfigurationCollector.ConfigWithFallbackSupport.FALLBACK_DELEGATE_NAME import io.gitlab.arturbosch.detekt.generator.collection.ConfigurationCollector.ConfigWithFallbackSupport.checkUsingInvalidFallbackReference import io.gitlab.arturbosch.detekt.generator.collection.ConfigurationCollector.ConfigWithFallbackSupport.isFallbackConfigDelegate +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.exception.InvalidDocumentationException import org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.KtCallableReferenceExpression @@ -24,7 +27,7 @@ import io.gitlab.arturbosch.detekt.api.internal.Configuration as ConfigAnnotatio class ConfigurationCollector { - private val constantsByName = mutableMapOf() + private val constantsByName = mutableMapOf() private val properties = mutableListOf() fun getConfiguration(): List { @@ -43,25 +46,22 @@ class ConfigurationCollector { ) } - private fun resolveConstantOrNull(prop: KtProperty): Pair? { + private fun resolveConstantOrNull(prop: KtProperty): Pair? { if (prop.isVar) return null val propertyName = checkNotNull(prop.name) - val constantOrNull = prop.getConstantValueAsStringOrNull() + val constantOrNull = prop.getConstantValue() return constantOrNull?.let { propertyName to it } } - private fun KtProperty.getConstantValueAsStringOrNull(): String? { + private fun KtProperty.getConstantValue(): DefaultValue? { if (hasListDeclaration()) { - return getListDeclaration() - .valueArguments - .map { "'${it.text.withoutQuotes()}'" } - .toString() + return DefaultValue.of(getListDeclaration().valueArguments.map { it.text.withoutQuotes() }) } - return findDescendantOfType()?.text - ?: findDescendantOfType()?.text?.withoutQuotes() + return findDescendantOfType()?.toDefaultValueIfLiteral() + ?: findDescendantOfType()?.toDefaultValueIfLiteral() } private fun KtProperty.parseConfigurationAnnotation(): Configuration? = when { @@ -84,59 +84,61 @@ class ConfigurationCollector { val propertyName: String = checkNotNull(name) val deprecationMessage = firstAnnotationParameterOrNull(Deprecated::class) val description: String = firstAnnotationParameter(ConfigAnnotation::class) - val defaultValueAsString = getDefaultValueAsString() - val defaultAndroidValueAsString = getDefaultAndroidValueAsString() + val defaultValue = getDefaultValue(constantsByName) + val defaultAndroidValue = getAndroidDefaultValue(constantsByName) return Configuration( name = propertyName, description = description, - defaultValue = defaultValueAsString, - defaultAndroidValue = defaultAndroidValueAsString, + defaultValue = defaultValue, + defaultAndroidValue = defaultAndroidValue, deprecated = deprecationMessage, ) } - private fun KtProperty.getDefaultValueAsString(): String { - val defaultValueArgument = getValueArgument( - name = DEFAULT_VALUE_ARGUMENT_NAME, - actionForPositionalMatch = { arguments -> - when { - isFallbackConfigDelegate() -> arguments[1] - isAndroidVariantConfigDelegate() -> arguments[0] - else -> arguments[0] + private object DefaultValueSupport { + fun KtProperty.getDefaultValue(constantsByName: Map): DefaultValue { + val defaultValueArgument = getValueArgument( + name = DEFAULT_VALUE_ARGUMENT_NAME, + actionForPositionalMatch = { arguments -> + when { + isFallbackConfigDelegate() -> arguments[1] + isAndroidVariantConfigDelegate() -> arguments[0] + else -> arguments[0] + } } - } - ) ?: invalidDocumentation { "'$name' is not a delegated property" } - return formatDefaultValueExpression(checkNotNull(defaultValueArgument.getArgumentExpression())) - } - - private fun KtProperty.getDefaultAndroidValueAsString(): String? { - val defaultValueArgument = getValueArgument( - name = DEFAULT_ANDROID_VALUE_ARGUMENT_NAME, - actionForPositionalMatch = { arguments -> - when { - isAndroidVariantConfigDelegate() -> arguments[1] - else -> null - } - } - ) - val defaultValueExpression = defaultValueArgument?.getArgumentExpression() ?: return null - return formatDefaultValueExpression(defaultValueExpression) - } - - private fun KtProperty.formatDefaultValueExpression(ktExpression: KtExpression): String { - val listDeclarationForDefault = ktExpression.getListDeclarationOrNull() - if (listDeclarationForDefault != null) { - return listDeclarationForDefault.valueArguments.map { - val value = constantsByName[it.text] ?: it.text - "'${value.withoutQuotes()}'" - }.toString() + ) ?: invalidDocumentation { "'$name' is not a delegated property" } + return checkNotNull(defaultValueArgument.getArgumentExpression()).toDefaultValue(constantsByName) } - val defaultValueOrConstantName = checkNotNull(ktExpression.text.withoutQuotes()) - val defaultValue = constantsByName[defaultValueOrConstantName] ?: defaultValueOrConstantName - val needsQuotes = declaredTypeOrNull in TYPES_THAT_NEED_QUOTATION_FOR_DEFAULT - return if (needsQuotes) "'$defaultValue'" else defaultValue + fun KtProperty.getAndroidDefaultValue(constantsByName: Map): DefaultValue? { + val defaultValueArgument = getValueArgument( + name = DEFAULT_ANDROID_VALUE_ARGUMENT_NAME, + actionForPositionalMatch = { arguments -> + when { + isAndroidVariantConfigDelegate() -> arguments[1] + else -> null + } + } + ) + return defaultValueArgument?.getArgumentExpression()?.toDefaultValue(constantsByName) + } + + 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() + ?: constantsByName[text.withoutQuotes()] + ?: error("$text is neither a literal nor a constant") + } + + fun KtExpression.toDefaultValueIfLiteral(): DefaultValue? = createDefaultValueIfLiteral(text) } private object ConfigWithFallbackSupport { @@ -186,14 +188,6 @@ class ConfigurationCollector { private const val EMPTY_LIST = "emptyList" private val LIST_CREATORS = setOf(LIST_OF, EMPTY_LIST) - private const val TYPE_STRING = "String" - private const val TYPE_REGEX = "Regex" - private const val TYPE_SPLIT_PATTERN = "SplitPattern" - private val TYPES_THAT_NEED_QUOTATION_FOR_DEFAULT = listOf(TYPE_STRING, TYPE_REGEX, TYPE_SPLIT_PATTERN) - - private val KtProperty.declaredTypeOrNull: String? - get() = typeReference?.text - private fun KtElement.getListDeclaration(): KtCallExpression = checkNotNull(getListDeclarationOrNull()) 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 new file mode 100644 index 000000000..d04ba3055 --- /dev/null +++ b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/DefaultValue.kt @@ -0,0 +1,38 @@ +package io.gitlab.arturbosch.detekt.generator.collection + +sealed interface DefaultValue { + fun isNonEmptyList(): Boolean = false + fun getAsList(): List = error("default value is not a list") + fun getAsPlainString(): String = toString() + fun getQuotedIfNecessary(): String = getAsPlainString() + + companion object { + fun of(defaultValue: String): DefaultValue = StringDefault(defaultValue) + fun of(defaultValue: Boolean): DefaultValue = BooleanDefault(defaultValue) + fun of(defaultValue: Int): DefaultValue = IntegerDefault(defaultValue) + fun of(defaultValue: List): DefaultValue = StringListDefault(defaultValue) + } +} + +private data class StringDefault(private val defaultValue: String) : DefaultValue { + private val quoted = "'$defaultValue'" + 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() +} + +private data class IntegerDefault(private val defaultValue: Int) : DefaultValue { + override fun getAsPlainString(): String = defaultValue.toString() +} + +private data class StringListDefault(private val defaultValue: List) : DefaultValue { + private val quoted: String = defaultValue.map { "'$it'" }.toString() + + 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 +} diff --git a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/DefaultValues.kt b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/DefaultValues.kt new file mode 100644 index 000000000..4babe4aa6 --- /dev/null +++ b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/DefaultValues.kt @@ -0,0 +1,22 @@ +package io.gitlab.arturbosch.detekt.generator.collection + +import org.jetbrains.kotlin.lexer.KtTokens.FALSE_KEYWORD +import org.jetbrains.kotlin.lexer.KtTokens.TRUE_KEYWORD + +fun createDefaultValueIfLiteral(maybeLiteral: String): DefaultValue? = maybeLiteral.toDefaultValueIfLiteral() + +private fun String.toDefaultValueIfLiteral(): DefaultValue? { + return when { + isStringLiteral() -> DefaultValue.of(withoutQuotes()) + isBooleanLiteral() -> DefaultValue.of(toBoolean()) + isIntegerLiteral() -> DefaultValue.of(withoutUnderscores().toInt()) + else -> null + } +} + +private fun String.withoutUnderscores() = replace("_", "") +private fun String.isStringLiteral() = length > 1 && startsWith(QUOTES) && endsWith(QUOTES) +private fun String.isBooleanLiteral() = this == TRUE_KEYWORD.value || this == FALSE_KEYWORD.value +private fun String.isIntegerLiteral() = withoutUnderscores().toIntOrNull() != null + +private const val QUOTES = "\"" diff --git a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/RuleSetProviderCollector.kt b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/RuleSetProviderCollector.kt index 27636ccd1..b3553c007 100644 --- a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/RuleSetProviderCollector.kt +++ b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/collection/RuleSetProviderCollector.kt @@ -87,6 +87,8 @@ class RuleSetProviderVisitor : DetektVisitor() { override fun visitProperty(property: KtProperty) { super.visitProperty(property) + if (!containsRuleSetProvider) return + if (property.isOverride() && property.name != null && property.name == PROPERTY_RULE_SET_ID) { name = (property.initializer as? KtStringTemplateExpression)?.entries?.get(0)?.text ?: throw InvalidDocumentationException( @@ -95,7 +97,8 @@ class RuleSetProviderVisitor : DetektVisitor() { ) } if (property.isAnnotatedWith(ConfigAnnotation::class)) { - val defaultValue = formatDefaultValue( + val defaultValue = toDefaultValue( + name, checkNotNull(property.delegate?.expression as? KtCallExpression) .valueArguments .first() @@ -133,14 +136,11 @@ class RuleSetProviderVisitor : DetektVisitor() { } companion object { - - private const val DOUBLE_QUOTE = '"' - - private fun formatDefaultValue(defaultValueText: String): String = - if (defaultValueText.startsWith(DOUBLE_QUOTE) && defaultValueText.endsWith(DOUBLE_QUOTE)) { - "'${defaultValueText.removeSurrounding("$DOUBLE_QUOTE")}'" - } else { - defaultValueText - } + private fun toDefaultValue(providerName: String, defaultValueText: String): DefaultValue = + createDefaultValueIfLiteral(defaultValueText) + ?: throw InvalidDocumentationException( + "Unsupported default value format '$defaultValueText' " + + "in $providerName. Please use a Boolean, Int or String literal instead." + ) } } diff --git a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/printer/RuleSetPagePrinter.kt b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/printer/RuleSetPagePrinter.kt index 86c61a47e..8591bd7f1 100644 --- a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/printer/RuleSetPagePrinter.kt +++ b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/printer/RuleSetPagePrinter.kt @@ -68,8 +68,8 @@ object RuleSetPagePrinter : DocumentationPrinter { h4 { "Configuration options:" } list { rule.configuration.forEach { - val defaultValues = formatDefaultValues(it.defaultValue) - val defaultAndroidValues = it.defaultAndroidValue?.let(RuleSetPagePrinter::formatDefaultValues) + val defaultValues = it.defaultValue.getQuotedIfNecessary() + val defaultAndroidValues = it.defaultAndroidValue?.getQuotedIfNecessary() val defaultString = if (defaultAndroidValues != null) { "(default: ${code { defaultValues }}) (android default: ${code { defaultAndroidValues }})" } else { @@ -94,10 +94,6 @@ object RuleSetPagePrinter : DocumentationPrinter { } } - private fun formatDefaultValues(rawString: String) = rawString.lines().joinToString { - it.trim().removePrefix("- ") - } - private fun MarkdownContent.printRuleCodeExamples(rule: Rule) { if (rule.nonCompliantCodeExample.isNotEmpty()) { h4 { "Noncompliant Code:" } diff --git a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/printer/defaultconfig/ConfigPrinter.kt b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/printer/defaultconfig/ConfigPrinter.kt index 140471378..b9486df2b 100644 --- a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/printer/defaultconfig/ConfigPrinter.kt +++ b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/printer/defaultconfig/ConfigPrinter.kt @@ -66,7 +66,7 @@ object ConfigPrinter : DocumentationPrinter> { if (configuration.isDefaultValueNonEmptyList()) { list(configuration.name, configuration.getDefaultValueAsList()) } else { - keyValue { configuration.name to configuration.defaultValue } + keyValue { configuration.name to configuration.defaultValue.getQuotedIfNecessary() } } } diff --git a/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/collection/ConfigurationSpec.kt b/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/collection/ConfigurationSpec.kt index 8b9444c93..592a0c425 100644 --- a/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/collection/ConfigurationSpec.kt +++ b/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/collection/ConfigurationSpec.kt @@ -1,5 +1,6 @@ package io.gitlab.arturbosch.detekt.generator.collection +import io.gitlab.arturbosch.detekt.generator.collection.DefaultValue.Companion.of import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatIllegalStateException import org.junit.jupiter.api.Nested @@ -8,7 +9,7 @@ import org.junit.jupiter.api.Test private val defaultConfiguration = Configuration( name = "name", description = "description", - defaultValue = "", + defaultValue = of(""), defaultAndroidValue = null, deprecated = null ) @@ -19,7 +20,7 @@ class ConfigurationSpec { inner class `default value to list conversion` { @Nested inner class `empty default value` { - val subject = defaultConfiguration.copy(defaultValue = "") + private val subject = defaultConfiguration.copy(defaultValue = of("")) @Test fun `identifies default as not a list`() { @@ -34,7 +35,7 @@ class ConfigurationSpec { @Nested inner class `non list default value` { - val subject = defaultConfiguration.copy(defaultValue = "abc") + private val subject = defaultConfiguration.copy(defaultValue = of("abc")) @Test fun `identifies default as not a list`() { @@ -49,7 +50,7 @@ class ConfigurationSpec { @Nested inner class `empty list default value` { - val subject = defaultConfiguration.copy(defaultValue = "[ ]") + private val subject = defaultConfiguration.copy(defaultValue = of(emptyList())) @Test fun `identifies default as not a non empty list`() { @@ -64,7 +65,7 @@ class ConfigurationSpec { @Nested inner class `bracket list default value` { - val subject = defaultConfiguration.copy(defaultValue = "[ 'a', 'b' ]") + private val subject = defaultConfiguration.copy(defaultValue = of(listOf("a", "b"))) @Test fun `identifies default as a non empty list`() { 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 6a5f78014..e2b559422 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.generator.collection.DefaultValue.Companion.of import io.gitlab.arturbosch.detekt.generator.collection.exception.InvalidAliasesDeclaration import io.gitlab.arturbosch.detekt.generator.collection.exception.InvalidCodeExampleDocumentationException import io.gitlab.arturbosch.detekt.generator.collection.exception.InvalidDocumentationException @@ -204,7 +205,7 @@ class RuleCollectorSpec { val expectedConfiguration = Configuration( name = "config", description = "description", - defaultValue = "'[A-Z$]'", + defaultValue = of("[A-Z$]"), defaultAndroidValue = null, deprecated = null ) @@ -219,12 +220,12 @@ class RuleCollectorSpec { */ class SomeRandomClass() : Rule { @Configuration("description") - private val config: Int by config(99) + 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("99") + assertThat(items[0].configuration[0].defaultValue).isEqualTo(of(1_999_000)) } @Test @@ -239,7 +240,7 @@ class RuleCollectorSpec { } """ val items = subject.run(code) - assertThat(items[0].configuration[0].defaultValue).isEqualTo("'abcd'") + assertThat(items[0].configuration[0].defaultValue).isEqualTo(of("abcd")) } @Test @@ -254,7 +255,7 @@ class RuleCollectorSpec { } """ val items = subject.run(code) - assertThat(items[0].configuration[0].defaultValue).isEqualTo("99") + assertThat(items[0].configuration[0].defaultValue).isEqualTo(of(99)) } @Test @@ -274,7 +275,8 @@ class RuleCollectorSpec { } """ val items = subject.run(code) - assertThat(items[0].configuration[0].defaultValue).isEqualTo("['a', 'b']") + val expected = of(listOf("a", "b")) + assertThat(items[0].configuration[0].defaultValue).isEqualTo(expected) } @Test @@ -311,7 +313,7 @@ class RuleCollectorSpec { """ 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("'a'") + assertThat(items[0].configuration[0].defaultValue).isEqualTo(of("a")) } @Test @@ -330,7 +332,7 @@ class RuleCollectorSpec { } """ val items = subject.run(code) - assertThat(items[0].configuration[0].defaultValue).isEqualTo("99") + assertThat(items[0].configuration[0].defaultValue).isEqualTo(of(99)) } @Test @@ -349,7 +351,7 @@ class RuleCollectorSpec { } """ val items = subject.run(code) - assertThat(items[0].configuration[0].defaultValue).isEqualTo("99") + assertThat(items[0].configuration[0].defaultValue).isEqualTo(of(99)) } @Test @@ -368,7 +370,7 @@ class RuleCollectorSpec { } """ val items = subject.run(code) - assertThat(items[0].configuration[0].defaultValue).isEqualTo("'a'") + assertThat(items[0].configuration[0].defaultValue).isEqualTo(of("a")) } @Test @@ -391,7 +393,7 @@ class RuleCollectorSpec { } """ val items = subject.run(code) - val expected = "['a', 'b']" + val expected = of(listOf("a", "b")) assertThat(items[0].configuration[0].defaultValue).isEqualTo(expected) assertThat(items[0].configuration[1].defaultValue).isEqualTo(expected) } @@ -411,8 +413,9 @@ class RuleCollectorSpec { } """ val items = subject.run(code) - assertThat(items[0].configuration[0].defaultValue).isEqualTo("[]") - assertThat(items[0].configuration[1].defaultValue).isEqualTo("[]") + val expected = of(emptyList()) + assertThat(items[0].configuration[0].defaultValue).isEqualTo(expected) + assertThat(items[0].configuration[1].defaultValue).isEqualTo(expected) } @Test @@ -434,8 +437,9 @@ class RuleCollectorSpec { } """ val items = subject.run(code) - assertThat(items[0].configuration[0].defaultValue).isEqualTo("[]") - assertThat(items[0].configuration[1].defaultValue).isEqualTo("[]") + val expected = of(emptyList()) + assertThat(items[0].configuration[0].defaultValue).isEqualTo(expected) + assertThat(items[0].configuration[1].defaultValue).isEqualTo(expected) } @Test @@ -526,8 +530,8 @@ class RuleCollectorSpec { Configuration( name = "maxLineLength", description = "description", - defaultValue = "120", - defaultAndroidValue = "100", + defaultValue = of(120), + defaultAndroidValue = of(100), deprecated = null ) ) @@ -550,8 +554,8 @@ class RuleCollectorSpec { Configuration( name = "maxLineLength", description = "description", - defaultValue = "120", - defaultAndroidValue = "100", + defaultValue = of(120), + defaultAndroidValue = of(100), deprecated = null ) ) @@ -580,7 +584,7 @@ class RuleCollectorSpec { 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("99") + assertThat(fallbackProperties.map { it.defaultValue }).containsOnly(of(99)) } @Test @@ -618,13 +622,13 @@ class RuleCollectorSpec { @Test fun `extracts default value with transformer function`() { val items = subject.run(code) - assertThat(items[0].configuration[0].defaultValue).isEqualTo("'[a-z]+'") + 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("'false'") + assertThat(items[0].configuration[1].defaultValue).isEqualTo(of(false)) } } } diff --git a/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/collection/RuleSetProviderCollectorSpec.kt b/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/collection/RuleSetProviderCollectorSpec.kt index db47a1648..f46ac1092 100644 --- a/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/collection/RuleSetProviderCollectorSpec.kt +++ b/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/collection/RuleSetProviderCollectorSpec.kt @@ -1,9 +1,11 @@ package io.gitlab.arturbosch.detekt.generator.collection +import io.gitlab.arturbosch.detekt.generator.collection.DefaultValue.Companion.of import io.gitlab.arturbosch.detekt.generator.collection.exception.InvalidDocumentationException import io.gitlab.arturbosch.detekt.generator.util.run import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test @@ -287,7 +289,7 @@ class RuleSetProviderCollectorSpec { } @Nested - inner class `a correct RuleSetProvider class with full parameters missing` { + inner class `a correct RuleSetProvider class with full parameters and multiple rules` { val description = "This is a description" val ruleSetId = "test" val ruleName = "TestRule" @@ -365,14 +367,14 @@ class RuleSetProviderCollectorSpec { } } """ - val items = subject.run(code) + private val items = subject.run(code) @Test fun `extracts boolean configuration option`() { val conf = items[0].configuration[0] assertThat(conf.name).isEqualTo("aBool") assertThat(conf.description).isEqualTo("bool description") - assertThat(conf.defaultValue).isEqualTo("true") + assertThat(conf.defaultValue).isEqualTo(of(true)) assertThat(conf.deprecated).isNull() } @@ -381,7 +383,7 @@ class RuleSetProviderCollectorSpec { val conf = items[0].configuration[1] assertThat(conf.name).isEqualTo("anInt") assertThat(conf.description).isEqualTo("int description") - assertThat(conf.defaultValue).isEqualTo("99") + assertThat(conf.defaultValue).isEqualTo(of(99)) } @Test @@ -389,9 +391,39 @@ class RuleSetProviderCollectorSpec { val conf = items[0].configuration[2] assertThat(conf.name).isEqualTo("aString") assertThat(conf.description).isEqualTo("string description") - assertThat(conf.defaultValue).isEqualTo("'a'") + assertThat(conf.defaultValue).isEqualTo(of("a")) assertThat(conf.deprecated).isEqualTo("use something else") } } + + @Nested + inner class `a RuleSetProvider with unsupported configuration format` { + val code = """ + package foo + + /** + * description + */ + class TestProvider: RuleSetProvider { + override val ruleSetId: String = "ruleSetId" + + override fun instance(config: Config): RuleSet { + return RuleSet(ruleSetId, listOf(RruleName(config))) + } + + companion object { + @Configuration("a description") + val aConfig by ruleSetConfig(listOf("a")) + } + } + """ + + @Test + fun `fails`() { + assertThatThrownBy { subject.run(code) } + .isInstanceOf(InvalidDocumentationException::class.java) + .hasMessageContaining("""Unsupported default value format 'listOf("a")'""") + } + } } } diff --git a/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/util/RuleSetPageCreator.kt b/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/util/RuleSetPageCreator.kt index 851b9c12f..250acb3d3 100644 --- a/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/util/RuleSetPageCreator.kt +++ b/detekt-generator/src/test/kotlin/io/gitlab/arturbosch/detekt/generator/util/RuleSetPageCreator.kt @@ -2,6 +2,7 @@ package io.gitlab.arturbosch.detekt.generator.util import io.gitlab.arturbosch.detekt.generator.collection.Active import io.gitlab.arturbosch.detekt.generator.collection.Configuration +import io.gitlab.arturbosch.detekt.generator.collection.DefaultValue.Companion.of import io.gitlab.arturbosch.detekt.generator.collection.Inactive import io.gitlab.arturbosch.detekt.generator.collection.Rule import io.gitlab.arturbosch.detekt.generator.collection.RuleSetPage @@ -19,35 +20,35 @@ internal fun createRuleSetPage(): RuleSetPage { Configuration( name = "rulesetconfig1", description = "description rulesetconfig1", - defaultValue = "true", + defaultValue = of(true), defaultAndroidValue = null, deprecated = null ), Configuration( name = "rulesetconfig2", description = "description rulesetconfig2", - defaultValue = "['foo', 'bar']", + defaultValue = of(listOf("foo", "bar")), defaultAndroidValue = null, deprecated = null ), Configuration( name = "deprecatedSimpleConfig", description = "description deprecatedSimpleConfig", - defaultValue = "true", + defaultValue = of(true), defaultAndroidValue = null, deprecated = "is deprecated" ), Configuration( name = "deprecatedListConfig", description = "description deprecatedListConfig", - defaultValue = "['foo', 'bar']", + defaultValue = of(listOf("foo", "bar")), defaultAndroidValue = null, deprecated = "is deprecated" ), Configuration( name = "rulesetconfig3", description = "description rulesetconfig2", - defaultValue = "['first', 'se*cond']", + defaultValue = of(listOf("first", "se*cond")), defaultAndroidValue = null, deprecated = null ) @@ -68,11 +69,11 @@ internal fun createRules(): List { aliases = "alias1, alias2", parent = "", configuration = listOf( - Configuration("conf1", "a config option", "foo", null, null), - Configuration("conf2", "deprecated config", "false", null, "use conf1 instead"), - Configuration("conf3", "list config", "['a', 'b']", null, null), - Configuration("conf4", "deprecated list config", "['a', 'b']", null, "use conf3 instead"), - Configuration("conf5", "rule with android variants", "120", "100", null), + Configuration("conf1", "a config option", of("foo"), null, null), + Configuration("conf2", "deprecated config", of(false), null, "use conf1 instead"), + Configuration("conf3", "list config", of(listOf("a", "b")), null, null), + Configuration("conf4", "deprecated list config", of(listOf("a", "b")), null, "use conf3 instead"), + Configuration("conf5", "rule with android variants", of(120), of(100), null), ) ) val rule2 = Rule( diff --git a/detekt-generator/src/test/resources/RuleSet.md b/detekt-generator/src/test/resources/RuleSet.md index 014c7bba4..2b1821e39 100644 --- a/detekt-generator/src/test/resources/RuleSet.md +++ b/detekt-generator/src/test/resources/RuleSet.md @@ -12,7 +12,7 @@ a wildcard import #### Configuration options: -* ``conf1`` (default: ``foo``) +* ``conf1`` (default: ``'foo'``) a config option diff --git a/detekt-generator/src/test/resources/RuleSetConfig.yml b/detekt-generator/src/test/resources/RuleSetConfig.yml index eacd55134..85e774df9 100644 --- a/detekt-generator/src/test/resources/RuleSetConfig.yml +++ b/detekt-generator/src/test/resources/RuleSetConfig.yml @@ -10,7 +10,7 @@ style: WildcardImport: active: true excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - conf1: foo + conf1: 'foo' conf3: - 'a' - 'b'