-
-
Notifications
You must be signed in to change notification settings - Fork 360
WIP: Refactor return type of metamodel methods #1852
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
Conversation
@@ -165,20 +164,20 @@ public void removeArgument(CtExpression<?> argument) { | |||
|
|||
@Override | |||
@DerivedProperty | |||
public <T extends CtActualTypeContainer> T setActualTypeArguments(List<? extends CtTypeReference<?>> actualTypeArguments) { | |||
public CtConstructorCall setActualTypeArguments(List<? extends CtTypeReference<?>> actualTypeArguments) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I ssuggest to use CtConstructorCallImpl, here and in all Ct*Impl implementations
I think this PR is on good way |
It looks like this PR would break a lot of APIs: wouldn't it worth it to put it in the next release, as we already break lot of things? If I understood well the changes are not that complicated, once we got a proper list of method return type to change, am I right? Is it possible to compute that list ? |
I'm trying to understand how to help on this PR but I still have questions regarding how to generalize it. So I obtained:
CtTypedElement setType(CtTypeReference<T> type);
@Override
@UnsettableProperty
CtConstructor setType(CtTypeReference<T> type); But now I got a compilation error because CtNewArray<T[]> array = factory.Core().<T[]>createNewArray().setType(arrayReference); So it seems to me that unless we override the methods in each interface with the proper return type, we won't obtain a fluent API. What am I missing here? |
I have not analyzed it deep. Just my experience/feeling tells me that you are right. Yes it needs rewrite of all return types of all/most Spoon API methods. We get rid of useless generics in some places. |
Then I propose that we write first a contract to check that all interfaces are overriding their parents interfaces methods if needed (e.g. all set/add methods which return the same type as the current interface), and to automatically generate the proper signature. Then we should be able to to maintain the whole API properly when adding new methods somewhere. WDYT? |
would work, especially with the sniper mode
|
Still 159 methods to go (see log). There is really a tradeoff here between supporting Kotlin and maintaining a lot of methods with the proper return type (even if those methods can be generated). Well, we can now use the Sniper mode for this. |
API changes: 18 (Detected by Revapi) Old API: fr.inria.gforge.spoon:spoon-core:jar:7.1.0-20180918.224838-177 / New API: fr.inria.gforge.spoon:spoon-core:jar:7.1.0-SNAPSHOT
|
This change makes sense even for Java. Current return type is wrong, so I like this PR even if I do not use Kotlin.
So who has courage to try that? ;-) |
I have refactored spoon model (by sniper), so now it has correct return types, but now there is visible some incompatibility ... e.g. code like this: spoon.getFactory().Code().createConstructorCall(spoon.getFactory().Core().createTypeReference().setSimpleName("Bar")); is not compilable now, because <T extends CtNamedElement> T setSimpleName(String); then it is now CtNamedElement setSimpleName(String); therefore compilation of createConstructorCall fails on unexpected type CtNamedElement. Solutions: I vote for A) WDYT? |
I'm not sure those cases are so rare, and even so it would break client API, and we want to avoid that as much as possible. I'm in favor of B: can't we use the sniper to automatically add the method to inherited classes? |
I like this idea too, but it produces many methods in leaf interfaces, which are normally inherited, but this time they will be directly declared in sources. Why we need these methods? Because we want to use fluent API during creation of spoon model. But that solution is only partial. I have feeling like it cannot be done well this way. WDYT? Should we really dirty current Spoon API by helper methods or we keep API simple, but little bit incompatible for some rare cases? |
Note: even if we add nearly each inherited method into each leaf interface, the current (ugly?) code like this: return factory.Core().<A, T>createAssignment().<CtAssignment<A, T>>setAssignment(expression).setAssigned(vaccess); will be compilable - OK, ... return factory.Core().<A, T>createAssignment().setAssignment(expression).setAssigned(vaccess); Without helper methods in can be written like this return ((CtAssignment<A, T>>) factory.Core().<A, T>createAssignment().setAssignment(expression)).setAssigned(vaccess); or not fluent but readable CtAssignment<A, T>> assignment = factory.Core().createAssignment();
assignment.setAssignment(expression);
assignment.setAssigned(vaccess);
return assignment; The nicest would be of course: return factory.Builder().assignment().assignment(expression).assigned(vaccess).build(); |
@monperrus @surli I need your feedback here to be able to continue with #2615 |
Hi @pvojtechovsky, thanks for working on this one! I'm still standing for solution B:
|
@surli thanks for your feedback I personally prefer client incompatibility over duplicated methods in API, but I can live with both solutions. |
ping @monperrus I would like to hear your opinion. |
I don't have a strong opinion for now. On the one hand, we want backward compatibility. On the other hand, to my knowledge, very few clients use the fluent API, partly because it only works partially (see Pavel's example in issue). Note that we don't have to fix/generate all methods at once, we can do baby PRs one by one. For instance, we can leave the case of setSimpleName for the end. |
fix #1846
WIP
What do you think?