public static ThreeState canExtract(@NotNull PsiExpression ancestor, @NotNull PsiExpression expression) {
    if (expression == ancestor) return ThreeState.YES;
    if (PsiUtil.isConstantExpression(expression)) return ThreeState.YES;
    for (PsiVariable variable : VariableAccessUtils.collectUsedVariables(expression)) {
      if (variable instanceof PsiPatternVariable &&
          PsiTreeUtil.isAncestor(ancestor, variable, true) &&
          !PsiTreeUtil.isAncestor(expression, variable, true)) {
        // Pattern variable is used which is declared inside ancestor; cannot reorder
        return ThreeState.NO;
      }
    }
    PsiElement parent = expression.getParent();
    if (parent instanceof PsiExpressionList) {
      PsiExpression gParent = ObjectUtils.tryCast(parent.getParent(), PsiCallExpression.class);
      if (gParent != null) {
        PsiExpression[] args = ((PsiExpressionList)parent).getExpressions();
        int index = ArrayUtil.indexOf(args, expression);
        ThreeState result = ThreeState.YES;
        for (int i=0; i<index; i++) {
          if (SideEffectChecker.mayHaveSideEffects(args[i])) {
            result = ThreeState.UNSURE;
            break;
          }
        }
        return and(result, () -> canExtract(ancestor, gParent));
      }
    }
    PsiExpression expressionParent = ObjectUtils.tryCast(parent, PsiExpression.class);
    if (expressionParent == null) {
      if (PsiTreeUtil.isAncestor(ancestor, expression, true)) {
        return ThreeState.UNSURE;
      }
      throw new IllegalArgumentException("Should be an ancestor");
    }
    if (expressionParent instanceof PsiParenthesizedExpression || expressionParent instanceof PsiInstanceOfExpression ||
        expressionParent instanceof PsiTypeCastExpression) {
      return canExtract(ancestor, expressionParent);
    }
    if (expressionParent instanceof PsiReferenceExpression) {
      if (((PsiReferenceExpression)expressionParent).getQualifierExpression() == expression) {
        return canExtract(ancestor, expressionParent);
      }
    }
    if (expressionParent instanceof PsiConditionalExpression ternary) {
      PsiExpression condition = ternary.getCondition();
      if (condition == expression) {
        return canExtract(ancestor, expressionParent);
      }
      ThreeState result;
      if (isSideEffectFree(condition, false) &&
          isSideEffectFree(expression, false)) {
        result = ThreeState.YES;
      } else {
        boolean isNecessary =
          areConditionsNecessaryFor(new PsiExpression[]{condition}, expression, ternary.getElseExpression() == expression);
        result = isNecessary ? ThreeState.NO : ThreeState.UNSURE;
      }
      return and(result, () -> canExtract(ancestor, expressionParent));
    }
    if (expressionParent instanceof PsiLambdaExpression) {
      return ThreeState.NO;
    }
    if (expressionParent instanceof PsiUnaryExpression) {
      if (PsiUtil.isIncrementDecrementOperation(expressionParent)) return ThreeState.NO;
      return canExtract(ancestor, expressionParent);
    }
    if (expressionParent instanceof PsiPolyadicExpression polyadic) {
      PsiExpression[] operands = polyadic.getOperands();
      int index = ArrayUtil.indexOf(operands, expression);
      if (index == 0) {
        return canExtract(ancestor, expressionParent);
      }
      IElementType tokenType = polyadic.getOperationTokenType();
      if (tokenType.equals(JavaTokenType.ANDAND) || tokenType.equals(JavaTokenType.OROR)) {
        return and(canMoveToStart(polyadic, index), () -> canExtract(ancestor, expressionParent));
      }
      ThreeState result = ThreeState.YES;
      for (int i=0; i<index; i++) {
        if (SideEffectChecker.mayHaveSideEffects(operands[i])) {
          result = ThreeState.UNSURE;
          break;
        }
      }
      return and(result, () -> canExtract(ancestor, expressionParent));
    }
    if (expressionParent instanceof PsiAssignmentExpression) {
      if (expression == ((PsiAssignmentExpression)expressionParent).getLExpression()) return ThreeState.NO;
      return canExtract(ancestor, expressionParent);
    }
    return and(ThreeState.UNSURE, () -> canExtract(ancestor, expressionParent));
  }
public static ThreeState canExtract(@NotNull PsiExpression ancestor, @NotNull PsiExpression expression)