-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Fix bug when using javapoet with Eclipse compiler #303
Conversation
For the following code: public interface Interface1 {} public interface Interface 2 {} public T myMethod(T input) {} a code generator (javax.annotation.processing.Processor) in eclipse will have a TypeVariable for the return type T instead of a DeclaredType. This results in a stack overflow exception, because the return value of Collections.singletonList(upperBound) ends up returning a list containing, in essence, the input parameter.
Care to add a test case? |
Also, do you know about force pushing? You could have corrected your first PR. |
Fixes TypesTest#getTypeVariableTypeMirror
swankjesse: unfortunately I'm not sure a test case can be added; the problem is how the internal reflective structure of eclipse vs javac (which is why I broke the test). In eclipse, "T extends A & B" is represented as a TypeKind.TYPEVAR with multiple upper bound elements (which actually seems like the more correct way to do this). In javac, "T extends A & B" is represented as a TypeKind.DECLARED. Interestingly, it seems to represent "T extends A" as a TypeKind.TYPEVAR; seems really wrong that it represents these differently, but c'est la vie. My updated commit fixes the test & keeps everything working properly. |
kenzierocks: sorry, haven't used github in a long time; just came here to commit this change which I have locally; I think I did the right thing with my updated bug fix. |
Did a copy-paste from TypeKind.DECLARED and missed that there's no need to re-create a list here.
In What you're saying here, is that Eclipse has a bug and represents intersection types as type variables (whether it “seems like the more correct way to do this” or not doesn't really matter, it's wrong, per spec). Wrt testing, that's probably possible using an "integration test" (a whole project in |
Ah cool, didn't realize it was part of the spec; yah, sounds like a bug in Eclipse then (though I can understand their confusion; if an intersection type is represented as a DeclaredType, then why would TypeParameterElement have a getBounds() which returns a list?) I will admit that my familiarity with github is limited, and I'd have no idea how to add an eclipse dependency; it seems reasonable to have TypesTest run against both javac as well as the eclipse compiler. Is there any chance you guys could take over, as you are more familiar? I have my own test against Eclipse which I can verify is fixed by this code, I just have no idea how to set that up here. |
Yeah, I'd love to get this fixed regardless. |
Judging from https://github.com/eclipse/eclipse.jdt.core/blob/bbfd43e75c85d178e78e2e1f2cdd86b56e672481/org.eclipse.jdt.compiler.apt/src/org/eclipse/jdt/internal/compiler/apt/model/TypeVariableImpl.java#L54-L67 JDT will actually return a new An alternative would be, according to the Java 7 javadoc, to not use FWIW, the javadoc for |
I'm fine with whatever approach you guys feel is best; the difficulty I have with the code before my changes is that it is completely ignoring TypeParameterElement.getBounds() - even if the eclipse compiler is using it wrong, it is a field which has pertinent data, and it's not currently being processed. It just so happens that it also fixes the eclipse bug, but I don't think the code as I have it is nonsensical/crazy, it's simply taking this data into account. Whatever the case, the stack overflow exception in Eclipse isn't pretty, and so fixing this ASAP would be nice. |
If TypeParameterElement#getBounds does the trick, why complicate it? This works both in eclipse as well as javac.
Looking @ the javadoc: "These are the types given by the extends clause used to declare this type parameter" So, why have we complicated the code so much? Take a look at my latest commit; it works both in javac (all tests pass) + eclipse, and simplifies things a lot. |
Well, I thought about it too, but was afraid we might loose some information when going from type mirrors to elements; but I think for type variables / parameters it's probably safe. Maybe @eamonnmcmanus knows more about the details? (the JavaC code is hard to follow, but IIUC, when getting type parameters of an element, it actually loops over it's type's parameter types and extract their underlying type parameter; which would mean that there's indeed a one-to-one relation between type parameter elements and type variables, and it's safe to just do an |
@tbroyer TypesTest does a lot of checks based on this using javac, and they are all passing; if there's any information that can be lost that's not currently covered by TypesTest, it should probably be added there (so we can make sure this solution works) |
I also think that it would be safe to go through TypeParameterElement. I have a vague recollection of doing that in AutoValue at some point, though I can't find it. Currently AutoValue uses |
|
||
return Collections.singletonList(upperBound); | ||
TypeParameterElement upperBoundElement = | ||
(TypeParameterElement) ((javax.lang.model.type.TypeVariable) typeVariable).asElement(); |
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.
You don't need the first cast actually, and the variable should be renamed as it's not about the upper bounds. This can be simplified to:
return typeVariable.asElement().getBounds();
…and probably inlined then.
Sweet, thanks @tbroyer! |
For the following code:
a code generator (javax.annotation.processing.Processor) in eclipse will have a TypeVariable for the return type T instead of a DeclaredType.
This results in a stack overflow exception, because the return value of Collections.singletonList(upperBound) ends up returning a list containing, in essence, the input parameter.