All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.checkerframework.javacutil.ElementUtils Maven / Gradle / Ivy

Go to download

The Checker Framework enhances Java's type system to make it more powerful and useful. This lets software developers detect and prevent errors in their Java programs. The Checker Framework includes compiler plug-ins ("checkers") that find bugs or verify their absence. It also permits you to write your own compiler plug-ins.

There is a newer version: 3.47.0
Show newest version
package org.checkerframework.javacutil;

import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.code.Symbol.MethodSymbol;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.model.JavacTypes;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.util.Context;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayDeque;
import java.util.ArrayList;
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;
import java.util.Set;
import java.util.StringJoiner;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.Name;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.JavaFileObject;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.checker.signature.qual.BinaryName;
import org.checkerframework.checker.signature.qual.CanonicalName;
import org.plumelib.util.CollectionsPlume;

/**
 * Utility methods for analyzing {@code Element}s. This complements {@link Elements}, providing
 * functionality that it does not.
 */
public class ElementUtils {

  // Class cannot be instantiated.
  private ElementUtils() {
    throw new AssertionError("Class ElementUtils cannot be instantiated.");
  }

  /**
   * Returns the innermost type element enclosing the given element. Returns the element itself if
   * it is a type element.
   *
   * @param elem the enclosed element of a class
   * @return the innermost type element, or null if no type element encloses {@code elem}
   * @deprecated use {@link #enclosingTypeElement}
   */
  @Deprecated // 2021-01-16
  public static @Nullable TypeElement enclosingClass(final Element elem) {
    return enclosingTypeElement(elem);
  }

