private static List<FunctionDescriptor> getSuperFunctionsForMethod( @NotNull PsiMethodWrapper method, @NotNull BindingTrace trace, @NotNull ClassDescriptor containingClass) { List<FunctionDescriptor> superFunctions = Lists.newArrayList(); Map<ClassDescriptor, JetType> superclassToSupertype = getSuperclassToSupertypeMap(containingClass); Multimap<FqName, Pair<FunctionDescriptor, PsiMethod>> superclassToFunctions = getSuperclassToFunctionsMultimap(method, trace.getBindingContext(), containingClass); for (HierarchicalMethodSignature superSignature : method.getPsiMethod().getHierarchicalMethodSignature().getSuperSignatures()) { PsiMethod superMethod = superSignature.getMethod(); PsiClass psiClass = superMethod.getContainingClass(); assert psiClass != null; String classFqNameString = psiClass.getQualifiedName(); assert classFqNameString != null; FqName classFqName = new FqName(classFqNameString); if (!JavaToKotlinClassMap.getInstance().mapPlatformClass(classFqName).isEmpty()) { for (FunctionDescriptor superFun : JavaToKotlinMethodMap.INSTANCE.getFunctions(superMethod, containingClass)) { superFunctions.add(substituteSuperFunction(superclassToSupertype, superFun)); } continue; } DeclarationDescriptor superFun = superMethod instanceof JetClsMethod ? trace.get( BindingContext.DECLARATION_TO_DESCRIPTOR, ((JetClsMethod) superMethod).getOrigin()) : findSuperFunction(superclassToFunctions.get(classFqName), superMethod); if (superFun == null) { reportCantFindSuperFunction(method); continue; } assert superFun instanceof FunctionDescriptor : superFun.getClass().getName(); superFunctions.add( substituteSuperFunction(superclassToSupertype, (FunctionDescriptor) superFun)); } // sorting for diagnostic stability Collections.sort( superFunctions, new Comparator<FunctionDescriptor>() { @Override public int compare(FunctionDescriptor fun1, FunctionDescriptor fun2) { FqNameUnsafe fqName1 = getFQName(fun1.getContainingDeclaration()); FqNameUnsafe fqName2 = getFQName(fun2.getContainingDeclaration()); return fqName1.getFqName().compareTo(fqName2.getFqName()); } }); return superFunctions; }
private static void putInMap( PsiClass aClass, Map<MethodSignature, HierarchicalMethodSignature> result, Map<MethodSignature, HierarchicalMethodSignatureImpl> map, HierarchicalMethodSignature hierarchicalMethodSignature, MethodSignature signature) { if (!PsiUtil.isAccessible( aClass.getProject(), hierarchicalMethodSignature.getMethod(), aClass, aClass)) return; HierarchicalMethodSignatureImpl existing = map.get(signature); if (existing == null) { HierarchicalMethodSignatureImpl copy = copy(hierarchicalMethodSignature); LOG.assertTrue(copy.getMethod().isValid()); map.put(signature, copy); } else if (isReturnTypeIsMoreSpecificThan(hierarchicalMethodSignature, existing) && isSuperMethod(aClass, hierarchicalMethodSignature, existing)) { HierarchicalMethodSignatureImpl newSuper = copy(hierarchicalMethodSignature); mergeSupers(newSuper, existing); LOG.assertTrue(newSuper.getMethod().isValid()); map.put(signature, newSuper); } else if (isSuperMethod(aClass, existing, hierarchicalMethodSignature)) { mergeSupers(existing, hierarchicalMethodSignature); } // just drop an invalid method declaration there - to highlight accordingly else if (!result.containsKey(signature)) { LOG.assertTrue(hierarchicalMethodSignature.getMethod().isValid()); result.put(signature, hierarchicalMethodSignature); } }
private static boolean isReturnTypeIsMoreSpecificThan( @NotNull HierarchicalMethodSignature thisSig, @NotNull HierarchicalMethodSignature thatSig) { PsiType thisRet = thisSig.getSubstitutor().substitute(thisSig.getMethod().getReturnType()); PsiType thatRet = thatSig.getSubstitutor().substitute(thatSig.getMethod().getReturnType()); return thatRet != null && thisRet != null && !thatRet.equals(thisRet) && TypeConversionUtil.isAssignable(thatRet, thisRet, false); }
private static boolean isInheritFromObject(@NotNull PsiMethod method) { List<HierarchicalMethodSignature> superSignatures = method.getHierarchicalMethodSignature().getSuperSignatures(); for (HierarchicalMethodSignature s : superSignatures) { PsiClass containingClass = s.getMethod().getContainingClass(); String qualifiedName = containingClass != null ? containingClass.getQualifiedName() : ""; if (qualifiedName != null && qualifiedName.equals(JAVA_LANG_OBJECT)) { return true; } } return false; }
private static boolean normalCase(@NotNull PsiMethod method) { int counter = 0; for (HierarchicalMethodSignature s : method.getHierarchicalMethodSignature().getSuperSignatures()) { PsiClass containingClass = s.getMethod().getContainingClass(); String qualifiedName = containingClass != null ? containingClass.getQualifiedName() : ""; if (qualifiedName != null && !qualifiedName.equals(JAVA_LANG_OBJECT)) { counter++; } } return counter > 0; }
private static HierarchicalMethodSignatureImpl copy(HierarchicalMethodSignature hi) { HierarchicalMethodSignatureImpl hierarchicalMethodSignature = new HierarchicalMethodSignatureImpl(hi); for (HierarchicalMethodSignature his : hi.getSuperSignatures()) { hierarchicalMethodSignature.addSuperSignature(copy(his)); } return hierarchicalMethodSignature; }
private static boolean isSuperMethod( PsiClass aClass, HierarchicalMethodSignature hierarchicalMethodSignature, HierarchicalMethodSignature superSignatureHierarchical) { PsiMethod superMethod = superSignatureHierarchical.getMethod(); PsiClass superClass = superMethod.getContainingClass(); PsiClass containingClass = hierarchicalMethodSignature.getMethod().getContainingClass(); if (!superMethod.isConstructor()) { if (!aClass.equals(superClass)) { if (PsiUtil.isAccessible(aClass.getProject(), superMethod, aClass, aClass)) { if (MethodSignatureUtil.isSubsignature( superSignatureHierarchical, hierarchicalMethodSignature)) { if (superClass != null) { if (superClass.isInterface() || CommonClassNames.JAVA_LANG_OBJECT.equals(superClass.getQualifiedName())) { if (superMethod.hasModifierProperty(PsiModifier.DEFAULT) || hierarchicalMethodSignature .getMethod() .hasModifierProperty(PsiModifier.DEFAULT)) { return !InheritanceUtil.isInheritorOrSelf(superClass, containingClass, true); } return true; } if (containingClass != null) { if (containingClass.isInterface()) { return false; } if (!aClass.isInterface() && !InheritanceUtil.isInheritorOrSelf(superClass, containingClass, true)) { return true; } } } } } } } return false; }
@Nullable static List<MethodSignature> findFunctionCandidates(PsiClass psiClass) { if (psiClass instanceof PsiAnonymousClass) { psiClass = PsiUtil.resolveClassInType(((PsiAnonymousClass) psiClass).getBaseClassType()); } if (psiClass != null && psiClass.isInterface()) { final List<MethodSignature> methods = new ArrayList<MethodSignature>(); final Collection<HierarchicalMethodSignature> visibleSignatures = psiClass.getVisibleSignatures(); for (HierarchicalMethodSignature signature : visibleSignatures) { final PsiMethod psiMethod = signature.getMethod(); if (!psiMethod.hasModifierProperty(PsiModifier.ABSTRACT)) continue; if (psiMethod.hasModifierProperty(PsiModifier.STATIC)) continue; if (!overridesPublicObjectMethod(psiMethod)) { methods.add(signature); } } return hasSubsignature(methods); } return null; }
// uses hierarchy signature tree if available, traverses class structure by itself otherwise public static boolean processDirectSuperMethodsSmart( @NotNull PsiMethod method, @NotNull Processor<PsiMethod> superMethodProcessor) { // boolean old = PsiSuperMethodUtil.isSuperMethod(method, superMethod); PsiClass aClass = method.getContainingClass(); if (aClass == null) return false; if (!canHaveSuperMethod(method, true, false)) return false; Map<MethodSignature, HierarchicalMethodSignature> cachedMap = SIGNATURES_KEY.getCachedValueOrNull(aClass); if (cachedMap != null) { HierarchicalMethodSignature signature = cachedMap.get(method.getSignature(PsiSubstitutor.EMPTY)); if (signature != null) { List<HierarchicalMethodSignature> superSignatures = signature.getSuperSignatures(); for (HierarchicalMethodSignature superSignature : superSignatures) { if (!superMethodProcessor.process(superSignature.getMethod())) return false; } return true; } } PsiClassType[] directSupers = aClass.getSuperTypes(); for (PsiClassType directSuper : directSupers) { PsiClassType.ClassResolveResult resolveResult = directSuper.resolveGenerics(); if (resolveResult.getSubstitutor() != PsiSubstitutor.EMPTY) { // generics break; } PsiClass directSuperClass = resolveResult.getElement(); if (directSuperClass == null) continue; PsiMethod[] candidates = directSuperClass.findMethodsBySignature(method, false); for (PsiMethod candidate : candidates) { if (PsiUtil.canBeOverriden(candidate)) { if (!superMethodProcessor.process(candidate)) return false; } } return true; } List<HierarchicalMethodSignature> superSignatures = method.getHierarchicalMethodSignature().getSuperSignatures(); for (HierarchicalMethodSignature superSignature : superSignatures) { if (!superMethodProcessor.process(superSignature.getMethod())) return false; } return true; }
private static void mergeSupers( final HierarchicalMethodSignatureImpl existing, final HierarchicalMethodSignature superSignature) { for (HierarchicalMethodSignature existingSuper : existing.getSuperSignatures()) { if (existingSuper.getMethod() == superSignature.getMethod()) { for (HierarchicalMethodSignature signature : superSignature.getSuperSignatures()) { mergeSupers((HierarchicalMethodSignatureImpl) existingSuper, signature); } return; } } if (existing.getMethod() == superSignature.getMethod()) { List<HierarchicalMethodSignature> existingSupers = existing.getSuperSignatures(); for (HierarchicalMethodSignature supers : superSignature.getSuperSignatures()) { if (!existingSupers.contains(supers)) existing.addSuperSignature(copy(supers)); } } else { HierarchicalMethodSignatureImpl copy = copy(superSignature); existing.addSuperSignature(copy); } }
// uses hierarchy signature tree if available, traverses class structure by itself otherwise public static boolean isSuperMethodSmart( @NotNull PsiMethod method, @NotNull PsiMethod superMethod) { // boolean old = PsiSuperMethodUtil.isSuperMethod(method, superMethod); if (method == superMethod) return false; PsiClass aClass = method.getContainingClass(); PsiClass superClass = superMethod.getContainingClass(); if (aClass == null || superClass == null || superClass == aClass) return false; if (!canHaveSuperMethod(method, true, false)) return false; PsiMethod[] superMethods = null; Map<MethodSignature, HierarchicalMethodSignature> cachedMap = SIGNATURES_KEY.getCachedValueOrNull(aClass); if (cachedMap != null) { HierarchicalMethodSignature signature = cachedMap.get(method.getSignature(PsiSubstitutor.EMPTY)); if (signature != null) { superMethods = MethodSignatureUtil.convertMethodSignaturesToMethods(signature.getSuperSignatures()); } } if (superMethods == null) { PsiClassType[] directSupers = aClass.getSuperTypes(); List<PsiMethod> found = null; boolean canceled = false; for (PsiClassType directSuper : directSupers) { PsiClassType.ClassResolveResult resolveResult = directSuper.resolveGenerics(); if (resolveResult.getSubstitutor() != PsiSubstitutor.EMPTY) { // generics canceled = true; break; } PsiClass directSuperClass = resolveResult.getElement(); if (directSuperClass == null) continue; PsiMethod[] candidates = directSuperClass.findMethodsBySignature(method, false); if (candidates.length != 0) { if (found == null) found = new ArrayList<PsiMethod>(); for (PsiMethod candidate : candidates) { if (PsiUtil.canBeOverriden(candidate)) found.add(candidate); } } } superMethods = canceled ? null : found == null ? PsiMethod.EMPTY_ARRAY : found.toArray(new PsiMethod[found.size()]); } if (superMethods == null) { superMethods = MethodSignatureUtil.convertMethodSignaturesToMethods( method.getHierarchicalMethodSignature().getSuperSignatures()); } for (PsiMethod superCandidate : superMethods) { if (superMethod.equals(superCandidate) || isSuperMethodSmart(superCandidate, superMethod)) return true; } return false; }
private static Map<MethodSignature, HierarchicalMethodSignature> buildMethodHierarchy( PsiClass aClass, PsiSubstitutor substitutor, final boolean includePrivates, final Set<PsiClass> visited, boolean isInRawContext) { Map<MethodSignature, HierarchicalMethodSignature> result = new LinkedHashMap<MethodSignature, HierarchicalMethodSignature>(); final Map<MethodSignature, List<PsiMethod>> sameParameterErasureMethods = new THashMap<MethodSignature, List<PsiMethod>>( MethodSignatureUtil.METHOD_PARAMETERS_ERASURE_EQUALITY); Map<MethodSignature, HierarchicalMethodSignatureImpl> map = new THashMap<MethodSignature, HierarchicalMethodSignatureImpl>( new TObjectHashingStrategy<MethodSignature>() { @Override public int computeHashCode(MethodSignature signature) { return MethodSignatureUtil.METHOD_PARAMETERS_ERASURE_EQUALITY.computeHashCode( signature); } @Override public boolean equals(MethodSignature o1, MethodSignature o2) { if (!MethodSignatureUtil.METHOD_PARAMETERS_ERASURE_EQUALITY.equals(o1, o2)) return false; List<PsiMethod> list = sameParameterErasureMethods.get(o1); boolean toCheckReturnType = list != null && list.size() > 1; if (!toCheckReturnType) return true; PsiType returnType1 = ((MethodSignatureBackedByPsiMethod) o1).getMethod().getReturnType(); PsiType returnType2 = ((MethodSignatureBackedByPsiMethod) o2).getMethod().getReturnType(); if (returnType1 == null && returnType2 == null) return true; if (returnType1 == null || returnType2 == null) return false; PsiType erasure1 = TypeConversionUtil.erasure(o1.getSubstitutor().substitute(returnType1)); PsiType erasure2 = TypeConversionUtil.erasure(o2.getSubstitutor().substitute(returnType2)); return erasure1.equals(erasure2); } }); for (PsiMethod method : aClass.getMethods()) { if (!method.isValid()) { throw new PsiInvalidElementAccessException( method, "class.valid=" + aClass.isValid() + "; name=" + method.getName()); } if (!includePrivates && method.hasModifierProperty(PsiModifier.PRIVATE)) continue; final MethodSignatureBackedByPsiMethod signature = MethodSignatureBackedByPsiMethod.create(method, substitutor, isInRawContext); HierarchicalMethodSignatureImpl newH = new HierarchicalMethodSignatureImpl(signature); List<PsiMethod> list = sameParameterErasureMethods.get(signature); if (list == null) { list = new SmartList<PsiMethod>(); sameParameterErasureMethods.put(signature, list); } list.add(method); LOG.assertTrue(newH.getMethod().isValid()); result.put(signature, newH); map.put(signature, newH); } for (PsiClassType superType : aClass.getSuperTypes()) { PsiClassType.ClassResolveResult superTypeResolveResult = superType.resolveGenerics(); PsiClass superClass = superTypeResolveResult.getElement(); if (superClass == null) continue; if (!visited.add(superClass)) continue; // cyclic inheritance final PsiSubstitutor superSubstitutor = superTypeResolveResult.getSubstitutor(); PsiSubstitutor finalSubstitutor = obtainFinalSubstitutor(superClass, superSubstitutor, substitutor, isInRawContext); final boolean isInRawContextSuper = (isInRawContext || PsiUtil.isRawSubstitutor(superClass, superSubstitutor)) && superClass.getTypeParameters().length != 0; Map<MethodSignature, HierarchicalMethodSignature> superResult = buildMethodHierarchy(superClass, finalSubstitutor, false, visited, isInRawContextSuper); visited.remove(superClass); List<Pair<MethodSignature, HierarchicalMethodSignature>> flattened = new ArrayList<Pair<MethodSignature, HierarchicalMethodSignature>>(); for (Map.Entry<MethodSignature, HierarchicalMethodSignature> entry : superResult.entrySet()) { HierarchicalMethodSignature hms = entry.getValue(); MethodSignature signature = entry.getKey(); PsiClass containingClass = hms.getMethod().getContainingClass(); List<HierarchicalMethodSignature> supers = new ArrayList<HierarchicalMethodSignature>(hms.getSuperSignatures()); for (HierarchicalMethodSignature aSuper : supers) { PsiClass superContainingClass = aSuper.getMethod().getContainingClass(); if (containingClass != null && superContainingClass != null && !containingClass.isInheritor(superContainingClass, true)) { // methods must be inherited from unrelated classes, so flatten hierarchy here // class C implements SAM1, SAM2 { void methodimpl() {} } // hms.getSuperSignatures().remove(aSuper); flattened.add( new Pair<MethodSignature, HierarchicalMethodSignature>(signature, aSuper)); } } putInMap(aClass, result, map, hms, signature); } for (Pair<MethodSignature, HierarchicalMethodSignature> pair : flattened) { putInMap(aClass, result, map, pair.second, pair.first); } } for (Map.Entry<MethodSignature, HierarchicalMethodSignatureImpl> entry : map.entrySet()) { HierarchicalMethodSignatureImpl hierarchicalMethodSignature = entry.getValue(); MethodSignature methodSignature = entry.getKey(); if (result.get(methodSignature) == null && PsiUtil.isAccessible( aClass.getProject(), hierarchicalMethodSignature.getMethod(), aClass, aClass)) { LOG.assertTrue(hierarchicalMethodSignature.getMethod().isValid()); result.put(methodSignature, hierarchicalMethodSignature); } } return result; }