Repository: detekt/detekt (kotlin)

Issue:
MagicNumber suppression for ranges does not work for negative numbers
In #1368 a configuration was added to allow suppressing MagicNumber for ranges

## Expected Behavior
That works fine for positive values but does not work for negative values. It should not report Magic Number for ranges that involve negative numbers

## Observed Behavior
```
val doesNotReportMagicNumber = 5..10
val doesReportMagicNumber = -15..-10
```

## Context
Requires me to suppress MagicNumber

Pull request description:
Fix MagicNumber rule to support suppression of negative constants in ranges (#8338)

* Fix MagicNumber rule to correctly suppress negative values in ranges

- Enhance `isPartOfRange()` to detect negative constants wrapped in `KtPrefixExpression`
- Add support for cases like `-15..-10`, `-20 downTo -25`, etc.

Fixes #8338

* test: add test case for negative range expression

Patch:
diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/MagicNumber.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/MagicNumber.kt
index c3c8a4e7471c..0abdf2ad8353 100644
--- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/MagicNumber.kt
+++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/MagicNumber.kt
@@ -194,15 +194,27 @@ class MagicNumber(config: Config) : Rule(
                 else -> false
             }
 
+    @Suppress("ReturnCount")
     private fun KtConstantExpression.isPartOfRange(): Boolean {
         val theParent = parent
-        val rangeOperators = setOf("downTo", "until", "step")
-        return if (theParent is KtBinaryExpression) {
-            theParent.operationToken == KtTokens.RANGE ||
-                theParent.operationReference.getReferencedName() in rangeOperators
-        } else {
-            false
+
+        // Case 1: Direct range expression
+        if (theParent is KtBinaryExpression) {
+            return theParent.operationToken == KtTokens.RANGE ||
+                theParent.operationReference.getReferencedName() in RANGE_OPERATORS
+        }
+
+        // Case 2: Negative number part of a range
+        if (theParent is KtPrefixExpression) {
+            val opRef = theParent.firstChild as? KtOperationReferenceExpression
+            if (opRef?.operationSignTokenType != KtTokens.MINUS) return false
+
+            val grandParent = theParent.parent as? KtBinaryExpression ?: return false
+            return grandParent.operationToken == KtTokens.RANGE ||
+                grandParent.operationReference.getReferencedName() in RANGE_OPERATORS
         }
+
+        return false
     }
 
     private fun KtConstantExpression.isSubjectOfExtensionFunction(): Boolean = parent is KtDotQualifiedExpression
@@ -232,5 +244,6 @@ class MagicNumber(config: Config) : Rule(
     companion object {
         private const val HEX_RADIX = 16
         private const val BINARY_RADIX = 2
+        private val RANGE_OPERATORS = setOf("downTo", "until", "step")
     }
 }


Test patch:
diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/MagicNumberSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/MagicNumberSpec.kt
index 0d513081501b..ebef6628fa32 100644
--- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/MagicNumberSpec.kt
+++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/MagicNumberSpec.kt
@@ -879,9 +879,11 @@ class MagicNumberSpec {
         @Suppress("UnusedPrivateFunction")
         private fun cases() = listOf(
             "val range = 1..27",
+            "val range = -1..-27",
             "val range = (1..27)",
             "val range = 27 downTo 1",
             "val range = 1 until 27 step 1",
+            "val range = -1 until -27 step 1",
             "val inRange = 1 in 1..27",
             "val inRange = (1 in 27 downTo 0 step 1)",
             "val inRange = (1..27 step 1).last"