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
Add baseline support for records #3017
Conversation
Treat records as regular classes. Avoid throwing exceptions when records are encounterd in source code. Keep backwards compatibility with Java 8 and 11.
Thanks for the pull request. CI is failing because the changes don't compile. Could you please correct that and get the CI jobs passing? Thanks! Ask for help if you get stuck. |
I'm trying to understand the latest build failure in misc_jdk8/test-misc.sh. Is it telling me to add javadoc to these methods? If so, why?
I reverted the changes outside of dataflow and javacutil, since I realized I'm only using those two and can't vouch for any other changes. |
@michaelhixson The rule requires documentation for anything that a pull request affects. The rule includes any lines adjacent to ones changed in the pull request. I added documentation for those two lines. |
I'll try to figure out if I caused the latest build errors and/or if I can fix them. Also, I have a couple of questions about code that I'm not sure if I should change. 1. Resolver.findClassInPackageCurrently, checker-framework/javacutil/src/main/java/org/checkerframework/javacutil/Resolver.java Lines 278 to 289 in cff1a03
Here is that method's only caller. Lines 494 to 498 in cff1a03
Should that check in if (res.getKind().isClass() || res.getKind().isInterface()) {
return (ClassSymbol) res; if (res instanceof ClassSymbol) {
return (ClassSymbol) res; 2. AnnotationUtils.getElementKindsForElementTypeCurrently, checker-framework/javacutil/src/main/java/org/checkerframework/javacutil/AnnotationUtils.java Lines 556 to 560 in cff1a03
In this PR, I added the following code right below that. if (elementType.name().equals("RECORD_COMPONENT")) {
return EnumSet.of(ElementKind.valueOf("RECORD_COMPONENT"));
} Shouldn't the module-handling code be changed? If the caller is passing if (elementType.name().contentEquals("MODULE")) {
return EnumSet.of(ElementKind.valueOf("MODULE"));
} |
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.
Thanks for these fixes! I have a few small comments, but this looks good.
Regarding the first question about Resolver.findClassInPackage
I agree that that looks strange. Could you file an issue to fix and/or document this?
I've answered Q2 in the review.
|| tree.getKind() == Tree.Kind.ENUM | ||
|| tree.getKind() == Tree.Kind.INTERFACE | ||
|| tree.getKind() == Tree.Kind.ANNOTATION_TYPE; | ||
assert TreeUtils.isClassTree(tree); |
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 we should just remove this assert. The parameter is a ClassTree
and I don't see what this is trying to guard against.
case ENUM_CONSTANT: | ||
case FIELD: | ||
TypeMirror fieldType = TreeUtils.typeOf(memberSelectTree); | ||
Receiver r = internalReprOf(provider, memberSelectTree.getExpression()); | ||
return new FieldAccess(r, fieldType, (VariableElement) ele); | ||
default: | ||
if (ele.getKind().isClass() || ele.getKind().isInterface()) { | ||
TypeMirror selectType = TreeUtils.typeOf(memberSelectTree); |
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.
Could you copy over the two comments from the previous cases?
ElementKind.INTERFACE, | ||
ElementKind.ANNOTATION_TYPE, | ||
ElementKind.ENUM); | ||
EnumSet<ElementKind> typeKinds = EnumSet.noneOf(ElementKind.class); |
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'm wondering whether it's worth to also extract this into a field, like is done for trees.
This method isn't on a hot path as far as I can tell, but having the symmetry and separate code might still improve readability of the code.
What do you think?
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 sounds reasonable. I'm wondering how far I should take the symmetry.
TreeUtils
has:
private static final Set<Tree.Kind> classTreeKinds
public static Set<Tree.Kind> classTreeKinds()
public static boolean isClassTree(Tree)
I could add these to ElementUtils
:
private static final Set<ElementKind> classElementKinds
public static Set<ElementKind> classElementKinds()
public static boolean isClassElement(Element)
and then I could refactor the places currently doing element.getKind().isClass() || element.getKind().isInterface()
to instead do ElementUtils.isClassElement(element)
.
Does that sound worthwhile, or is that going too far?
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.
Oops, one minor correction. I would need to specify EnumSet
as the type of ElementUtils.classElementKinds
if I wanted to return it from AnnotationUtils.getElementKindsForElementType
. That, or return EnumSet.copyOf(ElementUtils.classElementKinds())
(which would take an optimized path in EnumSet.copyOf(...)
, since the collection it's copying also happens to be an EnumSet
).
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.
That sounds worthwhile. Using EnumSet sounds good and would probably also be good for the Tree.Kind sets. Also, we should make defensive copies to prevent somebody from modifying the definitions of these sets.
Feel free to open an issue for further cleanups that aren't relevant for this PR.
Thanks!
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.
Mind taking another look? I did not end up changing the static set types from Set
to EnumSet
or making defensive copies, because that was spiraling out into too many unrelated changes (changing other similar sets in TreeUtils
, changing internal callers to avoid the defensive copies, but realizing there are no external callers and so no one needs the defensive copy, and so on). IMHO it's ok as is, but let me know if you disagree.
@@ -558,6 +560,9 @@ public static boolean hasInheritedMeta(AnnotationMirror anno) { | |||
if (elementType.name().contentEquals("MODULE")) { | |||
return EnumSet.noneOf(ElementKind.class); |
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.
Regarding your second question: 3ef0af3 added this work-around.
I'm not quite sure why we need it, as we use at least a Java 9 compiler, so using MODULE should always work.
I agree that using ElementKind.valueOf("MODULE")
should also work here.
Could you try that as a separate PR?
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.
Thanks for filing #3019.
default: | ||
return false; | ||
} | ||
// These tree kinds are always declarations. Uses of the declared types |
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 removed this comment in the Element
version of this method. I agree that the comment doesn't add anything useful, so I would be fine with removing it here too.
Provides symmetry with classTreeKinds and isClassTree of TreeUtils.
@wmdietl I think I addressed all the feedback. |
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.
Thanks for these improvements! It's great that we can support record without any version-specific code.
@@ -558,6 +560,9 @@ public static boolean hasInheritedMeta(AnnotationMirror anno) { | |||
if (elementType.name().contentEquals("MODULE")) { | |||
return EnumSet.noneOf(ElementKind.class); |
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.
Thanks for filing #3019.
Treat records as regular classes. Avoid throwing exceptions when records
are encounterd in source code. Keep backwards compatibility with Java 8
and 11.
Related to #3014