@Override
  public PsiType getType() {
    if (PsiUtil.isLanguageLevel8OrHigher(this) &&
        PsiPolyExpressionUtil.isPolyExpression(this) &&
        !MethodCandidateInfo.isOverloadCheck(PsiUtil.skipParenthesizedExprUp(getParent()))) {
      return InferenceSession.getTargetType(this);
    }

    List<PsiExpression> resultExpressions = PsiUtil.getSwitchResultExpressions(this);

    Set<PsiType> resultTypes = new HashSet<>();
    for (PsiExpression expression : resultExpressions) {
      PsiType resultExpressionType = expression.getType();
      if (resultExpressionType == null) return null;
      resultTypes.add(resultExpressionType);
    }

    if (resultTypes.isEmpty()) return null;

    //If the result expressions all have the same type (which may be the null type), then that is the type of the switch expression.
    if (resultTypes.size() == 1) {
      return ContainerUtil.getFirstItem(resultTypes);
    }

    //Otherwise, if the type of each result expression is boolean or Boolean, 
    //an unboxing conversion (5.1.8) is applied to each result expression of type Boolean, and the switch expression has type boolean.
    if (resultTypes.stream().allMatch(type -> PsiTypes.booleanType().isAssignableFrom(type))) {
      return PsiTypes.booleanType();
    }

    //Otherwise, if the type of each result expression is convertible to a numeric type (5.1.8), 
    // the type of the switch expression is given by numeric promotion (5.6.3) applied to the result expressions.
    int[] ranks = resultTypes.stream().mapToInt(type -> TypeConversionUtil.getTypeRank(type)).toArray();
    int maxRank = ArrayUtil.max(ranks);
    if (TypeConversionUtil.isNumericType(maxRank)) {
      if (maxRank == TypeConversionUtil.DOUBLE_RANK) {
        return PsiTypes.doubleType();
      }
      if (maxRank == TypeConversionUtil.FLOAT_RANK) {
        return PsiTypes.floatType();
      }
      if (maxRank == TypeConversionUtil.LONG_RANK) {
        return PsiTypes.longType();
      }

      if (isNumericPromotion(resultExpressions, ranks, PsiTypes.charType())) {
        return PsiTypes.charType();
      }

      if (isNumericPromotion(resultExpressions, ranks, PsiTypes.shortType())) {
        return PsiTypes.shortType();
      }

      if (isNumericPromotion(resultExpressions, ranks, PsiTypes.byteType())) {
        return PsiTypes.byteType();
      }
      return PsiTypes.intType();
    }

    //Otherwise, boxing conversion (5.1.7) is applied to each result expression that has a primitive type, after which the type of the switch expression is the result of applying capture conversion (5.1.10) 
    // to the least upper bound (4.10.4) of the types of the result expressions.
    PsiType leastUpperBound = PsiTypes.nullType();
    for (PsiType type : resultTypes) {
      if (TypeConversionUtil.isPrimitiveAndNotNull(type)) {
        type = ((PsiPrimitiveType)type).getBoxedType(this);
      }
      
      leastUpperBound = GenericsUtil.getLeastUpperBound(type, leastUpperBound, getManager());
    }
    return leastUpperBound != null ? PsiUtil.captureToplevelWildcards(leastUpperBound, this) : null;
  }
@Override
  public PsiType getType()