@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; }
@Nullable private JetType lookupNamespaceOrClassObject( @NotNull JetSimpleNameExpression expression, @NotNull ResolutionContext context) { Name referencedName = expression.getReferencedNameAsName(); final ClassifierDescriptor classifier = context.scope.getClassifier(referencedName); if (classifier != null) { JetType classObjectType = classifier.getClassObjectType(); if (classObjectType != null) { context.trace.record(REFERENCE_TARGET, expression, classifier); JetType result = getExtendedClassObjectType(classObjectType, referencedName, classifier, context); if (result == null) { context.trace.report(NO_CLASS_OBJECT.on(expression, classifier)); } return DataFlowUtils.checkType(result, expression, context); } } JetType[] result = new JetType[1]; TemporaryBindingTrace temporaryTrace = TemporaryBindingTrace.create( context.trace, "trace for namespace/class object lookup of name", referencedName); if (furtherNameLookup(expression, result, context.replaceBindingTrace(temporaryTrace))) { temporaryTrace.commit(); return DataFlowUtils.checkType(result[0], expression, context); } // To report NO_CLASS_OBJECT when no namespace found if (classifier != null) { if (classifier instanceof TypeParameterDescriptor) { if (context.expressionPosition == ExpressionPosition.FREE) { context.trace.report( TYPE_PARAMETER_IS_NOT_AN_EXPRESSION.on( expression, (TypeParameterDescriptor) classifier)); } else { context.trace.report( TYPE_PARAMETER_ON_LHS_OF_DOT.on(expression, (TypeParameterDescriptor) classifier)); } } else if (context.expressionPosition == ExpressionPosition.FREE) { context.trace.report(NO_CLASS_OBJECT.on(expression, classifier)); } context.trace.record(REFERENCE_TARGET, expression, classifier); JetScope scopeForStaticMembersResolution = classifier instanceof ClassDescriptor ? getStaticNestedClassesScope((ClassDescriptor) classifier) : new JetScopeImpl() { @NotNull @Override public DeclarationDescriptor getContainingDeclaration() { return classifier; } @Override public String toString() { return "Scope for the type parameter on the left hand side of dot"; } }; return new NamespaceType(referencedName, scopeForStaticMembersResolution); } temporaryTrace.commit(); return result[0]; }
@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; }
private String renderDefaultType(JetType type, boolean shortNamesOnly) { StringBuilder sb = new StringBuilder(); ClassifierDescriptor cd = type.getConstructor().getDeclarationDescriptor(); Object typeNameObject; if (cd == null || cd instanceof TypeParameterDescriptor) { typeNameObject = type.getConstructor(); } else { if (shortNamesOnly) { // for nested classes qualified name should be used typeNameObject = cd.getName(); DeclarationDescriptor parent = cd.getContainingDeclaration(); while (parent instanceof ClassDescriptor) { typeNameObject = parent.getName() + "." + typeNameObject; parent = parent.getContainingDeclaration(); } } else { typeNameObject = DescriptorUtils.getFQName(cd); } } sb.append(typeNameObject); if (!type.getArguments().isEmpty()) { sb.append("<"); appendTypeProjections(sb, type.getArguments(), shortNamesOnly); sb.append(">"); } if (type.isNullable()) { sb.append("?"); } return sb.toString(); }
// 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; }
@Nullable public static Name getNameIfStandardType(@NotNull JetType type) { ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor(); if (descriptor != null && descriptor.getContainingDeclaration() == KotlinBuiltIns.getInstance().getBuiltInsPackageFragment()) { return descriptor.getName(); } return null; }
@NotNull private String renderTypeName(@NotNull TypeConstructor typeConstructor) { ClassifierDescriptor cd = typeConstructor.getDeclarationDescriptor(); if (cd instanceof TypeParameterDescriptor) { return renderName(cd.getName()); } else if (cd instanceof ClassDescriptor) { return renderClassName((ClassDescriptor) cd); } else { assert cd == null : "Unexpected classifier: " + cd.getClass(); return typeConstructor.toString(); } }
@Nullable private JetType lookupNamespaceOrClassObject( @NotNull JetSimpleNameExpression expression, @NotNull ResolutionContext context) { Name referencedName = expression.getReferencedNameAsName(); ClassifierDescriptor classifier = context.scope.getClassifier(referencedName); if (classifier != null) { JetType classObjectType = classifier.getClassObjectType(); if (classObjectType != null) { context.trace.record(REFERENCE_TARGET, expression, classifier); JetType result; if (context.expressionPosition == ExpressionPosition.LHS_OF_DOT && classifier instanceof ClassDescriptor) { JetScope scope = new ChainedScope( classifier, classObjectType.getMemberScope(), getStaticNestedClassesScope((ClassDescriptor) classifier)); result = new NamespaceType(referencedName, scope); } else if (context.expressionPosition == ExpressionPosition.LHS_OF_DOT || classifier.isClassObjectAValue()) { result = classObjectType; } else { context.trace.report(NO_CLASS_OBJECT.on(expression, classifier)); result = null; } return DataFlowUtils.checkType(result, expression, context); } } JetType[] result = new JetType[1]; TemporaryBindingTrace temporaryTrace = TemporaryBindingTrace.create( context.trace, "trace for namespace/class object lookup of name", referencedName); if (furtherNameLookup(expression, result, context.replaceBindingTrace(temporaryTrace))) { temporaryTrace.commit(); return DataFlowUtils.checkType(result[0], expression, context); } // To report NO_CLASS_OBJECT when no namespace found if (classifier != null) { if (context.expressionPosition == ExpressionPosition.FREE) { context.trace.report(NO_CLASS_OBJECT.on(expression, classifier)); } context.trace.record(REFERENCE_TARGET, expression, classifier); JetScope scopeForStaticMembersResolution = classifier instanceof ClassDescriptor ? getStaticNestedClassesScope((ClassDescriptor) classifier) : JetScope.EMPTY; return new NamespaceType(referencedName, scopeForStaticMembersResolution); } temporaryTrace.commit(); return result[0]; }
@Nullable public static JetType getPrimitiveRangeElementType(JetType rangeType) { ClassifierDescriptor declarationDescriptor = rangeType.getConstructor().getDeclarationDescriptor(); assert declarationDescriptor != null; if (declarationDescriptor != KotlinBuiltIns.getInstance() .getBuiltInsScope() .getClassifier(declarationDescriptor.getName())) { // Must be a standard library class return null; } return RANGE_TO_ELEMENT_TYPE.get(declarationDescriptor.getName().getName()); }
@Nullable private JetType getExtendedClassObjectType( @NotNull JetType classObjectType, @NotNull Name referencedName, @NotNull ClassifierDescriptor classifier, @NotNull ResolutionContext context) { if (context.expressionPosition == ExpressionPosition.LHS_OF_DOT && classifier instanceof ClassDescriptor) { List<JetScope> scopes = new ArrayList<JetScope>(3); scopes.add(classObjectType.getMemberScope()); scopes.add(getStaticNestedClassesScope((ClassDescriptor) classifier)); NamespaceDescriptor namespace = context.scope.getNamespace(referencedName); if (namespace != null) { scopes.add(namespace.getMemberScope()); } JetScope scope = new ChainedScope(classifier, scopes.toArray(new JetScope[scopes.size()])); return new NamespaceType(referencedName, scope); } else if (context.expressionPosition == ExpressionPosition.LHS_OF_DOT || classifier.isClassObjectAValue()) { return classObjectType; } else { return null; } }
@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; }
/** * Remember that we are trying to cast something of type {@code supertype} to {@code subtype}. * * <p>Since at runtime we can only check the class (type constructor), the rest of the subtype * should be known statically, from supertype. This method reconstructs all static information * that can be obtained from supertype. * * <p>Example 1: supertype = Collection<String> subtype = List<...> result = List<String>, all * arguments are inferred * * <p>Example 2: supertype = Any subtype = List<...> result = List<*>, some arguments were not * inferred, replaced with '*' */ public static TypeReconstructionResult findStaticallyKnownSubtype( @NotNull JetType supertype, @NotNull TypeConstructor subtypeConstructor) { assert !supertype.isNullable() : "This method only makes sense for non-nullable types"; // Assume we are casting an expression of type Collection<Foo> to List<Bar> // First, let's make List<T>, where T is a type variable ClassifierDescriptor descriptor = subtypeConstructor.getDeclarationDescriptor(); assert descriptor != null : "Can't create default type for " + subtypeConstructor; JetType subtypeWithVariables = descriptor.getDefaultType(); // Now, let's find a supertype of List<T> that is a Collection of something, // in this case it will be Collection<T> JetType supertypeWithVariables = TypeCheckingProcedure.findCorrespondingSupertype(subtypeWithVariables, supertype); final List<TypeParameterDescriptor> variables = subtypeWithVariables.getConstructor().getParameters(); Map<TypeConstructor, TypeProjection> substitution; if (supertypeWithVariables != null) { // Now, let's try to unify Collection<T> and Collection<Foo> solution is a map from T to Foo TypeUnifier.UnificationResult solution = TypeUnifier.unify( new TypeProjectionImpl(supertype), new TypeProjectionImpl(supertypeWithVariables), new Predicate<TypeConstructor>() { @Override public boolean apply(TypeConstructor typeConstructor) { ClassifierDescriptor descriptor = typeConstructor.getDeclarationDescriptor(); return descriptor instanceof TypeParameterDescriptor && variables.contains(descriptor); } }); substitution = Maps.newHashMap(solution.getSubstitution()); } else { // If there's no corresponding supertype, no variables are determined // This may be OK, e.g. in case 'Any as List<*>' substitution = Maps.newHashMapWithExpectedSize(variables.size()); } // If some of the parameters are not determined by unification, it means that these parameters // are lost, // let's put stars instead, so that we can only cast to something like List<*>, e.g. (a: Any) as // List<*> boolean allArgumentsInferred = true; for (TypeParameterDescriptor variable : variables) { TypeProjection value = substitution.get(variable.getTypeConstructor()); if (value == null) { substitution.put( variable.getTypeConstructor(), SubstitutionUtils.makeStarProjection(variable)); allArgumentsInferred = false; } } // At this point we have values for all type parameters of List // Let's make a type by substituting them: List<T> -> List<Foo> JetType substituted = TypeSubstitutor.create(substitution).substitute(subtypeWithVariables, Variance.INVARIANT); return new TypeReconstructionResult(substituted, allArgumentsInferred); }