  /**
   * Returns the innermost type element that is, or encloses, the given element.
   *
   * 

Note that in this code: * *

{@code
   * class Outer {
   *   static class Inner {  }
   * }
   * }
* * {@code Inner} has no enclosing type, but this method returns {@code Outer}. * * @param elem the enclosed element of a class * @return the innermost type element (possibly the argument itself), or null if {@code elem} is * not, and is not enclosed by, a type element */ public static @Nullable TypeElement enclosingTypeElement(final Element elem) { Element result = elem; while (result != null && !isTypeElement(result)) { result = result.getEnclosingElement(); } return (TypeElement) result; } /** * Returns the innermost type element enclosing the given element, that is different from the * element itself. By contrast, {@link #enclosingTypeElement} returns its argument if the argument * is a type element. * * @param elem the enclosed element of a class * @return the innermost type element, or null if no type element encloses {@code elem} */ public static @Nullable TypeElement strictEnclosingTypeElement(final Element elem) { Element enclosingElement = elem.getEnclosingElement(); if (enclosingElement == null) { return null; } return enclosingTypeElement(enclosingElement); } /** * Returns the top-level type element that contains {@code element}. * * @param element the element whose enclosing tye element to find * @return a type element containing {@code element} that isn't contained in another class */ public static TypeElement toplevelEnclosingTypeElement(Element element) { TypeElement result = enclosingTypeElement(element); if (result == null) { return (TypeElement) element; } TypeElement enclosing = strictEnclosingTypeElement(result); while (enclosing != null) { result = enclosing; enclosing = strictEnclosingTypeElement(enclosing); } return result; } /** * Returns the binary name of the class enclosing {@code executableElement}. * * @param executableElement the ExecutableElement * @return the binary name of the class enclosing {@code executableElement} */ public static @BinaryName String getEnclosingClassName(ExecutableElement executableElement) { return getBinaryName(((MethodSymbol) executableElement).enclClass()); } /** * Returns the binary name of the class enclosing {@code variableElement}. * * @param variableElement the VariableElement * @return the binary name of the class enclosing {@code variableElement} */ public static @BinaryName String getEnclosingClassName(VariableElement variableElement) { TypeElement enclosingType = enclosingTypeElement(variableElement); if (enclosingType == null) { throw new BugInCF("enclosingTypeElement(%s) is null", variableElement); } return getBinaryName(enclosingType); } /** * Returns the innermost package element enclosing the given element. The same effect as {@link * javax.lang.model.util.Elements#getPackageOf(Element)}. Returns the element itself if it is a * package. * * @param elem the enclosed element of a package * @return the innermost package element */ public static PackageElement enclosingPackage(final Element elem) { Element result = elem; while (result != null && result.getKind() != ElementKind.PACKAGE) { @Nullable Element encl = result.getEnclosingElement(); result = encl; } return (PackageElement) result; } /** * Returns the "parent" package element for the given package element. For package "A.B" it gives * "A". For package "A" it gives the default package. For the default package it returns null. * *

Note that packages are not enclosed within each other, we have to manually climb the * namespaces. Calling "enclosingPackage" on a package element returns the package element itself * again. * * @param elem the package to start from * @return the parent package element or {@code null} */ public static @Nullable PackageElement parentPackage( final PackageElement elem, final Elements e) { // The following might do the same thing: // ((Symbol) elt).owner; // TODO: verify and see whether the change is worth it. String fqnstart = elem.getQualifiedName().toString(); String fqn = fqnstart; if (fqn != null && !fqn.isEmpty()) { int dotPos = fqn.lastIndexOf('.'); if (dotPos != -1) { return e.getPackageElement(fqn.substring(0, dotPos)); } } return null; } /** * Returns true if the element is a static element: whether it is a static field, static method, * or static class. * * @return true if element is static */ public static boolean isStatic(Element element) { return element.getModifiers().contains(Modifier.STATIC); } /** * Returns true if the element is a final element: a final field, final method, or final class. * * @return true if the element is final */ public static boolean isFinal(Element element) { return element.getModifiers().contains(Modifier.FINAL); } /** * Returns true if the element is a effectively final element. * * @return true if the element is effectively final */ public static boolean isEffectivelyFinal(Element element) { Symbol sym = (Symbol) element; if (sym.getEnclosingElement().getKind() == ElementKind.METHOD && (sym.getEnclosingElement().flags() & Flags.ABSTRACT) != 0) { return true; } return (sym.flags() & (Flags.FINAL | Flags.EFFECTIVELY_FINAL)) != 0; } /** * Returns the {@code TypeMirror} for usage of Element as a value. It returns the return type of a * method element, the class type of a constructor, or simply the type mirror of the element * itself. * * @param element the element whose type to obtain * @return the type for the element used as a value */ @SuppressWarnings("nullness:dereference.of.nullable") // a constructor has an enclosing class public static TypeMirror getType(Element element) { if (element.getKind() == ElementKind.METHOD) { return ((ExecutableElement) element).getReturnType(); } else if (element.getKind() == ElementKind.CONSTRUCTOR) { return enclosingClass(element).asType(); } else { return element.asType(); } } /** * Returns the qualified name of the innermost class enclosing the provided {@code Element}. * * @param element an element enclosed by a class, or a {@code TypeElement} * @return the qualified {@code Name} of the innermost class enclosing the element */ public static @Nullable Name getQualifiedClassName(Element element) { if (element.getKind() == ElementKind.PACKAGE) { PackageElement elem = (PackageElement) element; return elem.getQualifiedName(); } TypeElement elem = enclosingClass(element); if (elem == null) { return null; } return elem.getQualifiedName(); } /** * Returns a verbose name that identifies the element. * * @param elt the element whose name to obtain * @return the qualified name of the given element */ public static String getQualifiedName(Element elt) { if (elt.getKind() == ElementKind.PACKAGE || isTypeElement(elt)) { Name n = getQualifiedClassName(elt); if (n == null) { return "Unexpected element: " + elt; } return n.toString(); } else { return getQualifiedName(elt.getEnclosingElement()) + "." + elt; } } /** * Returns the binary name of the given type. * * @param te a type * @return the binary name of the type */ @SuppressWarnings("signature:return") // string manipulation public static @BinaryName String getBinaryName(TypeElement te) { Element enclosing = te.getEnclosingElement(); String simpleName = te.getSimpleName().toString(); if (enclosing == null) { // is this possible? return simpleName; } if (ElementUtils.isTypeElement(enclosing)) { return getBinaryName((TypeElement) enclosing) + "$" + simpleName; } else if (enclosing.getKind() == ElementKind.PACKAGE) { PackageElement pe = (PackageElement) enclosing; if (pe.isUnnamed()) { return simpleName; } else { return pe.getQualifiedName() + "." + simpleName; } } else { // This case occurs for anonymous inner classes. Fall back to the flatname method. return ((ClassSymbol) te).flatName().toString(); } } /** * Returns the canonical representation of the method declaration, which contains simple names of * the types only. * * @param element a method declaration * @return the simple name of the method, followed by the simple names of the formal parameter * types */ public static String getSimpleSignature(ExecutableElement element) { // note: constructor simple name is StringJoiner sj = new StringJoiner(",", element.getSimpleName() + "(", ")"); for (Iterator i = element.getParameters().iterator(); i.hasNext(); ) { sj.add(TypesUtils.simpleTypeName(i.next().asType())); } return sj.toString(); } /** * Returns a user-friendly name for the given method. Does not return {@code ""} or {@code * ""} as ExecutableElement.getSimpleName() does. * * @param element a method declaration * @return a user-friendly name for the method */ public static CharSequence getSimpleNameOrDescription(ExecutableElement element) { Name result = element.getSimpleName(); switch (result.toString()) { case "": return element.getEnclosingElement().getSimpleName(); case "": return "class initializer"; default: return result; } } /** * Check if the element is an element for 'java.lang.Object' * * @param element the type element * @return true iff the element is java.lang.Object element */ public static boolean isObject(TypeElement element) { return element.getQualifiedName().contentEquals("java.lang.Object"); } /** * Check if the element is an element for 'java.lang.String' * * @param element the type element * @return true iff the element is java.lang.String element */ public static boolean isString(TypeElement element) { return element.getQualifiedName().contentEquals("java.lang.String"); } /** * Returns true if the element is a reference to a compile-time constant. * * @param elt an element * @return true if the element is a reference to a compile-time constant */ public static boolean isCompileTimeConstant(Element elt) { return elt != null && (elt.getKind() == ElementKind.FIELD || elt.getKind() == ElementKind.LOCAL_VARIABLE) && ((VariableElement) elt).getConstantValue() != null; } /** * Checks whether a given element came from a source file. * *

By contrast, {@link ElementUtils#isElementFromByteCode(Element)} returns true if there is a * classfile for the given element, even if there is also a source file. * * @param element the element to check, or null * @return true if a source file containing the element is being compiled */ public static boolean isElementFromSourceCode(@Nullable Element element) { if (element == null) { return false; } TypeElement enclosingClass = enclosingClass(element); if (enclosingClass == null) { throw new BugInCF("enclosingClass(%s) is null", element); } return isElementFromSourceCodeImpl((Symbol.ClassSymbol) enclosingClass); } /** * Checks whether a given ClassSymbol came from a source file. * *

By contrast, {@link ElementUtils#isElementFromByteCode(Element)} returns true if there is a * classfile for the given element, even if there is also a source file. * * @param symbol the class to check * @return true if a source file containing the class is being compiled */ private static boolean isElementFromSourceCodeImpl(Symbol.ClassSymbol symbol) { // This is a bit of a hack to avoid treating JDK as source files. JDK files' toUri() method // returns just the name of the file (e.g. "Object.java"), but any file actually being // compiled returns a file URI to the source file. return symbol.sourcefile != null && symbol.sourcefile.getKind() == JavaFileObject.Kind.SOURCE && symbol.sourcefile.toUri().toString().startsWith("file:"); } /** * Returns true if the element is declared in ByteCode. Always return false if elt is a package. * * @param elt some element * @return true if the element is declared in ByteCode */ public static boolean isElementFromByteCode(@Nullable Element elt) { if (elt == null) { return false; } if (elt instanceof Symbol.ClassSymbol) { Symbol.ClassSymbol clss = (Symbol.ClassSymbol) elt; if (null != clss.classfile) { // The class file could be a .java file return clss.classfile.getKind() == JavaFileObject.Kind.CLASS; } else { return elt.asType().getKind().isPrimitive(); } } return isElementFromByteCode(elt.getEnclosingElement()); } /** * Returns the path to the source file containing {@code element}, which must be from source code. * * @param element the type element to look at * @return path to the source file containing {@code element} */ public static String getSourceFilePath(TypeElement element) { return ((ClassSymbol) element).sourcefile.toUri().getPath(); } /** * Returns the field of the class or {@code null} if not found. * * @param type TypeElement to search * @param name name of a field * @return the VariableElement for the field if it was found, null otherwise */ public static @Nullable VariableElement findFieldInType(TypeElement type, String name) { for (VariableElement field : ElementFilter.fieldsIn(type.getEnclosedElements())) { if (field.getSimpleName().contentEquals(name)) { return field; } } return null; } /** * Returns the elements of the fields whose simple names are {@code names} and are declared in * {@code type}. * *

If a field isn't declared in {@code type}, its element isn't included in the returned set. * If none of the fields is declared in {@code type}, the empty set is returned. * * @param type where to look for fields * @param names simple names of fields that might be declared in {@code type} * @return the elements of the fields whose simple names are {@code names} and are declared in * {@code type} */ public static Set findFieldsInType(TypeElement type, Collection names) { Set results = new HashSet<>(); for (VariableElement field : ElementFilter.fieldsIn(type.getEnclosedElements())) { if (names.contains(field.getSimpleName().toString())) { results.add(field); } } return results; } /** * Returns non-private field elements, and side-effects {@code names} to remove them. For every * field name in {@code names} that is declared in {@code type} or a supertype, add its element to * the returned set and remove it from {@code names}. * *

When this routine returns, the combination of the return value and {@code names} has the * same cardinality, and represents the same fields, as {@code names} did when the method was * called. * * @param type where to look for fields * @param names simple names of fields that might be declared in {@code type} or a supertype * (Names that are found are removed from this list.) * @return the {@code VariableElement}s for non-private fields that are declared in {@code type} * whose simple names were in {@code names} when the method was called. */ public static Set findFieldsInTypeOrSuperType( TypeMirror type, Collection names) { int origCardinality = names.size(); Set elements = new HashSet<>(); findFieldsInTypeOrSuperType(type, names, elements); // Since names may contain duplicates, I don't trust the claim in the documentation about // cardinality. (Does any code depend on the invariant, though?) if (origCardinality != names.size() + elements.size()) { throw new BugInCF("Bad sizes: %d != %d + %d", origCardinality, names.size(), elements.size()); } return elements; } /** * Side-effects both {@code foundFields} (which starts empty) and {@code notFound}, conceptually * moving elements from {@code notFound} to {@code foundFields}. */ private static void findFieldsInTypeOrSuperType( TypeMirror type, Collection notFound, Set foundFields) { if (TypesUtils.isObject(type)) { return; } TypeElement elt = TypesUtils.getTypeElement(type); assert elt != null : "@AssumeAssertion(nullness): assumption"; Set fieldElts = findFieldsInType(elt, notFound); for (VariableElement field : new HashSet(fieldElts)) { if (!field.getModifiers().contains(Modifier.PRIVATE)) { notFound.remove(field.getSimpleName().toString()); } else { fieldElts.remove(field); } } foundFields.addAll(fieldElts); if (!notFound.isEmpty()) { findFieldsInTypeOrSuperType(elt.getSuperclass(), notFound, foundFields); } } /** * Returns true if {@code element} is "com.sun.tools.javac.comp.Resolve$SymbolNotFoundError". * * @param element the element to test * @return true if {@code element} is "com.sun.tools.javac.comp.Resolve$SymbolNotFoundError" */ public static boolean isError(Element element) { return element.getClass().getName() == "com.sun.tools.javac.comp.Resolve$SymbolNotFoundError"; // interned } /** * Does the given element need a receiver for accesses? For example, an access to a local variable * does not require a receiver. * * @param element the element to test * @return whether the element requires a receiver for accesses */ public static boolean hasReceiver(Element element) { if (element.getKind() == ElementKind.CONSTRUCTOR) { // The enclosing element of a constructor is the class it creates. // A constructor can only have a receiver if the class it creates has an outer type. TypeMirror t = element.getEnclosingElement().asType(); return TypesUtils.hasEnclosingType(t); } else if (element.getKind() == ElementKind.FIELD) { if (ElementUtils.isStatic(element) // Artificial fields in interfaces are not marked as static, so check that // the field is not declared in an interface. || element.getEnclosingElement().getKind().isInterface()) { return false; } else { // In constructors, the element for "this" is a non-static field, but that field // does not have a receiver. return !element.getSimpleName().contentEquals("this"); } } return element.getKind() == ElementKind.METHOD && !ElementUtils.isStatic(element); } /** * Returns a type's superclass, or null if it does not have a superclass (it is object or an * interface, or the superclass is not on the classpath). * * @param typeElt a type element * @return the superclass of {@code typeElt} */ public static @Nullable TypeElement getSuperClass(TypeElement typeElt) { TypeMirror superTypeMirror; try { superTypeMirror = typeElt.getSuperclass(); } catch (com.sun.tools.javac.code.Symbol.CompletionFailure cf) { // Looking up a supertype failed. This sometimes happens // when transitive dependencies are not on the classpath. // As javac didn't complain, let's also not complain. return null; } if (superTypeMirror == null || superTypeMirror.getKind() == TypeKind.NONE) { return null; } else { return (TypeElement) ((DeclaredType) superTypeMirror).asElement(); } } /** * Determine all type elements for the supertypes of the given type element. This is the * transitive closure of the extends and implements clauses. * *

TODO: can we learn from the implementation of * com.sun.tools.javac.model.JavacElements.getAllMembers(TypeElement)? * * @param type the type whose supertypes to return * @param elements the Element utilities * @return supertypes of {@code type} */ public static List getSuperTypes(TypeElement type, Elements elements) { if (type == null) { return Collections.emptyList(); } List superelems = new ArrayList<>(); // Set up a stack containing type, which is our starting point. Deque stack = new ArrayDeque<>(); stack.push(type); while (!stack.isEmpty()) { TypeElement current = stack.pop(); // For each direct supertype of the current type element, if it // hasn't already been visited, push it onto the stack and // add it to our superelems set. TypeElement supercls = ElementUtils.getSuperClass(current); if (supercls != null) { if (!superelems.contains(supercls)) { stack.push(supercls); superelems.add(supercls); } } for (TypeMirror supertypeitf : current.getInterfaces()) { TypeElement superitf = (TypeElement) ((DeclaredType) supertypeitf).asElement(); if (!superelems.contains(superitf)) { stack.push(superitf); superelems.add(superitf); } } } // Include java.lang.Object as implicit superclass for all classes and interfaces. TypeElement jlobject = elements.getTypeElement("java.lang.Object"); if (!superelems.contains(jlobject)) { superelems.add(jlobject); } return Collections.unmodifiableList(superelems); } /** * Return all fields declared in the given type or any superclass/interface. * *

TODO: should this use javax.lang.model.util.Elements.getAllMembers(TypeElement) instead of * our own getSuperTypes? * * @param type the type whose fields to return * @param elements the Element utilities * @return fields of {@code type} */ public static List getAllFieldsIn(TypeElement type, Elements elements) { List fields = new ArrayList<>(ElementFilter.fieldsIn(type.getEnclosedElements())); List alltypes = getSuperTypes(type, elements); for (TypeElement atype : alltypes) { fields.addAll(ElementFilter.fieldsIn(atype.getEnclosedElements())); } return Collections.unmodifiableList(fields); } /** * Return all methods declared in the given type or any superclass/interface. Note that no * constructors will be returned. * *

TODO: should this use javax.lang.model.util.Elements.getAllMembers(TypeElement) instead of * our own getSuperTypes? * * @param type the type whose methods to return * @param elements the Element utilities * @return methods of {@code type} */ public static List getAllMethodsIn(TypeElement type, Elements elements) { List meths = new ArrayList<>(ElementFilter.methodsIn(type.getEnclosedElements())); List alltypes = getSuperTypes(type, elements); for (TypeElement atype : alltypes) { meths.addAll(ElementFilter.methodsIn(atype.getEnclosedElements())); } return Collections.unmodifiableList(meths); } /** * Return all nested/inner classes/interfaces declared in the given type. * * @param type a type * @return all nested/inner classes/interfaces declared in {@code type} */ public static List getAllTypeElementsIn(TypeElement type) { return ElementFilter.typesIn(type.getEnclosedElements()); } /** The set of kinds that represent types. */ private static final Set typeElementKinds; static { typeElementKinds = EnumSet.noneOf(ElementKind.class); for (ElementKind kind : ElementKind.values()) { if (kind.isClass() || kind.isInterface()) { typeElementKinds.add(kind); } } } /** * Return the set of kinds that represent classes. * * @return the set of kinds that represent classes * @deprecated use {@link #typeElementKinds()} */ @Deprecated // 2020-12-11 public static Set classElementKinds() { return typeElementKinds(); } /** * Return the set of kinds that represent classes. * * @return the set of kinds that represent classes */ public static Set typeElementKinds() { return typeElementKinds; } /** * Is the given element kind a type, 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 * @deprecated use {@link #isTypeElement} */ @Deprecated // 2020-12-11 public static boolean isClassElement(Element element) { return isTypeElement(element); } /** * Is the given element kind a type, 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 isTypeElement(Element element) { return typeElementKinds().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; } /** * Return true if the element is a binding variable. * *

Note: This is to conditionally support Java 15 instanceof pattern matching. When available, * this should use {@code ElementKind.BINDING_VARIABLE} directly. * * @param element the element to test * @return true if the element is a binding variable */ public static boolean isBindingVariable(Element element) { return "BINDING_VARIABLE".equals(element.getKind().name()); } /** * Check that a method Element matches a signature. * *

Note: Matching the receiver type must be done elsewhere as the Element receiver type is only * populated when annotated. * * @param method the method Element to be tested * @param methodName the goal method name * @param parameters the goal formal parameter Classes * @return true if the method matches the methodName and parameters */ public static boolean matchesElement( ExecutableElement method, String methodName, Class... parameters) { if (!method.getSimpleName().contentEquals(methodName)) { return false; } if (method.getParameters().size() != parameters.length) { return false; } else { for (int i = 0; i < method.getParameters().size(); i++) { if (!method.getParameters().get(i).asType().toString().equals(parameters[i].getName())) { return false; } } } return true; } /** Returns true if the given element is, or overrides, method. */ public static boolean isMethod( ExecutableElement questioned, ExecutableElement method, ProcessingEnvironment env) { TypeElement enclosing = (TypeElement) questioned.getEnclosingElement(); return questioned.equals(method) || env.getElementUtils().overrides(questioned, method, enclosing); } /** * Given an annotation name, return true if the element has the annotation of that name. * *

It is more efficient to use {@code Element#getAnnotation(Class)}, but note that both methods * ignore types from annotation files, such as stub or ajava files. * *

To include types from annotation files, use {@code AnnotatedTypeFactory#fromElement} or * {@code AnnotatedTypeFactory#getDeclAnnotations}. * * @param element the element * @param annotName name of the annotation * @return true if the element has the annotation of that name */ public static boolean hasAnnotation(Element element, String annotName) { for (AnnotationMirror anm : element.getAnnotationMirrors()) { if (AnnotationUtils.areSameByName(anm, annotName)) { return true; } } return false; } /** * Returns the TypeElement for the given class. * * @param processingEnv the processing environment * @param clazz a class * @return the TypeElement for the class */ public static TypeElement getTypeElement(ProcessingEnvironment processingEnv, Class clazz) { @CanonicalName String className = clazz.getCanonicalName(); if (className == null) { throw new Error("Anonymous class " + clazz + " has no canonical name"); } return processingEnv.getElementUtils().getTypeElement(className); } /** * Get all the supertypes of a given type, including the type itself. The result includes both * superclasses and implemented interfaces. * * @param type a type * @param env the processing environment * @return list including the type and all its supertypes, with a guarantee that direct supertypes * (i.e. those that appear in extends or implements clauses) appear before indirect supertypes */ public static List getAllSupertypes(TypeElement type, ProcessingEnvironment env) { Context ctx = ((JavacProcessingEnvironment) env).getContext(); com.sun.tools.javac.code.Types javacTypes = com.sun.tools.javac.code.Types.instance(ctx); return CollectionsPlume.mapList( t -> (TypeElement) t.tsym, javacTypes.closure(((Symbol) type).type)); } /** * Returns the methods that are overriden or implemented by a given method. * * @param m a method * @param types the type utilities * @return the methods that {@code m} overrides or implements */ public static Set getOverriddenMethods( ExecutableElement m, Types types) { JavacTypes t = (JavacTypes) types; return t.getOverriddenMethods(m); } /** * Returns true if the two elements are in the same class. The two elements should be class * members, such as methods or fields. * * @param e1 an element * @param e2 an element * @return true if the two elements are in the same class */ public static boolean inSameClass(Element e1, Element e2) { return e1.getEnclosingElement().equals(e2.getEnclosingElement()); } /** * Calls getKind() on the given Element, but returns CLASS if the ElementKind is RECORD. This is * needed because the Checker Framework runs on JDKs before the RECORD item was added, so RECORD * can't be used in case statements, and usually we want to treat them the same as classes. * * @param elt the element to get the kind for * @return the kind of the element, but CLASS if the kind was RECORD */ public static ElementKind getKindRecordAsClass(Element elt) { ElementKind kind = elt.getKind(); if (kind.name().equals("RECORD")) { kind = ElementKind.CLASS; } return kind; } /** * Calls getRecordComponents on the given TypeElement. Uses reflection because this method is not * available before JDK 16. On earlier JDKs, which don't support records anyway, an exception is * thrown. * * @param element the type element to call getRecordComponents on * @return the return value of calling getRecordComponents, or empty list if the method is not * available */ @SuppressWarnings({"unchecked", "nullness"}) // because of cast from reflection public static List getRecordComponents(TypeElement element) { try { return (@NonNull List) TypeElement.class.getMethod("getRecordComponents").invoke(element); } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new Error("Cannot access TypeElement.getRecordComponents", e); } } /** * Check if the given element is a compact canonical record constructor. * * @param elt the element to check * @return true if the element is a compact canonical constructor of a record */ public static boolean isCompactCanonicalRecordConstructor(Element elt) { if (!(elt instanceof Symbol)) { return false; } return elt.getKind() == ElementKind.CONSTRUCTOR && (((Symbol) elt).flags() & TreeUtils.Flags_COMPACT_RECORD_CONSTRUCTOR) != 0; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy