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

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

import com.sun.source.tree.BlockTree;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompoundAssignmentTree;
import com.sun.source.tree.ConditionalExpressionTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.VariableTree;
import com.sun.source.util.TreePath;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.Set;
import java.util.StringJoiner;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.plumelib.util.IPair;

/**
 * Utility methods for obtaining or analyzing a javac {@code TreePath}.
 *
 * @see TreeUtils
 */
public final class TreePathUtil {

  /** Do not instantiate; this class is a collection of static methods. */
  private TreePathUtil() {
    throw new BugInCF("Class TreeUtils cannot be instantiated.");
  }

  ///
  /// Retrieving a path (from another path)
  ///

  /**
   * Gets path to the first (innermost) enclosing tree of the given kind. May return {@code path}
   * itself.
   *
   * @param path the path defining the tree node
   * @param kind the kind of the desired tree
   * @return the path to the enclosing tree of the given type, {@code null} otherwise
   */
  public static @Nullable TreePath pathTillOfKind(TreePath path, Tree.Kind kind) {
    return pathTillOfKind(path, EnumSet.of(kind));
  }

  /**
   * Gets path to the first (innermost) enclosing tree with any one of the given kinds. May return
   * {@code path} itself.
   *
   * @param path the path defining the tree node
   * @param kinds the set of kinds of the desired tree
   * @return the path to the enclosing tree of the given type, {@code null} otherwise
   */
  public static @Nullable TreePath pathTillOfKind(TreePath path, Set kinds) {
    for (TreePath p = path; p != null; p = p.getParentPath()) {
      if (kinds.contains(p.getLeaf().getKind())) {
        return p;
      }
    }
    return null;
  }

  /**
   * Gets path to the first (innermost) enclosing class tree, where class is defined by the {@link
   * TreeUtils#classTreeKinds()} method. May return {@code path} itself.
   *
   * @param path the path defining the tree node
   * @return the path to the enclosing class tree, {@code null} otherwise
   */
  public static @Nullable TreePath pathTillClass(TreePath path) {
    return pathTillOfKind(path, TreeUtils.classTreeKinds());
  }

  /**
   * Gets path to the first (innermost) enclosing method tree. May return {@code path} itself.
   *
   * @param path the path defining the tree node
   * @return the path to the enclosing class tree, {@code null} otherwise
   */
  public static @Nullable TreePath pathTillMethod(TreePath path) {
    return pathTillOfKind(path, Tree.Kind.METHOD);
  }

  ///
  /// Retrieving a tree (from a path)
  ///

  /**
   * Gets the first (innermost) enclosing tree in path, of the given kind. May return the leaf of
   * {@code path} itself.
   *
   * @param path the path defining the tree node
   * @param kind the kind of the desired tree
   * @return the enclosing tree of the given type as given by the path, {@code null} otherwise
   */
  public static @Nullable Tree enclosingOfKind(TreePath path, Tree.Kind kind) {
    return enclosingOfKind(path, EnumSet.of(kind));
  }

  /**
   * Gets the first (innermost) enclosing tree in path, with any one of the given kinds. May return
   * the leaf of {@code path} itself.
   *
   * @param path the path defining the tree node
   * @param kinds the set of kinds of the desired tree
   * @return the enclosing tree of the given type as given by the path, {@code null} otherwise
   */
  public static @Nullable Tree enclosingOfKind(TreePath path, Set kinds) {
    TreePath p = pathTillOfKind(path, kinds);
    return (p == null) ? null : p.getLeaf();
  }

  /**
   * Gets the first (innermost) enclosing tree in path, of the given class. May return the leaf of
   * {@code path} itself.
   *
   * @param  the type of {@code treeClass}
   * @param path the path defining the tree node
   * @param treeClass the class of the desired tree
   * @return the enclosing tree of the given type as given by the path, {@code null} otherwise
   */
  public static  @Nullable T enclosingOfClass(TreePath path, Class treeClass) {
    TreePath p = path;

    while (p != null) {
      Tree leaf = p.getLeaf();
      if (treeClass.isInstance(leaf)) {
        return treeClass.cast(leaf);
      }
      p = p.getParentPath();
    }

    return null;
  }

  /**
   * Gets the path to nearest enclosing declaration (class, method, or variable) of the tree node
   * defined by the given {@link TreePath}. May return the leaf of {@code path} itself.
   *
   * @param path the path defining the tree node
   * @return path to the nearest enclosing class/method/variable in the path, or {@code null} if one
   *     does not exist
   */
  public static @Nullable TreePath enclosingDeclarationPath(TreePath path) {
    return pathTillOfKind(path, TreeUtils.declarationTreeKinds());
  }

