diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java index 9b95f7e2f28..bb94a0905f2 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java @@ -349,6 +349,11 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { KtExpression receiver = ((KtQualifiedExpression) parent).getReceiverExpression(); return PsiTreeUtil.isAncestor(receiver, expression, false); } + // in binary expression, left argument can be a receiver and right an argument + // in unary expression, left argument can be a receiver + if (parent instanceof KtBinaryExpression || parent instanceof KtUnaryExpression) { + return true; + } return false; } diff --git a/compiler/testData/diagnostics/tests/cast/AsInBinaryUnary.kt b/compiler/testData/diagnostics/tests/cast/AsInBinaryUnary.kt new file mode 100644 index 00000000000..766d7ffb719 --- /dev/null +++ b/compiler/testData/diagnostics/tests/cast/AsInBinaryUnary.kt @@ -0,0 +1,30 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER + +interface Base + +class Impl1 : Base + +class Impl2 : Base + +operator fun Base.plus(arg: Base) = Impl1() + +operator fun Impl2.plus(arg: Base) = Impl2() + +operator fun Base.plus(arg: Impl2) = Impl2() + +operator fun Base.unaryMinus() = Impl1() + +operator fun Impl2.unaryMinus() = Impl2() + +// See also KT-10384: in non-error functions, as is necessary! +fun add1(x: Impl2, y: Base): Impl1 = x as Base + y + +fun error1(x: Impl2, y: Base): Impl1 = x + y + +fun add2(x: Base, y: Impl2): Impl1 = x + y as Base + +fun error2(x: Base, y: Impl2): Impl1 = x + y + +fun minus3(x: Impl2): Impl1 = -(x as Base) + +fun error3(x: Impl2): Impl1 = -x diff --git a/compiler/testData/diagnostics/tests/cast/AsInBinaryUnary.txt b/compiler/testData/diagnostics/tests/cast/AsInBinaryUnary.txt new file mode 100644 index 00000000000..540eedf77fd --- /dev/null +++ b/compiler/testData/diagnostics/tests/cast/AsInBinaryUnary.txt @@ -0,0 +1,33 @@ +package + +public fun add1(/*0*/ x: Impl2, /*1*/ y: Base): Impl1 +public fun add2(/*0*/ x: Base, /*1*/ y: Impl2): Impl1 +public fun error1(/*0*/ x: Impl2, /*1*/ y: Base): Impl1 +public fun error2(/*0*/ x: Base, /*1*/ y: Impl2): Impl1 +public fun error3(/*0*/ x: Impl2): Impl1 +public fun minus3(/*0*/ x: Impl2): Impl1 +public operator fun Base.plus(/*0*/ arg: Base): Impl1 +public operator fun Base.plus(/*0*/ arg: Impl2): Impl2 +public operator fun Impl2.plus(/*0*/ arg: Base): Impl2 +public operator fun Base.unaryMinus(): Impl1 +public operator fun Impl2.unaryMinus(): Impl2 + +public interface Base { + 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 Impl1 : Base { + public constructor Impl1() + 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 Impl2 : Base { + public constructor Impl2() + 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/testsWithStdLib/cast/AsInsideIn.kt b/compiler/testData/diagnostics/testsWithStdLib/cast/AsInsideIn.kt new file mode 100644 index 00000000000..83f41e31eec --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/cast/AsInsideIn.kt @@ -0,0 +1,20 @@ +// See also KT-10386 +interface A +class B : A +fun foo1(list: List, arg: B?): Boolean { + // contains(T): Boolean is deprecated + return arg in list +} +fun foo2(list: List, arg: B?): Boolean { + // FAKE: no cast needed + return arg as A? in list +} +fun foo3(list: List, arg: B?): Boolean { + // No warning but KNPE risk + return arg!! in list +} +// But +fun foo4(list: List, arg: B): Boolean { + // Ok + return arg in list +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/cast/AsInsideIn.txt b/compiler/testData/diagnostics/testsWithStdLib/cast/AsInsideIn.txt new file mode 100644 index 00000000000..40c95db45b7 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/cast/AsInsideIn.txt @@ -0,0 +1,19 @@ +package + +public fun foo1(/*0*/ list: kotlin.List, /*1*/ arg: B?): kotlin.Boolean +public fun foo2(/*0*/ list: kotlin.List, /*1*/ arg: B?): kotlin.Boolean +public fun foo3(/*0*/ list: kotlin.List, /*1*/ arg: B?): kotlin.Boolean +public fun foo4(/*0*/ list: kotlin.List, /*1*/ arg: B): kotlin.Boolean + +public interface 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 : A { + 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 +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index bac1973e037..53174091738 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -2157,6 +2157,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("AsInBinaryUnary.kt") + public void testAsInBinaryUnary() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/cast/AsInBinaryUnary.kt"); + doTest(fileName); + } + @TestMetadata("constants.kt") public void testConstants() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/cast/constants.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java index ef57788e999..6ca79d2990a 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java @@ -568,6 +568,12 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/cast"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("AsInsideIn.kt") + public void testAsInsideIn() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/cast/AsInsideIn.kt"); + doTest(fileName); + } + @TestMetadata("IsArray.kt") public void testIsArray() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/cast/IsArray.kt");