Refine type arguments resolution and rendering

In case of type constructors captured parameters from outer classes

 #KT-5510 Fixed
 #KT-3112 Fixed
 #KT-6325 Fixed
 #KT-408  Fixed
 #KT-6337 Fixed
This commit is contained in:
Denis Zharkov
2015-11-03 14:28:34 +03:00
parent 990bd7e71d
commit deea0643ad
72 changed files with 1450 additions and 94 deletions
@@ -104,6 +104,8 @@ public interface Errors {
DiagnosticFactory0<PsiElement> EXPANSIVE_INHERITANCE = DiagnosticFactory0.create(ERROR);
DiagnosticFactory1<PsiElement, String> EXPANSIVE_INHERITANCE_IN_JAVA = DiagnosticFactory1.create(WARNING);
DiagnosticFactory0<KtTypeArgumentList> TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED = DiagnosticFactory0.create(ERROR);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Errors in declarations
@@ -180,8 +182,6 @@ public interface Errors {
DiagnosticFactory0<PsiElement> NO_GENERICS_IN_SUPERTYPE_SPECIFIER = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> GENERICS_IN_CONTAINING_TYPE_NOT_ALLOWED = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtTypeReference> MANY_CLASSES_IN_SUPERTYPE_LIST = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtTypeReference> SUPERTYPE_APPEARS_TWICE = DiagnosticFactory0.create(ERROR);
DiagnosticFactory3<KtDelegationSpecifierList, TypeParameterDescriptor, ClassDescriptor, Collection<KotlinType>>
@@ -328,7 +328,6 @@ public class DefaultErrorMessages {
MAP.put(TYPE_PARAMETER_IS_NOT_AN_EXPRESSION, "Type parameter ''{0}'' is not an expression", NAME);
MAP.put(TYPE_PARAMETER_ON_LHS_OF_DOT, "Type parameter ''{0}'' cannot have or inherit a companion object, so it cannot be on the left hand side of dot", NAME);
MAP.put(NO_GENERICS_IN_SUPERTYPE_SPECIFIER, "Generic arguments of the base type must be specified");
MAP.put(GENERICS_IN_CONTAINING_TYPE_NOT_ALLOWED, "Generic arguments in containing types are not allowed");
MAP.put(NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE, "Nested {0} accessed via instance reference", RENDER_CLASS_OR_OBJECT_NAME);
MAP.put(NESTED_CLASS_SHOULD_BE_QUALIFIED, "Nested {0} should be qualified as ''{1}''", RENDER_CLASS_OR_OBJECT_NAME, TO_STRING);
@@ -512,6 +511,8 @@ public class DefaultErrorMessages {
MAP.put(REDUNDANT_PROJECTION, "Projection is redundant: the corresponding type parameter of {0} has the same variance", NAME);
MAP.put(CONFLICTING_PROJECTION, "Projection is conflicting with variance of the corresponding type parameter of {0}. Remove the projection or replace it with ''*''", NAME);
MAP.put(TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED, "Type arguments for outer class are redundant when nested class is referenced");
MAP.put(TYPE_MISMATCH_IN_FOR_LOOP, "The loop iterates over values of type {0} but the parameter is declared to be {1}", RENDER_TYPE,
RENDER_TYPE);
MAP.put(TYPE_MISMATCH_IN_CONDITION, "Condition must be of type kotlin.Boolean, but is of type {0}", RENDER_TYPE);
@@ -50,6 +50,7 @@ public class KtUserType extends KtElementImplStub<KotlinUserTypeStub> implements
return visitor.visitUserType(this, data);
}
@Nullable
public KtTypeArgumentList getTypeArgumentList() {
return getStubOrPsiChild(KtStubElementTypes.TYPE_ARGUMENT_LIST);
}
@@ -1199,7 +1199,7 @@ public class DescriptorResolver {
return;
}
assert jetTypeArguments.size() == arguments.size() : typeElement.getText() + ": " + jetTypeArguments + " - " + arguments;
assert jetTypeArguments.size() <= arguments.size() : typeElement.getText() + ": " + jetTypeArguments + " - " + arguments;
TypeSubstitutor substitutor = TypeSubstitutor.create(type);
for (int i = 0; i < jetTypeArguments.size(); i++) {
@@ -50,24 +50,35 @@ public class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageVa
}
}
public fun resolveDescriptorForUserType(
data class TypeQualifierResolutionResult(
val qualifierParts: List<QualifierPart>,
val classifierDescriptor: ClassifierDescriptor? = null
) {
val allProjections: List<KtTypeProjection>
get() = qualifierParts.flatMap { it.typeArguments?.arguments.orEmpty() }
}
public fun resolveDescriptorForType(
userType: KtUserType,
scope: LexicalScope,
trace: BindingTrace
): ClassifierDescriptor? {
): TypeQualifierResolutionResult {
if (userType.qualifier == null && !userType.startWithPackage) { // optimization for non-qualified types
return userType.referenceExpression?.let {
val descriptor = userType.referenceExpression?.let {
val classifier = scope.findClassifier(it.getReferencedNameAsName(), KotlinLookupLocation(it))
storeResult(trace, it, classifier, scope.ownerDescriptor, inImport = false, isQualifier = false)
classifier
}
return TypeQualifierResolutionResult(userType.asQualifierPartList().first, descriptor)
}
val module = scope.ownerDescriptor.module
val (qualifierPartList, hasError) = userType.asQualifierPartList()
if (hasError) {
resolveToPackageOrClass(qualifierPartList, module, trace, scope.ownerDescriptor, scope, inImport = false)
return null
val descriptor = resolveToPackageOrClass(
qualifierPartList, module, trace, scope.ownerDescriptor, scope, inImport = false) as? ClassifierDescriptor
return TypeQualifierResolutionResult(qualifierPartList, descriptor)
}
assert(qualifierPartList.size() >= 1) {
"Too short qualifier list for user type $userType : ${qualifierPartList.joinToString()}"
@@ -76,7 +87,7 @@ public class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageVa
val qualifier = resolveToPackageOrClass(
qualifierPartList.subList(0, qualifierPartList.size() - 1), module,
trace, scope.ownerDescriptor, scope.check { !userType.startWithPackage }, inImport = false
) ?: return null
) ?: return TypeQualifierResolutionResult(qualifierPartList, null)
val lastPart = qualifierPartList.last()
val classifier = when (qualifier) {
@@ -85,7 +96,8 @@ public class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageVa
else -> null
}
storeResult(trace, lastPart.expression, classifier, scope.ownerDescriptor, inImport = false, isQualifier = false)
return classifier
return TypeQualifierResolutionResult(qualifierPartList, classifier)
}
private val KtUserType.startWithPackage: Boolean
@@ -266,7 +278,7 @@ public class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageVa
return result.asReversed()
}
private data class QualifierPart(
data class QualifierPart(
val name: Name,
val expression: KtSimpleNameExpression,
val typeArguments: KtTypeArgumentList? = null
@@ -16,14 +16,13 @@
package org.jetbrains.kotlin.resolve
import com.intellij.util.SmartList
import org.jetbrains.kotlin.context.TypeLazinessToken
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.diagnostics.Errors.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.codeFragmentUtil.debugTypeInfo
@@ -37,6 +36,7 @@ import org.jetbrains.kotlin.resolve.lazy.LazyEntity
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.resolve.scopes.LazyScopeAdapter
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.resolve.scopes.utils.findClassifier
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.types.*
@@ -154,9 +154,12 @@ public class TypeResolver(
var result: PossiblyBareType? = null
typeElement?.accept(object : KtVisitorVoid() {
override fun visitUserType(type: KtUserType) {
val classifierDescriptor = resolveClass(c.scope, type, c.trace)
val qualifierResolutionResults = resolveDescriptorForType(c.scope, type, c.trace)
val (qualifierParts, classifierDescriptor) = qualifierResolutionResults
if (classifierDescriptor == null) {
val arguments = resolveTypeProjections(c, ErrorUtils.createErrorType("No type").getConstructor(), type.getTypeArguments())
val arguments = resolveTypeProjections(
c, ErrorUtils.createErrorType("No type").constructor, qualifierResolutionResults.allProjections)
result = type(ErrorUtils.createErrorTypeWithArguments(type.getDebugText(), arguments))
return
}
@@ -167,15 +170,16 @@ public class TypeResolver(
c.trace.record(BindingContext.REFERENCE_TARGET, referenceExpression, classifierDescriptor)
if (type.hasTypesWithTypeArgsInside()) {
c.trace.report(Errors.GENERICS_IN_CONTAINING_TYPE_NOT_ALLOWED.on(type))
}
result = when (classifierDescriptor) {
is TypeParameterDescriptor -> {
assert(qualifierParts.size == 1) {
"Type parameter can be resolved only by it's short name, but '${type.text}' is contradiction " +
"with ${qualifierParts.size} qualifier parts"
}
type(resolveTypeForTypeParameter(c, annotations, classifierDescriptor, referenceExpression, type))
}
is ClassDescriptor -> resolveTypeForClass(c, annotations, classifierDescriptor, type)
is ClassDescriptor -> resolveTypeForClass(c, annotations, classifierDescriptor, type, qualifierResolutionResults)
else -> error("Unexpected classifier type: ${classifierDescriptor.javaClass}")
}
}
@@ -288,35 +292,41 @@ public class TypeResolver(
private fun resolveTypeForClass(
c: TypeResolutionContext, annotations: Annotations,
classDescriptor: ClassDescriptor, type: KtUserType
classDescriptor: ClassDescriptor, type: KtUserType,
qualifierResolutionResult: QualifiedExpressionResolver.TypeQualifierResolutionResult
): PossiblyBareType {
val typeConstructor = classDescriptor.typeConstructor
val arguments = resolveTypeProjections(c, typeConstructor, type.typeArguments)
val parameters = typeConstructor.parameters
val expectedArgumentCount = parameters.size()
val actualArgumentCount = arguments.size()
if (ErrorUtils.isError(classDescriptor)) {
return type(ErrorUtils.createErrorTypeWithArguments("[Error type: $typeConstructor]", arguments))
val projectionFromAllQualifierParts = qualifierResolutionResult.allProjections
val parameters = typeConstructor.parameters
if (c.allowBareTypes && projectionFromAllQualifierParts.isEmpty() && parameters.isNotEmpty()) {
// See docs for PossiblyBareType
return PossiblyBareType.bare(typeConstructor, false)
}
if (actualArgumentCount != expectedArgumentCount) {
if (actualArgumentCount == 0) {
// See docs for PossiblyBareType
if (c.allowBareTypes) {
return PossiblyBareType.bare(typeConstructor, false)
}
c.trace.report(WRONG_NUMBER_OF_TYPE_ARGUMENTS.on(type, expectedArgumentCount))
}
else {
c.trace.report(WRONG_NUMBER_OF_TYPE_ARGUMENTS.on(type.typeArgumentList, expectedArgumentCount))
}
if (ErrorUtils.isError(classDescriptor)) {
return createErrorTypeAndResolveArguments(c, projectionFromAllQualifierParts, "[Error type: $typeConstructor]")
}
return type(ErrorUtils.createErrorTypeWithArguments("" + typeConstructor, arguments))
val collectedArgumentAsTypeProjections =
collectArgumentsForClassTypeConstructor(c, classDescriptor, qualifierResolutionResult.qualifierParts)
?: return createErrorTypeAndResolveArguments(c, projectionFromAllQualifierParts, typeConstructor.toString())
assert(collectedArgumentAsTypeProjections.size <= parameters.size) {
"Collected arguments count should be not greater then parameters count," +
" but ${collectedArgumentAsTypeProjections.size} instead of ${parameters.size} found in ${type.text}"
}
val argumentsFromUserType = resolveTypeProjections(c, typeConstructor, collectedArgumentAsTypeProjections)
val arguments = argumentsFromUserType + appendDefaultArgumentsForInnerScope(argumentsFromUserType.size, parameters)
assert(arguments.size == parameters.size) {
"Collected arguments count should be equal to parameters count," +
" but ${collectedArgumentAsTypeProjections.size} instead of ${parameters.size} found in ${type.text}"
}
if (Flexibility.FLEXIBLE_TYPE_CLASSIFIER.asSingleFqName().toUnsafe() == DescriptorUtils.getFqName(classDescriptor)
&& classDescriptor.typeConstructor.parameters.size() == 2) {
&& parameters.size == 2) {
// We create flexible types by convention here
// This is not intended to be used in normal users' environments, only for tests and debugger etc
return type(DelegatingFlexibleType.create(
@@ -332,7 +342,7 @@ public class TypeResolver(
for (i in parameters.indices) {
val parameter = parameters[i]
val argument = arguments[i].type
val typeReference = type.typeArguments[i].typeReference
val typeReference = collectedArgumentAsTypeProjections.getOrNull(i)?.typeReference
if (typeReference != null) {
DescriptorResolver.checkBounds(typeReference, argument, parameter, substitutor, c.trace)
@@ -343,6 +353,114 @@ public class TypeResolver(
return type(resultingType)
}
private fun collectArgumentsForClassTypeConstructor(
c: TypeResolutionContext,
classDescriptor: ClassDescriptor,
qualifierParts: List<QualifiedExpressionResolver.QualifierPart>
): List<KtTypeProjection>? {
val classDescriptorChain = classDescriptor.classDescriptorChain()
val reversedQualifierParts = qualifierParts.asReversed()
var wasStatic = false
var result = SmartList<KtTypeProjection>()
val classChainLastIndex = Math.min(classDescriptorChain.size, reversedQualifierParts.size) - 1
for (index in 0..classChainLastIndex) {
val qualifierPart = reversedQualifierParts[index]
val currentArguments = qualifierPart.typeArguments?.arguments.orEmpty()
val declaredTypeParameters = classDescriptorChain[index].declaredTypeParameters
val currentParameters = if (wasStatic) emptyList() else declaredTypeParameters
if (wasStatic && currentArguments.isNotEmpty() && declaredTypeParameters.isNotEmpty()) {
c.trace.report(TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED.on(qualifierPart.typeArguments!!))
return null
}
if (currentArguments.size != currentParameters.size) {
c.trace.report(
WRONG_NUMBER_OF_TYPE_ARGUMENTS.on(qualifierPart.typeArguments ?: qualifierPart.expression, currentParameters.size))
return null
}
result.addAll(currentArguments)
wasStatic = wasStatic || !classDescriptorChain[index].isInner
}
val nonClassQualifierParts =
reversedQualifierParts.subList(
Math.min(classChainLastIndex + 1, reversedQualifierParts.size),
reversedQualifierParts.size)
for (qualifierPart in nonClassQualifierParts) {
if (qualifierPart.typeArguments != null) {
c.trace.report(WRONG_NUMBER_OF_TYPE_ARGUMENTS.on(qualifierPart.typeArguments, 0))
return null
}
}
val parameters = classDescriptor.typeConstructor.parameters
if (result.size < parameters.size) {
if (parameters.subList(result.size, parameters.size).any { parameter -> !parameter.isDeclaredInScope(c) }) {
c.trace.report(WRONG_NUMBER_OF_TYPE_ARGUMENTS.on(qualifierParts.last().expression, parameters.size))
return null
}
}
return result
}
private fun ClassifierDescriptor?.classDescriptorChain(): List<ClassDescriptor>
= sequence({ this as? ClassDescriptor }, { it.containingDeclaration as? ClassDescriptor }).toList()
private fun TypeParameterDescriptor.isDeclaredInScope(c: TypeResolutionContext): Boolean {
assert(containingDeclaration is ClassDescriptor) { "This function is implemented for classes only, but $containingDeclaration was given" }
// This function checks whether this@TypeParameterDescriptor ()is reachable from current scope by it's name
// The only way it can be is that we are within class that contains it
// We could just walk through containing declarations as it's done on last return, but it can be rather slow, so we at first look into scope.
// Latter can fail if we found some other classifier with same name, but parameter can still be reachable, so
// in case of fail we fall back into slow but exact computation.
val contributedClassifier = c.scope.findClassifier(name, NoLookupLocation.WHEN_RESOLVING_DEFAULT_TYPE_ARGUMENTS) ?: return false
if (contributedClassifier.typeConstructor == typeConstructor) return true
return c.scope.ownerDescriptor.isInsideOfClass(original.containingDeclaration as ClassDescriptor)
}
private fun DeclarationDescriptor.isInsideOfClass(classDescriptor: ClassDescriptor)
= sequence(this, { it.containingDeclaration }).any { it.original == classDescriptor }
private fun resolveTypeProjectionsWithErrorConstructor(
c: TypeResolutionContext,
argumentElements: List<KtTypeProjection>,
message: String = "Error type for resolving type projections"
) = resolveTypeProjections(c, ErrorUtils.createErrorTypeConstructor(message), argumentElements)
private fun createErrorTypeAndResolveArguments(
c: TypeResolutionContext,
argumentElements: List<KtTypeProjection>,
message: String = ""
): PossiblyBareType
= type(ErrorUtils.createErrorTypeWithArguments(message, resolveTypeProjectionsWithErrorConstructor(c, argumentElements)))
// In cases like
// class Outer<F> {
// inner class Inner<E>
// val inner: Inner<String>
// }
//
// FQ type of 'inner' val is Outer<F>.Inner<String> (saying strictly it's Outer.Inner<String, F>), but 'F' is implicitly came from scope
// So we just add it explicitly to make type complete, in a sense of having arguments count equal to parameters one.
private fun appendDefaultArgumentsForInnerScope(
fromIndex: Int,
constructorParameters: List<TypeParameterDescriptor>
) = constructorParameters.subList(fromIndex, constructorParameters.size).map {
TypeProjectionImpl((it.original as TypeParameterDescriptor).defaultType)
}
private fun resolveTypeProjections(c: TypeResolutionContext, constructor: TypeConstructor, argumentElements: List<KtTypeProjection>): List<TypeProjection> {
return argumentElements.mapIndexed { i, argumentElement ->
@@ -378,7 +496,13 @@ public class TypeResolver(
}
}
public fun resolveClass(scope: LexicalScope, userType: KtUserType, trace: BindingTrace): ClassifierDescriptor? {
public fun resolveClass(
scope: LexicalScope, userType: KtUserType, trace: BindingTrace
): ClassifierDescriptor? = resolveDescriptorForType(scope, userType, trace).classifierDescriptor
public fun resolveDescriptorForType(
scope: LexicalScope, userType: KtUserType, trace: BindingTrace
): QualifiedExpressionResolver.TypeQualifierResolutionResult {
if (userType.qualifier != null) { // we must resolve all type references in arguments of qualifier type
for (typeArgument in userType.qualifier!!.typeArguments) {
typeArgument.typeReference?.let {
@@ -387,11 +511,12 @@ public class TypeResolver(
}
}
val classifierDescriptor = qualifiedExpressionResolver.resolveDescriptorForUserType(userType, scope, trace)
if (classifierDescriptor != null) {
PlatformTypesMappedToKotlinChecker.reportPlatformClassMappedToKotlin(moduleDescriptor, trace, userType, classifierDescriptor)
val result = qualifiedExpressionResolver.resolveDescriptorForType(userType, scope, trace)
if (result.classifierDescriptor != null) {
PlatformTypesMappedToKotlinChecker.reportPlatformClassMappedToKotlin(
moduleDescriptor, trace, userType, result.classifierDescriptor)
}
return classifierDescriptor
return result
}
companion object {