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.49.2
Show newest version
package org.checkerframework.javacutil;

/*>>>
import org.checkerframework.checker.nullness.qual.Nullable;
*/

import static com.sun.tools.javac.code.Flags.ABSTRACT;
import static com.sun.tools.javac.code.Flags.EFFECTIVELY_FINAL;
import static com.sun.tools.javac.code.Flags.FINAL;

import com.sun.tools.javac.code.Symbol;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
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.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;

/** A Utility class for analyzing {@code Element}s. */
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
     *
     * @param elem the enclosed element of a class
     * @return the innermost type element
     */
    public static TypeElement enclosingClass(final Element elem) {
        Element result = elem;
        while (result != null && !result.getKind().isClass() && !result.getKind().isInterface()) {
            /*@Nullable*/ Element encl = result.getEnclosingElement();
            result = encl;
        }
        return (TypeElement) result;
    }

    /**
     * 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 */ public static 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() && fqn.contains(".")) { fqn = fqn.substring(0, fqn.lastIndexOf('.')); return e.getPackageElement(fqn); } 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() & ABSTRACT) != 0) { return true; } return (sym.flags() & (FINAL | 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. * * @return the type for the element used as a value */ 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 inner most 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. */ public static String getVerboseName(Element elt) { if (elt.getKind() == ElementKind.PACKAGE || elt.getKind().isClass() || elt.getKind().isInterface()) { return getQualifiedClassName(elt).toString(); } else { return getQualifiedClassName(elt) + "." + elt.toString(); } } /** * 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"); } /** Returns true if the element is a constant time reference */ public static boolean isCompileTimeConstant(Element elt) { return elt != null && (elt.getKind() == ElementKind.FIELD || elt.getKind() == ElementKind.LOCAL_VARIABLE) && ((VariableElement) elt).getConstantValue() != null; } /** * Returns true if the element is declared in ByteCode. Always return false if elt is a package. */ public static boolean isElementFromByteCode(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.getName().endsWith(".class"); } else { return false; } } return isElementFromByteCode(elt.getEnclosingElement(), elt); } /** * Returns true if the element is declared in ByteCode. Always return false if elt is a package. */ private static boolean isElementFromByteCode(Element elt, Element orig) { 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.getName().endsWith(".class") || clss.classfile.getName().endsWith(".class)") || clss.classfile.getName().endsWith(".class)]")); } else { return false; } } return isElementFromByteCode(elt.getEnclosingElement(), elt); } /** Returns the field of the class */ public static 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) { Set elements = new HashSet<>(); findFieldsInTypeOrSuperType(type, names, elements); 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); 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); } } public static boolean isError(Element element) { return element.getClass() .getName() .equals("com.sun.tools.javac.comp.Resolve$SymbolNotFoundError"); } /** * 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) { return (element.getKind().isField() || element.getKind() == ElementKind.METHOD || element.getKind() == ElementKind.CONSTRUCTOR) && !ElementUtils.isStatic(element); } /** * Determine all type elements for the classes and interfaces referenced (directly or * indirectly) in the extends/implements clauses of the given type element. * *

TODO: can we learn from the implementation of * com.sun.tools.javac.model.JavacElements.getAllMembers(TypeElement)? */ public static List getSuperTypes(TypeElement type, Elements elements) { List superelems = new ArrayList(); if (type == null) { return superelems; } // 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. TypeMirror supertypecls; try { supertypecls = current.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. // TODO: Use an expanded ErrorReporter to output a message. supertypecls = null; } if (supertypecls != null && supertypecls.getKind() != TypeKind.NONE) { TypeElement supercls = (TypeElement) ((DeclaredType) supertypecls).asElement(); 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? */ public static List getAllFieldsIn(TypeElement type, Elements elements) { List fields = new ArrayList(); fields.addAll(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? */ public static List getAllMethodsIn(TypeElement type, Elements elements) { List meths = new ArrayList(); meths.addAll(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. */ public static List getAllTypeElementsIn(TypeElement type) { List types = new ArrayList<>(); types.addAll(ElementFilter.typesIn(type.getEnclosedElements())); 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; default: return false; } } /** * 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 * @param methodName the name of the method * @param parameters the formal parameters' Classes * @return true if the method matches */ 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); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy