Support for new double colon expressions in type checker
Despite the fact that the LHS of a double colon expression is now KtExpression instead of KtUserType, we must still consider it a type for callable references to work. Extract and reuse relevant parts of QualifiedExpressionResolver and TypeResolver to support this behavior
This commit is contained in:
@@ -22,6 +22,20 @@ import org.jetbrains.kotlin.KtNodeTypes
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
|
||||
abstract class KtDoubleColonExpression(node: ASTNode) : KtExpressionImpl(node) {
|
||||
val receiverExpression: KtExpression?
|
||||
get() = node.firstChildNode.psi as? KtExpression
|
||||
|
||||
val hasQuestionMarks: Boolean
|
||||
get() {
|
||||
for (element in generateSequence(node.firstChildNode, ASTNode::getTreeNext)) {
|
||||
when (element.elementType) {
|
||||
KtTokens.QUEST -> return true
|
||||
KtTokens.COLONCOLON -> return false
|
||||
}
|
||||
}
|
||||
error("Double colon expression must have '::': $text")
|
||||
}
|
||||
|
||||
val typeReference: KtTypeReference?
|
||||
get() = findChildByType(KtNodeTypes.TYPE_REFERENCE)
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.Qualifier;
|
||||
import org.jetbrains.kotlin.types.DeferredType;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
import org.jetbrains.kotlin.types.expressions.CaptureKind;
|
||||
import org.jetbrains.kotlin.types.expressions.DoubleColonLHS;
|
||||
import org.jetbrains.kotlin.types.expressions.KotlinTypeInfo;
|
||||
import org.jetbrains.kotlin.types.expressions.PreliminaryDeclarationVisitor;
|
||||
import org.jetbrains.kotlin.util.Box;
|
||||
@@ -102,6 +103,8 @@ public interface BindingContext {
|
||||
*/
|
||||
WritableSlice<KtExpression, Qualifier> QUALIFIER = new BasicWritableSlice<KtExpression, Qualifier>(DO_NOTHING);
|
||||
|
||||
WritableSlice<KtExpression, DoubleColonLHS> DOUBLE_COLON_LHS = new BasicWritableSlice<KtExpression, DoubleColonLHS>(DO_NOTHING);
|
||||
|
||||
WritableSlice<KtSuperExpression, KotlinType> THIS_TYPE_FOR_SUPER_EXPRESSION =
|
||||
new BasicWritableSlice<KtSuperExpression, KotlinType>(DO_NOTHING);
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.jetbrains.kotlin.resolve.scopes.utils.memberScopeAsImportingScope
|
||||
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
|
||||
import org.jetbrains.kotlin.resolve.validation.SymbolUsageValidator
|
||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext
|
||||
import org.jetbrains.kotlin.types.expressions.isWithoutValueArguments
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.check
|
||||
|
||||
@@ -69,7 +70,7 @@ class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageValidator
|
||||
isDebuggerContext: Boolean
|
||||
): TypeQualifierResolutionResult {
|
||||
val ownerDescriptor = if (!isDebuggerContext) scope.ownerDescriptor else null
|
||||
if (userType.qualifier == null && !userType.startWithPackage) { // optimization for non-qualified types
|
||||
if (userType.qualifier == null && !userType.startWithPackage) {
|
||||
val descriptor = userType.referenceExpression?.let {
|
||||
val classifier = scope.findClassifier(it.getReferencedNameAsName(), KotlinLookupLocation(it))
|
||||
storeResult(trace, it, classifier, ownerDescriptor, position = QualifierPosition.TYPE, isQualifier = false)
|
||||
@@ -87,13 +88,25 @@ class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageValidator
|
||||
) as? ClassifierDescriptor
|
||||
return TypeQualifierResolutionResult(qualifierPartList, descriptor)
|
||||
}
|
||||
assert(qualifierPartList.size >= 1) {
|
||||
"Too short qualifier list for user type $userType : ${qualifierPartList.joinToString()}"
|
||||
}
|
||||
|
||||
return resolveQualifierPartListForType(
|
||||
qualifierPartList, ownerDescriptor, module, scope.check { !userType.startWithPackage }, trace, isQualifier = false
|
||||
)
|
||||
}
|
||||
|
||||
private fun resolveQualifierPartListForType(
|
||||
qualifierPartList: List<QualifierPart>,
|
||||
ownerDescriptor: DeclarationDescriptor?,
|
||||
module: ModuleDescriptor,
|
||||
scope: LexicalScope?,
|
||||
trace: BindingTrace,
|
||||
isQualifier: Boolean
|
||||
): TypeQualifierResolutionResult {
|
||||
assert(qualifierPartList.isNotEmpty()) { "Qualifier list should not be empty" }
|
||||
|
||||
val qualifier = resolveToPackageOrClass(
|
||||
qualifierPartList.subList(0, qualifierPartList.size - 1), module,
|
||||
trace, ownerDescriptor, scope.check { !userType.startWithPackage }, position = QualifierPosition.TYPE
|
||||
qualifierPartList.subList(0, qualifierPartList.size - 1), module, trace, ownerDescriptor, scope,
|
||||
position = QualifierPosition.TYPE
|
||||
) ?: return TypeQualifierResolutionResult(qualifierPartList, null)
|
||||
|
||||
val lastPart = qualifierPartList.last()
|
||||
@@ -102,10 +115,36 @@ class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageValidator
|
||||
is ClassDescriptor -> qualifier.unsubstitutedInnerClassesScope.getContributedClassifier(lastPart.name, lastPart.location)
|
||||
else -> null
|
||||
}
|
||||
storeResult(trace, lastPart.expression, classifier, ownerDescriptor, position = QualifierPosition.TYPE, isQualifier = false)
|
||||
storeResult(trace, lastPart.expression, classifier, ownerDescriptor, position = QualifierPosition.TYPE, isQualifier = isQualifier)
|
||||
return TypeQualifierResolutionResult(qualifierPartList, classifier)
|
||||
}
|
||||
|
||||
fun resolveDescriptorForDoubleColonLHS(
|
||||
expression: KtExpression,
|
||||
scope: LexicalScope,
|
||||
trace: BindingTrace,
|
||||
isDebuggerContext: Boolean
|
||||
): TypeQualifierResolutionResult {
|
||||
val ownerDescriptor = if (!isDebuggerContext) scope.ownerDescriptor else null
|
||||
|
||||
val qualifierPartList = expression.asQualifierPartList(doubleColonLHS = true)
|
||||
if (qualifierPartList.isEmpty()) {
|
||||
return TypeQualifierResolutionResult(qualifierPartList, null)
|
||||
}
|
||||
|
||||
if (qualifierPartList.size == 1) {
|
||||
val (name, simpleName) = qualifierPartList.single()
|
||||
val descriptor = scope.findClassifier(name, KotlinLookupLocation(simpleName))
|
||||
storeResult(trace, simpleName, descriptor, ownerDescriptor, position = QualifierPosition.TYPE, isQualifier = true)
|
||||
|
||||
return TypeQualifierResolutionResult(qualifierPartList, descriptor)
|
||||
}
|
||||
|
||||
return resolveQualifierPartListForType(
|
||||
qualifierPartList, ownerDescriptor, scope.ownerDescriptor.module, scope, trace, isQualifier = true
|
||||
)
|
||||
}
|
||||
|
||||
private val KtUserType.startWithPackage: Boolean
|
||||
get() {
|
||||
var firstPart = this
|
||||
@@ -270,24 +309,32 @@ class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageValidator
|
||||
storeResult(trace, lastPart.expression, descriptors, shouldBeVisibleFrom = null, position = QualifierPosition.IMPORT, isQualifier = false)
|
||||
}
|
||||
|
||||
private fun KtExpression.asQualifierPartList(): List<QualifierPart> {
|
||||
private fun KtExpression.asQualifierPartList(doubleColonLHS: Boolean = false): List<QualifierPart> {
|
||||
val result = SmartList<QualifierPart>()
|
||||
var expression: KtExpression? = this
|
||||
loop@ while (expression != null) {
|
||||
when (expression) {
|
||||
is KtSimpleNameExpression -> {
|
||||
result.add(QualifierPart(expression.getReferencedNameAsName(), expression))
|
||||
break@loop
|
||||
}
|
||||
is KtQualifiedExpression -> {
|
||||
(expression.selectorExpression as? KtSimpleNameExpression)?.let {
|
||||
result.add(QualifierPart(it.getReferencedNameAsName(), it))
|
||||
}
|
||||
expression = expression.receiverExpression
|
||||
}
|
||||
else -> expression = null
|
||||
|
||||
fun addQualifierPart(expression: KtExpression?): Boolean {
|
||||
if (expression is KtSimpleNameExpression) {
|
||||
result.add(QualifierPart(expression))
|
||||
return true
|
||||
}
|
||||
if (doubleColonLHS && expression is KtCallExpression && expression.isWithoutValueArguments) {
|
||||
val simpleName = expression.calleeExpression as KtSimpleNameExpression
|
||||
result.add(QualifierPart(simpleName.getReferencedNameAsName(), simpleName, expression.typeArgumentList))
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var expression: KtExpression? = this
|
||||
while (true) {
|
||||
if (addQualifierPart(expression)) break
|
||||
if (expression !is KtQualifiedExpression) break
|
||||
|
||||
addQualifierPart(expression.selectorExpression)
|
||||
|
||||
expression = expression.receiverExpression
|
||||
}
|
||||
|
||||
return result.asReversed()
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,6 @@ import org.jetbrains.kotlin.storage.StorageManager
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.Variance.*
|
||||
import org.jetbrains.kotlin.types.typeUtil.isArrayOfNothing
|
||||
import java.util.*
|
||||
|
||||
class TypeResolver(
|
||||
private val annotationResolver: AnnotationResolver,
|
||||
@@ -163,37 +162,21 @@ class TypeResolver(
|
||||
var result: PossiblyBareType? = null
|
||||
typeElement?.accept(object : KtVisitorVoid() {
|
||||
override fun visitUserType(type: KtUserType) {
|
||||
val qualifierResolutionResults = resolveDescriptorForType(c.scope, type, c.trace, c.isDebuggerContext)
|
||||
val (qualifierParts, classifierDescriptor) = qualifierResolutionResults
|
||||
val qualifierResolutionResult = resolveDescriptorForType(c.scope, type, c.trace, c.isDebuggerContext)
|
||||
val classifier = qualifierResolutionResult.classifierDescriptor
|
||||
|
||||
if (classifierDescriptor == null) {
|
||||
if (classifier == null) {
|
||||
val arguments = resolveTypeProjections(
|
||||
c, ErrorUtils.createErrorType("No type").constructor, qualifierResolutionResults.allProjections)
|
||||
c, ErrorUtils.createErrorType("No type").constructor, qualifierResolutionResult.allProjections
|
||||
)
|
||||
result = type(ErrorUtils.createErrorTypeWithArguments(type.getDebugText(), arguments))
|
||||
return
|
||||
}
|
||||
|
||||
val referenceExpression = type.getReferenceExpression()
|
||||
val referencedName = type.getReferencedName()
|
||||
if (referenceExpression == null || referencedName == null) return
|
||||
val referenceExpression = type.referenceExpression ?: return
|
||||
c.trace.record(BindingContext.REFERENCE_TARGET, referenceExpression, classifier)
|
||||
|
||||
c.trace.record(BindingContext.REFERENCE_TARGET, referenceExpression, classifierDescriptor)
|
||||
|
||||
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, qualifierResolutionResults)
|
||||
is TypeAliasDescriptor ->
|
||||
resolveTypeForTypeAlias(c, annotations, classifierDescriptor, type, qualifierResolutionResults)
|
||||
else -> error("Unexpected classifier type: ${classifierDescriptor.javaClass}")
|
||||
}
|
||||
result = resolveTypeForClassifier(c, classifier, qualifierResolutionResult, type, annotations)
|
||||
}
|
||||
|
||||
override fun visitNullableType(nullableType: KtNullableType) {
|
||||
@@ -308,13 +291,13 @@ class TypeResolver(
|
||||
c: TypeResolutionContext, annotations: Annotations,
|
||||
typeParameter: TypeParameterDescriptor,
|
||||
referenceExpression: KtSimpleNameExpression,
|
||||
type: KtUserType
|
||||
typeArgumentList: KtTypeArgumentList?
|
||||
): KotlinType {
|
||||
val scopeForTypeParameter = getScopeForTypeParameter(c, typeParameter)
|
||||
|
||||
val arguments = resolveTypeProjections(c, ErrorUtils.createErrorType("No type").constructor, type.typeArguments)
|
||||
if (!arguments.isEmpty()) {
|
||||
c.trace.report(TYPE_ARGUMENTS_NOT_ALLOWED.on(type.typeArgumentList!!, "for type parameters"))
|
||||
if (typeArgumentList != null) {
|
||||
resolveTypeProjections(c, ErrorUtils.createErrorType("No type").constructor, typeArgumentList.arguments)
|
||||
c.trace.report(TYPE_ARGUMENTS_NOT_ALLOWED.on(typeArgumentList, "for type parameters"))
|
||||
}
|
||||
|
||||
val containing = typeParameter.containingDeclaration
|
||||
@@ -342,9 +325,34 @@ class TypeResolver(
|
||||
}
|
||||
}
|
||||
|
||||
fun resolveTypeForClassifier(
|
||||
c: TypeResolutionContext,
|
||||
descriptor: ClassifierDescriptor,
|
||||
qualifierResolutionResult: QualifiedExpressionResolver.TypeQualifierResolutionResult,
|
||||
element: KtElement,
|
||||
annotations: Annotations
|
||||
): PossiblyBareType {
|
||||
val qualifierParts = qualifierResolutionResult.qualifierParts
|
||||
|
||||
return when (descriptor) {
|
||||
is TypeParameterDescriptor -> {
|
||||
assert(qualifierParts.size == 1) {
|
||||
"Type parameter can be resolved only by it's short name, but '${element.text}' is contradiction " +
|
||||
"with ${qualifierParts.size} qualifier parts"
|
||||
}
|
||||
|
||||
val qualifierPart = qualifierParts.single()
|
||||
type(resolveTypeForTypeParameter(c, annotations, descriptor, qualifierPart.expression, qualifierPart.typeArguments))
|
||||
}
|
||||
is ClassDescriptor -> resolveTypeForClass(c, annotations, descriptor, element, qualifierResolutionResult)
|
||||
is TypeAliasDescriptor -> resolveTypeForTypeAlias(c, annotations, descriptor, element as KtUserType /* TODO */, qualifierResolutionResult)
|
||||
else -> error("Unexpected classifier type: ${descriptor.javaClass}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun resolveTypeForClass(
|
||||
c: TypeResolutionContext, annotations: Annotations,
|
||||
classDescriptor: ClassDescriptor, type: KtUserType,
|
||||
classDescriptor: ClassDescriptor, element: KtElement,
|
||||
qualifierResolutionResult: QualifiedExpressionResolver.TypeQualifierResolutionResult
|
||||
): PossiblyBareType {
|
||||
val typeConstructor = classDescriptor.typeConstructor
|
||||
@@ -366,7 +374,7 @@ class TypeResolver(
|
||||
|
||||
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}"
|
||||
" but ${collectedArgumentAsTypeProjections.size} instead of ${parameters.size} found in ${element.text}"
|
||||
}
|
||||
|
||||
val argumentsFromUserType = resolveTypeProjections(c, typeConstructor, collectedArgumentAsTypeProjections)
|
||||
@@ -374,7 +382,7 @@ class TypeResolver(
|
||||
|
||||
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}"
|
||||
" but ${collectedArgumentAsTypeProjections.size} instead of ${parameters.size} found in ${element.text}"
|
||||
}
|
||||
|
||||
val resultingType = KotlinTypeImpl.create(annotations, classDescriptor, false, arguments)
|
||||
@@ -397,7 +405,7 @@ class TypeResolver(
|
||||
}
|
||||
|
||||
if (resultingType.isArrayOfNothing()) {
|
||||
c.trace.report(UNSUPPORTED.on(type, "Array<Nothing> is illegal"))
|
||||
c.trace.report(UNSUPPORTED.on(element, "Array<Nothing> is illegal"))
|
||||
}
|
||||
|
||||
return type(resultingType)
|
||||
@@ -783,10 +791,13 @@ class TypeResolver(
|
||||
TypeProjectionImpl((it.original as TypeParameterDescriptor).defaultType)
|
||||
}
|
||||
|
||||
private fun resolveTypeProjections(c: TypeResolutionContext, constructor: TypeConstructor, argumentElements: List<KtTypeProjection>): List<TypeProjection> {
|
||||
fun resolveTypeProjections(
|
||||
c: TypeResolutionContext,
|
||||
constructor: TypeConstructor,
|
||||
argumentElements: List<KtTypeProjection>
|
||||
): List<TypeProjection> {
|
||||
return argumentElements.mapIndexed { i, argumentElement ->
|
||||
|
||||
val projectionKind = argumentElement.getProjectionKind()
|
||||
val projectionKind = argumentElement.projectionKind
|
||||
ModifierCheckerCore.check(argumentElement, c.trace, null)
|
||||
if (projectionKind == KtProjectionKind.STAR) {
|
||||
val parameters = constructor.parameters
|
||||
|
||||
+2
-1
@@ -265,12 +265,13 @@ fun getReflectionTypeForCandidateDescriptor(
|
||||
fun createReflectionTypeForResolvedCallableReference(
|
||||
reference: KtCallableReferenceExpression,
|
||||
lhsType: KotlinType?,
|
||||
ignoreReceiver: Boolean,
|
||||
descriptor: CallableDescriptor,
|
||||
context: ResolutionContext<*>,
|
||||
reflectionTypes: ReflectionTypes
|
||||
): KotlinType? {
|
||||
val type = createReflectionTypeForCallableDescriptor(
|
||||
descriptor, lhsType, reflectionTypes, context.trace, reference.callableReference, reference.isEmptyLHS
|
||||
descriptor, lhsType, reflectionTypes, context.trace, reference.callableReference, ignoreReceiver
|
||||
) ?: return null
|
||||
when (descriptor) {
|
||||
is FunctionDescriptor -> {
|
||||
|
||||
+126
-47
@@ -24,13 +24,9 @@ import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.diagnostics.Errors.*
|
||||
import org.jetbrains.kotlin.diagnostics.Severity
|
||||
import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
|
||||
import org.jetbrains.kotlin.psi.KtClassLiteralExpression
|
||||
import org.jetbrains.kotlin.psi.KtUserType
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.TemporaryBindingTrace
|
||||
import org.jetbrains.kotlin.resolve.TypeResolutionContext
|
||||
import org.jetbrains.kotlin.resolve.TypeResolver
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.codeFragmentUtil.suppressDiagnosticsInDebugMode
|
||||
import org.jetbrains.kotlin.resolve.*
|
||||
import org.jetbrains.kotlin.resolve.callableReferences.createReflectionTypeForResolvedCallableReference
|
||||
import org.jetbrains.kotlin.resolve.callableReferences.resolveCallableReferenceTarget
|
||||
import org.jetbrains.kotlin.resolve.calls.CallExpressionResolver
|
||||
@@ -43,63 +39,147 @@ import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.KotlinTypeImpl
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.createTypeInfo
|
||||
import javax.inject.Inject
|
||||
|
||||
// TODO: use a language level option
|
||||
val BOUND_REFERENCES_ENABLED by lazy { System.getProperty("kotlin.lang.enable.bound.references") == "true" }
|
||||
|
||||
sealed class DoubleColonLHS(val type: KotlinType) {
|
||||
class Expression(val typeInfo: KotlinTypeInfo) : DoubleColonLHS(typeInfo.type!!)
|
||||
|
||||
class Type(type: KotlinType, val possiblyBareType: PossiblyBareType) : DoubleColonLHS(type)
|
||||
}
|
||||
|
||||
// Returns true if this expression has the form "A<B>" which means it's a type on the LHS of a double colon expression
|
||||
internal val KtCallExpression.isWithoutValueArguments: Boolean
|
||||
get() = valueArgumentList == null && lambdaArguments.isEmpty()
|
||||
|
||||
class DoubleColonExpressionResolver(
|
||||
val callResolver: CallResolver,
|
||||
val callExpressionResolver: CallExpressionResolver,
|
||||
val qualifiedExpressionResolver: QualifiedExpressionResolver,
|
||||
val dataFlowAnalyzer: DataFlowAnalyzer,
|
||||
val reflectionTypes: ReflectionTypes,
|
||||
val typeResolver: TypeResolver
|
||||
) {
|
||||
private lateinit var expressionTypingServices: ExpressionTypingServices
|
||||
|
||||
// component dependency cycle
|
||||
@Inject
|
||||
fun setExpressionTypingServices(expressionTypingServices: ExpressionTypingServices) {
|
||||
this.expressionTypingServices = expressionTypingServices
|
||||
}
|
||||
|
||||
fun visitClassLiteralExpression(expression: KtClassLiteralExpression, c: ExpressionTypingContext): KotlinTypeInfo {
|
||||
val type = resolveClassLiteral(expression, c)
|
||||
if (type != null && !type.isError) {
|
||||
return dataFlowAnalyzer.createCheckedTypeInfo(reflectionTypes.getKClassType(Annotations.EMPTY, type), c, expression)
|
||||
if (expression.isEmptyLHS) {
|
||||
// "::class" will maybe mean "this::class", a class of "this" instance
|
||||
c.trace.report(UNSUPPORTED.on(expression, "Class literals with empty left hand side are not yet supported"))
|
||||
}
|
||||
else {
|
||||
val result = resolveDoubleColonLHS(expression.receiverExpression!!, expression, c)
|
||||
val type = result?.type
|
||||
if (type != null && !type.isError) {
|
||||
checkClassLiteral(c, expression, result!!)
|
||||
return dataFlowAnalyzer.createCheckedTypeInfo(reflectionTypes.getKClassType(Annotations.EMPTY, type), c, expression)
|
||||
}
|
||||
}
|
||||
|
||||
return createTypeInfo(ErrorUtils.createErrorType("Unresolved class"), c)
|
||||
}
|
||||
|
||||
private fun resolveClassLiteral(expression: KtClassLiteralExpression, c: ExpressionTypingContext): KotlinType? {
|
||||
if (expression.isEmptyLHS) {
|
||||
// "::class" will maybe mean "this::class", a class of "this" instance
|
||||
c.trace.report(UNSUPPORTED.on(expression, "Class literals with empty left hand side are not yet supported"))
|
||||
return null
|
||||
}
|
||||
private fun checkClassLiteral(c: ExpressionTypingContext, expression: KtClassLiteralExpression, result: DoubleColonLHS) {
|
||||
if (result !is DoubleColonLHS.Type) return
|
||||
|
||||
val context = TypeResolutionContext(
|
||||
c.scope, c.trace, /* checkBounds = */ false, /* allowBareTypes = */ true, /* isDebuggerContext = */ false
|
||||
)
|
||||
val possiblyBareType = typeResolver.resolvePossiblyBareType(context, expression.typeReference!!)
|
||||
|
||||
if (!possiblyBareType.isBare && possiblyBareType.actualType.isError) {
|
||||
return null
|
||||
}
|
||||
|
||||
var reportError = false
|
||||
val type: KotlinType
|
||||
if (possiblyBareType.isBare) {
|
||||
val descriptor = possiblyBareType.bareTypeConstructor.declarationDescriptor as? ClassDescriptor
|
||||
?: error("Only classes can produce bare types: $possiblyBareType")
|
||||
if (KotlinBuiltIns.isNonPrimitiveArray(descriptor)) {
|
||||
context.trace.report(ARRAY_CLASS_LITERAL_REQUIRES_ARGUMENT.on(expression))
|
||||
val type = result.type
|
||||
val reportError: Boolean
|
||||
if (result.possiblyBareType.isBare) {
|
||||
val descriptor = type.constructor.declarationDescriptor
|
||||
if (descriptor is ClassDescriptor && KotlinBuiltIns.isNonPrimitiveArray(descriptor)) {
|
||||
c.trace.report(ARRAY_CLASS_LITERAL_REQUIRES_ARGUMENT.on(expression))
|
||||
}
|
||||
|
||||
type = KotlinTypeImpl.create(
|
||||
Annotations.EMPTY, descriptor, possiblyBareType.isNullable,
|
||||
descriptor.typeConstructor.parameters.map(TypeUtils::makeStarProjection)
|
||||
)
|
||||
reportError = false
|
||||
}
|
||||
else {
|
||||
type = possiblyBareType.actualType
|
||||
reportError = !isAllowedInClassLiteral(type)
|
||||
}
|
||||
|
||||
if (type.isMarkedNullable || reportError) {
|
||||
context.trace.report(CLASS_LITERAL_LHS_NOT_A_CLASS.on(expression))
|
||||
c.trace.report(CLASS_LITERAL_LHS_NOT_A_CLASS.on(expression))
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if the expression is not a call expression without value arguments (such as "A<B>") or a qualified expression
|
||||
// which contains such call expression as one of its parts.
|
||||
// In this case it's pointless to attempt to type check an expression on the LHS in "A<B>::class", since "A<B>" certainly means a type.
|
||||
private fun KtExpression.canBeConsideredProperExpression(): Boolean {
|
||||
return when (this) {
|
||||
is KtCallExpression ->
|
||||
!isWithoutValueArguments
|
||||
is KtDotQualifiedExpression ->
|
||||
receiverExpression.canBeConsideredProperExpression() &&
|
||||
selectorExpression?.let { it.canBeConsideredProperExpression() } ?: false
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
||||
private fun resolveDoubleColonLHS(
|
||||
expression: KtExpression, doubleColonExpression: KtDoubleColonExpression, c: ExpressionTypingContext
|
||||
): DoubleColonLHS? {
|
||||
// First, try resolving the LHS as expression, if possible
|
||||
|
||||
if (BOUND_REFERENCES_ENABLED && expression.canBeConsideredProperExpression() &&
|
||||
!doubleColonExpression.hasQuestionMarks /* TODO: test this */) {
|
||||
val traceForExpr = TemporaryTraceAndCache.create(c, "resolve '::' LHS as expression", expression)
|
||||
val contextForExpr = c.replaceTraceAndCache(traceForExpr)
|
||||
val typeInfo = expressionTypingServices.getTypeInfo(expression, contextForExpr)
|
||||
val type = typeInfo.type
|
||||
// TODO (!!!): it's wrong to only check type, should check that there's a companion qualifier
|
||||
if (type != null && !DescriptorUtils.isCompanionObject(type.constructor.declarationDescriptor)) {
|
||||
traceForExpr.commit()
|
||||
return DoubleColonLHS.Expression(typeInfo).apply {
|
||||
c.trace.record(BindingContext.DOUBLE_COLON_LHS, expression, this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return type
|
||||
// Then, try resolving it as type
|
||||
|
||||
val qualifierResolutionResult =
|
||||
qualifiedExpressionResolver.resolveDescriptorForDoubleColonLHS(expression, c.scope, c.trace, c.isDebuggerContext)
|
||||
|
||||
val typeResolutionContext = TypeResolutionContext(
|
||||
c.scope, c.trace, /* checkBounds = */ true, /* allowBareTypes = */ true,
|
||||
/* isDebuggerContext = */ expression.suppressDiagnosticsInDebugMode() /* TODO: test this */
|
||||
)
|
||||
|
||||
val classifier = qualifierResolutionResult.classifierDescriptor
|
||||
if (classifier == null) {
|
||||
typeResolver.resolveTypeProjections(
|
||||
typeResolutionContext, ErrorUtils.createErrorType("No type").constructor, qualifierResolutionResult.allProjections
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
val possiblyBareType = typeResolver.resolveTypeForClassifier(
|
||||
typeResolutionContext, classifier, qualifierResolutionResult, expression, Annotations.EMPTY
|
||||
)
|
||||
|
||||
val type = if (possiblyBareType.isBare) {
|
||||
val descriptor = possiblyBareType.bareTypeConstructor.declarationDescriptor as? ClassDescriptor
|
||||
?: error("Only classes can produce bare types: $possiblyBareType")
|
||||
|
||||
KotlinTypeImpl.create(
|
||||
Annotations.EMPTY, descriptor, possiblyBareType.isNullable || doubleColonExpression.hasQuestionMarks,
|
||||
descriptor.typeConstructor.parameters.map(TypeUtils::makeStarProjection)
|
||||
)
|
||||
}
|
||||
else {
|
||||
TypeUtils.makeNullableAsSpecified(possiblyBareType.actualType, doubleColonExpression.hasQuestionMarks)
|
||||
}
|
||||
|
||||
return DoubleColonLHS.Type(type, possiblyBareType).apply {
|
||||
c.trace.record(BindingContext.DOUBLE_COLON_LHS, expression, this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isAllowedInClassLiteral(type: KotlinType): Boolean {
|
||||
@@ -122,11 +202,8 @@ class DoubleColonExpressionResolver(
|
||||
}
|
||||
|
||||
fun visitCallableReferenceExpression(expression: KtCallableReferenceExpression, c: ExpressionTypingContext): KotlinTypeInfo {
|
||||
val typeReference = expression.typeReference
|
||||
|
||||
val receiverType = typeReference?.let { typeReference ->
|
||||
typeResolver.resolveType(c.scope, typeReference, c.trace, false)
|
||||
}
|
||||
val lhs = expression.receiverExpression?.let { resolveDoubleColonLHS(it, expression, c) }
|
||||
val receiverType = lhs?.type // TODO: handle .Expression and .Type
|
||||
|
||||
val callableReference = expression.callableReference
|
||||
if (callableReference.getReferencedName().isEmpty()) {
|
||||
@@ -136,7 +213,7 @@ class DoubleColonExpressionResolver(
|
||||
}
|
||||
|
||||
val trace = TemporaryBindingTrace.create(c.trace, "Callable reference type")
|
||||
val result = getCallableReferenceType(expression, receiverType, c.replaceBindingTrace(trace))
|
||||
val result = getCallableReferenceType(expression, receiverType, lhs is DoubleColonLHS.Expression, c.replaceBindingTrace(trace))
|
||||
val hasErrors = hasErrors(trace) // Do not inline this local variable (execution order is important)
|
||||
trace.commit()
|
||||
if (!hasErrors && result != null) {
|
||||
@@ -181,6 +258,7 @@ class DoubleColonExpressionResolver(
|
||||
private fun getCallableReferenceType(
|
||||
expression: KtCallableReferenceExpression,
|
||||
lhsType: KotlinType?,
|
||||
isBound: Boolean,
|
||||
context: ExpressionTypingContext
|
||||
): KotlinType? {
|
||||
val reference = expression.callableReference
|
||||
@@ -205,6 +283,7 @@ class DoubleColonExpressionResolver(
|
||||
context.trace.report(CALLABLE_REFERENCE_TO_ANNOTATION_CONSTRUCTOR.on(reference))
|
||||
}
|
||||
|
||||
return createReflectionTypeForResolvedCallableReference(expression, lhsType, descriptor, context, reflectionTypes)
|
||||
val ignoreReceiver = isBound || expression.isEmptyLHS
|
||||
return createReflectionTypeForResolvedCallableReference(expression, lhsType, ignoreReceiver, descriptor, context, reflectionTypes)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// MODULE: m1
|
||||
// FILE: 1.kt
|
||||
|
||||
package a
|
||||
|
||||
class b {
|
||||
class c
|
||||
}
|
||||
|
||||
// MODULE: m2
|
||||
// FILE: 2.kt
|
||||
|
||||
package a.b
|
||||
|
||||
class c {
|
||||
fun foo() {}
|
||||
}
|
||||
|
||||
// MODULE: m3(m1, m2)
|
||||
// FILE: test.kt
|
||||
|
||||
package test
|
||||
|
||||
fun test() = a.b.c::foo
|
||||
@@ -0,0 +1,61 @@
|
||||
// -- Module: <m1> --
|
||||
package
|
||||
|
||||
package a {
|
||||
|
||||
public final class b {
|
||||
public constructor b()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
|
||||
public final class c {
|
||||
public constructor c()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -- Module: <m2> --
|
||||
package
|
||||
|
||||
package a {
|
||||
|
||||
package a.b {
|
||||
|
||||
public final class c {
|
||||
public constructor c()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public final fun foo(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -- Module: <m3> --
|
||||
package
|
||||
|
||||
package a {
|
||||
|
||||
public final class b {
|
||||
// -- Module: <m1> --
|
||||
}
|
||||
|
||||
package a.b {
|
||||
|
||||
public final class c {
|
||||
// -- Module: <m2> --
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
package test {
|
||||
public fun test(): kotlin.reflect.KFunction1<a.b.c, kotlin.Unit>
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// !DIAGNOSTICS: -UNUSED_EXPRESSION
|
||||
// FILE: simpleName.kt
|
||||
|
||||
package foo
|
||||
|
||||
fun test() {
|
||||
<!UNRESOLVED_REFERENCE!>foo<!>::test
|
||||
}
|
||||
|
||||
// FILE: qualifiedName.kt
|
||||
|
||||
package foo.bar
|
||||
|
||||
fun test() {
|
||||
foo.<!UNRESOLVED_REFERENCE!>bar<!>::test
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package
|
||||
|
||||
package foo {
|
||||
public fun test(): kotlin.Unit
|
||||
|
||||
package foo.bar {
|
||||
public fun test(): kotlin.Unit
|
||||
}
|
||||
}
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
package test
|
||||
|
||||
class Foo {
|
||||
fun <T> bar(x: Int) = x
|
||||
}
|
||||
|
||||
fun test() {
|
||||
Foo::<!TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>bar<!> <!SYNTAX!>< <!DEBUG_INFO_MISSING_UNRESOLVED!>Int<!> ><!> <!SYNTAX!>(2 <!DEBUG_INFO_MISSING_UNRESOLVED!>+<!> 2)<!>
|
||||
}
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
package
|
||||
|
||||
package test {
|
||||
public fun test(): kotlin.Unit
|
||||
|
||||
public final class Foo {
|
||||
public constructor Foo()
|
||||
public final fun </*0*/ T> bar(/*0*/ x: kotlin.Int): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// !CHECK_TYPE
|
||||
|
||||
class Foo
|
||||
|
||||
fun Foo?.bar() {}
|
||||
|
||||
fun test() {
|
||||
val r1 = Foo ?:: bar
|
||||
checkSubtype<(Foo?) -> Unit>(r1)
|
||||
|
||||
val r2 = Foo ? :: bar
|
||||
checkSubtype<(Foo?) -> Unit>(r2)
|
||||
|
||||
val r3 = Foo ? ? :: bar
|
||||
checkSubtype<(Foo?) -> Unit>(r3)
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package
|
||||
|
||||
public fun test(): kotlin.Unit
|
||||
public fun Foo?.bar(): kotlin.Unit
|
||||
|
||||
public final class Foo {
|
||||
public constructor Foo()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
class A
|
||||
|
||||
val a1 = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>A?::class<!>
|
||||
val a2 = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>A?<!REDUNDANT_NULLABLE!>?<!>::class<!>
|
||||
val a2 = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>A??::class<!>
|
||||
|
||||
val l1 = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>List<String>?::class<!>
|
||||
val l2 = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>List?::class<!>
|
||||
|
||||
@@ -1653,6 +1653,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/callableReference"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("classVsPackage.kt")
|
||||
public void testClassVsPackage() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/classVsPackage.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ea81649_errorPropertyLHS.kt")
|
||||
public void testEa81649_errorPropertyLHS() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/ea81649_errorPropertyLHS.kt");
|
||||
@@ -1677,12 +1683,30 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("packageInLhs.kt")
|
||||
public void testPackageInLhs() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/packageInLhs.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("parsingPriorityOfGenericArgumentsVsLess.kt")
|
||||
public void testParsingPriorityOfGenericArgumentsVsLess() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/parsingPriorityOfGenericArgumentsVsLess.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("unused.kt")
|
||||
public void testUnused() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/unused.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("whitespacesInExpression.kt")
|
||||
public void testWhitespacesInExpression() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/whitespacesInExpression.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/callableReference/function")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user