Add resolution status to report about unsuccessful smartcast

#KT-10248 Fixed
 #KT-11119 Fixed
This commit is contained in:
Mikhail Zarechenskiy
2017-06-19 13:22:45 +03:00
parent 0f4497256b
commit cd1ae7f0f2
12 changed files with 178 additions and 10 deletions
@@ -559,7 +559,7 @@ class CandidateResolver(
}
if (!smartCastResult.isCorrect) {
// Error about unstable smart cast reported within checkAndRecordPossibleCast
return OTHER_ERROR
return UNSTABLE_SMARTCAST_ERROR
}
}
}
@@ -24,6 +24,7 @@ public enum ResolutionStatus {
UNKNOWN_STATUS,
UNSAFE_CALL_ERROR,
WRONG_NUMBER_OF_TYPE_ARGUMENTS_ERROR,
UNSTABLE_SMARTCAST_ERROR,
OTHER_ERROR,
ARGUMENTS_MAPPING_ERROR,
// '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[] {
EnumSet.of(UNSAFE_CALL_ERROR), // weakest
EnumSet.of(WRONG_NUMBER_OF_TYPE_ARGUMENTS_ERROR),
EnumSet.of(UNSTABLE_SMARTCAST_ERROR),
EnumSet.of(OTHER_ERROR),
EnumSet.of(ARGUMENTS_MAPPING_ERROR),
EnumSet.of(RECEIVER_TYPE_ERROR),
@@ -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");
* 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 org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.synthetic.SyntheticMemberDescriptor
import org.jetbrains.kotlin.resolve.DescriptorEquivalenceForOverrides
import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.resolve.calls.context.CheckArgumentTypesMode
@@ -70,7 +72,22 @@ class OverloadingConflictResolver<C : Any>(
}
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) {
return noOverrides
}
@@ -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
})
}
}
@@ -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
}
@@ -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)
}
}
}
@@ -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
}
@@ -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)
}
}
@@ -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
}
@@ -20,7 +20,7 @@ class B : A() {
}
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);
}
@TestMetadata("nullableReceiverWithOverloadedMethod.kt")
public void testNullableReceiverWithOverloadedMethod() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/nullableReceiverWithOverloadedMethod.kt");
doTest(fileName);
}
@TestMetadata("PreferExtensionsOnNullableReceiver.kt")
public void testPreferExtensionsOnNullableReceiver() throws Exception {
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");
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")
@@ -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");
* you may not use this file except in compliance with the License.
@@ -16,9 +16,11 @@
package org.jetbrains.kotlin.resolve;
import kotlin.Pair;
import kotlin.Unit;
import kotlin.collections.CollectionsKt;
import kotlin.jvm.functions.Function1;
import kotlin.jvm.functions.Function2;
import org.jetbrains.annotations.Mutable;
import org.jetbrains.annotations.NotNull;
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.checker.KotlinTypeChecker;
import org.jetbrains.kotlin.types.checker.KotlinTypeCheckerImpl;
import org.jetbrains.kotlin.utils.FunctionsKt;
import org.jetbrains.kotlin.utils.SmartSet;
import java.util.*;
@@ -72,23 +73,29 @@ public class OverridingUtil {
*/
@NotNull
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
public static <D> Set<D> filterOverrides(
@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;
Set<D> result = new LinkedHashSet<D>();
outerLoop:
for (D meD : candidateSet) {
CallableDescriptor me = transform.invoke(meD);
for (Iterator<D> iterator = result.iterator(); iterator.hasNext(); ) {
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)) {
iterator.remove();
}