/** * 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 KotlinType supertype, @NotNull TypeConstructor subtypeConstructor) { assert !supertype.isMarkedNullable() : "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; KotlinType 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> KotlinType 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(), TypeUtils.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> KotlinType substituted = TypeSubstitutor.create(substitution).substitute(subtypeWithVariables, Variance.INVARIANT); return new TypeReconstructionResult(substituted, allArgumentsInferred); }
@NotNull private ClassDescriptor findClassDescriptor( @NotNull KtNamedDeclaration classObjectOrScript, @NotNull LookupLocation location) { MemberScope scope = getMemberScopeDeclaredIn(classObjectOrScript, location); // Why not use the result here. Because it may be that there is a redeclaration: // class A {} class A { fun foo(): A<completion here>} // and if we find the class by name only, we may b-not get the right one. // This call is only needed to make sure the classes are written to trace ClassifierDescriptor scopeDescriptor = scope.getContributedClassifier(classObjectOrScript.getNameAsSafeName(), location); DeclarationDescriptor descriptor = getBindingContext().get(BindingContext.DECLARATION_TO_DESCRIPTOR, classObjectOrScript); if (descriptor == null) { throw new IllegalArgumentException( String.format( "Could not find a classifier for %s.\n" + "Found descriptor: %s (%s).\n", PsiUtilsKt.getElementTextWithContext(classObjectOrScript), scopeDescriptor != null ? DescriptorRenderer.DEBUG_TEXT.render(scopeDescriptor) : "null", scopeDescriptor != null ? (scopeDescriptor.getContainingDeclaration().getClass()) : null)); } return (ClassDescriptor) descriptor; }
private static boolean isConstructedFromGivenClass( @NotNull KotlinType type, @NotNull FqNameUnsafe fqName) { ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor(); return descriptor != null && /* quick check to avoid creation of full FqName instance */ descriptor .getName() .equals(fqName.shortName()) && fqName.equals(getFqName(descriptor)); }
@NotNull public ClassDescriptor getAnnotationClassByName(@NotNull Name simpleName) { ClassifierDescriptor classifier = annotationPackageFragment .getMemberScope() .getContributedClassifier(simpleName, NoLookupLocation.FROM_BUILTINS); assert classifier instanceof ClassDescriptor : "Must be a class descriptor " + simpleName + ", but was " + (classifier == null ? "null" : classifier.toString()); return (ClassDescriptor) classifier; }
// TODO: should be internal @Nullable public static ClassDescriptor getInnerClassByName( @NotNull ClassDescriptor classDescriptor, @NotNull String innerClassName, @NotNull LookupLocation location) { ClassifierDescriptor classifier = classDescriptor .getDefaultType() .getMemberScope() .getContributedClassifier(Name.identifier(innerClassName), location); assert classifier instanceof ClassDescriptor : "Inner class " + innerClassName + " in " + classDescriptor + " should be instance of ClassDescriptor, but was: " + (classifier == null ? "null" : classifier.getClass()); return (ClassDescriptor) classifier; }