fix getAllInvolvedRawTypes() recursing infinitely#1276
Conversation
getAllInvolvedRawTypes() recursing infinitely
archunit/src/test/java/com/tngtech/archunit/core/domain/JavaClassTest.java
Show resolved
Hide resolved
archunit/src/test/java/com/tngtech/archunit/core/domain/JavaTypeTraversalTest.java
Show resolved
Hide resolved
| } | ||
|
|
||
| default Result visitArrayType(JavaClass type) { | ||
| return CONTINUE; |
There was a problem hiding this comment.
I wonder whether the default implementation should be
| return CONTINUE; | |
| return visitClass(type); |
There was a problem hiding this comment.
Hmm, but that would seem strange to me. Because the contract should be that visitClass is never called with an array type, right? (Otherwise, why even have visitClass and visitArrayType separately in the visitor? If you can't rely on isArray() to return false whenever visitClass is called?)
The traversal behavior will just traverse down to the component type by default, right? So implementing CONTINUE will finally call visitClass with the base component type, and that seems a reasonable behavior if I e.g. only implement visitClass but not visitArrayType 🤷
There was a problem hiding this comment.
Because the contract should be that visitClass is never called with an array type, right?
Let's document the contract. 😉
I was checking whether we could make more use of the new SignatureVisitor internally, and found JavaClassDependencies.dependenciesOfType(JavaType). SignatureVisitor won't be a nice solution there, but I realized that there might be more use cases where one previously had
if (javaType instanceof JavaClass) {
// do something with `(JavaClass) javaType`
} else if (javaType instanceof JavaParameterizedType) {
// do something with `(JavaParameterizedType) javaType`
} else if (javaType instanceof JavaWildcardType) {
// do something with `(JavaWildcardType) javaType`
}
and it might not immediately obvious that refactoring this to the SignatureVisitor pattern requires overwriting both visitClass and visitArrayType for the do something with `(JavaClass) javaType` part.
So yes, the question is actually whether SignatureVisitor should have individual methods for array and non-array JavaClasses. Having only one might be more consistent (why just distinguish arrays, and not, e.g. interfaces?) and therefore less confusing.
There was a problem hiding this comment.
Hmm, I see your point, I just figured it would be kind of nice that the default behavior would traverse up the component types and automatically only give you the element type (as I found the JLS calls our "base component type" 🤪). But maybe that in the end creates a more confusing API instead of helping 🤔
There was a problem hiding this comment.
One argument for the separate visitArrayType method might be that it's somewhat consistent with visitGenericArrayType 🤔 But I guess there the class hierarchy really is also distinct 🤷
Guess I'm gonna remove the array type method then...
75a50b5 to
07bf2cd
Compare
After switching my job the old email address is now dead. Signed-off-by: Peter Gafert <peter.gafert@archunit.org>
The Reflection API considers the visibility of an array type class object to be equal to the visibility of the element type class object. ArchUnit so far considered every array type class object as public. Since we usually strive for being as similar to the Reflection API as possible, we fix this now by deriving the visibility from the element type as well. Signed-off-by: Peter Gafert <peter.gafert@archunit.org>
At the moment, handling `JavaType`s is not convenient, because there are many different subtypes. And effectively the only way to handle those is a chain of `instanceof` checks with individual handling for each type. We can make this more convenient by adding a visitor pattern API that allows to simply define what to do on every partial type encountered in the signature (i.e. what to do when a parameterized type is encountered, what to do when each actual type argument of the parameterized type is encountered, and so on). As a benefit, we can use this API in the next step to fix the infinite recursion problem we have for the `getAllRawTypes()` method at the moment. Because, there we haven't handled the case where type variables are defined recursively. Adding this visitor pattern API we can solve this problem in a generic way once at the infrastructure level, i.e. implement the traversal correctly once and utilize it in more specific use cases. Signed-off-by: Peter Gafert <peter.gafert@archunit.org>
For recursive type parameter definitions like `class Example<T extends Comparable<T>>` the method `JavaClass.getAllInvolvedRawTypes()` causes a `StackOverflowError` at the moment. This is because we have a simple recursion without any short circuit condition if we encounter a type we've already seen before. We can utilize `JavaType.traverseSignature(..)` to solve this, because there the problem is already solved in the visitor pattern implementation. Signed-off-by: Peter Gafert <peter.gafert@archunit.org>
07bf2cd to
cc5fec5
Compare
This fixes the problem of recursing infinitely when calling
JavaType.getAllInvolvedRawTypes()for a recursive type parameter declaration (e.g.
class Example<T extends Example<T>>).It also adds a new public API to conveniently traverse
JavaTypesignatureswithout the need to add chains of
instanceofchecks and manually handle how to traverse further.Resolves: #1237