private static boolean isDefinitelyRValue(Node rvalue) {
    Node parent = rvalue.getParent();

    switch (parent.getToken()) {
      case AND:
      case COMMA:
      case HOOK:
      case OR:
        // Function values pass through conditionals.
      case EQ:
      case NOT:
      case SHEQ:
        // Functions can be usefully compared for equality / existence.
      case ARRAYLIT:
      case CALL:
      case NEW:
      case TAGGED_TEMPLATELIT:
        // Functions are the callees and parameters of an invocation.
      case INSTANCEOF:
      case TYPEOF:
        // Often used to determine if a ctor/method exists/matches.
      case GETELEM:
      case GETPROP:
        // Many functions, especially ctors, have properties.
      case RETURN:
      case YIELD:
        // Higher order functions return functions.
        return true;

      case SWITCH:
      case CASE:
        // Delegating on the identity of a function.
      case IF:
      case WHILE:
        // Checking the existence of an optional function.
        return rvalue.isFirstChildOf(parent);

      case EXPR_RESULT:
        // Extern declarations are sometimes stubs. These must be considered L-values with no
        // associated R-values.
        return !rvalue.isFromExterns();

      case CLASS: // `extends` clause.
      case ASSIGN:
        return rvalue.isSecondChildOf(parent);

      case STRING_KEY: // Assignment to an object literal property. Excludes object destructuring.
        return parent.getParent().isObjectLit();

      default:
        // Anything not explicitly listed may not be an R-value. We only worry about the likely
        // cases for nominal function values since those are what interest us and its safe to miss
        // some R-values. It's more important that we correctly identify L-values.
        return false;
    }
  }
private static boolean isDefinitelyRValue ( Node rvalue ) { Node parent = rvalue . getParent ( ) ; switch ( parent . getToken ( ) ) { case AND : case COMMA : case HOOK : case OR : case EQ : case NOT : case SHEQ : case ARRAYLIT : case CALL : case NEW : case TAGGED_TEMPLATELIT : case INSTANCEOF : case TYPEOF : case GETELEM : case GETPROP : case RETURN : case YIELD : return true ; case SWITCH : case CASE : case IF : case WHILE : return rvalue . isFirstChildOf ( parent ) ; case EXPR_RESULT : return ! rvalue . isFromExterns ( ) ; case CLASS : case ASSIGN : return rvalue . isSecondChildOf ( parent ) ; case STRING_KEY : return parent . getParent ( ) . isObjectLit ( ) ; default : return false ; } }
Return {@code true} only if {@code rvalue} is defintely a reference reading a value.

<p>For the most part it's sufficient to cover cases where a nominal function reference might
reasonably be expected, since those are the values that matter to analysis.

<p>It's very important that this never returns {@code true} for an L-value, including when new
syntax is added to the language. That would cause some impure functions to be considered pure.
Therefore, this method is a very explict whitelist. Anything that's unrecognized is considered
not an R-value. This is insurance against new syntax.

<p>New cases can be added as needed to increase the accuracy of the analysis. They just have to
be verified as always R-values.