It’s a misuse because (if you really are only using it for code reuse) it’s creating an essential relationship between different objects that is actually incidental.
A list containing two different subclasses of your avoiding-copy-paste base class is now most likely a logical error.
You could just as well argue that there should never be any code reuse ever, code reuse always couples objects, code reuse should never be done if the two different parts doesn't have the same purpose. So the same rules for all code reuse also applies for inheritance, if you follow them then there are no problems using inheritance. If you have a set of data kinds which all needs to implement the same fields that has the same purposes then having a list of that parent class doesn't lead to bugs.
I don’t think it’s true that reuse means tight coupling. With composition you can swap out the composed object at runtime and as far as compile time coupling is concerned you can embed an interface to decouple the outer object from the inner object.
All code reuse creates coupling on some level. Creating more abstractions in between reduces coupling but creates code bloat. There is a trade off.
For example, if you call the same function from many locations those locations are now coupled since if you change the function you change all of those locations behaviour. Many times that is desirable, in which case it is good coupling. The exact same rule applies to inheritance.
Can you elaborate a bit? How is there coupling between a thing which uses an interface and a thing which implements the interface of the two don’t know about each other? Especially if the interface is implicit à la Go interfaces or structural subtyping?
Every class implementing the interface needs to follow the same interface contract, that is coupling. If you want to change the interface contract for one of them you have to go and change it for all of them or you have buggy interfaces. Every time you create an interface you create a contract for it, if you have the same discipline with inheritance then inheritance isn't an issue.
The only problem with inheritance is that people can use it for classes that weren't written to be base classes, and therefore a ton of bad programmers use it for code reuse without thinking about the contract it is supposed to implement at all. With that in mind, allowing inheritance only for abstract classes isn't an issue at all.
There is no state to track. If there is, 99% composition has worked out way better for me in the long run since I don't have this really strong coupling between two objects.
So you don't have a need for code reuse then since you don't have many types with the same base structure, just say that instead of saying that it is bad.
There are often more straightforward ways to reuse code than to use inheritance. ~Two~ three advantages of composition for code reuse:
1. Composition is often more explicit than inheritance. Inheritance is overly magic.
2. Composition avoids incidental coupling of methods. With inheritance this is unavoidable.
3. Composition is more difficult to misuse. Both composition and inheritance have their purposes. In the wild, I’ve seen inheritance misapplied much more often than I have seen with composition.
A list containing two different subclasses of your avoiding-copy-paste base class is now most likely a logical error.