예제 #1
0
  /**
   * 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);
  }
예제 #2
0
  @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;
  }
예제 #3
0
 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));
 }
예제 #4
0
 @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;
 }
예제 #5
0
 // 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;
 }