Repository: detekt/detekt (kotlin)

Issue:
UnusedPrivateClass Rule does not detect casts as usage
All described behavior below applies to the default detekt configuration (the one that is generated by the `detektGenerateConfig` gradle task)

## Expected Behavior
`UnusedPrivateClass` should only trigger if a private class is not used in any way that is relevant to the code

## Observed Behavior
If a private class is only used for a cast (using `as`), `UnusedPrivateClass` still triggers. If an explicit type annotation is used instead of the cast, `UnusedPrivateClass` does not trigger, even though the two cases are functionally the same.

## Steps to Reproduce
The following code will trigger `UnusedPrivateClass` at `1:1`:
```kotlin
private class Foo(val bar: String)

private val fooCasted = null as Foo?
// private val fooExplicitType: Foo? = null

fun use() {
    println(fooCasted)
    // println(fooExplicitType)
}

```
Commenting in `fooExplicitType` will remove the rule violation.

## Context
The case with casting `null` to a nullable type is crafted to demonstrate the most simple reproduction example. For me, the issue arised because I declared a `private external class` in Kotlin/JS and tried to cast the result of a `js("")` call to this class:

```kotlin
private external class Crypto {
    val subtle: SubtleCrypto
}
private val crypto = js("window ? (window.crypto ? window.crypto : window.msCrypto) : self.crypto") as Crypto
```
When an explicit type is used instead of the cast:
```kotlin
private val crypto: Crypto = js("window ? (window.crypto ? window.crypto : window.msCrypto) : self.crypto")
```
the detekt rule violation goes away but the IDE warns me "Implicit (unsafe) cast from dynamic to Crypto" and suggests adding an explicit cast (which then removes the need to explicitly annotate the type of `crypto`).

## Your Environment

* Version of detekt used: 1.23.5
* Version of Kotlin used: Kotlin/JVM 1.9.22
* Version of Gradle used: 8.6
* Gradle scan link: https://scans.gradle.com/s/xewkclerzfekg
* Operating System and version: Windows 11

Pull request:
Fixes #6982

Patch:
diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnusedPrivateClass.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnusedPrivateClass.kt
index 8dc4aba96078..5dc066af7d5d 100644
--- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnusedPrivateClass.kt
+++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnusedPrivateClass.kt
@@ -8,6 +8,7 @@ import io.gitlab.arturbosch.detekt.api.Entity
 import io.gitlab.arturbosch.detekt.api.Rule
 import org.jetbrains.kotlin.name.FqName
 import org.jetbrains.kotlin.psi.KtAnnotationEntry
+import org.jetbrains.kotlin.psi.KtBinaryExpressionWithTypeRHS
 import org.jetbrains.kotlin.psi.KtCallExpression
 import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
 import org.jetbrains.kotlin.psi.KtClass
@@ -17,6 +18,7 @@ import org.jetbrains.kotlin.psi.KtExpression
 import org.jetbrains.kotlin.psi.KtFile
 import org.jetbrains.kotlin.psi.KtFunctionType
 import org.jetbrains.kotlin.psi.KtImportDirective
+import org.jetbrains.kotlin.psi.KtIsExpression
 import org.jetbrains.kotlin.psi.KtNameReferenceExpression
 import org.jetbrains.kotlin.psi.KtNamedDeclaration
 import org.jetbrains.kotlin.psi.KtNamedFunction
@@ -143,6 +145,16 @@ class UnusedPrivateClass(config: Config) : Rule(
             super.visitProperty(property)
         }
 
+        override fun visitBinaryWithTypeRHSExpression(expression: KtBinaryExpressionWithTypeRHS) {
+            expression.right?.run { registerAccess(this) }
+            super.visitBinaryWithTypeRHSExpression(expression)
+        }
+
+        override fun visitIsExpression(expression: KtIsExpression) {
+            expression.typeReference?.run { registerAccess(this) }
+            super.visitIsExpression(expression)
+        }
+
         override fun visitCallExpression(expression: KtCallExpression) {
             expression.calleeExpression?.text?.run { namedClasses.add(this) }
             expression.typeArguments


Test patch:
diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnusedPrivateClassSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnusedPrivateClassSpec.kt
index c2c57a2f985b..89dbdba0e800 100644
--- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnusedPrivateClassSpec.kt
+++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnusedPrivateClassSpec.kt
@@ -307,6 +307,26 @@ class UnusedPrivateClassSpec {
 
             assertThat(findings).isEmpty()
         }
+
+        @Test
+        fun `should not report them if used for type conversion`() {
+            val code = """
+                private class Foo(val bar: String)
+                private fun f(a: Any) = a as Foo
+            """.trimIndent()
+            val findings = subject.compileAndLint(code)
+            assertThat(findings).isEmpty()
+        }
+
+        @Test
+        fun `should not report them if used for type checking`() {
+            val code = """
+                private class Foo(val bar: String)
+                private fun f(a: Any) = a is Foo
+            """.trimIndent()
+            val findings = subject.compileAndLint(code)
+            assertThat(findings).isEmpty()
+        }
     }
 
     @Nested