-
-
Notifications
You must be signed in to change notification settings - Fork 352
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
bug: ParentNotInitializedException in AbstractTypingContext#adaptType() #3734
Comments
Actually, looking through the CtElementImpl code I've found a bunch of problems. The first one, which is related to the issue, is that if an element has Then things get more interesting about the two getParent overloads. Those overloads throw an exception if current element's parent is null (to match the main no-parameter overload; exception is thrown by the main overload), or return null when no parents match the predicate. But the thing is that one of the overloads doesn't behave like this: spoon/src/main/java/spoon/support/reflect/declaration/CtElementImpl.java Lines 384 to 392 in 3435fac
// yikes
CtTypeReference<?> object = factory.Type().OBJECT;
object.getParent(CtClass.java); // null
object.getParent(new TypeFilter<>(CtClass.java)); // exception The solution: fix the "Class" overload so both overloads handle null-parent the same way as the main method. The javadoc states that the "Filter" overload can return the same object (which doesn't make sense for me and probably for a lot of other people, it's a getParent method), but it doesn't state the same for the "Class" overload. That's just confusing: spoon/src/main/java/spoon/reflect/declaration/CtElement.java Lines 250 to 259 in 3435fac
The good news: neither implementation does this! (and it should be that way) spoon/src/main/java/spoon/support/reflect/declaration/CtElementImpl.java Lines 396 to 414 in 3435fac
The solution: fix the javadoc. And that's not all. I just... what? We failed casting spoon/src/main/java/spoon/support/reflect/declaration/CtElementImpl.java Lines 399 to 407 in 3435fac
The solution: remove this nonsense. I can make a pull request for the small stuff (the overload madness) if my solutions seem plausible. But "the main issue" needs more discussion. Maybe "ParentNotInitializedException" shouldn't even be used at all. |
Nice finds on those issues. Here are my two cents.
Since we have inconsistent behavior here, this might be the time to introduce a "null object" for parent (in the same spirit as #3705. To me, one of the largest usability issues of Spoon is the proliferation of methods that sometimes return an object and sometimes null. Calling code "done right" becomes cluttered with null checks.
I agree, the documented (but not actual) behavior is very counter-intuitive.
This is not what it looks like. When casting to a type parameter (which you really shouldn't do), you actually in practice cast to the erasure of that type parameter. In the case of The |
I don't think that that's a good idea. I dislike null-objects in general because in most cases those prevent "the scary NPE" but hide invalid logic which in my opinion is a lot worse. Imagine a "null" bank account - it allows you to send money into nothing instead of just failing a transaction if there is no "invalid destination" check. These objects, of course, have their own niche, but I don't think that's the right situation to use them. Here's why:
As for an alternative solution - we can add a flag
Maybe it's time to introduce JetBrains' NonNull/Nullable annotations (or some alternatives I'm not aware of)? Those help a lot with eliminating redundant checks and adding necessary ones. That's a lot of work across the library, but gradually adding those here and there should be fine and will prevent more and more problems with time while improving end user experience (as the users should get these annotations as well because they have "class" retention).
Ugh, what a mess generics are. Sometimes I wish I wasn't a Java developer. |
Just based on a hunch, I don't think you'd often need to check if it's But I don't disagree that it could cause other problems. For example, it only shifts the problem of the return value of
Do you mean that
This is a non-breaking improvement so I see no reason why not to do this. There are many variations of those annotations, and we should then pick the annotations that are most universally supported (I have no idea of which ones are).
Indeed, the code is very hard to understand and should be rewritten. |
Actually, thinking a bit more about it, using annotations to denote which methods may or may not return |
Yeah, breaking the API isn't good. And optionals are usually a hassle to use anyway. I'm also warming up a bit to the idea of using null-objects here. Perhaps they could be good enough if combined with the separate "isParentInitialized" flag (or just treating
I'll try fixing the getParents overloads in the meantime. |
This line throws an exception when the
result
is a CtTypeReference without a parent, but with type parameters. And "standard" types from aTypeFactory
don't have parents.It's also kinda connected to #3733:
java.lang.Object
gets corrupted with type parametersgetBound()
returns the corrupted type reference forjava.lang.Object
hereadaptType()
because it doesn't have a parent as a "standard" type in aTypeFactory
I haven't managed to make a minimal reproducible sample and can't share the main code, sorry.
The text was updated successfully, but these errors were encountered: