Example #1
0
 @Override
 public ClassifierDescriptor getClassifier(@NotNull Name name) {
   // TODO: creating an FqName every time may be a performance problem
   Name actualName =
       resolveSession.resolveClassifierAlias(DescriptorUtils.getFqNameSafe(thisDescriptor), name);
   return super.getClassifier(actualName);
 }
public class DeprecatedAnnotationVisitor extends AfterAnalysisHighlightingVisitor {

  private static final TokenSet PROPERTY_SET_OPERATIONS =
      TokenSet.create(
          JetTokens.EQ,
          JetTokens.PLUSEQ,
          JetTokens.MINUSEQ,
          JetTokens.MULTEQ,
          JetTokens.DIVEQ,
          JetTokens.PERCEQ,
          JetTokens.PLUSPLUS,
          JetTokens.MINUSMINUS);
  private static final FqName JAVA_DEPRECATED = new FqName(Deprecated.class.getName());
  private static final FqName KOTLIN_DEPRECATED =
      DescriptorUtils.getFqNameSafe(KotlinBuiltIns.getInstance().getDeprecatedAnnotation());

  protected DeprecatedAnnotationVisitor(AnnotationHolder holder, BindingContext bindingContext) {
    super(holder, bindingContext);
  }

  @Override
  public void visitSuperExpression(@NotNull JetSuperExpression expression) {
    // Deprecated for super expression. Unnecessary to mark it as Deprecated
  }

  @Override
  public void visitReferenceExpression(@NotNull JetReferenceExpression expression) {
    super.visitReferenceExpression(expression);
    ResolvedCall resolvedCall = CallUtilPackage.getResolvedCall(expression, bindingContext);
    if (resolvedCall != null && resolvedCall instanceof VariableAsFunctionResolvedCall) {
      // Deprecated for invoke()
      JetCallExpression parent = PsiTreeUtil.getParentOfType(expression, JetCallExpression.class);
      if (parent != null) {
        reportAnnotationIfNeeded(parent, resolvedCall.getResultingDescriptor(), true);
      }
    }
    if (expression.getNode().getElementType() == JetNodeTypes.OPERATION_REFERENCE) {
      // Deprecated for operations (mark as warning)
      checkDeprecatedForOperations(expression);
    } else {
      checkDeprecatedForReferenceExpression(expression);
    }
  }

  private void checkDeprecatedForReferenceExpression(@NotNull JetReferenceExpression expression) {
    DeclarationDescriptor target = bindingContext.get(BindingContext.REFERENCE_TARGET, expression);
    if (target != null) {
      if (target instanceof ConstructorDescriptor) {
        checkConstructorDescriptor(expression, target);
      } else if (target instanceof ClassDescriptor) {
        checkClassDescriptor(expression, (ClassDescriptor) target);
      } else if (target instanceof PropertyDescriptor) {
        checkPropertyDescriptor(expression, (PropertyDescriptor) target);
      } else if (target instanceof FunctionDescriptor) {
        checkFunctionDescriptor(expression, target);
      }
    }
  }

  private void checkFunctionDescriptor(JetExpression expression, DeclarationDescriptor target) {
    // Deprecated for Function
    reportAnnotationIfNeeded(expression, target, expression instanceof JetArrayAccessExpression);
  }

  private void checkConstructorDescriptor(
      @NotNull JetExpression expression, @NotNull DeclarationDescriptor target) {
    // Deprecated for Class and for Constructor
    DeclarationDescriptor containingDeclaration = target.getContainingDeclaration();
    if (containingDeclaration != null) {
      if (!reportAnnotationIfNeeded(expression, containingDeclaration)) {
        reportAnnotationIfNeeded(expression, target);
      }
    }
  }

  private void checkClassDescriptor(
      @NotNull JetExpression expression, @NotNull ClassDescriptor target) {
    // Deprecated for Class, for ClassObject (if reference isn't in UserType or in ModifierList
    // (trait))
    if (!reportAnnotationIfNeeded(expression, target)) {
      if (PsiTreeUtil.getParentOfType(expression, JetUserType.class) == null
          && PsiTreeUtil.getParentOfType(expression, JetModifierList.class) == null) {
        ClassDescriptor classObjectDescriptor = target.getClassObjectDescriptor();
        if (classObjectDescriptor != null) {
          reportAnnotationIfNeeded(expression, classObjectDescriptor);
        }
      }
    }
  }

  private void checkPropertyDescriptor(
      @NotNull JetExpression expression, @NotNull PropertyDescriptor propertyDescriptor) {
    // Deprecated for Property
    if (reportAnnotationIfNeeded(expression, propertyDescriptor, propertyDescriptor.isVar())) {
      return;
    }

    // Deprecated for Getter (val, var), Setter (var)
    if (!propertyDescriptor.isVar()) {
      checkPropertyGetter(propertyDescriptor, expression);
    } else {
      IElementType operation = null;
      JetBinaryExpression binaryExpression =
          PsiTreeUtil.getParentOfType(expression, JetBinaryExpression.class);
      if (binaryExpression != null) {
        JetExpression left = binaryExpression.getLeft();
        if (left == expression) {
          operation = binaryExpression.getOperationToken();
        } else {
          JetReferenceExpression[] jetReferenceExpressions =
              PsiTreeUtil.getChildrenOfType(left, JetReferenceExpression.class);
          if (jetReferenceExpressions != null) {
            for (JetReferenceExpression expr : jetReferenceExpressions) {
              if (expr == expression) {
                operation = binaryExpression.getOperationToken();
                break;
              }
            }
          }
        }
      } else {
        JetUnaryExpression unaryExpression =
            PsiTreeUtil.getParentOfType(expression, JetUnaryExpression.class);
        if (unaryExpression != null) {
          operation = unaryExpression.getOperationReference().getReferencedNameElementType();
        }
      }

      if (operation != null && PROPERTY_SET_OPERATIONS.contains(operation)) {
        checkPropertySetter(propertyDescriptor, expression);
      } else {
        checkPropertyGetter(propertyDescriptor, expression);
      }
    }
  }

