public static PsiType captureReturnType( PsiMethodCallExpression call, PsiMethod method, PsiType ret, JavaResolveResult result, LanguageLevel languageLevel) { PsiSubstitutor substitutor = result.getSubstitutor(); PsiType substitutedReturnType = substitutor.substitute(ret); if (substitutedReturnType == null) { return TypeConversionUtil.erasure(ret); } if (InferenceSession.wasUncheckedConversionPerformed(call)) { // 18.5.2 // if unchecked conversion was necessary, then this substitution provides the parameter types // of the invocation type, // while the return type and thrown types are given by the erasure of m's type (without // applying θ'). // due to https://bugs.openjdk.java.net/browse/JDK-8135087 erasure is called on // substitutedReturnType and not on ret type itself as by spec return TypeConversionUtil.erasure(substitutedReturnType); } // 15.12.2.6. Method Invocation Type // If unchecked conversion was necessary for the method to be applicable, // the parameter types of the invocation type are the parameter types of the method's type, // and the return type and thrown types are given by the erasures of the return type and thrown // types of the method's type. if (!languageLevel.isAtLeast(LanguageLevel.JDK_1_8) && (method.hasTypeParameters() || JavaVersionService.getInstance().isAtLeast(call, JavaSdkVersion.JDK_1_8)) && result instanceof MethodCandidateInfo && ((MethodCandidateInfo) result).isApplicable()) { final PsiType[] args = call.getArgumentList().getExpressionTypes(); final boolean allowUncheckedConversion = false; final int applicabilityLevel = PsiUtil.getApplicabilityLevel( method, substitutor, args, languageLevel, allowUncheckedConversion, true); if (applicabilityLevel == MethodCandidateInfo.ApplicabilityLevel.NOT_APPLICABLE) { return TypeConversionUtil.erasure(substitutedReturnType); } } if (PsiUtil.isRawSubstitutor(method, substitutor)) { final PsiType returnTypeErasure = TypeConversionUtil.erasure(ret); if (Comparing.equal(TypeConversionUtil.erasure(substitutedReturnType), returnTypeErasure)) { return returnTypeErasure; } } return PsiUtil.captureToplevelWildcards(substitutedReturnType, call); }
public static PsiSubstitutor obtainFinalSubstitutor( @NotNull PsiClass candidateClass, @NotNull PsiSubstitutor candidateSubstitutor, @NotNull PsiClass aClass, @NotNull PsiSubstitutor substitutor, @NotNull PsiElementFactory elementFactory, @NotNull LanguageLevel languageLevel) { if (PsiUtil.isRawSubstitutor(aClass, substitutor)) { return elementFactory.createRawSubstitutor(candidateClass); } final PsiType containingType = elementFactory.createType(candidateClass, candidateSubstitutor, languageLevel); PsiType type = substitutor.substitute(containingType); if (!(type instanceof PsiClassType)) return candidateSubstitutor; return ((PsiClassType) type).resolveGenerics().getSubstitutor(); }
private void specialCase( InferenceSession session, List<ConstraintFormula> constraints, PsiSubstitutor substitutor, PsiParameter[] targetParameters, boolean ignoreRaw) { final PsiElement qualifier = myExpression.getQualifier(); PsiType qualifierType = null; if (qualifier instanceof PsiTypeElement) { qualifierType = ((PsiTypeElement) qualifier).getType(); final PsiClass qualifierClass = PsiUtil.resolveClassInType(qualifierType); if (qualifierClass != null) { qualifierType = JavaPsiFacade.getElementFactory(myExpression.getProject()) .createType(qualifierClass, PsiSubstitutor.EMPTY); } } else if (qualifier instanceof PsiExpression) { qualifierType = ((PsiExpression) qualifier).getType(); if (qualifierType == null && qualifier instanceof PsiReferenceExpression) { final JavaResolveResult resolveResult = ((PsiReferenceExpression) qualifier).advancedResolve(false); final PsiElement res = resolveResult.getElement(); if (res instanceof PsiClass) { PsiClass containingClass = (PsiClass) res; final boolean isRawSubst = !ignoreRaw && !myExpression.isConstructor() && PsiUtil.isRawSubstitutor(containingClass, resolveResult.getSubstitutor()); qualifierType = JavaPsiFacade.getElementFactory(res.getProject()) .createType( containingClass, isRawSubst ? PsiSubstitutor.EMPTY : resolveResult.getSubstitutor()); } } } final PsiClass qualifierClass = PsiUtil.resolveClassInType(qualifierType); if (qualifierClass != null) { session.initBounds(myExpression, qualifierClass.getTypeParameters()); constraints.add( new StrictSubtypingConstraint( session.substituteWithInferenceVariables(qualifierType), session.substituteWithInferenceVariables( substitutor.substitute(targetParameters[0].getType())))); } }
public static boolean processDeclarationsInClass( @NotNull PsiClass aClass, @NotNull final PsiScopeProcessor processor, @NotNull ResolveState state, @Nullable Set<PsiClass> visited, PsiElement last, @NotNull PsiElement place, boolean isRaw) { if (last instanceof PsiTypeParameterList || last instanceof PsiModifierList) { return true; // TypeParameterList and ModifierList do not see our declarations } if (visited != null && visited.contains(aClass)) return true; PsiSubstitutor substitutor = state.get(PsiSubstitutor.KEY); isRaw = isRaw || PsiUtil.isRawSubstitutor(aClass, substitutor); ParameterizedCachedValue<MembersMap, PsiClass> cache = getValues(aClass); // aClass.getUserData(MAP_IN_CLASS_KEY); boolean upToDate = cache.hasUpToDateValue(); LanguageLevel languageLevel = PsiUtil.getLanguageLevel(place); if ( /*true || */ upToDate) { final NameHint nameHint = processor.getHint(NameHint.KEY); if (nameHint != null) { String name = nameHint.getName(state); return processCachedMembersByName( aClass, processor, state, visited, last, place, isRaw, substitutor, cache.getValue(aClass), name, languageLevel); } } return processDeclarationsInClassNotCached( aClass, processor, state, visited, last, place, isRaw, languageLevel); }
private static boolean processSuperTypes( @NotNull PsiClass aClass, @NotNull PsiScopeProcessor processor, @Nullable Set<PsiClass> visited, PsiElement last, @NotNull PsiElement place, @NotNull ResolveState state, boolean isRaw, @NotNull PsiElementFactory factory, @NotNull LanguageLevel languageLevel) { boolean resolved = false; for (final PsiClassType superType : aClass.getSuperTypes()) { final PsiClassType.ClassResolveResult superTypeResolveResult = superType.resolveGenerics(); PsiClass superClass = superTypeResolveResult.getElement(); if (superClass == null) continue; PsiSubstitutor finalSubstitutor = obtainFinalSubstitutor( superClass, superTypeResolveResult.getSubstitutor(), aClass, state.get(PsiSubstitutor.KEY), factory, languageLevel); if (aClass instanceof PsiTypeParameter && PsiUtil.isRawSubstitutor(superClass, finalSubstitutor)) { finalSubstitutor = PsiSubstitutor.EMPTY; } if (!processDeclarationsInClass( superClass, processor, state.put(PsiSubstitutor.KEY, finalSubstitutor), visited, last, place, isRaw)) { resolved = true; } } return !resolved; }
@Override public boolean reduce(InferenceSession session, List<ConstraintFormula> constraints) { if (!LambdaUtil.isFunctionalType(myT)) { return false; } final PsiType groundTargetType = FunctionalInterfaceParameterizationUtil.getGroundTargetType(myT); final PsiClassType.ClassResolveResult classResolveResult = PsiUtil.resolveGenericsClassInType(groundTargetType); final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(classResolveResult); if (interfaceMethod == null) { return false; } final PsiSubstitutor substitutor = LambdaUtil.getSubstitutor(interfaceMethod, classResolveResult); final PsiParameter[] targetParameters = interfaceMethod.getParameterList().getParameters(); final PsiType interfaceMethodReturnType = interfaceMethod.getReturnType(); final PsiType returnType = substitutor.substitute(interfaceMethodReturnType); final PsiType[] typeParameters = myExpression.getTypeParameters(); final PsiMethodReferenceUtil.QualifierResolveResult qualifierResolveResult = PsiMethodReferenceUtil.getQualifierResolveResult(myExpression); if (!myExpression.isExact()) { for (PsiParameter parameter : targetParameters) { if (!session.isProperType(substitutor.substitute(parameter.getType()))) { return false; } } } else { final PsiMember applicableMember = myExpression.getPotentiallyApplicableMember(); LOG.assertTrue(applicableMember != null); final PsiClass applicableMemberContainingClass = applicableMember.getContainingClass(); final PsiClass containingClass = qualifierResolveResult.getContainingClass(); PsiSubstitutor psiSubstitutor = qualifierResolveResult.getSubstitutor(); psiSubstitutor = applicableMemberContainingClass == null || containingClass == null || myExpression.isConstructor() ? psiSubstitutor : TypeConversionUtil.getSuperClassSubstitutor( applicableMemberContainingClass, containingClass, psiSubstitutor); PsiType applicableMethodReturnType = applicableMember instanceof PsiMethod ? ((PsiMethod) applicableMember).getReturnType() : null; int idx = 0; for (PsiTypeParameter param : ((PsiTypeParameterListOwner) applicableMember).getTypeParameters()) { if (idx < typeParameters.length) { psiSubstitutor = psiSubstitutor.put(param, typeParameters[idx++]); } } final PsiParameter[] parameters = applicableMember instanceof PsiMethod ? ((PsiMethod) applicableMember).getParameterList().getParameters() : PsiParameter.EMPTY_ARRAY; if (targetParameters.length == parameters.length + 1) { specialCase(session, constraints, substitutor, targetParameters, true); for (int i = 1; i < targetParameters.length; i++) { constraints.add( new TypeCompatibilityConstraint( session.substituteWithInferenceVariables( psiSubstitutor.substitute(parameters[i - 1].getType())), substitutor.substitute(targetParameters[i].getType()))); } } else if (targetParameters.length == parameters.length) { for (int i = 0; i < targetParameters.length; i++) { constraints.add( new TypeCompatibilityConstraint( session.substituteWithInferenceVariables( psiSubstitutor.substitute(parameters[i].getType())), substitutor.substitute(targetParameters[i].getType()))); } } else { return false; } if (returnType != PsiType.VOID && returnType != null) { if (applicableMethodReturnType == PsiType.VOID) { return false; } if (applicableMethodReturnType != null) { constraints.add( new TypeCompatibilityConstraint( returnType, session.substituteWithInferenceVariables( psiSubstitutor.substitute(applicableMethodReturnType)))); } else if (applicableMember instanceof PsiClass || applicableMember instanceof PsiMethod && ((PsiMethod) applicableMember).isConstructor()) { final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(applicableMember.getProject()); if (containingClass != null) { final PsiType classType = session.substituteWithInferenceVariables( elementFactory.createType(containingClass, psiSubstitutor)); constraints.add(new TypeCompatibilityConstraint(returnType, classType)); } } } return true; } final Map<PsiElement, PsiType> map = LambdaUtil.getFunctionalTypeMap(); final PsiType added = map.put(myExpression, session.startWithFreshVars(groundTargetType)); final JavaResolveResult resolve; try { resolve = myExpression.advancedResolve(true); } finally { if (added == null) { map.remove(myExpression); } } final PsiElement element = resolve.getElement(); if (element == null) { return false; } if (PsiType.VOID.equals(returnType) || returnType == null) { return true; } if (element instanceof PsiMethod) { final PsiMethod method = (PsiMethod) element; final PsiType referencedMethodReturnType; final PsiClass containingClass = method.getContainingClass(); LOG.assertTrue(containingClass != null, method); PsiClass qContainingClass = qualifierResolveResult.getContainingClass(); PsiSubstitutor psiSubstitutor = qualifierResolveResult.getSubstitutor(); if (qContainingClass != null) { if (PsiUtil.isRawSubstitutor(qContainingClass, psiSubstitutor)) { psiSubstitutor = PsiSubstitutor.EMPTY; } if (qContainingClass.isInheritor(containingClass, true)) { psiSubstitutor = TypeConversionUtil.getClassSubstitutor( containingClass, qContainingClass, PsiSubstitutor.EMPTY); LOG.assertTrue(psiSubstitutor != null); } } if (method.isConstructor()) { referencedMethodReturnType = JavaPsiFacade.getElementFactory(method.getProject()) .createType(containingClass, PsiSubstitutor.EMPTY); } else { referencedMethodReturnType = method.getReturnType(); } LOG.assertTrue(referencedMethodReturnType != null, method); if (!PsiTreeUtil.isContextAncestor(containingClass, myExpression, false) || PsiUtil.getEnclosingStaticElement(myExpression, containingClass) != null) { session.initBounds(myExpression, containingClass.getTypeParameters()); } session.initBounds(myExpression, method.getTypeParameters()); // if i) the method reference elides NonWildTypeArguments, // ii) the compile-time declaration is a generic method, and // iii) the return type of the compile-time declaration mentions at least one of the method's // type parameters; if (typeParameters.length == 0 && method.getTypeParameters().length > 0) { final PsiClass interfaceClass = classResolveResult.getElement(); LOG.assertTrue(interfaceClass != null); if (PsiPolyExpressionUtil.mentionsTypeParameters( referencedMethodReturnType, ContainerUtil.newHashSet(method.getTypeParameters()))) { // the constraint reduces to the bound set B3 which would be used to determine the method // reference's invocation type // when targeting the return type of the function type, as defined in 18.5.2. session.collectApplicabilityConstraints( myExpression, ((MethodCandidateInfo) resolve), groundTargetType); session.registerReturnTypeConstraints(referencedMethodReturnType, returnType); return true; } } if (PsiType.VOID.equals(referencedMethodReturnType)) { return false; } int idx = 0; for (PsiTypeParameter param : method.getTypeParameters()) { if (idx < typeParameters.length) { psiSubstitutor = psiSubstitutor.put(param, typeParameters[idx++]); } } final PsiParameter[] parameters = method.getParameterList().getParameters(); if (targetParameters.length == parameters.length + 1 && !method.isVarArgs() && PsiPolyExpressionUtil.mentionsTypeParameters( referencedMethodReturnType, ContainerUtil.newHashSet( containingClass.getTypeParameters()))) { // todo specification bug? specialCase(session, constraints, substitutor, targetParameters, false); } constraints.add( new TypeCompatibilityConstraint( returnType, session.substituteWithInferenceVariables( psiSubstitutor.substitute(referencedMethodReturnType)))); } return true; }
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; }
private Specifics isMoreSpecific( @NotNull MethodCandidateInfo info1, @NotNull MethodCandidateInfo info2, @MethodCandidateInfo.ApplicabilityLevelConstant int applicabilityLevel, @NotNull LanguageLevel languageLevel) { PsiMethod method1 = info1.getElement(); PsiMethod method2 = info2.getElement(); final PsiClass class1 = method1.getContainingClass(); final PsiClass class2 = method2.getContainingClass(); final PsiParameter[] params1 = method1.getParameterList().getParameters(); final PsiParameter[] params2 = method2.getParameterList().getParameters(); final PsiTypeParameter[] typeParameters1 = method1.getTypeParameters(); final PsiTypeParameter[] typeParameters2 = method2.getTypeParameters(); final PsiSubstitutor classSubstitutor1 = info1.getSubstitutor(false); // substitutions for method type parameters will be ignored final PsiSubstitutor classSubstitutor2 = info2.getSubstitutor(false); final int max = Math.max(params1.length, params2.length); PsiType[] types1 = PsiType.createArray(max); PsiType[] types2 = PsiType.createArray(max); final boolean varargsPosition = applicabilityLevel == MethodCandidateInfo.ApplicabilityLevel.VARARGS; for (int i = 0; i < max; i++) { ProgressManager.checkCanceled(); PsiType type1 = params1.length > 0 ? params1[Math.min(i, params1.length - 1)].getType() : null; PsiType type2 = params2.length > 0 ? params2[Math.min(i, params2.length - 1)].getType() : null; if (varargsPosition) { if (type1 instanceof PsiEllipsisType && type2 instanceof PsiEllipsisType && params1.length == params2.length && class1 != null && (!JavaVersionService.getInstance().isAtLeast(class1, JavaSdkVersion.JDK_1_7) || ((PsiArrayType) type1) .getComponentType() .equalsToText(CommonClassNames.JAVA_LANG_OBJECT) || ((PsiArrayType) type2) .getComponentType() .equalsToText(CommonClassNames.JAVA_LANG_OBJECT))) { type1 = ((PsiEllipsisType) type1).toArrayType(); type2 = ((PsiEllipsisType) type2).toArrayType(); } else { type1 = type1 instanceof PsiEllipsisType ? ((PsiArrayType) type1).getComponentType() : type1; type2 = type2 instanceof PsiEllipsisType ? ((PsiArrayType) type2).getComponentType() : type2; } } types1[i] = type1; types2[i] = type2; } boolean sameBoxing = true; int[] boxingHappened = new int[2]; for (int i = 0; i < types1.length; i++) { ProgressManager.checkCanceled(); PsiType type1 = classSubstitutor1.substitute(types1[i]); PsiType type2 = classSubstitutor2.substitute(types2[i]); PsiType argType = i < getActualParameterTypes().length ? getActualParameterTypes()[i] : null; boolean boxingInFirst = false; if (isBoxingHappened(argType, type1, languageLevel)) { boxingHappened[0] += 1; boxingInFirst = true; } boolean boxingInSecond = false; if (isBoxingHappened(argType, type2, languageLevel)) { boxingHappened[1] += 1; boxingInSecond = true; } sameBoxing &= boxingInFirst == boxingInSecond; } if (boxingHappened[0] == 0 && boxingHappened[1] > 0) return Specifics.FIRST; if (boxingHappened[0] > 0 && boxingHappened[1] == 0) return Specifics.SECOND; if (sameBoxing) { final PsiSubstitutor siteSubstitutor1 = info1.getSiteSubstitutor(); final PsiSubstitutor siteSubstitutor2 = info2.getSiteSubstitutor(); final PsiType[] types2AtSite = typesAtSite(types2, siteSubstitutor2); final PsiType[] types1AtSite = typesAtSite(types1, siteSubstitutor1); final PsiSubstitutor methodSubstitutor1 = calculateMethodSubstitutor( typeParameters1, method1, siteSubstitutor1, types1, types2AtSite, languageLevel); boolean applicable12 = isApplicableTo( types2AtSite, method1, languageLevel, varargsPosition, methodSubstitutor1, method2); final PsiSubstitutor methodSubstitutor2 = calculateMethodSubstitutor( typeParameters2, method2, siteSubstitutor2, types2, types1AtSite, languageLevel); boolean applicable21 = isApplicableTo( types1AtSite, method2, languageLevel, varargsPosition, methodSubstitutor2, method1); if (!myLanguageLevel.isAtLeast(LanguageLevel.JDK_1_8)) { final boolean typeArgsApplicable12 = GenericsUtil.isTypeArgumentsApplicable( typeParameters1, methodSubstitutor1, myArgumentsList, !applicable21); final boolean typeArgsApplicable21 = GenericsUtil.isTypeArgumentsApplicable( typeParameters2, methodSubstitutor2, myArgumentsList, !applicable12); if (!typeArgsApplicable12) { applicable12 = false; } if (!typeArgsApplicable21) { applicable21 = false; } } if (applicable12 || applicable21) { if (applicable12 && !applicable21) return Specifics.SECOND; if (applicable21 && !applicable12) return Specifics.FIRST; final boolean abstract1 = method1.hasModifierProperty(PsiModifier.ABSTRACT); final boolean abstract2 = method2.hasModifierProperty(PsiModifier.ABSTRACT); if (abstract1 && !abstract2) { return Specifics.SECOND; } if (abstract2 && !abstract1) { return Specifics.FIRST; } } if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8) && myArgumentsList instanceof PsiExpressionList && (typeParameters1.length == 0 || typeParameters2.length == 0)) { boolean toCompareFunctional = false; if (types1.length > 0 && types2.length > 0) { for (int i = 0; i < getActualParametersLength(); i++) { final PsiType type1 = types1[Math.min(i, types1.length - 1)]; final PsiType type2 = types2[Math.min(i, types2.length - 1)]; // from 15.12.2.5 Choosing the Most Specific Method // In addition, a functional interface type S is more specific than a functional // interface type T for an expression exp // if T is not a subtype of S and one of the following conditions apply. if (LambdaUtil.isFunctionalType(type1) && !TypeConversionUtil.erasure(type1).isAssignableFrom(type2) && LambdaUtil.isFunctionalType(type2) && !TypeConversionUtil.erasure(type2).isAssignableFrom(type1)) { types1AtSite[Math.min(i, types1.length - 1)] = PsiType.NULL; types2AtSite[Math.min(i, types2.length - 1)] = PsiType.NULL; toCompareFunctional = true; } } } if (toCompareFunctional) { final boolean applicable12ignoreFunctionalType = isApplicableTo( types2AtSite, method1, languageLevel, varargsPosition, calculateMethodSubstitutor( typeParameters1, method1, siteSubstitutor1, types1, types2AtSite, languageLevel), null); final boolean applicable21ignoreFunctionalType = isApplicableTo( types1AtSite, method2, languageLevel, varargsPosition, calculateMethodSubstitutor( typeParameters2, method2, siteSubstitutor2, types2, types1AtSite, languageLevel), null); if (applicable12ignoreFunctionalType || applicable21ignoreFunctionalType) { Specifics specifics = null; for (int i = 0; i < getActualParametersLength(); i++) { if (types1AtSite[Math.min(i, types1.length - 1)] == PsiType.NULL && types2AtSite[Math.min(i, types2.length - 1)] == PsiType.NULL) { Specifics specific = isFunctionalTypeMoreSpecific( info1, info2, ((PsiExpressionList) myArgumentsList).getExpressions()[i], i); if (specific == Specifics.NEITHER) { specifics = Specifics.NEITHER; break; } if (specifics == null) { specifics = specific; } else if (specifics != specific) { specifics = Specifics.NEITHER; break; } } } if (!applicable12ignoreFunctionalType && applicable21ignoreFunctionalType) { return specifics == Specifics.FIRST ? Specifics.FIRST : Specifics.NEITHER; } if (!applicable21ignoreFunctionalType && applicable12ignoreFunctionalType) { return specifics == Specifics.SECOND ? Specifics.SECOND : Specifics.NEITHER; } return specifics; } } } } else if (varargsPosition) { final PsiType lastParamType1 = classSubstitutor1.substitute(types1[types1.length - 1]); final PsiType lastParamType2 = classSubstitutor2.substitute(types2[types1.length - 1]); final boolean assignable1 = TypeConversionUtil.isAssignable(lastParamType2, lastParamType1); final boolean assignable2 = TypeConversionUtil.isAssignable(lastParamType1, lastParamType2); if (assignable1 && !assignable2) { return Specifics.FIRST; } if (assignable2 && !assignable1) { return Specifics.SECOND; } } if (class1 != class2) { if (class2.isInheritor(class1, true) || class1.isInterface() && !class2.isInterface()) { if (MethodSignatureUtil.isSubsignature( method1.getSignature(info1.getSubstitutor(false)), method2.getSignature(info2.getSubstitutor(false)))) { return Specifics.SECOND; } else if (method1.hasModifierProperty(PsiModifier.STATIC) && method2.hasModifierProperty(PsiModifier.STATIC) && boxingHappened[0] == 0) { return Specifics.SECOND; } } else if (MethodSignatureUtil.areErasedParametersEqual( method1.getSignature(PsiSubstitutor.EMPTY), method2.getSignature(PsiSubstitutor.EMPTY)) && MethodSignatureUtil.isSubsignature( method2.getSignature(info2.getSubstitutor(false)), method1.getSignature(info1.getSubstitutor(false)))) { return Specifics.FIRST; } else if (class1.isInheritor(class2, true) || class2.isInterface()) { if (method1.hasModifierProperty(PsiModifier.STATIC) && method2.hasModifierProperty(PsiModifier.STATIC) && boxingHappened[0] == 0) { return Specifics.FIRST; } } } final boolean raw1 = PsiUtil.isRawSubstitutor(method1, classSubstitutor1); final boolean raw2 = PsiUtil.isRawSubstitutor(method2, classSubstitutor2); if (raw1 ^ raw2) { return raw1 ? Specifics.SECOND : Specifics.FIRST; } final boolean varargs1 = info1.isVarargs(); final boolean varargs2 = info2.isVarargs(); if (varargs1 ^ varargs2) { return varargs1 ? Specifics.SECOND : Specifics.FIRST; } return Specifics.NEITHER; }
private static boolean emptyOrRaw(PsiClass containingClass, PsiSubstitutor psiSubstitutor) { return PsiUtil.isRawSubstitutor(containingClass, psiSubstitutor) || psiSubstitutor.getSubstitutionMap().isEmpty(); }
private static PsiMethod generateDelegateMethod( PsiMethod method, PsiClass superClass, PsiSubstitutor substitutor, boolean keepParameterAnnotations) { final LightMethodBuilder builder = new LightMethodBuilder( superClass.getManager(), GroovyFileType.GROOVY_LANGUAGE, method.getName()); builder.setContainingClass(superClass); builder.setMethodReturnType(substitutor.substitute(method.getReturnType())); builder.setNavigationElement(method); builder.addModifier(PsiModifier.PUBLIC); final PsiTypeParameter[] typeParameters = method.getTypeParameters(); final PsiClass containingClass = method.getContainingClass(); boolean isRaw = containingClass != null && PsiUtil.isRawSubstitutor(containingClass, substitutor); if (isRaw) { substitutor = JavaPsiFacade.getInstance(method.getProject()) .getElementFactory() .createRawSubstitutor(substitutor, typeParameters); } if (!isRaw) { for (PsiTypeParameter typeParameter : typeParameters) { builder.addTypeParameter(typeParameter); } } final PsiParameter[] originalParameters = method.getParameterList().getParameters(); for (int i = 0; i < originalParameters.length; i++) { PsiParameter originalParameter = originalParameters[i]; PsiType type; if (isRaw) { type = TypeConversionUtil.erasure(substitutor.substitute(originalParameter.getType())); } else { type = substitutor.substitute(originalParameter.getType()); } if (type == null) { type = PsiType.getJavaLangObject(superClass.getManager(), superClass.getResolveScope()); } final LightParameter lightParameter = new LightParameter( StringUtil.notNullize(originalParameter.getName(), "p" + i), type, builder, JavaLanguage.INSTANCE); if (keepParameterAnnotations) { final PsiCompositeModifierList delegatingModifierList = new PsiCompositeModifierList( method.getManager(), Collections.singletonList(originalParameter.getModifierList())); lightParameter.setModifierList(delegatingModifierList); } builder.addParameter(lightParameter); } builder.setBaseIcon(JetgroovyIcons.Groovy.Method); return new DelegatedMethod(builder, method); }