Skip to content

Commit

Permalink
add allowNonStandardGrouping config property
Browse files Browse the repository at this point in the history
  • Loading branch information
Markus Schwarz committed Nov 17, 2021
1 parent 1f05ce0 commit 9cc086a
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 16 deletions.
Expand Up @@ -29,7 +29,6 @@ import java.util.Locale
*
* <compliant>
* const val DEFAULT_AMOUNT = 1_000_000
* const val DEFAULT_AMOUNT = 10_000_00
* </compliant>
*/
class UnderscoresInNumericLiterals(config: Config = Config.empty) : Rule(config) {
Expand All @@ -48,27 +47,37 @@ class UnderscoresInNumericLiterals(config: Config = Config.empty) : Rule(config)

@Suppress("DEPRECATION")
@OptIn(UnstableApi::class)
@Configuration("Maximum number of consecutive digits that a number can have without using an underscore")
@Configuration("Maximum number of consecutive digits that a numeric literal can have without using an underscore")
private val acceptableLength: Int by configWithFallback(::acceptableDecimalLength, 4)

private val nonCompliantRegex: Regex = "\\d{${acceptableLength + 1},}".toRegex()
@Configuration("If set to false, groups of exactly three digits must be used. If set to true, 100_00 is allowed.")
private val allowNonStandardGrouping: Boolean by config(false)

private val nonCompliantRegex: Regex = """\d{${acceptableLength + 1},}""".toRegex()

override fun visitConstantExpression(expression: KtConstantExpression) {
val normalizedText = normalizeForMatching(expression.text)
checkNormalized(normalizedText, expression)
}

if (isNotDecimalNumber(normalizedText) || expression.isSerialUidProperty()) {
private fun checkNormalized(normalizedText: String, expression: KtConstantExpression) {
if (isNotDecimalNumber(normalizedText)) {
return
}
if (expression.isSerialUidProperty()) {
return
}

val numberString = normalizedText.split('.').first()

if (!allowNonStandardGrouping && numberString.hasNonStandardGrouping()) {
return doReport(expression, "The number contains a non standard grouping.")
}

if (numberString.contains(nonCompliantRegex)) {
report(
CodeSmell(
issue,
Entity.from(expression),
"This number should be separated by underscores in order to increase readability."
)
return doReport(
expression,
"This number should be separated by underscores in order to increase readability."
)
}
}
Expand Down Expand Up @@ -102,7 +111,14 @@ class UnderscoresInNumericLiterals(config: Config = Config.empty) : Rule(config)
.removeSuffix("f")
}

private fun doReport(expression: KtConstantExpression, message: String) {
report(CodeSmell(issue, Entity.from(expression), message))
}

private fun String.hasNonStandardGrouping(): Boolean = contains('_') && !matches(HAS_ONLY_STANDARD_GROUPING)

companion object {
private val HAS_ONLY_STANDARD_GROUPING = """\d{1,3}(?:_\d{3})*""".toRegex()
private const val HEX_PREFIX = "0x"
private const val BIN_PREFIX = "0b"
private const val SERIALIZABLE = "Serializable"
Expand Down
Expand Up @@ -9,6 +9,7 @@ import org.spekframework.spek2.style.specification.describe

private const val ACCEPTABLE_DECIMAL_LENGTH = "acceptableDecimalLength"
private const val ACCEPTABLE_LENGTH = "acceptableLength"
private const val ALLOW_NON_STANDARD_GROUPING = "allowNonStandardGrouping"

class UnderscoresInNumericLiteralsSpec : Spek({

Expand Down Expand Up @@ -160,20 +161,27 @@ class UnderscoresInNumericLiteralsSpec : Spek({
}
}

describe("an Int of 1000_00") {
val code = "val myInt = 1000_00"
describe("an Int of 1000_00_00") {
val code = "val myInt = 1000_00_00"

it("should not be reported by default") {
it("should be reported by default") {
val findings = UnderscoresInNumericLiterals().compileAndLint(code)
assertThat(findings).isEmpty()
assertThat(findings).isNotEmpty
}

it("should be reported if acceptableLength is 3") {
it("should still be reported even if acceptableLength is 99") {
val findings = UnderscoresInNumericLiterals(
TestConfig(mapOf(ACCEPTABLE_LENGTH to "3"))
TestConfig(mapOf(ACCEPTABLE_LENGTH to "99"))
).compileAndLint(code)
assertThat(findings).isNotEmpty
}

it("should not be reported if allowNonStandardGrouping is true") {
val findings = UnderscoresInNumericLiterals(
TestConfig(mapOf(ALLOW_NON_STANDARD_GROUPING to true))
).compileAndLint(code)
assertThat(findings).isEmpty()
}
}

describe("a binary Int of 0b1011") {
Expand Down

0 comments on commit 9cc086a

Please sign in to comment.