@Nullable
  public static Runnable fillCompletionVariants(
      final JavaSmartCompletionParameters parameters, final Consumer<LookupElement> result) {
    final PsiElement element = parameters.getPosition();
    if (JavaSmartCompletionContributor.INSIDE_TYPECAST_EXPRESSION.accepts(element)) return null;
    if (JavaKeywordCompletion.isAfterPrimitiveOrArrayType(element)) return null;

    final int offset = parameters.getParameters().getOffset();
    final PsiJavaCodeReferenceElement reference =
        PsiTreeUtil.findElementOfClassAtOffset(
            element.getContainingFile(), offset, PsiJavaCodeReferenceElement.class, false);
    if (reference != null) {
      final ElementFilter filter = getReferenceFilter(element, false);
      for (final LookupElement item :
          completeFinalReference(element, reference, filter, parameters)) {
        result.consume(item);
      }

      final boolean secondTime = parameters.getParameters().getInvocationCount() >= 2;

      final Set<LookupElement> base =
          JavaSmartCompletionContributor.completeReference(
              element,
              reference,
              filter,
              false,
              true,
              parameters.getParameters(),
              PrefixMatcher.ALWAYS_TRUE);
      for (final LookupElement item : new LinkedHashSet<LookupElement>(base)) {
        ExpressionLookupItem access = ArrayMemberAccess.getSingleArrayElementAccess(element, item);
        if (access != null) {
          base.add(access);
          PsiType type = access.getType();
          if (type != null && parameters.getExpectedType().isAssignableFrom(type)) {
            result.consume(access);
          }
        }
      }

      if (secondTime) {
        return new SlowerTypeConversions(base, element, reference, parameters, result);
      }
    }
    return null;
  }
  static Set<LookupElement> completeFinalReference(
      final PsiElement element,
      PsiJavaCodeReferenceElement reference,
      ElementFilter filter,
      final JavaSmartCompletionParameters parameters) {
    final Set<PsiField> used =
        parameters.getParameters().getInvocationCount() < 2
            ? findConstantsUsedInSwitch(element)
            : Collections.<PsiField>emptySet();

    final Set<LookupElement> elements =
        JavaSmartCompletionContributor.completeReference(
            element,
            reference,
            new AndFilter(
                filter,
                new ElementFilter() {
                  @Override
                  public boolean isAcceptable(Object o, PsiElement context) {
                    if (o instanceof CandidateInfo) {
                      final CandidateInfo info = (CandidateInfo) o;
                      final PsiElement member = info.getElement();

                      final PsiType expectedType = parameters.getExpectedType();
                      if (expectedType.equals(PsiType.VOID)) {
                        return member instanceof PsiMethod;
                      }

                      //noinspection SuspiciousMethodCalls
                      if (member instanceof PsiEnumConstant
                          && used.contains(CompletionUtil.getOriginalOrSelf(member))) {
                        return false;
                      }

                      return AssignableFromFilter.isAcceptable(
                          member, element, expectedType, info.getSubstitutor());
                    }
                    return false;
                  }

                  @Override
                  public boolean isClassAcceptable(Class hintClass) {
                    return true;
                  }
                }),
            false,
            true,
            parameters.getParameters(),
            PrefixMatcher.ALWAYS_TRUE);
    for (LookupElement lookupElement : elements) {
      if (lookupElement.getObject() instanceof PsiMethod) {
        final JavaMethodCallElement item =
            lookupElement.as(JavaMethodCallElement.CLASS_CONDITION_KEY);
        if (item != null) {
          final PsiMethod method = (PsiMethod) lookupElement.getObject();
          if (SmartCompletionDecorator.hasUnboundTypeParams(method, parameters.getExpectedType())) {
            item.setInferenceSubstitutor(
                SmartCompletionDecorator.calculateMethodReturnTypeSubstitutor(
                    method, parameters.getExpectedType()),
                element);
          }
        }
      }
    }

    return elements;
  }