  private void checkPropertySetter(
      @NotNull PropertyDescriptor descriptor, @NotNull JetExpression expression) {
    PropertySetterDescriptor setter = descriptor.getSetter();
    if (setter != null) {
      checkPropertyAccessor(setter, expression, true);
    }
  }

  private void checkPropertyGetter(
      @NotNull PropertyDescriptor descriptor, @NotNull JetExpression expression) {
    PropertyGetterDescriptor getter = descriptor.getGetter();
    if (getter != null) {
      checkPropertyAccessor(getter, expression, descriptor.isVar());
    }
  }

  private void checkPropertyAccessor(
      @NotNull PropertyAccessorDescriptor accessor,
      @NotNull JetExpression expression,
      boolean isCrossingDisallowed) {
    reportAnnotationIfNeeded(expression, accessor, isCrossingDisallowed);
  }

  private void checkDeprecatedForOperations(@NotNull JetReferenceExpression expression) {
    DeclarationDescriptor target = bindingContext.get(BindingContext.REFERENCE_TARGET, expression);
    if (target != null) {
      reportAnnotationIfNeeded(expression, target, true);
    }
  }

  private boolean reportAnnotationIfNeeded(
      @NotNull PsiElement element, @NotNull DeclarationDescriptor descriptor) {
    return reportAnnotationIfNeeded(element, descriptor, false);
  }

  private boolean reportAnnotationIfNeeded(
      @NotNull PsiElement element,
      @NotNull DeclarationDescriptor descriptor,
      boolean isCrossingDisallowed) {
    AnnotationDescriptor deprecated = getDeprecated(descriptor);
    if (deprecated != null) {
      if (isCrossingDisallowed) {
        holder
            .createWarningAnnotation(element, composeTooltipString(descriptor, deprecated))
            .setTextAttributes(CodeInsightColors.WARNINGS_ATTRIBUTES);
      } else {
        holder
            .createWarningAnnotation(element, composeTooltipString(descriptor, deprecated))
            .setTextAttributes(CodeInsightColors.DEPRECATED_ATTRIBUTES);
      }
      return true;
    }
    return false;
  }

  @Nullable
  private static AnnotationDescriptor getDeprecated(DeclarationDescriptor descriptor) {
    AnnotationDescriptor kotlinDeprecated =
        descriptor.getAnnotations().findAnnotation(KOTLIN_DEPRECATED);
    return kotlinDeprecated != null
        ? kotlinDeprecated
        : descriptor.getAnnotations().findAnnotation(JAVA_DEPRECATED);
  }

  private static String composeTooltipString(
      @NotNull DeclarationDescriptor declarationDescriptor,
      @NotNull AnnotationDescriptor descriptor) {
    String fact = "'" + getDescriptorString(declarationDescriptor) + "' is deprecated.";
    String message = getMessageFromAnnotationDescriptor(descriptor);
    return message == null ? fact : fact + " " + message;
  }

  @Nullable
  private static String getMessageFromAnnotationDescriptor(
      @NotNull AnnotationDescriptor descriptor) {
    ClassDescriptor classDescriptor = TypeUtils.getClassDescriptor(descriptor.getType());
    if (classDescriptor != null) {
      ValueParameterDescriptor parameter =
          DescriptorResolverUtils.getAnnotationParameterByName(
              DEFAULT_ANNOTATION_MEMBER_NAME, classDescriptor);
      if (parameter != null) {
        CompileTimeConstant<?> valueArgument = descriptor.getValueArgument(parameter);
        if (valueArgument != null) {
          Object value = valueArgument.getValue();
          if (value instanceof String) {
            return String.valueOf(value);
          }
        }
      }
    }
    return null;
  }

  private static String getDescriptorString(@NotNull DeclarationDescriptor descriptor) {
    if (descriptor instanceof ClassDescriptor) {
      return DescriptorUtils.getFqName(descriptor).asString();
    } else if (descriptor instanceof ConstructorDescriptor) {
      DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
      assert containingDeclaration != null;
      return "constructor for " + containingDeclaration.getName();
    } else if (descriptor instanceof PropertyGetterDescriptor) {
      return "getter for "
          + ((PropertyGetterDescriptor) descriptor).getCorrespondingProperty().getName();
    } else if (descriptor instanceof PropertySetterDescriptor) {
      return "setter for "
          + ((PropertySetterDescriptor) descriptor).getCorrespondingProperty().getName();
    } else if (descriptor instanceof PropertyDescriptor) {
      if (((PropertyDescriptor) descriptor).isVar()) {
        return "var " + descriptor.getName();
      }
      return "val " + descriptor.getName();
    } else if (descriptor instanceof FunctionDescriptor) {
      return "fun "
          + descriptor.getName()
          + DescriptorRenderer.FQ_NAMES_IN_TYPES.renderFunctionParameters(
              (FunctionDescriptor) descriptor);
    }
    return DescriptorRenderer.FQ_NAMES_IN_TYPES.render(descriptor);
  }
}