Properly resolve values in 1st qualifier part (see implicitReceiverProperty test).

Look into QUALIFIER for left-most "unrolled" qualified expression
    in CallExpressionResolver#getQualifiedExpressionTypeInfo.
Drop some unused functions.
This commit is contained in:
Dmitry Petrov
2015-11-10 14:37:51 +03:00
parent 49570ba1ea
commit b532fa2bbf
13 changed files with 228 additions and 113 deletions
@@ -20,26 +20,24 @@ import com.intellij.util.SmartList
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.incremental.KotlinLookupLocation import org.jetbrains.kotlin.incremental.KotlinLookupLocation
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.calls.CallExpressionElement import org.jetbrains.kotlin.resolve.calls.CallExpressionElement
import org.jetbrains.kotlin.resolve.calls.unrollToLeftMostQualifiedExpression
import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.scopes.ImportingScope import org.jetbrains.kotlin.resolve.scopes.ImportingScope
import org.jetbrains.kotlin.resolve.scopes.LexicalScope import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.resolve.scopes.receivers.ClassQualifier
import org.jetbrains.kotlin.resolve.scopes.receivers.PackageQualifier import org.jetbrains.kotlin.resolve.scopes.receivers.PackageQualifier
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.resolve.scopes.receivers.createClassifierQualifier import org.jetbrains.kotlin.resolve.scopes.receivers.createClassifierQualifier
import org.jetbrains.kotlin.resolve.scopes.utils.containsFunctionOrVariable
import org.jetbrains.kotlin.resolve.scopes.utils.findClassifier import org.jetbrains.kotlin.resolve.scopes.utils.findClassifier
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
import org.jetbrains.kotlin.resolve.validation.SymbolUsageValidator import org.jetbrains.kotlin.resolve.validation.SymbolUsageValidator
import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext
import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices
import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.check import org.jetbrains.kotlin.utils.addToStdlib.check
import java.util.*
public class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageValidator) { public class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageValidator) {
@@ -52,7 +50,7 @@ public class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageVa
val packageNames = packageDirective.packageNames val packageNames = packageDirective.packageNames
for ((index, nameExpression) in packageNames.withIndex()) { for ((index, nameExpression) in packageNames.withIndex()) {
storeResult(trace, nameExpression, module.getPackage(packageDirective.getFqName(nameExpression)), storeResult(trace, nameExpression, module.getPackage(packageDirective.getFqName(nameExpression)),
shouldBeVisibleFrom = null, inImport = false, isQualifier = index != packageNames.lastIndex) shouldBeVisibleFrom = null, position = QualifierPosition.PACKAGE_HEADER, isQualifier = index != packageNames.lastIndex)
} }
} }
@@ -72,7 +70,7 @@ public class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageVa
if (userType.qualifier == null && !userType.startWithPackage) { // optimization for non-qualified types if (userType.qualifier == null && !userType.startWithPackage) { // optimization for non-qualified types
val descriptor = userType.referenceExpression?.let { val descriptor = userType.referenceExpression?.let {
val classifier = scope.findClassifier(it.getReferencedNameAsName(), KotlinLookupLocation(it)) val classifier = scope.findClassifier(it.getReferencedNameAsName(), KotlinLookupLocation(it))
storeResult(trace, it, classifier, scope.ownerDescriptor, inImport = false, isQualifier = false) storeResult(trace, it, classifier, scope.ownerDescriptor, position = QualifierPosition.TYPE, isQualifier = false)
classifier classifier
} }
@@ -102,8 +100,7 @@ public class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageVa
is ClassDescriptor -> qualifier.unsubstitutedInnerClassesScope.getContributedClassifier(lastPart.name, lastPart.location) is ClassDescriptor -> qualifier.unsubstitutedInnerClassesScope.getContributedClassifier(lastPart.name, lastPart.location)
else -> null else -> null
} }
storeResult(trace, lastPart.expression, classifier, scope.ownerDescriptor, inImport = false, isQualifier = false) storeResult(trace, lastPart.expression, classifier, scope.ownerDescriptor, position = QualifierPosition.TYPE, isQualifier = false)
return TypeQualifierResolutionResult(qualifierPartList, classifier) return TypeQualifierResolutionResult(qualifierPartList, classifier)
} }
@@ -181,14 +178,14 @@ public class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageVa
val candidates = collectCandidateDescriptors(lastPart, packageOrClassDescriptor) val candidates = collectCandidateDescriptors(lastPart, packageOrClassDescriptor)
if (candidates.isNotEmpty()) { if (candidates.isNotEmpty()) {
storeResult(trace, lastPart.expression, candidates, packageFragmentForVisibilityCheck, inImport = true, isQualifier = false) storeResult(trace, lastPart.expression, candidates, packageFragmentForVisibilityCheck, position = QualifierPosition.IMPORT, isQualifier = false)
} }
else { else {
tryResolveDescriptorsWhichCannotBeImported(trace, moduleDescriptor, packageOrClassDescriptor, lastPart) tryResolveDescriptorsWhichCannotBeImported(trace, moduleDescriptor, packageOrClassDescriptor, lastPart)
return null return null
} }
val importedDescriptors = candidates.filter { isVisible(it, packageFragmentForVisibilityCheck, inImport = true) }. val importedDescriptors = candidates.filter { isVisible(it, packageFragmentForVisibilityCheck, position = QualifierPosition.IMPORT) }.
check { it.isNotEmpty() } ?: candidates check { it.isNotEmpty() } ?: candidates
return SingleImportScope(aliasName, importedDescriptors) return SingleImportScope(aliasName, importedDescriptors)
@@ -258,7 +255,7 @@ public class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageVa
else -> throw IllegalStateException("Should be class or package: $packageOrClassDescriptor") else -> throw IllegalStateException("Should be class or package: $packageOrClassDescriptor")
} }
storeResult(trace, lastPart.expression, descriptors, shouldBeVisibleFrom = null, inImport = true, isQualifier = false) storeResult(trace, lastPart.expression, descriptors, shouldBeVisibleFrom = null, position = QualifierPosition.IMPORT, isQualifier = false)
} }
private fun KtExpression.asQualifierPartList(trace: BindingTrace): List<QualifierPart> { private fun KtExpression.asQualifierPartList(trace: BindingTrace): List<QualifierPart> {
@@ -296,7 +293,7 @@ public class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageVa
} }
private enum class QualifierPosition { private enum class QualifierPosition {
IMPORT, TYPE, EXPRESSION PACKAGE_HEADER, IMPORT, TYPE, EXPRESSION
} }
private fun resolveToPackageOrClass( private fun resolveToPackageOrClass(
@@ -323,21 +320,20 @@ public class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageVa
trace: BindingTrace, trace: BindingTrace,
shouldBeVisibleFrom: DeclarationDescriptor?, shouldBeVisibleFrom: DeclarationDescriptor?,
scopeForFirstPart: LexicalScope?, scopeForFirstPart: LexicalScope?,
position: QualifierPosition position: QualifierPosition,
isValue: ((KtSimpleNameExpression) -> Boolean)? = null
): Pair<DeclarationDescriptor?, Int> { ): Pair<DeclarationDescriptor?, Int> {
if (path.isEmpty()) { if (path.isEmpty()) {
return Pair(moduleDescriptor.getPackage(FqName.ROOT), 0) return Pair(moduleDescriptor.getPackage(FqName.ROOT), 0)
} }
val inImport = position == QualifierPosition.IMPORT
val firstPart = path.first() val firstPart = path.first()
if (position == QualifierPosition.EXPRESSION) { if (position == QualifierPosition.EXPRESSION) {
// In expression position, value wins against classifier (and package). // In expression position, value wins against classifier (and package).
// If we see a function or variable (possibly ambiguous), // If we see a function or variable (possibly ambiguous),
// tell resolver we have no qualifier and let it perform the context-dependent resolution. // tell resolver we have no qualifier and let it perform the context-dependent resolution.
if (scopeForFirstPart != null && scopeForFirstPart.containsFunctionOrVariable(firstPart.name, firstPart.location)) { if (scopeForFirstPart != null && isValue != null && isValue(firstPart.expression)) {
return Pair(null, 0) return Pair(null, 0)
} }
} }
@@ -347,14 +343,14 @@ public class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageVa
} }
if (classifierDescriptor != null) { if (classifierDescriptor != null) {
storeResult(trace, firstPart.expression, classifierDescriptor, shouldBeVisibleFrom, inImport) storeResult(trace, firstPart.expression, classifierDescriptor, shouldBeVisibleFrom, position)
} }
val (prefixDescriptor, nextIndexAfterPrefix) = val (prefixDescriptor, nextIndexAfterPrefix) =
if (classifierDescriptor != null) if (classifierDescriptor != null)
Pair(classifierDescriptor, 1) Pair(classifierDescriptor, 1)
else else
moduleDescriptor.quickResolveToPackage(path, trace, inImport) moduleDescriptor.quickResolveToPackage(path, trace, position)
var currentDescriptor: DeclarationDescriptor? = prefixDescriptor var currentDescriptor: DeclarationDescriptor? = prefixDescriptor
for (qualifierPartIndex in nextIndexAfterPrefix .. path.size - 1) { for (qualifierPartIndex in nextIndexAfterPrefix .. path.size - 1) {
@@ -383,7 +379,7 @@ public class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageVa
// If we are in expression, this name can denote a value (not a package or class). // If we are in expression, this name can denote a value (not a package or class).
if (!(position == QualifierPosition.EXPRESSION && nextPackageOrClassDescriptor == null)) { if (!(position == QualifierPosition.EXPRESSION && nextPackageOrClassDescriptor == null)) {
storeResult(trace, qualifierPart.expression, nextPackageOrClassDescriptor, shouldBeVisibleFrom, inImport) storeResult(trace, qualifierPart.expression, nextPackageOrClassDescriptor, shouldBeVisibleFrom, position)
} }
if (nextPackageOrClassDescriptor == null) { if (nextPackageOrClassDescriptor == null) {
@@ -399,69 +395,51 @@ public class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageVa
public fun resolveQualifierInExpressionAndUnroll( public fun resolveQualifierInExpressionAndUnroll(
expression: KtQualifiedExpression, expression: KtQualifiedExpression,
context: ExpressionTypingContext context: ExpressionTypingContext,
isValue: (KtSimpleNameExpression) -> Boolean
): List<CallExpressionElement> { ): List<CallExpressionElement> {
val qualifiedExpressions = unrollToLeftMostQualifiedExpression(expression) val qualifiedExpressions = unrollToLeftMostQualifiedExpression(expression)
val maxPossibleQualifierPrefix = getMaxPossibleQualifierPrefix(qualifiedExpressions) val maxPossibleQualifierPrefix = getMaxPossibleQualifierPrefix(qualifiedExpressions)
val (packageOrClassDescriptor, nextIndexAfterPrefix) = resolveToPackageOrClassPrefix( val nextIndexAfterPrefix = resolveToPackageOrClassPrefix(
path = maxPossibleQualifierPrefix, path = maxPossibleQualifierPrefix,
moduleDescriptor = context.scope.ownerDescriptor.module, moduleDescriptor = context.scope.ownerDescriptor.module,
trace = context.trace, trace = context.trace,
shouldBeVisibleFrom = context.scope.ownerDescriptor, shouldBeVisibleFrom = context.scope.ownerDescriptor,
scopeForFirstPart = context.scope, scopeForFirstPart = context.scope,
position = QualifierPosition.EXPRESSION position = QualifierPosition.EXPRESSION,
) isValue = isValue
).second
if (nextIndexAfterPrefix > 0) { val firstExpressionAfterQualifierIndex =
val qualifierReferenceExpression = maxPossibleQualifierPrefix[nextIndexAfterPrefix - 1].expression if (nextIndexAfterPrefix == 0) 0 else nextIndexAfterPrefix - 1
val qualifier =
when (packageOrClassDescriptor) {
is PackageViewDescriptor ->
PackageQualifier(qualifierReferenceExpression, packageOrClassDescriptor)
is ClassDescriptor ->
ClassQualifier(qualifierReferenceExpression, packageOrClassDescriptor)
else ->
null
}
if (qualifier != null) {
context.trace.record(BindingContext.QUALIFIER, qualifier.expression, qualifier)
}
}
return qualifiedExpressions.map { CallExpressionElement(it) } return qualifiedExpressions
} .subList(firstExpressionAfterQualifierIndex, qualifiedExpressions.size)
.map { CallExpressionElement(it) }
private fun unrollToLeftMostQualifiedExpression(expression: KtQualifiedExpression): List<KtQualifiedExpression> {
val unrolled = arrayListOf<KtQualifiedExpression>()
var finger = expression
while (true) {
unrolled.add(finger)
val receiver = finger.receiverExpression
if (receiver !is KtQualifiedExpression) {
break
}
finger = receiver
}
Collections.reverse(unrolled)
return unrolled
} }
private fun getMaxPossibleQualifierPrefix(qualifiedExpressions: List<KtQualifiedExpression>) : List<QualifierPart> { private fun getMaxPossibleQualifierPrefix(qualifiedExpressions: List<KtQualifiedExpression>) : List<QualifierPart> {
if (qualifiedExpressions.isEmpty()) return emptyList() if (qualifiedExpressions.isEmpty()) return emptyList()
val first = qualifiedExpressions.first() val first = qualifiedExpressions.first()
if (first.operationSign != KtTokens.DOT) return emptyList() if (first !is KtDotQualifiedExpression) return emptyList()
val firstReceiver = first.receiverExpression val firstReceiver = first.receiverExpression
if (firstReceiver !is KtSimpleNameExpression) return emptyList() if (firstReceiver !is KtSimpleNameExpression) return emptyList()
// Qualifier parts are receiver name for the leftmost expression
// and selector names for all but the rightmost qualified expressions
// (since rightmost selector should denote a value in expression position,
// and thus can't be a qualifier part).
// E.g.:
// qualified expression 'a.b': qualifier parts == ['a']
// qualified expression 'a.b.c.d': qualifier parts == ['a', 'b', 'c']
val qualifierParts = arrayListOf<QualifierPart>() val qualifierParts = arrayListOf<QualifierPart>()
qualifierParts.add(QualifierPart(firstReceiver)) qualifierParts.add(QualifierPart(firstReceiver))
for (qualifiedExpression in qualifiedExpressions.dropLast(1)) { for (qualifiedExpression in qualifiedExpressions.dropLast(1)) {
if (qualifiedExpression.operationSign != KtTokens.DOT) break if (qualifiedExpression !is KtDotQualifiedExpression) break
val selector = qualifiedExpression.selectorExpression val selector = qualifiedExpression.selectorExpression
if (selector !is KtSimpleNameExpression) break if (selector !is KtSimpleNameExpression) break
qualifierParts.add(QualifierPart(selector)) qualifierParts.add(QualifierPart(selector))
@@ -473,7 +451,7 @@ public class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageVa
private fun ModuleDescriptor.quickResolveToPackage( private fun ModuleDescriptor.quickResolveToPackage(
path: List<QualifierPart>, path: List<QualifierPart>,
trace: BindingTrace, trace: BindingTrace,
inImport: Boolean position: QualifierPosition
): Pair<PackageViewDescriptor, Int> { ): Pair<PackageViewDescriptor, Int> {
val possiblePackagePrefixSize = path.indexOfFirst { it.typeArguments != null }.let { if (it == -1) path.size() else it + 1 } val possiblePackagePrefixSize = path.indexOfFirst { it.typeArguments != null }.let { if (it == -1) path.size() else it + 1 }
var fqName = path.subList(0, possiblePackagePrefixSize).fold(FqName.ROOT) { fqName, qualifierPart -> var fqName = path.subList(0, possiblePackagePrefixSize).fold(FqName.ROOT) { fqName, qualifierPart ->
@@ -483,7 +461,7 @@ public class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageVa
while (!fqName.isRoot) { while (!fqName.isRoot) {
val packageDescriptor = getPackage(fqName) val packageDescriptor = getPackage(fqName)
if (!packageDescriptor.isEmpty()) { if (!packageDescriptor.isEmpty()) {
recordPackageViews(path.subList(0, prefixSize), packageDescriptor, trace, inImport) recordPackageViews(path.subList(0, prefixSize), packageDescriptor, trace, position)
return Pair(packageDescriptor, prefixSize) return Pair(packageDescriptor, prefixSize)
} }
fqName = fqName.parent() fqName = fqName.parent()
@@ -496,10 +474,10 @@ public class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageVa
path: List<QualifierPart>, path: List<QualifierPart>,
packageView: PackageViewDescriptor, packageView: PackageViewDescriptor,
trace: BindingTrace, trace: BindingTrace,
inImport: Boolean position: QualifierPosition
) { ) {
path.foldRight(packageView) { qualifierPart, currentView -> path.foldRight(packageView) { qualifierPart, currentView ->
storeResult(trace, qualifierPart.expression, currentView, shouldBeVisibleFrom = null, inImport = inImport) storeResult(trace, qualifierPart.expression, currentView, shouldBeVisibleFrom = null, position = position)
val parentView = currentView.containingDeclaration val parentView = currentView.containingDeclaration
assert(parentView != null) { assert(parentView != null) {
"Containing Declaration must be not null for package with fqName: ${currentView.fqName}, " + "Containing Declaration must be not null for package with fqName: ${currentView.fqName}, " +
@@ -514,11 +492,11 @@ public class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageVa
referenceExpression: KtSimpleNameExpression, referenceExpression: KtSimpleNameExpression,
descriptors: Collection<DeclarationDescriptor>, descriptors: Collection<DeclarationDescriptor>,
shouldBeVisibleFrom: DeclarationDescriptor?, shouldBeVisibleFrom: DeclarationDescriptor?,
inImport: Boolean, position: QualifierPosition,
isQualifier: Boolean = true isQualifier: Boolean = true
) { ) {
if (descriptors.size() > 1) { if (descriptors.size() > 1) {
val visibleDescriptors = descriptors.filter { isVisible(it, shouldBeVisibleFrom, inImport) } val visibleDescriptors = descriptors.filter { isVisible(it, shouldBeVisibleFrom, position) }
if (visibleDescriptors.isEmpty()) { if (visibleDescriptors.isEmpty()) {
val descriptor = descriptors.first() as DeclarationDescriptorWithVisibility val descriptor = descriptors.first() as DeclarationDescriptorWithVisibility
trace.report(Errors.INVISIBLE_REFERENCE.on(referenceExpression, descriptor, descriptor.visibility, descriptor)) trace.report(Errors.INVISIBLE_REFERENCE.on(referenceExpression, descriptor, descriptor.visibility, descriptor))
@@ -527,11 +505,11 @@ public class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageVa
trace.record(BindingContext.AMBIGUOUS_REFERENCE_TARGET, referenceExpression, visibleDescriptors) trace.record(BindingContext.AMBIGUOUS_REFERENCE_TARGET, referenceExpression, visibleDescriptors)
} }
else { else {
storeResult(trace, referenceExpression, visibleDescriptors.single(), null, inImport, isQualifier) storeResult(trace, referenceExpression, visibleDescriptors.single(), null, position, isQualifier)
} }
} }
else { else {
storeResult(trace, referenceExpression, descriptors.singleOrNull(), shouldBeVisibleFrom, inImport, isQualifier) storeResult(trace, referenceExpression, descriptors.singleOrNull(), shouldBeVisibleFrom, position, isQualifier)
} }
} }
@@ -540,7 +518,7 @@ public class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageVa
referenceExpression: KtSimpleNameExpression, referenceExpression: KtSimpleNameExpression,
descriptor: DeclarationDescriptor?, descriptor: DeclarationDescriptor?,
shouldBeVisibleFrom: DeclarationDescriptor?, shouldBeVisibleFrom: DeclarationDescriptor?,
inImport: Boolean, position: QualifierPosition,
isQualifier: Boolean = true isQualifier: Boolean = true
) { ) {
if (descriptor == null) { if (descriptor == null) {
@@ -562,17 +540,17 @@ public class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageVa
else { else {
shouldBeVisibleFrom shouldBeVisibleFrom
} }
if (!isVisible(descriptor, fromToCheck, inImport)) { if (!isVisible(descriptor, fromToCheck, position)) {
trace.report(Errors.INVISIBLE_REFERENCE.on(referenceExpression, descriptor, descriptor.visibility, descriptor)) trace.report(Errors.INVISIBLE_REFERENCE.on(referenceExpression, descriptor, descriptor.visibility, descriptor))
} }
} }
if (isQualifier) { if (isQualifier) {
storageQualifier(trace, referenceExpression, descriptor) storeQualifier(trace, referenceExpression, descriptor)
} }
} }
private fun storageQualifier(trace: BindingTrace, referenceExpression: KtSimpleNameExpression, descriptor: DeclarationDescriptor) { private fun storeQualifier(trace: BindingTrace, referenceExpression: KtSimpleNameExpression, descriptor: DeclarationDescriptor) {
val qualifier = val qualifier =
when (descriptor) { when (descriptor) {
is PackageViewDescriptor -> PackageQualifier(referenceExpression, descriptor) is PackageViewDescriptor -> PackageQualifier(referenceExpression, descriptor)
@@ -586,12 +564,12 @@ public class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageVa
private fun isVisible( private fun isVisible(
descriptor: DeclarationDescriptor, descriptor: DeclarationDescriptor,
shouldBeVisibleFrom: DeclarationDescriptor?, shouldBeVisibleFrom: DeclarationDescriptor?,
inImport: Boolean position: QualifierPosition
): Boolean { ): Boolean {
if (descriptor !is DeclarationDescriptorWithVisibility || shouldBeVisibleFrom == null) return true if (descriptor !is DeclarationDescriptorWithVisibility || shouldBeVisibleFrom == null) return true
val visibility = descriptor.visibility val visibility = descriptor.visibility
if (inImport) { if (position == QualifierPosition.IMPORT) {
if (Visibilities.isPrivate(visibility)) return false if (Visibilities.isPrivate(visibility)) return false
if (!visibility.mustCheckInImports()) return true if (!visibility.mustCheckInImports()) return true
} }
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.resolve.calls;
import com.intellij.lang.ASTNode; import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiTreeUtil;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
@@ -329,17 +330,51 @@ public class CallExpressionResolver {
*/ */
@NotNull @NotNull
public KotlinTypeInfo getQualifiedExpressionTypeInfo( public KotlinTypeInfo getQualifiedExpressionTypeInfo(
@NotNull KtQualifiedExpression expression, @NotNull ExpressionTypingContext context @NotNull KtQualifiedExpression expression, @NotNull final ExpressionTypingContext context
) { ) {
ExpressionTypingContext currentContext = context.replaceExpectedType(NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT); ExpressionTypingContext currentContext = context.replaceExpectedType(NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT);
Function1<KtSimpleNameExpression, Boolean> isValueFunction = new Function1<KtSimpleNameExpression, Boolean>() {
@Override
public Boolean invoke(KtSimpleNameExpression nameExpression) {
TemporaryTraceAndCache temporaryForVariable = TemporaryTraceAndCache.create(
context, "trace to resolve as local variable or property", nameExpression);
Call call = CallMaker.makePropertyCall(ReceiverValue.NO_RECEIVER, null, nameExpression);
BasicCallResolutionContext contextForVariable = BasicCallResolutionContext.create(
context.replaceTraceAndCache(temporaryForVariable),
call, CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS);
OverloadResolutionResults<VariableDescriptor> resolutionResult = callResolver.resolveSimpleProperty(contextForVariable);
if (resolutionResult.isSingleResult() &&
resolutionResult.getResultingDescriptor() instanceof FakeCallableDescriptorForObject) {
return false;
}
return !resolutionResult.isNothing();
}
};
List<CallExpressionElement> elementChain = List<CallExpressionElement> elementChain =
qualifiedExpressionResolver.resolveQualifierInExpressionAndUnroll(expression, context); qualifiedExpressionResolver.resolveQualifierInExpressionAndUnroll(expression, context, isValueFunction);
KotlinTypeInfo receiverTypeInfo;
KotlinType receiverType;
DataFlowInfo receiverDataFlowInfo;
CallExpressionElement firstElement = elementChain.iterator().next(); CallExpressionElement firstElement = elementChain.iterator().next();
KotlinTypeInfo receiverTypeInfo = expressionTypingServices.getTypeInfo(firstElement.getReceiver(), currentContext); KtExpression firstReceiver = firstElement.getReceiver();
KotlinType receiverType = receiverTypeInfo.getType(); Qualifier firstQualifier = context.trace.get(BindingContext.QUALIFIER, firstReceiver);
DataFlowInfo receiverDataFlowInfo = receiverTypeInfo.getDataFlowInfo(); if (firstQualifier == null) {
receiverTypeInfo = expressionTypingServices.getTypeInfo(firstReceiver, currentContext);
receiverType = receiverTypeInfo.getType();
receiverDataFlowInfo = receiverTypeInfo.getDataFlowInfo();
}
else {
receiverType = null;
receiverDataFlowInfo = currentContext.dataFlowInfo;
//noinspection ConstantConditions
receiverTypeInfo = new KotlinTypeInfo(receiverType, receiverDataFlowInfo);
}
KotlinTypeInfo resultTypeInfo = receiverTypeInfo; KotlinTypeInfo resultTypeInfo = receiverTypeInfo;
boolean unconditional = true; boolean unconditional = true;
@@ -366,7 +401,9 @@ public class CallExpressionResolver {
getSelectorReturnTypeInfo(receiver, element.getNode(), selectorExpression, currentContext); getSelectorReturnTypeInfo(receiver, element.getNode(), selectorExpression, currentContext);
KotlinType selectorReturnType = selectorReturnTypeInfo.getType(); KotlinType selectorReturnType = selectorReturnTypeInfo.getType();
resolveDeferredReceiverInQualifiedExpression(qualifierReceiver, element.getQualified(), currentContext); if (qualifierReceiver != null) {
resolveDeferredReceiverInQualifiedExpression(qualifierReceiver, element.getQualified(), currentContext);
}
checkNestedClassAccess(element.getQualified(), currentContext); checkNestedClassAccess(element.getQualified(), currentContext);
boolean safeCall = element.getSafe(); boolean safeCall = element.getSafe();
@@ -415,11 +452,10 @@ public class CallExpressionResolver {
} }
private void resolveDeferredReceiverInQualifiedExpression( private void resolveDeferredReceiverInQualifiedExpression(
@Nullable QualifierReceiver qualifierReceiver, @NotNull QualifierReceiver qualifierReceiver,
@NotNull KtQualifiedExpression qualifiedExpression, @NotNull KtQualifiedExpression qualifiedExpression,
@NotNull ExpressionTypingContext context @NotNull ExpressionTypingContext context
) { ) {
if (qualifierReceiver == null) return;
KtExpression calleeExpression = KtExpression calleeExpression =
KtPsiUtil.deparenthesize(CallUtilKt.getCalleeExpressionIfAny(qualifiedExpression.getSelectorExpression())); KtPsiUtil.deparenthesize(CallUtilKt.getCalleeExpressionIfAny(qualifiedExpression.getSelectorExpression()));
DeclarationDescriptor selectorDescriptor = DeclarationDescriptor selectorDescriptor =
@@ -22,14 +22,20 @@ import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtQualifiedExpression import org.jetbrains.kotlin.psi.KtQualifiedExpression
import java.util.* import java.util.*
internal fun unroll(root: KtQualifiedExpression): Deque<CallExpressionElement> { public fun unrollToLeftMostQualifiedExpression(expression: KtQualifiedExpression): List<KtQualifiedExpression> {
var current: KtExpression = root val unrolled = arrayListOf<KtQualifiedExpression>()
val result = linkedListOf<CallExpressionElement>()
while (current is KtQualifiedExpression) { var finger = expression
result.addFirst(CallExpressionElement(current)) while (true) {
current = result.first.receiver unrolled.add(finger)
val receiver = finger.receiverExpression
if (receiver !is KtQualifiedExpression) {
break
}
finger = receiver
} }
return result
return unrolled.asReversed()
} }
public data class CallExpressionElement internal constructor (val qualified: KtQualifiedExpression) { public data class CallExpressionElement internal constructor (val qualified: KtQualifiedExpression) {
@@ -38,7 +38,6 @@ import org.jetbrains.kotlin.resolve.scopes.utils.memberScopeAsImportingScope
import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext
import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.* import java.util.*
import kotlin.properties.Delegates
public interface Qualifier: Receiver { public interface Qualifier: Receiver {
@@ -150,11 +149,7 @@ class ClassQualifier(
override fun toString() = "Class{$classifier}" override fun toString() = "Class{$classifier}"
} }
fun createQualifier( fun createQualifier(expression: KtSimpleNameExpression, receiver: Receiver, context: ExpressionTypingContext): QualifierReceiver? {
expression: KtSimpleNameExpression,
receiver: Receiver,
context: ExpressionTypingContext
): QualifierReceiver? {
val receiverScope = when { val receiverScope = when {
!receiver.exists() -> context.scope !receiver.exists() -> context.scope
receiver is QualifierReceiver -> receiver.scope.memberScopeAsImportingScope() receiver is QualifierReceiver -> receiver.scope.memberScopeAsImportingScope()
@@ -98,17 +98,6 @@ public fun HierarchicalScope.findFunction(name: Name, location: LookupLocation,
return null return null
} }
public fun HierarchicalScope.containsFunctionOrVariable(name: Name, location: LookupLocation): Boolean {
processForMeAndParent {
if (it.getContributedFunctions(name, location).isNotEmpty() ||
it.getContributedVariables(name, location).isNotEmpty()
) {
return true
}
}
return false
}
public fun HierarchicalScope.collectSyntheticExtensionProperties(receiverTypes: Collection<KotlinType>, name: Name, location: LookupLocation) public fun HierarchicalScope.collectSyntheticExtensionProperties(receiverTypes: Collection<KotlinType>, name: Name, location: LookupLocation)
= collectAllFromImportingScopes { it.getContributedSyntheticExtensionProperties(receiverTypes, name, location) } = collectAllFromImportingScopes { it.getContributedSyntheticExtensionProperties(receiverTypes, name, location) }
@@ -30,7 +30,7 @@ import org.jetbrains.kotlin.types.KotlinType
* At the end current data flow info is x != null && y != null, but jump data flow info is x != null only. * At the end current data flow info is x != null && y != null, but jump data flow info is x != null only.
* Both break and continue are counted as possible jump outside of a loop, but return is not. * Both break and continue are counted as possible jump outside of a loop, but return is not.
*/ */
class KotlinTypeInfo( class KotlinTypeInfo @JvmOverloads constructor(
val type: KotlinType?, val type: KotlinType?,
val dataFlowInfo: DataFlowInfo, val dataFlowInfo: DataFlowInfo,
val jumpOutPossible: Boolean = false, val jumpOutPossible: Boolean = false,
@@ -0,0 +1,8 @@
class A {
@Deprecated("deprecated") companion object
class B
}
val x1 = A.B()
@@ -0,0 +1,24 @@
package
public val x1: A.B
public final class A {
public constructor A()
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 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
}
@kotlin.Deprecated(message = "deprecated") public companion object Companion {
private constructor Companion()
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
}
}
@@ -0,0 +1,33 @@
// FILE: a.kt
package a
class A(val b: String) {
companion object {
val c: String = ""
}
fun mtd() = c.length
}
// FILE: b.kt
package b
// FILE: c.kt
package c
// FILE: test.kt
package test
import a.A
fun <T, R> T.with(f: T.() -> R) = f()
fun A.extFun1() = b.length
// fun A.extFun2() = c.length // TODO fix KT-9953
val x1 = A("").with { b.length }
// val x2 = A("").with { c.length } // TODO fix KT-9953
val x3 = A.with { c.length }
@@ -0,0 +1,34 @@
package
package a {
public final class A {
public constructor A(/*0*/ b: kotlin.String)
public final val b: kotlin.String
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 final fun mtd(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
public companion object Companion {
private constructor Companion()
public final val c: kotlin.String = ""
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
}
}
}
package b {
}
package c {
}
package test {
public val x1: kotlin.Int
public val x3: kotlin.Int
public fun a.A.extFun1(): kotlin.Int
public fun </*0*/ T, /*1*/ R> T.with(/*0*/ f: T.() -> R): R
}
@@ -4767,6 +4767,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("classWithCompanionObject.kt")
public void testClassWithCompanionObject() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/deprecated/classWithCompanionObject.kt");
doTest(fileName);
}
@TestMetadata("componentUsage.kt") @TestMetadata("componentUsage.kt")
public void testComponentUsage() throws Exception { public void testComponentUsage() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/deprecated/componentUsage.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/deprecated/componentUsage.kt");
@@ -13074,6 +13080,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("implicitReceiverProperty.kt")
public void testImplicitReceiverProperty() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/resolve/implicitReceiverProperty.kt");
doTest(fileName);
}
@TestMetadata("incompleteConstructorInvocation.kt") @TestMetadata("incompleteConstructorInvocation.kt")
public void testIncompleteConstructorInvocation() throws Exception { public void testIncompleteConstructorInvocation() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/resolve/incompleteConstructorInvocation.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/resolve/incompleteConstructorInvocation.kt");
@@ -19,8 +19,8 @@ import bar.*
this./*c:foo.A*/a this./*c:foo.A*/a
this./*c:foo.A*/foo() this./*c:foo.A*/foo()
/*c:foo.A c:foo.A.Companion p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io c:foo.A(getBaz) c:foo.A(getBAZ)*/baz() /*c:foo.A c:foo.A.Companion p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io c:foo.A(getBaz) c:foo.A(getBAZ)*/baz()
/*p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io c:foo.A c:foo.A.Companion*/Companion./*c:foo.A.Companion*/a /*c:foo.A c:foo.A.Companion p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io*/Companion./*c:foo.A.Companion*/a
/*p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io c:foo.A c:foo.A.Companion*/O./*c:foo.A.O*/v = "OK" /*c:foo.A c:foo.A.Companion p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io*/O./*c:foo.A.O*/v = "OK"
} }
class B { class B {
@@ -64,8 +64,8 @@ import bar.*
val a = 1 val a = 1
fun foo() { fun foo() {
/*c:foo.E*/a /*c:foo.E*/a
/*p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io c:foo.E*/Y./*c:foo.E*/a /*c:foo.E p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io*/Y./*c:foo.E*/a
/*c:foo.E*/foo() /*c:foo.E*/foo()
/*p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io c:foo.E*/X./*c:foo.E*/foo() /*c:foo.E p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io*/X./*c:foo.E*/foo()
} }
} }
@@ -9,15 +9,15 @@ import bar.*
/*p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io*/A()./*c:foo.A*/d = "new value" /*p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io*/A()./*c:foo.A*/d = "new value"
/*p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io*/A()./*c:foo.A*/foo() /*p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io*/A()./*c:foo.A*/foo()
/*p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io*/A./*c:foo.A*/B()./*c:foo.A.B*/a /*p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io*/A./*c:foo.A*/B()./*c:foo.A.B*/a
/*p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io*/A./*c:foo.A c:foo.A.Companion*/B./*c:foo.A.B c:foo.A.B.CO*/bar(1) /*p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io*/A./*c:foo.A*/B./*c:foo.A.B c:foo.A.B.CO*/bar(1)
/*p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io*/A./*c:foo.A c:foo.A.Companion*/B./*c:foo.A.B c:foo.A.B.CO*/CO./*c:foo.A.B.CO*/bar(1) /*p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io*/A./*c:foo.A*/B./*c:foo.A.B*/CO./*c:foo.A.B.CO*/bar(1)
/*p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io*/A /*p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io*/A
/*p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io*/A./*c:foo.A c:foo.A.Companion*/a /*p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io*/A./*c:foo.A c:foo.A.Companion*/a
/*p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io*/A./*c:foo.A c:foo.A.Companion*/baz() /*p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io*/A./*c:foo.A c:foo.A.Companion*/baz()
/*p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io*/A./*c:foo.A*/Companion /*p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io*/A./*c:foo.A*/Companion
/*p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io*/A./*c:foo.A c:foo.A.Companion*/Companion./*c:foo.A.Companion*/baz() /*p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io*/A./*c:foo.A*/Companion./*c:foo.A.Companion*/baz()
/*p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io*/A./*c:foo.A*/O /*p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io*/A./*c:foo.A*/O
/*p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io*/A./*c:foo.A c:foo.A.Companion*/O./*c:foo.A.O*/v = "OK" /*p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io*/A./*c:foo.A*/O./*c:foo.A.O*/v = "OK"
/*p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io*/A./*c:foo.A c:foo.A.Companion p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io c:foo.A.Companion(getVala) c:foo.A.Companion(getVALA)*/vala /*p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io*/A./*c:foo.A c:foo.A.Companion p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io c:foo.A.Companion(getVala) c:foo.A.Companion(getVALA)*/vala
/*p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io*/A./*c:foo.A c:foo.A.Companion p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io c:foo.A.Companion(getVara) c:foo.A.Companion(getVARA)*/vara() /*p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io*/A./*c:foo.A c:foo.A.Companion p:foo p:bar p:java.lang p:kotlin p:kotlin.annotation p:kotlin.jvm p:kotlin.io c:foo.A.Companion(getVara) c:foo.A.Companion(getVARA)*/vara()