-
-
Notifications
You must be signed in to change notification settings - Fork 357
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
review1: feature: CtTypeParameter#isSubtypeOf #1218
review1: feature: CtTypeParameter#isSubtypeOf #1218
Conversation
a59ce12
to
6789ac9
Compare
I will use these prefixes:
|
4f8d34a
to
34b1226
Compare
Revapi Analysis resultsOld API: fr.inria.gforge.spoon:spoon-core:jar:5.6.0-20170313.234505-78 New API: fr.inria.gforge.spoon:spoon-core:jar:5.6.0-SNAPSHOT Detected changes: 4. Change 1
Change 2
Change 3
Change 4
|
34b1226
to
23ace58
Compare
a22c4d4
to
9bbc8a8
Compare
I updated first comment of this PR to better fit current content. I suggest following steps:
Tomorrow I will improve code to pass all other Spoon tests. |
9bbc8a8
to
c8c1efc
Compare
//model
<T extends spoon.reflect.code.CtStatement> T getLastStatement();
//test code
CtTypeParameterReference paramT = ...//'T' from example above
assertTrue(paramT.isSubtypeOf(factory.Code().createCtTypeReference(CtElement.class))); Do you agree that But this method |
I see now that problem is not in |
In legacy spoon code there are problems with CtExecutableReference, which WDYT? |
7bf2049
to
83680e3
Compare
Revapi Analysis resultsOld API: fr.inria.gforge.spoon:spoon-core:jar:5.7.0-20170405.224526-35 New API: fr.inria.gforge.spoon:spoon-core:jar:5.7.0-SNAPSHOT Detected changes: 1. Change 1
|
Tests finally passed! Jupeee .. now "just" little step ... to pass Martin ;) |
Thanks Pavel. The first comment is that 22 files changed, it's a lot. I'm not sure they're baby Prs and that they are all necessary here. Maybe we'll have to break this into smaller PRs. I first concentrate on "adaptType':
|
Revapi Analysis resultsOld API: fr.inria.gforge.spoon:spoon-core:jar:5.7.0-20170407.224529-37 New API: fr.inria.gforge.spoon:spoon-core:jar:5.7.0-SNAPSHOT Detected changes: 1. Change 1
|
You are right, they are not baby steps. I am aware of that, so let's discuss what how to break it into smaller useful parts.
good choice. The adaptType of type parameter declared in scope of CtType is simpler.
yes, they are OK, Thanks for that!
why? Java lang specification uses term "adapting"
so why do you look for different name? I think that "adapt" represents that process better then "resolve", because there is source context X and target contexts Y and that process is adapting the type from one context into another context. There can be more target contexts. I think "resolving" is more related to founding of ONE solution, and it is not the case of that adapting process, where you can adapt into more then one target contexts. But you are the master of spoon, so if you still like "resolve" more, I can rename it and live with that. No problem :-) |
assertEquals("spoon.test.generics.testclasses.Mole", typingContextOfDisgust.adaptType(ctClassLunch_A).getQualifiedName()); | ||
|
||
// I don't understand the goal and utility of this one | ||
assertEquals("java.lang.Double", typingContextOfDisgust.getEnclosingGenericTypeAdapter().adaptType(ctClassLunch_A).getQualifiedName()); |
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.
This shows that java class hierarchies of inner classes are different on each inner class level. In this test model there are two INDEPENDENT class hierarchies:
First is hierachy of top level classes:
CelebrationLunch<K,L,M> extends Lunch<M,K>
and second there is hierarchy of inner class
WeddingLunch<X> extends CelebrationLunch<Tacos, Paella, X> extends Lunch<X,Tacos>
Because in this wild example model, both ( 1. top level class CelebrationLunch
and 2. inner class WeddingLunch
) extends from Lunch<A,B>
, there is necessary to be able to adapt type parameters A and B in both contexts.
So this test line shows how to move from context of inner class WeddingLunch
to the parent context of top level class CelebrationLunch
and to adapt type parameters there.
May be it is confusing in this example that
WeddingLunch
is inner class ofCelebrationLunch
WeddingLunch
extendsCelebrationLunch
too
ThetypingContextOfDisgust.getEnclosingGenericTypeAdapter()
is move from inner class to declaring class. It is NOT move from sub type to super type.
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.
It is test of contract of getEnclosingGenericTypeAdapter
method
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.
To simplify, can we create instead an adapter of the super class such as new CtSuperTypeHierarchy(Lunch).adaptType(ctClassLunch_A).getQualifiedName())?
Can we remove this method?
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.
client can of course create new CtSuperTypeHierarchy using correct reference or type and then s/he gets the same result.
Can we remove this method?
There is hierarchy of GenericTypeAdapter (GTA) instances, like GTA of Method -> GTA of Type -> GTA of enclosing type (recursivelly up to top level type). It is necessary because java allows such complex structures, so I think it is correct that GenericTypeAdapter has method getEnclosingGenericTypeAdapter, which gives access to parent adapter, which is already there and whose construction may need some computation ... so it is inefficient to create GenericTypeAdapter again if the needed one is already there.
If you want to remove it just because clients will not understand it, then note that all implementations of GenericTypeAdapter may be understood as internal helper classes. We can create nice facade methods in existing spoon model elements, so the normal client's does not need to learn/see that.
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 think it is correct that GenericTypeAdapter has method getEnclosingGenericTypeAdapter, which gives access to parent adapter,
What does "parent" mean in this context? the AST parent? or the super class? or something else?
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 am sorry, "parent" was not the best name. I mean "declaring type". See example:
class A1<K1> {
class B1<L1> {
<M1> M1 method(K1 k, L1 l){}
}
}
class A2<K2> extends A1<K2> {
class B2<L2> extends B1<L2> {
<M2> M2 method(K2 k, L2 l){}
}
}
In the model above the
//adapter for method `A2.B2#method`
CtMethodSuperTypeHierarchy methodAdapter = new CtMethodSuperTypeHierarchy(...method("A2.B2#method"));
//the enclosing adapter of `methodAdapter` is adapter of inner class `A2.B2`
CtSuperTypeHierarchy b2Adapter = methodAdapter.getEnclosingGenericTypeAdapter();
//the enclosing adapter of `b2Adapter ` is adapter of top level class `A2`
CtSuperTypeHierarchy a2Adapter = b2Adapter.getEnclosingGenericTypeAdapter();
//the enclosing adapter of `a2Adapter ` is null, because A2 is top level class
assertNull(a2Adapter.getEnclosingGenericTypeAdapter())
Note that methodAdapter
internally needs adapters of all enclosing declaring types recursively, because that method can contain typed parameters of
- method itself - there is type parameter M2
- declaring type B2 - there is type parameter L2
- declaring type A2 - there is type parameter K2
//adapt A to scope of enclosing class of CelebrationLunch<K,L,M>.WddingLunch<X>, which is CelebrationLunch<K,L,M> | ||
adapted = sthOftWeddingLunch_X.getEnclosingGenericTypeAdapter().adaptType(ctClassLunch_A); | ||
assertEquals("M", adapted.getQualifiedName()); | ||
assertEquals("M", sthOftWeddingLunch_X.getEnclosingGenericTypeAdapter().adaptType(ctClassLunch_A).getQualifiedName()); |
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.
Same like above. In this simplified model
class Lunch<A,B> {}
class CelebrationLunch<K,L,M> extends Lunch<M,K> {}
it shows that type parameter A
of Lunch
is adapted to M
of CelebrationLunch
.
//adapt A to scope of CelebrationLunch<Integer,Long,Double>.WeddingLunch<Mole> | ||
|
||
// in disgust, the A of Lunch is bound to "Mole" | ||
assertEquals("spoon.test.generics.testclasses.Mole", typingContextOfDisgust.adaptType(ctClassLunch_A).getQualifiedName()); |
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 have a doubt here, the "Lunch.A" of disgust should be Double?
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.
In the hierarchy of WeddingLunch<Mole> the Lunch.A is Mole
In the hierarchy of CelebrationLunch<Integer,Long,Double> the Lunch.A is Double
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.
OK.
OK |
The java language model is quite complicated if you start to combine inner classes, type parameters, generic methods and legacy methods ... Even after 17 years of daily java programming I had to learn a lot to understand how it works, to be able to implement this PR. It would be bad if spoon client's would need to understand it too, to be able to use that. Therefore I think that we should create easy and understandable facade methods in the spoon model, which will hide that complexity.
And it question whether somebody needs these methods:
But it does not mean that we should make GenericTypeAdapter and it's implementations private. We can just put them into some special spoon package so they are less visible for normal clients, but can be still directly used by experienced clients and by spoon core itself. |
I agree with the core semantic and API design of Let's start with the naming issue, I propose the following: |
GenericTypeAdapter, ClassTypingContext and MethodTypingContext moved to CtFormalTypeDeclarer#getGenericTypeAdapter removed. Created What next? ;-) |
0b850c0
to
dc9fdf2
Compare
Revapi Analysis resultsOld API: fr.inria.gforge.spoon:spoon-core:jar:5.7.0-20170415.141103-48 New API: fr.inria.gforge.spoon:spoon-core:jar:5.7.0-SNAPSHOT Detected changes: 1. Change 1
|
dc9fdf2
to
bc38eb5
Compare
bc38eb5
to
2411e3f
Compare
Revapi Analysis resultsOld API: fr.inria.gforge.spoon:spoon-core:jar:5.7.0-20170415.224541-49 New API: fr.inria.gforge.spoon:spoon-core:jar:5.7.0-SNAPSHOT Detected changes: 1. Change 1
|
|
||
CtType<?> oCtType = xCtType.getFactory().Type().get("spoon.test.ctType.testclasses.O"); |
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.
never delete existing test code in a feature PR.
when reviewing, if no test code is deleted, with only addition, it's easy to be sure that there is no regression.
this is particularly true for large PRs such as this one.
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.
Github comparer confused us. In the better comparer there is visible that red line 91 is not deleted by is located on green line 105, etc.
So I did not deleted test code ;)
let's close this old PR. I'm ready to merge so as to move on. OK to merge? |
Yes, merge is OK for me. |
Implements #1160.
The implementation of isSubtypeOf on type parameters is quite complex task, which needs all the basic steps below, which fits to commits of this PR.
There are:
BS1) new interface GenericTypeAdapter
BS2) CtSuperTypeHierarchy - adapts generic types to Type scope
BS3) CtMethodSuperTypeHierarchy - adapts generic types to Method/Constructor scope
BS4) CtFormalTypeDeclarer#getGenericTypeAdapter(), which creates instance of CtSuperTypeHierarchy or CtMethodSuperTypeHierarchy for the scope of this declarer
BS5) CtTypeParameterImpl#isSubtypeOf - finally the implementation of isSubtypeOf - the target of this PR
BS6) simpler and more correct implementation of
CtTypeParameterImpl#isSubtypeOf(CtTypeReference)
, based on new algorithms