String (final Binding bestBinding) { final @NonNls String[] data = new String[myElements.size()]; class Substitutor { PsiType substitute(final PsiType t) { if (t instanceof PsiWildcardType wcType) { final PsiType bound = wcType.getBound(); if (bound == null) { return t; } final PsiManager manager = PsiManager.getInstance(myProject); final PsiType subst = substitute(bound); return subst == null || subst instanceof PsiWildcardType ? subst : wcType.isExtends() ? PsiWildcardType.createExtends(manager, subst) : PsiWildcardType.createSuper(manager, subst); } else if (t instanceof PsiTypeVariable) { if (bestBinding != null) { final PsiType b = bestBinding.apply(t); if (b instanceof Bottom || b instanceof PsiTypeVariable) { return null; } return substitute(b); } return null; } else if (t instanceof Bottom) { return null; } else if (t instanceof PsiArrayType) { return substitute(((PsiArrayType)t).getComponentType()).createArrayType(); } else if (t instanceof PsiClassType) { final PsiClassType.ClassResolveResult result = ((PsiClassType)t).resolveGenerics(); final PsiClass aClass = result.getElement(); final PsiSubstitutor aSubst = result.getSubstitutor(); if (aClass == null) { return t; } PsiSubstitutor theSubst = PsiSubstitutor.EMPTY; for (final PsiTypeParameter parm : aSubst.getSubstitutionMap().keySet()) { final PsiType type = aSubst.substitute(parm); theSubst = theSubst.put(parm, substitute(type)); } return JavaPsiFacade.getElementFactory(aClass.getProject()).createType(aClass, theSubst); } else { return t; } } } final Substitutor binding = new Substitutor(); int i = 0; for (final PsiElement element : myElements) { final PsiType t = myTypes.get(element); if (t != null) { data[i++] = binding.substitute(t).getCanonicalText() + "\\n" + elementString(element); } else { data[i++] = "\\n" + elementString(element); } } return StreamEx.of(data).sorted().map(aData -> aData + "\n").joining(); }