diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/OverloadingConflictResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/OverloadingConflictResolver.kt index 2abcf636058..94e9fe80a1f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/OverloadingConflictResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/OverloadingConflictResolver.kt @@ -19,15 +19,13 @@ package org.jetbrains.kotlin.resolve.calls.results import gnu.trove.THashSet import gnu.trove.TObjectHashingStrategy import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.descriptors.CallableDescriptor -import org.jetbrains.kotlin.descriptors.ScriptDescriptor -import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor -import org.jetbrains.kotlin.descriptors.VariableDescriptor +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.resolve.calls.context.CheckArgumentTypesMode import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilderImpl import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPosition -import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.* +import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.RECEIVER_POSITION +import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.VALUE_PARAMETER_POSITION import org.jetbrains.kotlin.resolve.calls.inference.toHandle import org.jetbrains.kotlin.resolve.calls.model.MutableResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall @@ -42,7 +40,8 @@ class OverloadingConflictResolver(private val builtIns: KotlinBuiltIns) { fun findMaximallySpecific( candidates: Set>, checkArgumentsMode: CheckArgumentTypesMode, - discriminateGenerics: Boolean + discriminateGenerics: Boolean, + isDebuggerContext: Boolean ): MutableResolvedCall? = if (candidates.size <= 1) candidates.firstOrNull() @@ -56,7 +55,7 @@ class OverloadingConflictResolver(private val builtIns: KotlinBuiltIns) { }.singleOrNull() CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS -> - findMaximallySpecificCall(candidates, discriminateGenerics) + findMaximallySpecificCall(candidates, discriminateGenerics, isDebuggerContext) } fun findMaximallySpecificVariableAsFunctionCalls(candidates: Set>): Set> { @@ -67,7 +66,7 @@ class OverloadingConflictResolver(private val builtIns: KotlinBuiltIns) { throw AssertionError("Regular call among variable-as-function calls: $it") } - val maxSpecificVariableCall = findMaximallySpecificCall(variableCalls, false) ?: return emptySet() + val maxSpecificVariableCall = findMaximallySpecificCall(variableCalls, false, false) ?: return emptySet() return candidates.filterTo(newResolvedCallSet>(2)) { it.resultingVariableDescriptor == maxSpecificVariableCall.resultingDescriptor @@ -76,7 +75,8 @@ class OverloadingConflictResolver(private val builtIns: KotlinBuiltIns) { private fun findMaximallySpecificCall( candidates: Set>, - discriminateGenerics: Boolean + discriminateGenerics: Boolean, + isDebuggerContext: Boolean ): MutableResolvedCall? { val filteredCandidates = uniquifyCandidatesSet(candidates) @@ -97,7 +97,10 @@ class OverloadingConflictResolver(private val builtIns: KotlinBuiltIns) { } } - return bestCandidatesByParameterTypes.exactMaxWith { call1, call2 -> isOfNotLessSpecificShape(call1, call2) }?.resolvedCall + return bestCandidatesByParameterTypes.exactMaxWith { + call1, call2 -> + isOfNotLessSpecificShape(call1, call2) && isOfNotLessSpecificVisibilityForDebugger(call1, call2, isDebuggerContext) + }?.resolvedCall } private inline fun Collection.exactMaxWith(isNotWorse: (C, C) -> Boolean): C? { @@ -231,6 +234,22 @@ class OverloadingConflictResolver(private val builtIns: KotlinBuiltIns) { return true } + private fun isOfNotLessSpecificVisibilityForDebugger( + call1: CandidateCallWithArgumentMapping, + call2: CandidateCallWithArgumentMapping, + isDebuggerContext: Boolean + ): Boolean { + if (isDebuggerContext) { + val isMoreVisible1 = Visibilities.compare( + call1.resolvedCall.resultingDescriptor.visibility, + call2.resolvedCall.resultingDescriptor.visibility + ) + if (isMoreVisible1 != null && isMoreVisible1 < 0) return false + } + + return true + } + /** * Returns `true` if `d1` is definitely not less specific than `d2`, * `false` if `d1` is definitely less specific than `d2`, diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/ResolutionResultsHandler.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/ResolutionResultsHandler.java index 188964a8af0..18f09740d48 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/ResolutionResultsHandler.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/ResolutionResultsHandler.java @@ -96,7 +96,8 @@ public class ResolutionResultsHandler { Set> successfulAndIncomplete = Sets.newLinkedHashSet(); successfulAndIncomplete.addAll(successfulCandidates); successfulAndIncomplete.addAll(incompleteCandidates); - OverloadResolutionResultsImpl results = chooseAndReportMaximallySpecific(successfulAndIncomplete, true, checkArgumentsMode); + OverloadResolutionResultsImpl results = chooseAndReportMaximallySpecific( + successfulAndIncomplete, true, context.isDebuggerContext, checkArgumentsMode); if (results.isSingleResult()) { MutableResolvedCall resultingCall = results.getResultingCall(); resultingCall.getTrace().moveAllMyDataTo(context.trace); @@ -147,7 +148,7 @@ public class ResolutionResultsHandler { if (severityLevel.contains(ARGUMENTS_MAPPING_ERROR)) { return recordFailedInfo(tracing, trace, thisLevel); } - OverloadResolutionResultsImpl results = chooseAndReportMaximallySpecific(thisLevel, false, checkArgumentsMode); + OverloadResolutionResultsImpl results = chooseAndReportMaximallySpecific(thisLevel, false, false, checkArgumentsMode); return recordFailedInfo(tracing, trace, results.getResultingCalls()); } } @@ -188,6 +189,7 @@ public class ResolutionResultsHandler { public OverloadResolutionResultsImpl chooseAndReportMaximallySpecific( @NotNull Set> candidates, boolean discriminateGenerics, + boolean isDebuggerContext, @NotNull CheckArgumentTypesMode checkArgumentsMode ) { if (candidates.size() == 1) { @@ -203,14 +205,14 @@ public class ResolutionResultsHandler { return OverloadResolutionResultsImpl.success(noOverrides.iterator().next()); } - MutableResolvedCall maximallySpecific = overloadingConflictResolver.findMaximallySpecific(noOverrides, checkArgumentsMode, false); + MutableResolvedCall maximallySpecific = overloadingConflictResolver.findMaximallySpecific(noOverrides, checkArgumentsMode, false, isDebuggerContext); if (maximallySpecific != null) { return OverloadResolutionResultsImpl.success(maximallySpecific); } if (discriminateGenerics) { MutableResolvedCall maximallySpecificGenericsDiscriminated = overloadingConflictResolver.findMaximallySpecific( - noOverrides, checkArgumentsMode, true); + noOverrides, checkArgumentsMode, true, isDebuggerContext); if (maximallySpecificGenericsDiscriminated != null) { return OverloadResolutionResultsImpl.success(maximallySpecificGenericsDiscriminated); } diff --git a/idea/testData/debugger/tinyApp/outs/privateMembersPriority.out b/idea/testData/debugger/tinyApp/outs/privateMembersPriority.out new file mode 100644 index 00000000000..d96cff870b3 --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/privateMembersPriority.out @@ -0,0 +1,14 @@ +LineBreakpoint created at privateMembersPriority.kt:22 +LineBreakpoint created at privateMembersPriority.kt:34 +LineBreakpoint created at privateMembersPriority.kt:41 +!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !OUTPUT_PATH!;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! privateMembersPriority.PrivateMembersPriorityKt +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +privateMembersPriority.kt:22 +Compile bytecode for privateFun() +privateMembersPriority.kt:34 +Compile bytecode for javaClass.size +privateMembersPriority.kt:41 +Compile bytecode for TwoPrivateFun().foo(1) +Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' + +Process finished with exit code 0 diff --git a/idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/privateMembersPriority.kt b/idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/privateMembersPriority.kt new file mode 100644 index 00000000000..2025b508c7e --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/privateMembersPriority.kt @@ -0,0 +1,47 @@ +package privateMembersPriority + +fun main(args: Array) { + Receiver2().test1() + test2() + test3() +} + +class Receiver1 { + private fun privateFun() = 1 +} + +class Receiver2 { + fun privateFun() = 2 + + // In IDE -> privateFun() will return 2, because privateFun from Receiver is invisible in current scope + fun test1() { + with(Receiver1()) { + // EXPRESSION: privateFun() + // RESULT: 1: I + //Breakpoint! + privateFun() + } + } +} + +// In debuggerContext there are two properties size in ArrayList (java field + kotlin property) +fun test2() { + val javaClass = arrayListOf(1) + + // EXPRESSION: javaClass.size + // RESULT: 1: I + //Breakpoint! + javaClass.size +} + +fun test3() { + // EXPRESSION: TwoPrivateFun().foo(1) + // RESULT: 1: I + //Breakpoint! + val a = 1 +} + +class TwoPrivateFun { + private fun foo(i: Int) = 1 + private fun foo(i: Double) = 2 +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java index 560a5bf2f3a..e2f431c3564 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java @@ -778,6 +778,12 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat doMultipleBreakpointsTest(fileName); } + @TestMetadata("privateMembersPriority.kt") + public void testPrivateMembersPriority() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/privateMembersPriority.kt"); + doMultipleBreakpointsTest(fileName); + } + @TestMetadata("whenEntry.kt") public void testWhenEntry() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/whenEntry.kt");