Add resolution status to report about unsuccessful smartcast
#KT-10248 Fixed #KT-11119 Fixed
This commit is contained in:
@@ -559,7 +559,7 @@ class CandidateResolver(
|
|||||||
}
|
}
|
||||||
if (!smartCastResult.isCorrect) {
|
if (!smartCastResult.isCorrect) {
|
||||||
// Error about unstable smart cast reported within checkAndRecordPossibleCast
|
// Error about unstable smart cast reported within checkAndRecordPossibleCast
|
||||||
return OTHER_ERROR
|
return UNSTABLE_SMARTCAST_ERROR
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ public enum ResolutionStatus {
|
|||||||
UNKNOWN_STATUS,
|
UNKNOWN_STATUS,
|
||||||
UNSAFE_CALL_ERROR,
|
UNSAFE_CALL_ERROR,
|
||||||
WRONG_NUMBER_OF_TYPE_ARGUMENTS_ERROR,
|
WRONG_NUMBER_OF_TYPE_ARGUMENTS_ERROR,
|
||||||
|
UNSTABLE_SMARTCAST_ERROR,
|
||||||
OTHER_ERROR,
|
OTHER_ERROR,
|
||||||
ARGUMENTS_MAPPING_ERROR,
|
ARGUMENTS_MAPPING_ERROR,
|
||||||
// '1.foo()' shouldn't be resolved to 'fun String.foo()'
|
// '1.foo()' shouldn't be resolved to 'fun String.foo()'
|
||||||
@@ -40,6 +41,7 @@ public enum ResolutionStatus {
|
|||||||
public static final EnumSet<ResolutionStatus>[] SEVERITY_LEVELS = new EnumSet[] {
|
public static final EnumSet<ResolutionStatus>[] SEVERITY_LEVELS = new EnumSet[] {
|
||||||
EnumSet.of(UNSAFE_CALL_ERROR), // weakest
|
EnumSet.of(UNSAFE_CALL_ERROR), // weakest
|
||||||
EnumSet.of(WRONG_NUMBER_OF_TYPE_ARGUMENTS_ERROR),
|
EnumSet.of(WRONG_NUMBER_OF_TYPE_ARGUMENTS_ERROR),
|
||||||
|
EnumSet.of(UNSTABLE_SMARTCAST_ERROR),
|
||||||
EnumSet.of(OTHER_ERROR),
|
EnumSet.of(OTHER_ERROR),
|
||||||
EnumSet.of(ARGUMENTS_MAPPING_ERROR),
|
EnumSet.of(ARGUMENTS_MAPPING_ERROR),
|
||||||
EnumSet.of(RECEIVER_TYPE_ERROR),
|
EnumSet.of(RECEIVER_TYPE_ERROR),
|
||||||
|
|||||||
+19
-2
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2016 JetBrains s.r.o.
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -20,8 +20,10 @@ import gnu.trove.THashSet
|
|||||||
import gnu.trove.TObjectHashingStrategy
|
import gnu.trove.TObjectHashingStrategy
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
|
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||||
|
import org.jetbrains.kotlin.descriptors.synthetic.SyntheticMemberDescriptor
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorEquivalenceForOverrides
|
import org.jetbrains.kotlin.resolve.DescriptorEquivalenceForOverrides
|
||||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||||
import org.jetbrains.kotlin.resolve.calls.context.CheckArgumentTypesMode
|
import org.jetbrains.kotlin.resolve.calls.context.CheckArgumentTypesMode
|
||||||
@@ -70,7 +72,22 @@ class OverloadingConflictResolver<C : Any>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val noEquivalentCalls = filterOutEquivalentCalls(fixedCandidates)
|
val noEquivalentCalls = filterOutEquivalentCalls(fixedCandidates)
|
||||||
val noOverrides = OverridingUtil.filterOverrides(noEquivalentCalls) { it.resultingDescriptor }
|
val noOverrides = OverridingUtil.filterOverrides(noEquivalentCalls) { a, b ->
|
||||||
|
val aDescriptor = a.resultingDescriptor
|
||||||
|
val bDescriptor = b.resultingDescriptor
|
||||||
|
// Here we'd like to handle situation when we have two synthetic descriptors as in syntheticSAMExtensions.kt
|
||||||
|
|
||||||
|
// Without this, we'll pick all synthetic descriptors as they don't have overridden descriptors and
|
||||||
|
// then report ambiguity, which isn't very convenient
|
||||||
|
if (aDescriptor is SyntheticMemberDescriptor<*> && bDescriptor is SyntheticMemberDescriptor<*>) {
|
||||||
|
val aBaseDescriptor = aDescriptor.baseDescriptorForSynthetic
|
||||||
|
val bBaseDescriptor = bDescriptor.baseDescriptorForSynthetic
|
||||||
|
if (aBaseDescriptor is CallableMemberDescriptor && bBaseDescriptor is CallableMemberDescriptor) {
|
||||||
|
return@filterOverrides Pair(aBaseDescriptor, bBaseDescriptor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Pair(aDescriptor, bDescriptor)
|
||||||
|
}
|
||||||
if (noOverrides.size == 1) {
|
if (noOverrides.size == 1) {
|
||||||
return noOverrides
|
return noOverrides
|
||||||
}
|
}
|
||||||
|
|||||||
compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/nullableReceiverWithOverloadedMethod.kt
Vendored
+29
@@ -0,0 +1,29 @@
|
|||||||
|
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||||
|
|
||||||
|
class A {
|
||||||
|
fun f(x: Boolean): Int = 0
|
||||||
|
|
||||||
|
fun f(y: String): Int = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
class B {
|
||||||
|
private var a: A? = null
|
||||||
|
|
||||||
|
fun takeInt(i: Int) {}
|
||||||
|
|
||||||
|
fun f() {
|
||||||
|
a = A()
|
||||||
|
<!SMARTCAST_IMPOSSIBLE!>a<!>.f(true)
|
||||||
|
takeInt(<!SMARTCAST_IMPOSSIBLE!>a<!>.f(""))
|
||||||
|
a.<!NONE_APPLICABLE!>f<!>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun g() {
|
||||||
|
takeInt(if (3 > 2) {
|
||||||
|
a = A()
|
||||||
|
<!SMARTCAST_IMPOSSIBLE!>a<!>.f(true)
|
||||||
|
} else {
|
||||||
|
6
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public final class A {
|
||||||
|
public constructor A()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public final fun f(/*0*/ x: kotlin.Boolean): kotlin.Int
|
||||||
|
public final fun f(/*0*/ y: kotlin.String): kotlin.Int
|
||||||
|
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()
|
||||||
|
private final var a: A?
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public final fun f(): kotlin.Unit
|
||||||
|
public final fun g(): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public final fun takeInt(/*0*/ i: kotlin.Int): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||||
|
|
||||||
|
interface Ctx
|
||||||
|
class CtxImpl : Ctx {
|
||||||
|
fun doJob(a: Int) {}
|
||||||
|
fun doJob(s: String) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
open class Test(open val ctx: Ctx) {
|
||||||
|
fun test() {
|
||||||
|
when (ctx) {
|
||||||
|
is CtxImpl -> <!SMARTCAST_IMPOSSIBLE!>ctx<!>.doJob(2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public interface Ctx {
|
||||||
|
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 CtxImpl : Ctx {
|
||||||
|
public constructor CtxImpl()
|
||||||
|
public final fun doJob(/*0*/ a: kotlin.Int): kotlin.Unit
|
||||||
|
public final fun doJob(/*0*/ s: kotlin.String): kotlin.Unit
|
||||||
|
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 open class Test {
|
||||||
|
public constructor Test(/*0*/ ctx: Ctx)
|
||||||
|
public open val ctx: Ctx
|
||||||
|
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 test(): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||||
|
|
||||||
|
class A
|
||||||
|
class B
|
||||||
|
|
||||||
|
fun A.foo(a: A) {}
|
||||||
|
fun A.foo(b: B) {}
|
||||||
|
var a: A? = null
|
||||||
|
|
||||||
|
fun smartCastInterference(b: B) {
|
||||||
|
if (a != null) {
|
||||||
|
<!SMARTCAST_IMPOSSIBLE!>a<!>.foo(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public var a: A?
|
||||||
|
public fun smartCastInterference(/*0*/ b: B): kotlin.Unit
|
||||||
|
public fun A.foo(/*0*/ a: A): kotlin.Unit
|
||||||
|
public fun A.foo(/*0*/ b: B): kotlin.Unit
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
+1
-1
@@ -20,7 +20,7 @@ class B : A() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (d.x is B) {
|
if (d.x is B) {
|
||||||
<!SMARTCAST_IMPOSSIBLE!>d.x<!>.foo <!TYPE_MISMATCH!>{}<!>
|
<!SMARTCAST_IMPOSSIBLE!>d.x<!>.foo {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13912,6 +13912,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nullableReceiverWithOverloadedMethod.kt")
|
||||||
|
public void testNullableReceiverWithOverloadedMethod() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/nullableReceiverWithOverloadedMethod.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("PreferExtensionsOnNullableReceiver.kt")
|
@TestMetadata("PreferExtensionsOnNullableReceiver.kt")
|
||||||
public void testPreferExtensionsOnNullableReceiver() throws Exception {
|
public void testPreferExtensionsOnNullableReceiver() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/PreferExtensionsOnNullableReceiver.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/PreferExtensionsOnNullableReceiver.kt");
|
||||||
@@ -13959,6 +13965,18 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
|||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unnecessaryNotNullAssertion.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unnecessaryNotNullAssertion.kt");
|
||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("unstableSmartcastWhenOpenGetterWithOverloading.kt")
|
||||||
|
public void testUnstableSmartcastWhenOpenGetterWithOverloading() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unstableSmartcastWhenOpenGetterWithOverloading.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("unstableSmartcastWithOverloadedExtensions.kt")
|
||||||
|
public void testUnstableSmartcastWithOverloadedExtensions() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unstableSmartcastWithOverloadedExtensions.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/diagnostics/tests/nullableTypes")
|
@TestMetadata("compiler/testData/diagnostics/tests/nullableTypes")
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2016 JetBrains s.r.o.
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -16,9 +16,11 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.resolve;
|
package org.jetbrains.kotlin.resolve;
|
||||||
|
|
||||||
|
import kotlin.Pair;
|
||||||
import kotlin.Unit;
|
import kotlin.Unit;
|
||||||
import kotlin.collections.CollectionsKt;
|
import kotlin.collections.CollectionsKt;
|
||||||
import kotlin.jvm.functions.Function1;
|
import kotlin.jvm.functions.Function1;
|
||||||
|
import kotlin.jvm.functions.Function2;
|
||||||
import org.jetbrains.annotations.Mutable;
|
import org.jetbrains.annotations.Mutable;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@@ -33,7 +35,6 @@ import org.jetbrains.kotlin.types.KotlinTypeKt;
|
|||||||
import org.jetbrains.kotlin.types.TypeConstructor;
|
import org.jetbrains.kotlin.types.TypeConstructor;
|
||||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
|
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
|
||||||
import org.jetbrains.kotlin.types.checker.KotlinTypeCheckerImpl;
|
import org.jetbrains.kotlin.types.checker.KotlinTypeCheckerImpl;
|
||||||
import org.jetbrains.kotlin.utils.FunctionsKt;
|
|
||||||
import org.jetbrains.kotlin.utils.SmartSet;
|
import org.jetbrains.kotlin.utils.SmartSet;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -72,23 +73,29 @@ public class OverridingUtil {
|
|||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
public static <D extends CallableDescriptor> Set<D> filterOutOverridden(@NotNull Set<D> candidateSet) {
|
public static <D extends CallableDescriptor> Set<D> filterOutOverridden(@NotNull Set<D> candidateSet) {
|
||||||
return filterOverrides(candidateSet, FunctionsKt.<CallableDescriptor>identity());
|
return filterOverrides(candidateSet, new Function2<D, D, Pair<CallableDescriptor, CallableDescriptor>>() {
|
||||||
|
@Override
|
||||||
|
public Pair<CallableDescriptor, CallableDescriptor> invoke(D a, D b) {
|
||||||
|
return new Pair<CallableDescriptor, CallableDescriptor>(a, b);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static <D> Set<D> filterOverrides(
|
public static <D> Set<D> filterOverrides(
|
||||||
@NotNull Set<D> candidateSet,
|
@NotNull Set<D> candidateSet,
|
||||||
@NotNull Function1<? super D, ? extends CallableDescriptor> transform
|
@NotNull Function2<? super D, ? super D, Pair<CallableDescriptor, CallableDescriptor>> transformFirst
|
||||||
) {
|
) {
|
||||||
if (candidateSet.size() <= 1) return candidateSet;
|
if (candidateSet.size() <= 1) return candidateSet;
|
||||||
|
|
||||||
Set<D> result = new LinkedHashSet<D>();
|
Set<D> result = new LinkedHashSet<D>();
|
||||||
outerLoop:
|
outerLoop:
|
||||||
for (D meD : candidateSet) {
|
for (D meD : candidateSet) {
|
||||||
CallableDescriptor me = transform.invoke(meD);
|
|
||||||
for (Iterator<D> iterator = result.iterator(); iterator.hasNext(); ) {
|
for (Iterator<D> iterator = result.iterator(); iterator.hasNext(); ) {
|
||||||
D otherD = iterator.next();
|
D otherD = iterator.next();
|
||||||
CallableDescriptor other = transform.invoke(otherD);
|
Pair<CallableDescriptor, CallableDescriptor> meAndOther = transformFirst.invoke(meD, otherD);
|
||||||
|
CallableDescriptor me = meAndOther.component1();
|
||||||
|
CallableDescriptor other = meAndOther.component2();
|
||||||
if (overrides(me, other)) {
|
if (overrides(me, other)) {
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user