From e2874e776d046cb54cf172667f84202d5f8e4439 Mon Sep 17 00:00:00 2001 From: Michael Hixson Date: Tue, 31 Dec 2019 03:03:59 -0800 Subject: [PATCH] Add baseline support for records (#3017) Treat records as regular classes. Avoid throwing exceptions when records are encountered in source code. Keep backwards compatibility with Java 8 and 11. Co-authored-by: Michael Ernst Co-authored-by: Werner Dietl --- .../dataflow/analysis/FlowExpressions.java | 22 ++++---- .../dataflow/cfg/CFGBuilder.java | 34 ++++------- .../dataflow/cfg/node/ClassNameNode.java | 9 +-- .../javacutil/AnnotationUtils.java | 9 ++- .../javacutil/ElementUtils.java | 56 +++++++++++++------ .../checkerframework/javacutil/TreeUtils.java | 50 ++++++++--------- .../javacutil/TypesUtils.java | 13 ++--- 7 files changed, 97 insertions(+), 96 deletions(-) diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/analysis/FlowExpressions.java b/dataflow/src/main/java/org/checkerframework/dataflow/analysis/FlowExpressions.java index afc01b9d01a..a6f96321f28 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/analysis/FlowExpressions.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/analysis/FlowExpressions.java @@ -287,6 +287,10 @@ public static Receiver internalReprOf( assert TreeUtils.isUseOfElement(identifierTree) : "@AssumeAssertion(nullness): tree kind"; Element ele = TreeUtils.elementFromUse(identifierTree); + if (ElementUtils.isClassElement(ele)) { + receiver = new ClassName(ele.asType()); + break; + } switch (ele.getKind()) { case LOCAL_VARIABLE: case RESOURCE_VARIABLE: @@ -307,12 +311,6 @@ public static Receiver internalReprOf( new FieldAccess( fieldAccessExpression, typeOfId, (VariableElement) ele); break; - case CLASS: - case ENUM: - case ANNOTATION_TYPE: - case INTERFACE: - receiver = new ClassName(ele.asType()); - break; default: receiver = null; } @@ -372,16 +370,16 @@ private static Receiver internalReprOfMemberSelect( } assert TreeUtils.isUseOfElement(memberSelectTree) : "@AssumeAssertion(nullness): tree kind"; Element ele = TreeUtils.elementFromUse(memberSelectTree); + if (ElementUtils.isClassElement(ele)) { + // o instanceof MyClass.InnerClass + // o instanceof MyClass.InnerInterface + TypeMirror selectType = TreeUtils.typeOf(memberSelectTree); + return new ClassName(selectType); + } switch (ele.getKind()) { case METHOD: case CONSTRUCTOR: return internalReprOf(provider, memberSelectTree.getExpression()); - case CLASS: // o instanceof MyClass.InnerClass - case ENUM: - case INTERFACE: // o instanceof MyClass.InnerInterface - case ANNOTATION_TYPE: - TypeMirror selectType = TreeUtils.typeOf(memberSelectTree); - return new ClassName(selectType); case ENUM_CONSTANT: case FIELD: TypeMirror fieldType = TreeUtils.typeOf(memberSelectTree); diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/CFGBuilder.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/CFGBuilder.java index 505b5a3ec45..5f4cd027c13 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/CFGBuilder.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/CFGBuilder.java @@ -75,6 +75,7 @@ import java.util.Set; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Name; import javax.lang.model.element.TypeElement; @@ -3889,13 +3890,6 @@ public Node visitIdentifier(IdentifierTree tree, Void p) { } else { Element element = TreeUtils.elementFromUse(tree); switch (element.getKind()) { - case ANNOTATION_TYPE: - case CLASS: - case ENUM: - case INTERFACE: - case TYPE_PARAMETER: - node = new ClassNameNode(tree); - break; case FIELD: // Note that "this"/"super" is a field, but not a field access. if (element.getSimpleName().contentEquals("this")) { @@ -3914,6 +3908,10 @@ public Node visitIdentifier(IdentifierTree tree, Void p) { node = new PackageNameNode(tree); break; default: + if (ElementUtils.isTypeDeclaration(element)) { + node = new ClassNameNode(tree); + break; + } throw new BugInCF("bad element kind " + element.getKind()); } } @@ -4173,23 +4171,15 @@ public Node visitMemberSelect(MemberSelectTree tree, Void p) { Node expr = scan(tree.getExpression(), p); if (!TreeUtils.isFieldAccess(tree)) { // Could be a selector of a class or package - Node result = null; Element element = TreeUtils.elementFromUse(tree); - switch (element.getKind()) { - case ANNOTATION_TYPE: - case CLASS: - case ENUM: - case INTERFACE: - result = extendWithNode(new ClassNameNode(tree, expr)); - break; - case PACKAGE: - result = extendWithNode(new PackageNameNode(tree, (PackageNameNode) expr)); - break; - default: - assert false : "Unexpected element kind: " + element.getKind(); - return null; + if (ElementUtils.isClassElement(element)) { + return extendWithNode(new ClassNameNode(tree, expr)); + } else if (element.getKind() == ElementKind.PACKAGE) { + return extendWithNode(new PackageNameNode(tree, (PackageNameNode) expr)); + } else { + assert false : "Unexpected element kind: " + element.getKind(); + return null; } - return result; } Node node = new FieldAccessNode(tree, expr); diff --git a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/ClassNameNode.java b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/ClassNameNode.java index d5ab8193429..03318bb4b55 100644 --- a/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/ClassNameNode.java +++ b/dataflow/src/main/java/org/checkerframework/dataflow/cfg/node/ClassNameNode.java @@ -37,12 +37,13 @@ public ClassNameNode(IdentifierTree tree) { this.parent = null; } + /** + * Create a new ClassNameNode. + * + * @param tree the class tree for this node + */ public ClassNameNode(ClassTree tree) { super(TreeUtils.typeOf(tree)); - assert tree.getKind() == Tree.Kind.CLASS - || tree.getKind() == Tree.Kind.ENUM - || tree.getKind() == Tree.Kind.INTERFACE - || tree.getKind() == Tree.Kind.ANNOTATION_TYPE; this.tree = tree; this.element = TreeUtils.elementFromDeclaration(tree); this.parent = null; diff --git a/javacutil/src/main/java/org/checkerframework/javacutil/AnnotationUtils.java b/javacutil/src/main/java/org/checkerframework/javacutil/AnnotationUtils.java index 1d49a032930..43bfeb2427b 100644 --- a/javacutil/src/main/java/org/checkerframework/javacutil/AnnotationUtils.java +++ b/javacutil/src/main/java/org/checkerframework/javacutil/AnnotationUtils.java @@ -526,11 +526,7 @@ public static EnumSet getElementKindsForTarget(@Nullable Target tar public static EnumSet getElementKindsForElementType(ElementType elementType) { switch (elementType) { case TYPE: - return EnumSet.of( - ElementKind.CLASS, - ElementKind.INTERFACE, - ElementKind.ANNOTATION_TYPE, - ElementKind.ENUM); + return EnumSet.copyOf(ElementUtils.classElementKinds()); case FIELD: return EnumSet.of(ElementKind.FIELD, ElementKind.ENUM_CONSTANT); case METHOD: @@ -558,6 +554,9 @@ public static EnumSet getElementKindsForElementType(ElementType ele if (elementType.name().contentEquals("MODULE")) { return EnumSet.noneOf(ElementKind.class); } + if (elementType.name().equals("RECORD_COMPONENT")) { + return EnumSet.of(ElementKind.valueOf("RECORD_COMPONENT")); + } throw new BugInCF("Unrecognized ElementType: " + elementType); } } diff --git a/javacutil/src/main/java/org/checkerframework/javacutil/ElementUtils.java b/javacutil/src/main/java/org/checkerframework/javacutil/ElementUtils.java index 047d59140df..8a01d579761 100644 --- a/javacutil/src/main/java/org/checkerframework/javacutil/ElementUtils.java +++ b/javacutil/src/main/java/org/checkerframework/javacutil/ElementUtils.java @@ -7,6 +7,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Deque; +import java.util.EnumSet; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -45,7 +46,7 @@ private ElementUtils() { */ public static TypeElement enclosingClass(final Element elem) { Element result = elem; - while (result != null && !result.getKind().isClass() && !result.getKind().isInterface()) { + while (result != null && !isClassElement(result)) { @Nullable Element encl = result.getEnclosingElement(); result = encl; } @@ -171,9 +172,7 @@ public static String getVerboseName(Element elt) { if (n == null) { return "Unexpected element: " + elt; } - if (elt.getKind() == ElementKind.PACKAGE - || elt.getKind().isClass() - || elt.getKind().isInterface()) { + if (elt.getKind() == ElementKind.PACKAGE || isClassElement(elt)) { return n.toString(); } else { return n + "." + elt; @@ -492,22 +491,47 @@ public static List getAllTypeElementsIn(TypeElement type) { return types; } - public static boolean isTypeDeclaration(Element elt) { - switch (elt.getKind()) { - // These tree kinds are always declarations. Uses of the declared - // types have tree kind IDENTIFIER. - case ANNOTATION_TYPE: - case CLASS: - case ENUM: - case INTERFACE: - case TYPE_PARAMETER: - return true; + /** The set of kinds that represent classes. */ + private static final Set classElementKinds; - default: - return false; + static { + classElementKinds = EnumSet.noneOf(ElementKind.class); + for (ElementKind kind : ElementKind.values()) { + if (kind.isClass() || kind.isInterface()) { + classElementKinds.add(kind); + } } } + /** + * Return the set of kinds that represent classes. + * + * @return the set of kinds that represent classes + */ + public static Set classElementKinds() { + return classElementKinds; + } + + /** + * Is the given element kind a class, i.e. a class, enum, interface, or annotation type. + * + * @param element the element to test + * @return true, iff the given kind is a class kind + */ + public static boolean isClassElement(Element element) { + return classElementKinds().contains(element.getKind()); + } + + /** + * Return true if the element is a type declaration. + * + * @param elt the element to test + * @return true if the argument is a type declaration + */ + public static boolean isTypeDeclaration(Element elt) { + return isClassElement(elt) || elt.getKind() == ElementKind.TYPE_PARAMETER; + } + /** * Check that a method Element matches a signature. * diff --git a/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java index e28d4fe7f37..48798170912 100644 --- a/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java +++ b/javacutil/src/main/java/org/checkerframework/javacutil/TreeUtils.java @@ -456,15 +456,6 @@ public static Pair enclosingNonParen(final TreePath path) { } switch (tree.getKind()) { - case VARIABLE: - case METHOD: - case CLASS: - case ENUM: - case INTERFACE: - case ANNOTATION_TYPE: - case TYPE_PARAMETER: - return TreeInfo.symbolFor((JCTree) tree); - // symbol() only works on MethodSelects, so we need to get it manually // for method invocations. case METHOD_INVOCATION: @@ -485,6 +476,11 @@ public static Pair enclosingNonParen(final TreePath path) { return ((JCMemberReference) tree).sym; default: + if (isTypeDeclaration(tree) + || tree.getKind() == Tree.Kind.VARIABLE + || tree.getKind() == Tree.Kind.METHOD) { + return TreeInfo.symbolFor((JCTree) tree); + } return TreeInfo.symbol((JCTree) tree); } } @@ -806,13 +802,23 @@ public static boolean isCompileTimeString(ExpressionTree node) { // Adding Tree.Kind.NEW_CLASS here doesn't work, because then a // tree gets cast to ClassTree when it is actually a NewClassTree, // for example in enclosingClass above. - private static final Set classTreeKinds = - EnumSet.of( - Tree.Kind.CLASS, - Tree.Kind.ENUM, - Tree.Kind.INTERFACE, - Tree.Kind.ANNOTATION_TYPE); + /** The set of kinds that represent classes. */ + private static final Set classTreeKinds; + + static { + classTreeKinds = EnumSet.noneOf(Tree.Kind.class); + for (Tree.Kind kind : Tree.Kind.values()) { + if (kind.asInterface() == ClassTree.class) { + classTreeKinds.add(kind); + } + } + } + /** + * Return the set of kinds that represent classes. + * + * @return the set of kinds that represent classes + */ public static Set classTreeKinds() { return classTreeKinds; } @@ -1174,19 +1180,7 @@ public static boolean isEnumSuper(MethodInvocationTree node) { * @return true if the tree is a type declaration */ public static boolean isTypeDeclaration(Tree node) { - switch (node.getKind()) { - // These tree kinds are always declarations. Uses of the declared - // types have tree kind IDENTIFIER. - case ANNOTATION_TYPE: - case CLASS: - case ENUM: - case INTERFACE: - case TYPE_PARAMETER: - return true; - - default: - return false; - } + return isClassTree(node) || node.getKind() == Tree.Kind.TYPE_PARAMETER; } /** diff --git a/javacutil/src/main/java/org/checkerframework/javacutil/TypesUtils.java b/javacutil/src/main/java/org/checkerframework/javacutil/TypesUtils.java index 3cacc2dd9bf..63552ea3d10 100644 --- a/javacutil/src/main/java/org/checkerframework/javacutil/TypesUtils.java +++ b/javacutil/src/main/java/org/checkerframework/javacutil/TypesUtils.java @@ -351,7 +351,7 @@ public static Type wildUpperBound(TypeMirror tm, ProcessingEnvironment env) { if (w.isSuperBound()) { // returns true if w is unbound Symtab syms = Symtab.instance(context); // w.bound is null if the wildcard is from bytecode. - return w.bound == null ? syms.objectType : w.bound.bound; + return w.bound == null ? syms.objectType : w.bound.getUpperBound(); } else { return wildUpperBound(w.type, env); } @@ -643,15 +643,10 @@ public static TypeMirror substituteMethodReturnType( */ public static @Nullable TypeElement getTypeElement(TypeMirror type) { Element element = ((Type) type).asElement(); - switch (element.getKind()) { - case ANNOTATION_TYPE: - case CLASS: - case ENUM: - case INTERFACE: - return (TypeElement) element; - default: - return null; + if (ElementUtils.isClassElement(element)) { + return (TypeElement) element; } + return null; } /**