Repository: detekt/detekt (kotlin)

Issue:
UnnecessaryAbstractClass is not targeting class name
## Expected Behavior of the rule

UnnecessaryAbstractClass should target the name of the class

## Context
I just updated Detekt 1.19.0 to 1.20.0 and got this failure:
https://github.com/TWiStErRob/net.twisterrob.cinema/security/code-scanning/227

as you can see it's showing the `/**` as the "violation", but the real code is:
```
	/**
	 * Internal dependencies in this route group.
	 */
	@Module
	abstract class BackendModule {
```
it should be targeting this part:
```
	abstract class BackendModule {
                       -------------
```

![image](https://user-images.githubusercontent.com/2906988/164910247-930c8fdf-1dcd-40ad-9ae9-bd8268548bb9.png)

Pull request:
Fixes #4749

Patch:
diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnnecessaryAbstractClass.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnnecessaryAbstractClass.kt
index c7808c532e29..5d4e526a5497 100644
--- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnnecessaryAbstractClass.kt
+++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnnecessaryAbstractClass.kt
@@ -79,27 +79,29 @@ class UnnecessaryAbstractClass(config: Config = Config.empty) : Rule(config) {
         super.visitClass(klass)
     }
 
+    @Suppress("ComplexMethod")
     private fun KtClass.check() {
+        val nameIdentifier = this.nameIdentifier ?: return
         if (annotationExcluder.shouldExclude(annotationEntries) || isInterface() || !isAbstract()) return
         val members = members()
         when {
             members.isNotEmpty() -> {
                 val (abstractMembers, concreteMembers) = members.partition { it.isAbstract() }
                 if (abstractMembers.isEmpty() && !hasInheritedMember(true)) {
-                    report(CodeSmell(issue, Entity.from(this), noAbstractMember))
+                    report(CodeSmell(issue, Entity.from(nameIdentifier), noAbstractMember))
                     return
                 }
                 if (abstractMembers.any { it.isInternal() || it.isProtected() } || hasConstructorParameter()) {
                     return
                 }
                 if (concreteMembers.isEmpty() && !hasInheritedMember(false)) {
-                    report(CodeSmell(issue, Entity.from(this), noConcreteMember))
+                    report(CodeSmell(issue, Entity.from(nameIdentifier), noConcreteMember))
                 }
             }
             !hasConstructorParameter() ->
-                report(CodeSmell(issue, Entity.from(this), noConcreteMember))
+                report(CodeSmell(issue, Entity.from(nameIdentifier), noConcreteMember))
             else ->
-                report(CodeSmell(issue, Entity.from(this), noAbstractMember))
+                report(CodeSmell(issue, Entity.from(nameIdentifier), noAbstractMember))
         }
     }
 


Test patch:
diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnnecessaryAbstractClassSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnnecessaryAbstractClassSpec.kt
index 360405bb2749..ba65e69f96ce 100644
--- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnnecessaryAbstractClassSpec.kt
+++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnnecessaryAbstractClassSpec.kt
@@ -3,8 +3,8 @@ package io.gitlab.arturbosch.detekt.rules.style
 import io.gitlab.arturbosch.detekt.api.Finding
 import io.gitlab.arturbosch.detekt.rules.KotlinCoreEnvironmentTest
 import io.gitlab.arturbosch.detekt.test.TestConfig
+import io.gitlab.arturbosch.detekt.test.assertThat
 import io.gitlab.arturbosch.detekt.test.compileAndLintWithContext
-import org.assertj.core.api.Assertions.assertThat
 import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
 import org.junit.jupiter.api.Nested
 import org.junit.jupiter.api.Test
@@ -31,6 +31,7 @@ class UnnecessaryAbstractClassSpec(val env: KotlinCoreEnvironment) {
             """
             val findings = subject.compileAndLintWithContext(env, code)
             assertFindingMessage(findings, message)
+            assertThat(findings).hasSourceLocation(1, 16)
         }
 
         @Nested
@@ -40,6 +41,7 @@ class UnnecessaryAbstractClassSpec(val env: KotlinCoreEnvironment) {
                 val code = "abstract class A"
                 val findings = subject.compileAndLintWithContext(env, code)
                 assertFindingMessage(findings, message)
+                assertThat(findings).hasSourceLocation(1, 16)
             }
 
             @Test
@@ -173,6 +175,7 @@ class UnnecessaryAbstractClassSpec(val env: KotlinCoreEnvironment) {
             val code = "abstract class A(val i: Int)"
             val findings = subject.compileAndLintWithContext(env, code)
             assertFindingMessage(findings, message)
+            assertThat(findings).hasSourceLocation(1, 16)
         }
 
         @Test
@@ -182,6 +185,14 @@ class UnnecessaryAbstractClassSpec(val env: KotlinCoreEnvironment) {
             assertFindingMessage(findings, message)
         }
 
+        @Test
+        fun `reports no abstract members in an abstract class with just a constructor parameter`() {
+            val code = "abstract class A(i: Int)"
+            val findings = subject.compileAndLintWithContext(env, code)
+            assertFindingMessage(findings, message)
+            assertThat(findings).hasSourceLocation(1, 16)
+        }
+
         @Test
         fun `reports an abstract class with no abstract member derived from a class with abstract members`() {
             val code = """
@@ -282,5 +293,5 @@ class UnnecessaryAbstractClassSpec(val env: KotlinCoreEnvironment) {
 
 private fun assertFindingMessage(findings: List<Finding>, message: String) {
     assertThat(findings).hasSize(1)
-    assertThat(findings.first().message).isEqualTo(message)
+    assertThat(findings.first()).hasMessage(message)
 }