diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtDoubleColonExpression.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtDoubleColonExpression.kt index 2055ec848fa..67cdbc1fd1f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtDoubleColonExpression.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtDoubleColonExpression.kt @@ -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) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java index 35f3a5d0d2c..0d18142a02d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java @@ -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 QUALIFIER = new BasicWritableSlice(DO_NOTHING); + WritableSlice DOUBLE_COLON_LHS = new BasicWritableSlice(DO_NOTHING); + WritableSlice THIS_TYPE_FOR_SUPER_EXPRESSION = new BasicWritableSlice(DO_NOTHING); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/QualifiedExpressionResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/QualifiedExpressionResolver.kt index b257ae60b7e..76adcfe218e 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/QualifiedExpressionResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/QualifiedExpressionResolver.kt @@ -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, + 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 { + private fun KtExpression.asQualifierPartList(doubleColonLHS: Boolean = false): List { val result = SmartList() - 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() } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt index 9b0d4d012bc..29484d5ad45 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt @@ -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 is illegal")) + c.trace.report(UNSUPPORTED.on(element, "Array 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): List { + fun resolveTypeProjections( + c: TypeResolutionContext, + constructor: TypeConstructor, + argumentElements: List + ): List { 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 diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/callableReferences/CallableReferencesResolutionUtils.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/callableReferences/CallableReferencesResolutionUtils.kt index 873a72ba9bb..f31b099f56b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/callableReferences/CallableReferencesResolutionUtils.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/callableReferences/CallableReferencesResolutionUtils.kt @@ -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 -> { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt index f5f478c1b84..a14c36da88d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt @@ -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" 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") 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::class", since "A" 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) } } diff --git a/compiler/testData/diagnostics/tests/callableReference/classVsPackage.kt b/compiler/testData/diagnostics/tests/callableReference/classVsPackage.kt new file mode 100644 index 00000000000..13c46fdf61e --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/classVsPackage.kt @@ -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 diff --git a/compiler/testData/diagnostics/tests/callableReference/classVsPackage.txt b/compiler/testData/diagnostics/tests/callableReference/classVsPackage.txt new file mode 100644 index 00000000000..3eda9d000c5 --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/classVsPackage.txt @@ -0,0 +1,61 @@ +// -- Module: -- +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: -- +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: -- +package + +package a { + + public final class b { + // -- Module: -- + } + + package a.b { + + public final class c { + // -- Module: -- + } + } +} + +package test { + public fun test(): kotlin.reflect.KFunction1 +} + + diff --git a/compiler/testData/diagnostics/tests/callableReference/packageInLhs.kt b/compiler/testData/diagnostics/tests/callableReference/packageInLhs.kt new file mode 100644 index 00000000000..36b68744fac --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/packageInLhs.kt @@ -0,0 +1,16 @@ +// !DIAGNOSTICS: -UNUSED_EXPRESSION +// FILE: simpleName.kt + +package foo + +fun test() { + foo::test +} + +// FILE: qualifiedName.kt + +package foo.bar + +fun test() { + foo.bar::test +} diff --git a/compiler/testData/diagnostics/tests/callableReference/packageInLhs.txt b/compiler/testData/diagnostics/tests/callableReference/packageInLhs.txt new file mode 100644 index 00000000000..c8dc104063c --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/packageInLhs.txt @@ -0,0 +1,9 @@ +package + +package foo { + public fun test(): kotlin.Unit + + package foo.bar { + public fun test(): kotlin.Unit + } +} diff --git a/compiler/testData/diagnostics/tests/callableReference/parsingPriorityOfGenericArgumentsVsLess.kt b/compiler/testData/diagnostics/tests/callableReference/parsingPriorityOfGenericArgumentsVsLess.kt new file mode 100644 index 00000000000..0e6ceccb578 --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/parsingPriorityOfGenericArgumentsVsLess.kt @@ -0,0 +1,9 @@ +package test + +class Foo { + fun bar(x: Int) = x +} + +fun test() { + Foo::bar < Int > (2 + 2) +} diff --git a/compiler/testData/diagnostics/tests/callableReference/parsingPriorityOfGenericArgumentsVsLess.txt b/compiler/testData/diagnostics/tests/callableReference/parsingPriorityOfGenericArgumentsVsLess.txt new file mode 100644 index 00000000000..ef7318ada43 --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/parsingPriorityOfGenericArgumentsVsLess.txt @@ -0,0 +1,13 @@ +package + +package test { + public fun test(): kotlin.Unit + + public final class Foo { + public constructor Foo() + public final fun 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 + } +} diff --git a/compiler/testData/diagnostics/tests/callableReference/whitespacesInExpression.kt b/compiler/testData/diagnostics/tests/callableReference/whitespacesInExpression.kt new file mode 100644 index 00000000000..80d7bc794bb --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/whitespacesInExpression.kt @@ -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) +} diff --git a/compiler/testData/diagnostics/tests/callableReference/whitespacesInExpression.txt b/compiler/testData/diagnostics/tests/callableReference/whitespacesInExpression.txt new file mode 100644 index 00000000000..1952a2053bb --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/whitespacesInExpression.txt @@ -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 +} diff --git a/compiler/testData/diagnostics/tests/classLiteral/nonClassesOnLHS.kt b/compiler/testData/diagnostics/tests/classLiteral/nonClassesOnLHS.kt index 72161dcc101..e63e376884b 100644 --- a/compiler/testData/diagnostics/tests/classLiteral/nonClassesOnLHS.kt +++ b/compiler/testData/diagnostics/tests/classLiteral/nonClassesOnLHS.kt @@ -3,7 +3,7 @@ class A val a1 = A?::class -val a2 = A??::class +val a2 = A??::class val l1 = List?::class val l2 = List?::class diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 3e03c3415dd..f30393785f2 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -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)