boolean (@NotNull VarianceCandidate candidate, boolean isExtends) { if (!(candidate.type instanceof PsiClassType type)) return false; PsiClassType.ClassResolveResult resolve = type.resolveGenerics(); PsiClass typeParameter = resolve.getElement(); if (!(typeParameter instanceof PsiTypeParameter)) return false; PsiManager psiManager = typeParameter.getManager(); // consider only type-parameterized methods: "<T> method(X<T>...)" if (!psiManager.areElementsEquivalent(((PsiTypeParameter)typeParameter).getOwner(), candidate.method)) return false; PsiType returnType = candidate.method.getReturnType(); if (returnType == null) return true; boolean returnContainsT = containsDeepIn(returnType, (PsiTypeParameter)typeParameter, resolve.getSubstitutor(), false); if (!isExtends && returnContainsT) return false; PsiParameter[] parameters = candidate.method.getParameterList().getParameters(); for (PsiParameter parameter : parameters) { if (psiManager.areElementsEquivalent(candidate.methodParameter, parameter)) continue; PsiType parameterType = parameter.getType(); if (!containsDeepIn(parameterType, (PsiTypeParameter)typeParameter, resolve.getSubstitutor(), true)) continue; if (!isExtends && !typeResolvesTo(parameterType, (PsiTypeParameter)typeParameter, resolve.getSubstitutor())) return false; if (isExtends) return false; } return !isExtends || typeResolvesTo(returnType, (PsiTypeParameter)typeParameter, resolve.getSubstitutor()) || !returnContainsT; }