@NotNull
  private static Multimap<FqName, Pair<FunctionDescriptor, PsiMethod>>
      getSuperclassToFunctionsMultimap(
          @NotNull PsiMethodWrapper method,
          @NotNull BindingContext bindingContext,
          @NotNull ClassDescriptor containingClass) {
    Multimap<FqName, Pair<FunctionDescriptor, PsiMethod>> result = HashMultimap.create();

    Name functionName = Name.identifier(method.getName());
    int parameterCount = method.getParameters().size();

    for (JetType supertype : TypeUtils.getAllSupertypes(containingClass.getDefaultType())) {
      ClassifierDescriptor klass = supertype.getConstructor().getDeclarationDescriptor();
      assert klass != null;
      FqName fqName = DescriptorUtils.getFQName(klass).toSafe();

      for (FunctionDescriptor fun :
          klass.getDefaultType().getMemberScope().getFunctions(functionName)) {
        if (fun.getKind().isReal() && fun.getValueParameters().size() == parameterCount) {
          PsiElement declaration = BindingContextUtils.descriptorToDeclaration(bindingContext, fun);
          if (declaration instanceof PsiMethod) {
            result.put(fqName, Pair.create(fun, (PsiMethod) declaration));
          } // else declaration is null or JetNamedFunction: both cases are processed later
        }
      }
    }
    return result;
  }
  @Nullable
  private static InsertHandler<LookupElement> getInsertHandler(
      @NotNull DeclarationDescriptor descriptor) {
    if (descriptor instanceof FunctionDescriptor) {
      FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor;

      if (functionDescriptor.getValueParameters().isEmpty()) {
        return EMPTY_FUNCTION_HANDLER;
      }

      if (functionDescriptor.getValueParameters().size() == 1
          && KotlinBuiltIns.getInstance()
              .isFunctionOrExtensionFunctionType(
                  functionDescriptor.getValueParameters().get(0).getType())) {
        return PARAMS_BRACES_FUNCTION_HANDLER;
      }

      return PARAMS_PARENTHESIS_FUNCTION_HANDLER;
    }

    if (descriptor instanceof ClassDescriptor) {
      return JetClassInsertHandler.INSTANCE;
    }

    return null;
  }
  @NotNull
  private VarargCheckResult checkVarargInSuperFunctions(
      @NotNull ValueParameterDescriptor originalParam) {
    boolean someSupersVararg = false;
    boolean someSupersNotVararg = false;
    for (FunctionDescriptor superFunction : superFunctions) {
      if (superFunction.getValueParameters().get(originalParam.getIndex()).getVarargElementType()
          != null) {
        someSupersVararg = true;
      } else {
        someSupersNotVararg = true;
      }
    }

    JetType originalVarargElementType = originalParam.getVarargElementType();
    JetType originalType = originalParam.getType();

    if (someSupersVararg && someSupersNotVararg) {
      reportError("Incompatible super methods: some have vararg parameter, some have not");
      return new VarargCheckResult(originalType, originalVarargElementType != null);
    }

    KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance();
    if (someSupersVararg && originalVarargElementType == null) {
      // convert to vararg

      assert isArrayType(originalType);

      if (builtIns.isPrimitiveArray(originalType)) {
        // replace IntArray? with IntArray
        return new VarargCheckResult(TypeUtils.makeNotNullable(originalType), true);
      }

      // replace Array<out Foo>? with Array<Foo>
      JetType varargElementType = builtIns.getArrayElementType(originalType);
      return new VarargCheckResult(builtIns.getArrayType(INVARIANT, varargElementType), true);
    } else if (someSupersNotVararg && originalVarargElementType != null) {
      // convert to non-vararg

      assert isArrayType(originalType);

      if (builtIns.isPrimitiveArray(originalType)) {
        // replace IntArray with IntArray?
        return new VarargCheckResult(TypeUtils.makeNullable(originalType), false);
      }

      // replace Array<Foo> with Array<out Foo>?
      return new VarargCheckResult(
          TypeUtils.makeNullable(
              builtIns.getArrayType(Variance.OUT_VARIANCE, originalVarargElementType)),
          false);
    }

    return new VarargCheckResult(originalType, originalVarargElementType != null);
  }
  @NotNull
  private static FunctionDescriptor substituteSuperFunction(
      @NotNull Map<ClassDescriptor, JetType> superclassToSupertype,
      @NotNull FunctionDescriptor superFun) {
    DeclarationDescriptor superFunContainer = superFun.getContainingDeclaration();
    assert superFunContainer instanceof ClassDescriptor : superFunContainer;

    JetType supertype = superclassToSupertype.get(superFunContainer);
    assert supertype != null : "Couldn't find super type for super function: " + superFun;
    TypeSubstitutor supertypeSubstitutor = TypeSubstitutor.create(supertype);

    FunctionDescriptor substitutedSuperFun = superFun.substitute(supertypeSubstitutor);
    assert substitutedSuperFun != null;
    return substitutedSuperFun;
  }
  private List<TypeParameterDescriptor> modifyTypeParametersAccordingToSuperMethods(
      List<TypeParameterDescriptor> autoTypeParameters) {
    List<TypeParameterDescriptor> result = Lists.newArrayList();

    for (TypeParameterDescriptor autoParameter : autoTypeParameters) {
      int index = autoParameter.getIndex();
      TypeParameterDescriptorImpl modifiedTypeParameter =
          autoTypeParameterToModified.get(autoParameter);

      List<Iterator<JetType>> upperBoundFromSuperFunctionsIterators = Lists.newArrayList();
      for (FunctionDescriptor superFunction : superFunctions) {
        upperBoundFromSuperFunctionsIterators.add(
            superFunction.getTypeParameters().get(index).getUpperBounds().iterator());
      }

      for (JetType autoUpperBound : autoParameter.getUpperBounds()) {
        List<TypeAndVariance> upperBoundsFromSuperFunctions = Lists.newArrayList();

        for (Iterator<JetType> iterator : upperBoundFromSuperFunctionsIterators) {
          assert iterator.hasNext();
          upperBoundsFromSuperFunctions.add(new TypeAndVariance(iterator.next(), INVARIANT));
        }

        JetType modifiedUpperBound =
            modifyTypeAccordingToSuperMethods(
                autoUpperBound, upperBoundsFromSuperFunctions, UPPER_BOUND);
        modifiedTypeParameter.addUpperBound(modifiedUpperBound);
      }

      for (Iterator<JetType> iterator : upperBoundFromSuperFunctionsIterators) {
        assert !iterator.hasNext();
      }

      modifiedTypeParameter.setInitialized();
      result.add(modifiedTypeParameter);
    }

    return result;
  }
  public static Map<String, DeclarationDescriptor> prepareDefaultNameToDescriptors(
      Project project, KotlinCoreEnvironment environment) {
    KotlinBuiltIns builtIns = JvmPlatform.INSTANCE.getBuiltIns();

    Map<String, DeclarationDescriptor> nameToDescriptor =
        new HashMap<String, DeclarationDescriptor>();
    nameToDescriptor.put(
        "kotlin::Int.plus(Int)",
        standardFunction(builtIns.getInt(), "plus", project, builtIns.getIntType()));
    FunctionDescriptor descriptorForGet =
        standardFunction(builtIns.getArray(), "get", project, builtIns.getIntType());
    nameToDescriptor.put("kotlin::Array.get(Int)", descriptorForGet.getOriginal());
    nameToDescriptor.put(
        "kotlin::Int.compareTo(Double)",
        standardFunction(builtIns.getInt(), "compareTo", project, builtIns.getDoubleType()));
    @NotNull
    FunctionDescriptor descriptorForSet =
        standardFunction(
            builtIns.getArray(), "set", project, builtIns.getIntType(), builtIns.getIntType());
    nameToDescriptor.put("kotlin::Array.set(Int, Int)", descriptorForSet.getOriginal());

    return nameToDescriptor;
  }
  @NotNull
  public static LookupElement createLookupElement(
      @NotNull KotlinCodeAnalyzer analyzer,
      @NotNull DeclarationDescriptor descriptor,
      @Nullable PsiElement declaration) {
    if (declaration != null) {
      MutableLookupElement javaLookupElement = createJavaLookupElementIfPossible(declaration);
      if (javaLookupElement != null) {
        InsertHandler<LookupElement> customHandler = getInsertHandler(descriptor);
        if (customHandler != null) {
          return javaLookupElement.setInsertHandler(getInsertHandler(descriptor));
        } else {
          return javaLookupElement;
        }
      }
    }

    LookupElementBuilder element =
        LookupElementBuilder.create(
            new JetLookupObject(descriptor, analyzer, declaration), descriptor.getName().getName());

    String presentableText = descriptor.getName().getName();
    String typeText = "";
    String tailText = "";
    boolean tailTextGrayed = true;

    if (descriptor instanceof FunctionDescriptor) {
      FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor;
      JetType returnType = functionDescriptor.getReturnType();
      typeText = DescriptorRenderer.TEXT.renderType(returnType);
      presentableText += DescriptorRenderer.TEXT.renderFunctionParameters(functionDescriptor);

      boolean extensionFunction = functionDescriptor.getReceiverParameter() != null;
      DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
      if (containingDeclaration != null && extensionFunction) {
        tailText +=
            " for "
                + DescriptorRenderer.TEXT.renderType(
                    functionDescriptor.getReceiverParameter().getType());
        tailText += " in " + DescriptorUtils.getFQName(containingDeclaration);
      }
    } else if (descriptor instanceof VariableDescriptor) {
      JetType outType = ((VariableDescriptor) descriptor).getType();
      typeText = DescriptorRenderer.TEXT.renderType(outType);
    } else if (descriptor instanceof ClassDescriptor) {
      DeclarationDescriptor declaredIn = descriptor.getContainingDeclaration();
      assert declaredIn != null;
      tailText = " (" + DescriptorUtils.getFQName(declaredIn) + ")";
      tailTextGrayed = true;
    } else {
      typeText = DescriptorRenderer.TEXT.render(descriptor);
    }

    element = element.withInsertHandler(getInsertHandler(descriptor));
    element =
        element
            .withTailText(tailText, tailTextGrayed)
            .withTypeText(typeText)
            .withPresentableText(presentableText);
    element =
        element.withIcon(
            JetDescriptorIconProvider.getIcon(descriptor, Iconable.ICON_FLAG_VISIBILITY));
    element = element.withStrikeoutness(KotlinBuiltIns.getInstance().isDeprecated(descriptor));
    return element;
  }