public static HighlightInfo checkForeignInnerClassesUsed(final PsiAnnotation annotation) {
    final HighlightInfo[] infos = new HighlightInfo[1];
    final PsiAnnotationOwner owner = annotation.getOwner();
    if (owner instanceof PsiModifierList) {
      final PsiElement parent = ((PsiModifierList) owner).getParent();
      if (parent instanceof PsiClass) {
        annotation.accept(
            new JavaRecursiveElementWalkingVisitor() {
              @Override
              public void visitElement(PsiElement element) {
                if (infos[0] != null) return;
                super.visitElement(element);
              }

              @Override
              public void visitClassObjectAccessExpression(
                  PsiClassObjectAccessExpression expression) {
                super.visitClassObjectAccessExpression(expression);
                final PsiTypeElement operand = expression.getOperand();
                final PsiClass classType = PsiUtil.resolveClassInType(operand.getType());
                if (classType != null) {
                  checkAccessibility(expression, classType, HighlightUtil.formatClass(classType));
                }
              }

              @Override
              public void visitReferenceExpression(PsiReferenceExpression expression) {
                super.visitReferenceExpression(expression);
                final PsiElement resolve = expression.resolve();
                if (resolve instanceof PsiField) {
                  checkAccessibility(
                      expression,
                      (PsiMember) resolve,
                      HighlightUtil.formatField((PsiField) resolve));
                }
              }

              private void checkAccessibility(
                  PsiExpression expression, PsiMember resolve, String memberString) {
                if (resolve.hasModifierProperty(PsiModifier.PRIVATE)
                    && PsiTreeUtil.isAncestor(parent, resolve, true)) {
                  String description =
                      JavaErrorMessages.message(
                          "private.symbol",
                          memberString,
                          HighlightUtil.formatClass((PsiClass) parent));
                  infos[0] =
                      HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
                          .range(expression)
                          .descriptionAndTooltip(description)
                          .create();
                }
              }
            });
      }
    }
    return infos[0];
  }
  @Nullable
  private static String doCheckRepeatableAnnotation(@NotNull PsiAnnotation annotation) {
    PsiAnnotationOwner owner = annotation.getOwner();
    if (!(owner instanceof PsiModifierList)) return null;
    PsiElement target = ((PsiModifierList) owner).getParent();
    if (!(target instanceof PsiClass) || !((PsiClass) target).isAnnotationType()) return null;
    PsiClass container = getRepeatableContainer(annotation);
    if (container == null) return null;

    PsiMethod[] methods = container.findMethodsByName("value", false);
    if (methods.length == 0) {
      return JavaErrorMessages.message(
          "annotation.container.no.value", container.getQualifiedName());
    }

    if (methods.length == 1) {
      PsiType expected =
          new PsiImmediateClassType((PsiClass) target, PsiSubstitutor.EMPTY).createArrayType();
      if (!expected.equals(methods[0].getReturnType())) {
        return JavaErrorMessages.message(
            "annotation.container.bad.type",
            container.getQualifiedName(),
            JavaHighlightUtil.formatType(expected));
      }
    }

    RetentionPolicy targetPolicy = getRetentionPolicy((PsiClass) target);
    if (targetPolicy != null) {
      RetentionPolicy containerPolicy = getRetentionPolicy(container);
      if (containerPolicy != null && targetPolicy.compareTo(containerPolicy) > 0) {
        return JavaErrorMessages.message(
            "annotation.container.low.retention", container.getQualifiedName(), containerPolicy);
      }
    }

    Set<PsiAnnotation.TargetType> repeatableTargets =
        PsiImplUtil.getAnnotationTargets((PsiClass) target);
    if (repeatableTargets != null) {
      Set<PsiAnnotation.TargetType> containerTargets = PsiImplUtil.getAnnotationTargets(container);
      if (containerTargets != null && !repeatableTargets.containsAll(containerTargets)) {
        return JavaErrorMessages.message(
            "annotation.container.wide.target", container.getQualifiedName());
      }
    }

    return null;
  }
  @Nullable
  public static HighlightInfo checkApplicability(
      @NotNull PsiAnnotation annotation,
      @NotNull LanguageLevel languageLevel,
      @NotNull PsiFile containingFile) {
    if (ANY_ANNOTATION_ALLOWED.accepts(annotation)) {
      return null;
    }

    PsiJavaCodeReferenceElement nameRef = annotation.getNameReferenceElement();
    if (nameRef == null) return null;

    PsiAnnotationOwner owner = annotation.getOwner();
    PsiAnnotation.TargetType[] targets = PsiImplUtil.getTargetsForLocation(owner);
    if (owner == null || targets.length == 0) {
      String message = JavaErrorMessages.message("annotation.not.allowed.here");
      return annotationError(annotation, message);
    }

    if (!(owner instanceof PsiModifierList)) {
      HighlightInfo info =
          HighlightUtil.checkTypeAnnotationFeature(annotation, languageLevel, containingFile);
      if (info != null) return info;
    }

    PsiAnnotation.TargetType applicable = PsiImplUtil.findApplicableTarget(annotation, targets);
    if (applicable == PsiAnnotation.TargetType.UNKNOWN) return null;

    if (applicable == null) {
      String target = JavaErrorMessages.message("annotation.target." + targets[0]);
      String message =
          JavaErrorMessages.message("annotation.not.applicable", nameRef.getText(), target);
      return annotationError(annotation, message);
    }

    if (applicable == PsiAnnotation.TargetType.TYPE_USE) {
      if (owner instanceof PsiClassReferenceType) {
        PsiJavaCodeReferenceElement ref = ((PsiClassReferenceType) owner).getReference();
        HighlightInfo info = checkReferenceTarget(annotation, ref);
        if (info != null) return info;
      } else if (owner instanceof PsiModifierList) {
        PsiElement nextElement =
            PsiTreeUtil.skipSiblingsForward(
                (PsiModifierList) owner,
                PsiComment.class,
                PsiWhiteSpace.class,
                PsiTypeParameterList.class);
        if (nextElement instanceof PsiTypeElement) {
          PsiTypeElement typeElement = (PsiTypeElement) nextElement;
          PsiType type = typeElement.getType();
          if (PsiType.VOID.equals(type)) {
            String message = JavaErrorMessages.message("annotation.not.allowed.void");
            return annotationError(annotation, message);
          }
          if (!(type instanceof PsiPrimitiveType)) {
            PsiJavaCodeReferenceElement ref =
                getOutermostReferenceElement(typeElement.getInnermostComponentReferenceElement());
            HighlightInfo info = checkReferenceTarget(annotation, ref);
            if (info != null) return info;
          }
        }
      } else if (owner instanceof PsiTypeElement) {
        PsiElement context =
            PsiTreeUtil.skipParentsOfType((PsiTypeElement) owner, PsiTypeElement.class);
        if (context instanceof PsiClassObjectAccessExpression) {
          String message = JavaErrorMessages.message("annotation.not.allowed.class");
          return annotationError(annotation, message);
        }
      }
    }

    return null;
  }
  static HighlightInfo checkDuplicateAnnotations(@NotNull PsiAnnotation annotationToCheck) {
    PsiAnnotationOwner owner = annotationToCheck.getOwner();
    if (owner == null) return null;

    PsiJavaCodeReferenceElement element = annotationToCheck.getNameReferenceElement();
    if (element == null) return null;
    PsiElement resolved = element.resolve();
    if (!(resolved instanceof PsiClass)) return null;

    PsiClass annotationType = (PsiClass) resolved;

    PsiClass contained = contained(annotationType);
    String containedElementFQN = contained == null ? null : contained.getQualifiedName();

    if (containedElementFQN != null) {
      PsiClass container = annotationType;
      String containerName = container.getQualifiedName();
      if (isAnnotationRepeatedTwice(owner, containedElementFQN)) {
        String description =
            JavaErrorMessages.message("annotation.container.wrong.place", containerName);
        return annotationError(annotationToCheck, description);
      }
    } else if (isAnnotationRepeatedTwice(owner, annotationType.getQualifiedName())) {
      if (!PsiUtil.isLanguageLevel8OrHigher(annotationToCheck)) {
        String description = JavaErrorMessages.message("annotation.duplicate.annotation");
        return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
            .range(element)
            .descriptionAndTooltip(description)
            .create();
      }
      PsiAnnotation metaAnno =
          PsiImplUtil.findAnnotation(
              annotationType.getModifierList(), CommonClassNames.JAVA_LANG_ANNOTATION_REPEATABLE);

      if (metaAnno == null) {
        String explanation =
            JavaErrorMessages.message(
                "annotation.non.repeatable", annotationType.getQualifiedName());
        String description =
            JavaErrorMessages.message("annotation.duplicate.explained", explanation);
        return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
            .range(element)
            .descriptionAndTooltip(description)
            .create();
      }

      String explanation = doCheckRepeatableAnnotation(metaAnno);
      if (explanation != null) {
        String description =
            JavaErrorMessages.message("annotation.duplicate.explained", explanation);
        return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
            .range(element)
            .descriptionAndTooltip(description)
            .create();
      }

      PsiClass container = getRepeatableContainer(metaAnno);
      if (container != null) {
        PsiAnnotation.TargetType[] targets = PsiImplUtil.getTargetsForLocation(owner);
        PsiAnnotation.TargetType applicable = PsiImplUtil.findApplicableTarget(container, targets);
        if (applicable == null) {
          String target = JavaErrorMessages.message("annotation.target." + targets[0]);
          String message =
              JavaErrorMessages.message(
                  "annotation.container.not.applicable", container.getName(), target);
          return annotationError(annotationToCheck, message);
        }
      }
    }

    for (PsiAnnotation annotation : owner.getAnnotations()) {
      if (annotation == annotationToCheck) continue;
      PsiJavaCodeReferenceElement nameRef = annotation.getNameReferenceElement();
      if (nameRef == null) continue;
      PsiElement aClass = nameRef.resolve();
      if (!resolved.equals(aClass)) continue;
    }

    return null;
  }