@Override
  public Void visitBinary(BinaryTree node, Void p) {

    // No checking unless the operator is "==" or "!=".
    if (!(node.getKind() == Tree.Kind.EQUAL_TO || node.getKind() == Tree.Kind.NOT_EQUAL_TO))
      return super.visitBinary(node, p);

    ExpressionTree leftOp = node.getLeftOperand();
    ExpressionTree rightOp = node.getRightOperand();

    // Check passes if either arg is null.
    if (leftOp.getKind() == Tree.Kind.NULL_LITERAL || rightOp.getKind() == Tree.Kind.NULL_LITERAL)
      return super.visitBinary(node, p);

    AnnotatedTypeMirror left = atypeFactory.getAnnotatedType(leftOp);
    AnnotatedTypeMirror right = atypeFactory.getAnnotatedType(rightOp);

    // If either argument is a primitive, check passes due to auto-unboxing
    if (left.getKind().isPrimitive() || right.getKind().isPrimitive())
      return super.visitBinary(node, p);

    if (!(shouldCheckFor(leftOp) && shouldCheckFor(rightOp))) return super.visitBinary(node, p);

    // Syntactic checks for legal uses of ==
    if (suppressInsideComparison(node)) return super.visitBinary(node, p);
    if (suppressEarlyEquals(node)) return super.visitBinary(node, p);
    if (suppressEarlyCompareTo(node)) return super.visitBinary(node, p);

    if (suppressClassAnnotation(left, right)) {
      return super.visitBinary(node, p);
    }

    Element leftElt = null;
    Element rightElt = null;
    if (left
        instanceof org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType) {
      leftElt = ((DeclaredType) left.getUnderlyingType()).asElement();
    }
    if (right
        instanceof org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType) {
      rightElt = ((DeclaredType) right.getUnderlyingType()).asElement();
    }

    // if neither @Interned or @UsesObjectEquals, report error
    if (!(left.hasEffectiveAnnotation(INTERNED)
        || (leftElt != null && leftElt.getAnnotation(UsesObjectEquals.class) != null)))
      checker.report(Result.failure("not.interned", left), leftOp);
    if (!(right.hasEffectiveAnnotation(INTERNED)
        || (rightElt != null && rightElt.getAnnotation(UsesObjectEquals.class) != null)))
      checker.report(Result.failure("not.interned", right), rightOp);
    return super.visitBinary(node, p);
  }
    private void handleInitalizers(
        List<? extends ExpressionTree> initializers, AnnotatedArrayType type) {

      List<Integer> array = new ArrayList<>();
      array.add(initializers.size());
      type.replaceAnnotation(createArrayLenAnnotation(array));

      boolean singleDem = type.getComponentType().getKind() != TypeKind.ARRAY;
      if (singleDem) {
        return;
      }
      List<List<Integer>> summarylengths = new ArrayList<>();

      for (ExpressionTree init : initializers) {
        AnnotatedArrayType subArrayType = (AnnotatedArrayType) getAnnotatedType(init);
        AnnotatedTypeMirror componentType = subArrayType;
        int count = 0;
        while (componentType.getKind() == TypeKind.ARRAY) {
          AnnotationMirror arrayLen = componentType.getAnnotation(ArrayLen.class);
          List<Integer> currentLengths;
          if (arrayLen != null) {
            currentLengths = getArrayLength(arrayLen);
          } else {
            currentLengths = (new ArrayList<Integer>());
          }
          if (count == summarylengths.size()) {
            summarylengths.add(new ArrayList<Integer>());
          }
          summarylengths.get(count).addAll(currentLengths);
          count++;
          componentType = ((AnnotatedArrayType) componentType).getComponentType();
        }
      }

      AnnotatedTypeMirror componentType = type.getComponentType();
      int i = 0;
      while (componentType.getKind() == TypeKind.ARRAY) {
        componentType.addAnnotation(createArrayLenAnnotation(summarylengths.get(i)));
        componentType = ((AnnotatedArrayType) componentType).getComponentType();
        i++;
      }
    }
  @Override
  public Void visitArray(AnnotatedArrayType type, Tree tree) {
    // TODO: is there already or add a helper method
    // to determine the non-array component type
    AnnotatedTypeMirror comp = type;
    do {
      comp = ((AnnotatedArrayType) comp).getComponentType();
    } while (comp.getKind() == TypeKind.ARRAY);

    if (comp != null
        && comp.getKind() == TypeKind.DECLARED
        && checker.shouldSkipUses(((AnnotatedDeclaredType) comp).getUnderlyingType().asElement())) {
      return super.visitArray(type, tree);
    }

    if (!visitor.isValidUse(type, tree)) {
      reportError(type, tree);
    }

    return super.visitArray(type, tree);
  }
 @Override
 public Void visitTypeCast(TypeCastTree tree, AnnotatedTypeMirror type) {
   if (isUnderlyingTypeAValue(type)) {
     AnnotatedTypeMirror castedAnnotation = getAnnotatedType(tree.getExpression());
     List<?> values = getValues(castedAnnotation, type.getUnderlyingType());
     type.replaceAnnotation(resultAnnotationHandler(type.getUnderlyingType(), values, tree));
   } else if (type.getKind() == TypeKind.ARRAY) {
     if (tree.getExpression().getKind() == Kind.NULL_LITERAL) {
       type.replaceAnnotation(BOTTOMVAL);
     }
   }
   return null;
 }
    @Override
    public Void visitMemberSelect(MemberSelectTree tree, AnnotatedTypeMirror type) {
      if (TreeUtils.isFieldAccess(tree) && isUnderlyingTypeAValue(type)) {
        VariableElement elem = (VariableElement) InternalUtils.symbol(tree);
        Object value = elem.getConstantValue();
        if (value != null) {
          // compile time constant
          type.replaceAnnotation(
              resultAnnotationHandler(
                  type.getUnderlyingType(), Collections.singletonList(value), tree));
          return null;
        }
        if (ElementUtils.isStatic(elem) && ElementUtils.isFinal(elem)) {
          Element e = InternalUtils.symbol(tree.getExpression());
          if (e != null) {
            String classname = ElementUtils.getQualifiedClassName(e).toString();
            String fieldName = tree.getIdentifier().toString();
            value = evalutator.evaluateStaticFieldAccess(classname, fieldName, tree);
            if (value != null)
              type.replaceAnnotation(
                  resultAnnotationHandler(
                      type.getUnderlyingType(), Collections.singletonList(value), tree));
            return null;
          }
        }

        if (tree.getIdentifier().toString().equals("length")) {
          AnnotatedTypeMirror receiverType = getAnnotatedType(tree.getExpression());
          if (receiverType.getKind() == TypeKind.ARRAY) {
            AnnotationMirror arrayAnno = receiverType.getAnnotation(ArrayLen.class);
            if (arrayAnno != null) {
              // array.length, where array : @ArrayLen(x)
              List<Integer> lengths = ValueAnnotatedTypeFactory.getArrayLength(arrayAnno);
              type.replaceAnnotation(createNumberAnnotationMirror(new ArrayList<Number>(lengths)));
              return null;
            }
          }
        }
      }
      return null;
    }