Skip to content
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

Merged
merged 19 commits into from Dec 31, 2019

Conversation

michaelhixson
Copy link
Contributor

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

Treat records as regular classes.  Avoid throwing exceptions when records
are encounterd in source code.  Keep backwards compatibility with Java 8
and 11.
@michaelhixson michaelhixson mentioned this pull request Dec 25, 2019
@mernst
Copy link
Member

mernst commented Dec 26, 2019

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.

@michaelhixson
Copy link
Contributor Author

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?

/__w/1/s/javacutil/src/main/java/org/checkerframework/javacutil/ElementUtils.java:495: missing documentation for isTypeDeclaration
/__w/1/s/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java:820: missing documentation for classTreeKinds

##[error]Bash exited with code '1'.
Finishing: test-misc.sh

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.

@mernst
Copy link
Member

mernst commented Dec 26, 2019

@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.

@michaelhixson
Copy link
Contributor Author

michaelhixson commented Dec 26, 2019

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.findClassInPackage

Currently, Resolver#findClassInPackage(String, PackageSymbol, TreePath) finds an element and checks if its kind is exactly ElementKind.CLASS. It looks suspicious to me.

Element res =
wrapInvocationOnResolveInstance(
FIND_IDENT_IN_PACKAGE,
env,
pck,
names.fromString(name),
Kinds.KindSelector.TYP);
if (res.getKind() == ElementKind.CLASS) {
return (ClassSymbol) res;
} else {
return null;
}

Here is that method's only caller.

ClassSymbol classSymbol =
resolver.findClassInPackage(expr.getNameAsString(), packageSymbol, path);
if (classSymbol != null) {
return new ClassName(classSymbol.asType());
}

Should that check in findClassInPackage be changed to one of these instead?

if (res.getKind().isClass() || res.getKind().isInterface()) {
    return (ClassSymbol) res;
if (res instanceof ClassSymbol) {
    return (ClassSymbol) res;

2. AnnotationUtils.getElementKindsForElementType

Currently, AnnotationUtils#getElementKindsForElementType(ElementType) contains code to accomodate Java 9 modules. It uses a different strategy than the code I added for records, so I'm wondering if I should modify the existing code, my code, or neither.

// TODO: Add actual case to check for the enum constant and return Set containing
// ElementKind.MODULE. (Java 11)
if (elementType.name().contentEquals("MODULE")) {
return EnumSet.noneOf(ElementKind.class);
}

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 ElementType.MODULE, that implies that ElementKind.MODULE also exists, doesn't it?

if (elementType.name().contentEquals("MODULE")) {
    return EnumSet.of(ElementKind.valueOf("MODULE"));
}

@wmdietl wmdietl self-assigned this Dec 26, 2019
Copy link
Member

@wmdietl wmdietl left a 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);
Copy link
Member

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);
Copy link
Member

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);
Copy link
Member

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?

Copy link
Contributor Author

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?

Copy link
Contributor Author

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).

Copy link
Member

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!

Copy link
Contributor Author

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);
Copy link
Member

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?

Copy link
Member

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
Copy link
Member

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.

@michaelhixson
Copy link
Contributor Author

@wmdietl I think I addressed all the feedback.

@michaelhixson michaelhixson removed their assignment Dec 26, 2019
Copy link
Member

@wmdietl wmdietl left a 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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for filing #3019.

@wmdietl wmdietl merged commit e2874e7 into typetools:master Dec 31, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants