detekt: UnnecessaryAbstractClass false positive

Expected Behavior

Don’t report following code:

abstract class Fake : Base() // UnnecessaryAbstractClass
abstract class Base {
	open val order: Int get() = 0
	abstract fun String.foo()
}

Observed Behavior

“An abstract class without a concrete member can be refactored to an interface.”

No, it can’t:

  • Fake interface couldn’t extend a class.
  • it has concrete members inherited from Base
  • I get a detection even if I add an actual class using the “abstractness”:
class Foo : Fake() {
	override fun String.foo() = TODO("not implemented")
}

Note: String. might be a red herring, you can ignore that part, just wanted to report what I had. More minimal repro might exist.

Steps to Reproduce

Just copy above code into a test or code checked by Detekt 1.20.0.

Context

Real context is creating fakes for a test: image

The abstract method is never invoked in the test, so there’s no need to implement it, hence using abstract class. The class must be unique because it’s ::class object is used.

Your Environment

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 6
  • Comments: 20 (16 by maintainers)

Commits related to this issue

Most upvoted comments

Ask yourself if there’s a possibility for the case you’re flagging. Is it possible to write code that passes/fails the rule? And are both of them valid engineering choices? (i.e. due to decisions on preferences or restricted by constraints)

I’m fine with or without configuration. I suppose that in that case without is better because it keeps the api cleaner but that’s your call. I think that no one here have a hard opinion about this topic.

Go ahead

I think the rule of thumb is because Interface can't extend abstract class. I agree that we should at least provide a configuration to this rule.