@NotNull private JetType modifyTypeAccordingToSuperMethods( @NotNull JetType autoType, @NotNull List<TypeAndVariance> typesFromSuper, @NotNull TypeUsage howThisTypeIsUsed) { if (ErrorUtils.isErrorType(autoType)) { return autoType; } boolean resultNullable = typeMustBeNullable(autoType, typesFromSuper, howThisTypeIsUsed); ClassifierDescriptor resultClassifier = modifyTypeClassifier(autoType, typesFromSuper); List<TypeProjection> resultArguments = getTypeArgsOfType(autoType, resultClassifier, typesFromSuper); JetScope resultScope; if (resultClassifier instanceof ClassDescriptor) { resultScope = ((ClassDescriptor) resultClassifier).getMemberScope(resultArguments); } else { resultScope = autoType.getMemberScope(); } JetTypeImpl type = new JetTypeImpl( autoType.getAnnotations(), resultClassifier.getTypeConstructor(), resultNullable, resultArguments, resultScope); PropagationHeuristics.checkArrayInReturnType(this, type, typesFromSuper); return type; }
@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; }
// Returns list with type arguments info from supertypes // Example: // - Foo<A, B> is a subtype of Bar<A, List<B>>, Baz<Boolean, A> // - input: klass = Foo, typesFromSuper = [Bar<String, List<Int>>, Baz<Boolean, CharSequence>] // - output[0] = [String, CharSequence], output[1] = [] private static List<List<TypeProjectionAndVariance>> calculateTypeArgumentsFromSuper( @NotNull ClassDescriptor klass, @NotNull Collection<TypeAndVariance> typesFromSuper) { // For each superclass of klass and its parameters, hold their mapping to klass' parameters // #0 of Bar -> A // #1 of Bar -> List<B> // #0 of Baz -> Boolean // #1 of Baz -> A // #0 of Foo -> A (mapped to itself) // #1 of Foo -> B (mapped to itself) Multimap<TypeConstructor, TypeProjection> substitution = SubstitutionUtils.buildDeepSubstitutionMultimap( TypeUtils.makeUnsubstitutedType(klass, JetScope.EMPTY)); // for each parameter of klass, hold arguments in corresponding supertypes List<List<TypeProjectionAndVariance>> parameterToArgumentsFromSuper = Lists.newArrayList(); for (TypeParameterDescriptor ignored : klass.getTypeConstructor().getParameters()) { parameterToArgumentsFromSuper.add(new ArrayList<TypeProjectionAndVariance>()); } // Enumerate all types from super and all its parameters for (TypeAndVariance typeFromSuper : typesFromSuper) { for (TypeParameterDescriptor parameter : typeFromSuper.type.getConstructor().getParameters()) { TypeProjection argument = typeFromSuper.type.getArguments().get(parameter.getIndex()); // for given example, this block is executed four times: // 1. typeFromSuper = Bar<String, List<Int>>, parameter = "#0 of Bar", argument = // String // 2. typeFromSuper = Bar<String, List<Int>>, parameter = "#1 of Bar", argument = // List<Int> // 3. typeFromSuper = Baz<Boolean, CharSequence>, parameter = "#0 of Baz", argument = // Boolean // 4. typeFromSuper = Baz<Boolean, CharSequence>, parameter = "#1 of Baz", argument = // CharSequence // if it is mapped to klass' parameter, then store it into map for (TypeProjection projection : substitution.get(parameter.getTypeConstructor())) { // 1. projection = A // 2. projection = List<B> // 3. projection = Boolean // 4. projection = A ClassifierDescriptor classifier = projection.getType().getConstructor().getDeclarationDescriptor(); // this condition is true for 1 and 4, false for 2 and 3 if (classifier instanceof TypeParameterDescriptor && classifier.getContainingDeclaration() == klass) { int parameterIndex = ((TypeParameterDescriptor) classifier).getIndex(); Variance effectiveVariance = parameter.getVariance().superpose(typeFromSuper.varianceOfPosition); parameterToArgumentsFromSuper .get(parameterIndex) .add(new TypeProjectionAndVariance(argument, effectiveVariance)); } } } } return parameterToArgumentsFromSuper; }
@NotNull private List<TypeProjection> getTypeArgsOfType( @NotNull JetType autoType, @NotNull ClassifierDescriptor classifier, @NotNull List<TypeAndVariance> typesFromSuper) { List<TypeProjection> autoArguments = autoType.getArguments(); if (!(classifier instanceof ClassDescriptor)) { assert autoArguments.isEmpty() : "Unexpected type arguments when type constructor is not ClassDescriptor, type = " + autoType; return autoArguments; } List<List<TypeProjectionAndVariance>> typeArgumentsFromSuper = calculateTypeArgumentsFromSuper((ClassDescriptor) classifier, typesFromSuper); // Modify type arguments using info from typesFromSuper List<TypeProjection> resultArguments = Lists.newArrayList(); for (TypeParameterDescriptor parameter : classifier.getTypeConstructor().getParameters()) { TypeProjection argument = autoArguments.get(parameter.getIndex()); JetType argumentType = argument.getType(); List<TypeProjectionAndVariance> projectionsFromSuper = typeArgumentsFromSuper.get(parameter.getIndex()); List<TypeAndVariance> argTypesFromSuper = getTypes(projectionsFromSuper); JetType type = modifyTypeAccordingToSuperMethods(argumentType, argTypesFromSuper, TYPE_ARGUMENT); Variance projectionKind = calculateArgumentProjectionKindFromSuper(argument, projectionsFromSuper); resultArguments.add(new TypeProjection(projectionKind, type)); } return resultArguments; }