  /**
   * Gets the enclosing class of the tree node defined by the given {@link TreePath}. It returns a
   * {@link Tree}, from which {@code checkers.types.AnnotatedTypeMirror} or {@link Element} can be
   * obtained. May return the leaf of {@code path} itself.
   *
   * @param path the path defining the tree node
   * @return the enclosing class (or interface) as given by the path, or {@code null} if one does
   *     not exist
   */
  public static @Nullable ClassTree enclosingClass(TreePath path) {
    return (ClassTree) enclosingOfKind(path, TreeUtils.classTreeKinds());
  }

  /**
   * Gets the enclosing variable of a tree node defined by the given {@link TreePath}. May return
   * the leaf of {@code path} itself.
   *
   * @param path the path defining the tree node
   * @return the enclosing variable as given by the path, or {@code null} if one does not exist
   */
  public static @Nullable VariableTree enclosingVariable(TreePath path) {
    return (VariableTree) enclosingOfKind(path, Tree.Kind.VARIABLE);
  }

  /**
   * Gets the enclosing method of the tree node defined by the given {@link TreePath}. It returns a
   * {@link Tree}, from which an {@code checkers.types.AnnotatedTypeMirror} or {@link Element} can
   * be obtained. May return the leaf of {@code path} itself.
   *
   * 

Also see {@code AnnotatedTypeFactory#getEnclosingMethod} and {@code * AnnotatedTypeFactory#getEnclosingClassOrMethod}, which do not require a TreePath. * * @param path the path defining the tree node * @return the enclosing method as given by the path, or {@code null} if one does not exist */ public static @Nullable MethodTree enclosingMethod(TreePath path) { return (MethodTree) enclosingOfKind(path, Tree.Kind.METHOD); } /** * Gets the enclosing method or lambda expression of the tree node defined by the given {@link * TreePath}. It returns a {@link Tree}, from which an {@code checkers.types.AnnotatedTypeMirror} * or {@link Element} can be obtained. May return the leaf of {@code path} itself. * * @param path the path defining the tree node * @return the enclosing method or lambda as given by the path, or {@code null} if one does not * exist */ public static @Nullable Tree enclosingMethodOrLambda(TreePath path) { return enclosingOfKind(path, EnumSet.of(Tree.Kind.METHOD, Tree.Kind.LAMBDA_EXPRESSION)); } /** * Returns the top-level block that encloses the given path, or null if none does. Never returns * the leaf of {@code path} itself. * * @param path a path * @return the top-level block that encloses the given path, or null if none does */ public static @Nullable BlockTree enclosingTopLevelBlock(TreePath path) { TreePath parentPath = path.getParentPath(); while (parentPath != null && !TreeUtils.classTreeKinds().contains(parentPath.getLeaf().getKind())) { path = parentPath; parentPath = parentPath.getParentPath(); } if (path.getLeaf().getKind() == Tree.Kind.BLOCK) { return (BlockTree) path.getLeaf(); } return null; } /** * Gets the first (innermost) enclosing tree in path, that is not a parenthesis. Never returns the * leaf of {@code path} itself. * * @param path the path defining the tree node * @return a pair of a non-parenthesis tree that contains the argument, and its child that is the * argument or is a parenthesized version of it */ public static IPair enclosingNonParen(TreePath path) { TreePath parentPath = path.getParentPath(); Tree enclosing = parentPath.getLeaf(); Tree enclosingChild = path.getLeaf(); while (enclosing.getKind() == Tree.Kind.PARENTHESIZED) { parentPath = parentPath.getParentPath(); enclosingChild = enclosing; enclosing = parentPath.getLeaf(); } return IPair.of(enclosing, enclosingChild); } /** * Returns the "assignment context" for the leaf of {@code treePath}, which is often the leaf of * the parent of {@code treePath}. (Does not handle pseudo-assignment of an argument to a * parameter or a receiver expression to a receiver.) This is not the same as {@code * org.checkerframework.dataflow.cfg.node.AssignmentContext}, which represents the left-hand side * rather than the assignment itself. * *

The assignment context for {@code treePath} is the leaf of its parent, if that leaf is one * of the following trees: * *

    *
  • AssignmentTree *
  • CompoundAssignmentTree *
  • MethodInvocationTree *
  • NewArrayTree *
  • NewClassTree *
  • ReturnTree *
  • VariableTree *
* * If the parent is a ConditionalExpressionTree we need to distinguish two cases: If the leaf is * either the then or else branch of the ConditionalExpressionTree, then recurse on the parent. If * the leaf is the condition of the ConditionalExpressionTree, then return null to not consider * this assignment context. * *

If the leaf is a ParenthesizedTree, then recurse on the parent. * *

Otherwise, null is returned. * * @param treePath a path * @return the assignment context as described, {@code null} otherwise */ public static @Nullable Tree getAssignmentContext(TreePath treePath) { TreePath parentPath = treePath.getParentPath(); if (parentPath == null) { return null; } Tree parent = parentPath.getLeaf(); switch (parent.getKind()) { case ASSIGNMENT: // See below for CompoundAssignmentTree. case METHOD_INVOCATION: case NEW_ARRAY: case NEW_CLASS: case RETURN: case VARIABLE: return parent; case CONDITIONAL_EXPRESSION: ConditionalExpressionTree cet = (ConditionalExpressionTree) parent; @SuppressWarnings("interning:not.interned") // AST node comparison boolean conditionIsLeaf = (cet.getCondition() == treePath.getLeaf()); if (conditionIsLeaf) { // The assignment context for the condition is simply boolean. // No point in going on. return null; } // Otherwise use the context of the ConditionalExpressionTree. return getAssignmentContext(parentPath); case PARENTHESIZED: return getAssignmentContext(parentPath); default: // 11 Tree.Kinds are CompoundAssignmentTrees, // so use instanceof rather than listing all 11. if (parent instanceof CompoundAssignmentTree) { return parent; } return null; } } /// /// Predicates /// /** * Returns true if the tree is in a constructor or an initializer block. * * @param path the path to test * @return true if the path is in a constructor or an initializer block */ public static boolean inConstructor(TreePath path) { MethodTree method = enclosingMethod(path); // If method is null, this is an initializer block. return method == null || TreeUtils.isConstructor(method); } /** * Returns true if the leaf of the tree path is in a static scope. * * @param path a TreePath whose leaf may or may not be in static scope * @return true if the leaf of the tree path is in a static scope */ public static boolean isTreeInStaticScope(TreePath path) { MethodTree enclosingMethod = enclosingMethod(path); if (enclosingMethod != null) { return enclosingMethod.getModifiers().getFlags().contains(Modifier.STATIC); } // no enclosing method, check for static or initializer block BlockTree block = enclosingTopLevelBlock(path); if (block != null) { return block.isStatic(); } // check if it's in a variable initializer Tree t = enclosingVariable(path); if (t != null) { return ((VariableTree) t).getModifiers().getFlags().contains(Modifier.STATIC); } ClassTree classTree = enclosingClass(path); if (classTree != null) { return classTree.getModifiers().getFlags().contains(Modifier.STATIC); } return false; } /** * Returns true if the path is to a top-level (not within a loop) assignment within an initializer * block. The initializer block might be instance or static. Will return true for a re-assignment * even if there is another initialization (within this initializer block, another initializer * block, a constructor, or the variable declaration). * * @param path the path to test * @return true if the path is to an initialization within an initializer block */ public static boolean isTopLevelAssignmentInInitializerBlock(TreePath path) { TreePath origPath = path; if (path.getLeaf().getKind() != Tree.Kind.ASSIGNMENT) { return false; } path = path.getParentPath(); if (path.getLeaf().getKind() != Tree.Kind.EXPRESSION_STATEMENT) { return false; } Tree prevLeaf = path.getLeaf(); path = path.getParentPath(); for (Iterator itor = path.iterator(); itor.hasNext(); ) { Tree leaf = itor.next(); switch (leaf.getKind()) { case CLASS: case ENUM: case PARAMETERIZED_TYPE: return prevLeaf.getKind() == Tree.Kind.BLOCK; case COMPILATION_UNIT: throw new BugInCF("found COMPILATION_UNIT in " + toString(origPath)); case DO_WHILE_LOOP: case ENHANCED_FOR_LOOP: case FOR_LOOP: case LAMBDA_EXPRESSION: case METHOD: return false; default: prevLeaf = leaf; } } throw new BugInCF("path did not contain method or class: " + toString(origPath)); } /// /// Formatting /// /** * Return a printed representation of a TreePath. * * @param path a TreePath * @return a printed representation of the given TreePath */ public static String toString(TreePath path) { StringJoiner result = new StringJoiner(System.lineSeparator() + " "); result.add("TreePath:"); for (Tree t : path) { result.add(TreeUtils.toStringTruncated(t, 65) + " " + t.getKind()); } return result.toString(); } /** * Returns a string representation of the leaf of the given path, using {@link * TreeUtils#toStringTruncated}. * * @param path a path * @param length the maximum length for the result; must be at least 6 * @return a one-line string representation of the leaf of the given path that is no longer than * {@code length} characters long */ public static String leafToStringTruncated(@Nullable TreePath path, int length) { if (path == null) { return "null"; } return TreeUtils.toStringTruncated(path.getLeaf(), length); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy