KT-13426: store map from ImplicitReceiver into KotlinType for implicit receiver smart casts
This commit is contained in:
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemCompleter;
|
|||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
|
||||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue;
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue;
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.ImplicitSmartCasts;
|
||||||
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
|
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
|
||||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics;
|
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics;
|
||||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope;
|
import org.jetbrains.kotlin.resolve.scopes.LexicalScope;
|
||||||
@@ -144,7 +145,7 @@ public interface BindingContext {
|
|||||||
|
|
||||||
WritableSlice<KtExpression, KotlinType> SMARTCAST = Slices.createSimpleSlice();
|
WritableSlice<KtExpression, KotlinType> SMARTCAST = Slices.createSimpleSlice();
|
||||||
WritableSlice<KtExpression, Boolean> SMARTCAST_NULL = Slices.createSimpleSlice();
|
WritableSlice<KtExpression, Boolean> SMARTCAST_NULL = Slices.createSimpleSlice();
|
||||||
WritableSlice<KtExpression, KotlinType> IMPLICIT_RECEIVER_SMARTCAST = Slices.createSimpleSlice();
|
WritableSlice<KtExpression, ImplicitSmartCasts> IMPLICIT_RECEIVER_SMARTCAST = new BasicWritableSlice<KtExpression, ImplicitSmartCasts>(DO_NOTHING);
|
||||||
|
|
||||||
WritableSlice<KtWhenExpression, Boolean> EXHAUSTIVE_WHEN = Slices.createSimpleSlice();
|
WritableSlice<KtWhenExpression, Boolean> EXHAUSTIVE_WHEN = Slices.createSimpleSlice();
|
||||||
WritableSlice<KtWhenExpression, Boolean> IMPLICIT_EXHAUSTIVE_WHEN = Slices.createSimpleSlice();
|
WritableSlice<KtWhenExpression, Boolean> IMPLICIT_EXHAUSTIVE_WHEN = Slices.createSimpleSlice();
|
||||||
|
|||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.resolve.calls.smartcasts
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
|
||||||
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
|
||||||
|
data class ImplicitSmartCasts private constructor(val receiverTypes: Map<ImplicitReceiver, KotlinType>) {
|
||||||
|
operator fun plus(other: ImplicitSmartCasts) = ImplicitSmartCasts(receiverTypes + other.receiverTypes)
|
||||||
|
|
||||||
|
constructor(receiver: ImplicitReceiver, type: KotlinType): this(mapOf(receiver to type))
|
||||||
|
}
|
||||||
+11
-3
@@ -20,12 +20,13 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
|||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors.SMARTCAST_IMPOSSIBLE
|
import org.jetbrains.kotlin.diagnostics.Errors.SMARTCAST_IMPOSSIBLE
|
||||||
import org.jetbrains.kotlin.psi.KtExpression
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
|
import org.jetbrains.kotlin.psi.ValueArgument
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext.IMPLICIT_RECEIVER_SMARTCAST
|
import org.jetbrains.kotlin.resolve.BindingContext.*
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext.SMARTCAST
|
|
||||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||||
import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver
|
import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver
|
||||||
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
|
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.TypeIntersector
|
import org.jetbrains.kotlin.types.TypeIntersector
|
||||||
@@ -155,7 +156,14 @@ class SmartCastManager {
|
|||||||
recordCastOrError(expression, possibleType, c.trace, dataFlowValue, recordExpressionType)
|
recordCastOrError(expression, possibleType, c.trace, dataFlowValue, recordExpressionType)
|
||||||
}
|
}
|
||||||
else if (calleeExpression != null && dataFlowValue.isStable) {
|
else if (calleeExpression != null && dataFlowValue.isStable) {
|
||||||
c.trace.record(IMPLICIT_RECEIVER_SMARTCAST, calleeExpression, possibleType)
|
val receiver = (dataFlowValue.identifierInfo as? IdentifierInfo.Receiver)?.value
|
||||||
|
if (receiver is ImplicitReceiver) {
|
||||||
|
val oldSmartCasts = c.trace[IMPLICIT_RECEIVER_SMARTCAST, calleeExpression]
|
||||||
|
val newSmartCasts = ImplicitSmartCasts(receiver, possibleType)
|
||||||
|
c.trace.record(IMPLICIT_RECEIVER_SMARTCAST, calleeExpression,
|
||||||
|
oldSmartCasts?.let { it + newSmartCasts } ?: newSmartCasts)
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return SmartCastResult(possibleType, dataFlowValue.isStable)
|
return SmartCastResult(possibleType, dataFlowValue.isStable)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,13 @@ open class A {
|
|||||||
val a = "FAIL"
|
val a = "FAIL"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class C : A() {
|
||||||
|
val a = "FATAL"
|
||||||
|
}
|
||||||
|
|
||||||
fun foo(): String {
|
fun foo(): String {
|
||||||
if (this is B) return <!DEBUG_INFO_IMPLICIT_RECEIVER_SMARTCAST!>a<!>
|
if (this is B) return <!DEBUG_INFO_IMPLICIT_RECEIVER_SMARTCAST!>a<!>
|
||||||
|
else if (this is C) return <!DEBUG_INFO_IMPLICIT_RECEIVER_SMARTCAST!>a<!>
|
||||||
return "OK"
|
return "OK"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,15 @@ public open class A {
|
|||||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class C : A {
|
||||||
|
public constructor C()
|
||||||
|
public final val a: kotlin.String = "FATAL"
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public final override /*1*/ /*fake_override*/ fun foo(): kotlin.String
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class C {
|
public final class C {
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
interface IA
|
||||||
|
interface IB
|
||||||
|
interface IC
|
||||||
|
|
||||||
|
object Host : IB
|
||||||
|
|
||||||
|
object Prop : IA {
|
||||||
|
val Host.foo: Callee get() = Callee
|
||||||
|
}
|
||||||
|
|
||||||
|
object Callee
|
||||||
|
|
||||||
|
object Invoke : IC {
|
||||||
|
operator fun Callee.invoke() { }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test(a: IA, b: IB, c: IC) {
|
||||||
|
with(a) lambdaA@{
|
||||||
|
with(b) lambdaB@{
|
||||||
|
with(c) lambdaC@{
|
||||||
|
if (this@lambdaA is Prop && this@lambdaB is Host && this@lambdaC is Invoke) {
|
||||||
|
<!DEBUG_INFO_IMPLICIT_RECEIVER_SMARTCAST!>foo<!>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public fun test(/*0*/ a: IA, /*1*/ b: IB, /*2*/ c: IC): kotlin.Unit
|
||||||
|
|
||||||
|
public object Callee {
|
||||||
|
private constructor Callee()
|
||||||
|
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 object Host : IB {
|
||||||
|
private constructor Host()
|
||||||
|
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 interface IA {
|
||||||
|
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 interface IB {
|
||||||
|
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 interface IC {
|
||||||
|
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 object Invoke : IC {
|
||||||
|
private constructor Invoke()
|
||||||
|
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 operator fun Callee.invoke(): kotlin.Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
public object Prop : IA {
|
||||||
|
private constructor Prop()
|
||||||
|
public final val Host.foo: Callee
|
||||||
|
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,18 @@
|
|||||||
|
interface IA
|
||||||
|
interface IB
|
||||||
|
|
||||||
|
object A : IA {
|
||||||
|
fun B.foo() { }
|
||||||
|
}
|
||||||
|
|
||||||
|
object B : IB
|
||||||
|
|
||||||
|
fun test(a: IA, b: IB) {
|
||||||
|
with(a) lambda1@{
|
||||||
|
with(b) lambda2@{
|
||||||
|
if (this@lambda1 is A && this@lambda2 is B) {
|
||||||
|
<!DEBUG_INFO_IMPLICIT_RECEIVER_SMARTCAST!>foo<!>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public fun test(/*0*/ a: IA, /*1*/ b: IB): kotlin.Unit
|
||||||
|
|
||||||
|
public object A : IA {
|
||||||
|
private 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 fun B.foo(): kotlin.Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
public object B : IB {
|
||||||
|
private 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
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IA {
|
||||||
|
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 interface IB {
|
||||||
|
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
|
||||||
|
}
|
||||||
@@ -17661,6 +17661,18 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("threeImplicitReceivers.kt")
|
||||||
|
public void testThreeImplicitReceivers() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/threeImplicitReceivers.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("twoImplicitReceivers.kt")
|
||||||
|
public void testTwoImplicitReceivers() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/twoImplicitReceivers.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("typeDegradation.kt")
|
@TestMetadata("typeDegradation.kt")
|
||||||
public void testTypeDegradation() throws Exception {
|
public void testTypeDegradation() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/typeDegradation.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/typeDegradation.kt");
|
||||||
|
|||||||
+12
-3
@@ -29,6 +29,8 @@ import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
|||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext.*
|
import org.jetbrains.kotlin.resolve.BindingContext.*
|
||||||
import org.jetbrains.kotlin.resolve.calls.tasks.isDynamic
|
import org.jetbrains.kotlin.resolve.calls.tasks.isDynamic
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
|
||||||
import org.jetbrains.kotlin.types.expressions.CaptureKind
|
import org.jetbrains.kotlin.types.expressions.CaptureKind
|
||||||
|
|
||||||
internal class VariablesHighlightingVisitor(holder: AnnotationHolder, bindingContext: BindingContext)
|
internal class VariablesHighlightingVisitor(holder: AnnotationHolder, bindingContext: BindingContext)
|
||||||
@@ -79,9 +81,16 @@ internal class VariablesHighlightingVisitor(holder: AnnotationHolder, bindingCon
|
|||||||
override fun visitExpression(expression: KtExpression) {
|
override fun visitExpression(expression: KtExpression) {
|
||||||
val implicitSmartCast = bindingContext.get(IMPLICIT_RECEIVER_SMARTCAST, expression)
|
val implicitSmartCast = bindingContext.get(IMPLICIT_RECEIVER_SMARTCAST, expression)
|
||||||
if (implicitSmartCast != null) {
|
if (implicitSmartCast != null) {
|
||||||
holder.createInfoAnnotation(expression,
|
for ((receiver, type) in implicitSmartCast.receiverTypes) {
|
||||||
"Implicit receiver smart cast to " + DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(implicitSmartCast))
|
val receiverName = when (receiver) {
|
||||||
.textAttributes = SMART_CAST_RECEIVER
|
is ExtensionReceiver -> "Extension implicit receiver"
|
||||||
|
is ImplicitClassReceiver -> "Implicit receiver"
|
||||||
|
else -> "Unknown receiver"
|
||||||
|
}
|
||||||
|
holder.createInfoAnnotation(expression,
|
||||||
|
"$receiverName smart cast to " + DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(type))
|
||||||
|
.textAttributes = SMART_CAST_RECEIVER
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val nullSmartCast = bindingContext.get(SMARTCAST_NULL, expression) == true
|
val nullSmartCast = bindingContext.get(SMARTCAST_NULL, expression) == true
|
||||||
|
|||||||
+1
-1
@@ -160,7 +160,7 @@ private fun KtCodeFragment.markSmartCasts() {
|
|||||||
val factory = KtPsiFactory(project)
|
val factory = KtPsiFactory(project)
|
||||||
|
|
||||||
getContentElement()?.forEachDescendantOfType<KtExpression> { expression ->
|
getContentElement()?.forEachDescendantOfType<KtExpression> { expression ->
|
||||||
val smartCast = bindingContext.get(SMARTCAST, expression) ?: bindingContext.get(IMPLICIT_RECEIVER_SMARTCAST, expression)
|
val smartCast = bindingContext.get(SMARTCAST, expression)
|
||||||
if (smartCast != null) {
|
if (smartCast != null) {
|
||||||
val smartCastedExpression = factory.createExpressionByPattern(
|
val smartCastedExpression = factory.createExpressionByPattern(
|
||||||
"($0 as ${DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(smartCast)})",
|
"($0 as ${DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(smartCast)})",
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
interface IA
|
||||||
|
interface IB
|
||||||
|
interface IC
|
||||||
|
|
||||||
|
object Host : IB
|
||||||
|
|
||||||
|
object Prop : IA {
|
||||||
|
val Host.foo: Callee <info descr="null">get</info>() = Callee
|
||||||
|
}
|
||||||
|
|
||||||
|
object Callee
|
||||||
|
|
||||||
|
object Invoke : IC {
|
||||||
|
<info descr="null">operator</info> fun Callee.invoke() { }
|
||||||
|
}
|
||||||
|
|
||||||
|
<info descr="null">public</info> <info descr="null">inline</info> fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
|
||||||
|
|
||||||
|
fun test(a: IA, b: IB, c: IC) {
|
||||||
|
with(a) lambdaA@{
|
||||||
|
with(b) lambdaB@{
|
||||||
|
with(c) lambdaC@{
|
||||||
|
if (this@lambdaA is Prop && this@lambdaB is Host && this@lambdaC is Invoke) {
|
||||||
|
<info descr="Extension implicit receiver smart cast to Host"><info descr="Extension implicit receiver smart cast to Prop">foo</info></info>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
interface IA
|
||||||
|
interface IB
|
||||||
|
|
||||||
|
object A : IA {
|
||||||
|
fun B.foo() { }
|
||||||
|
}
|
||||||
|
|
||||||
|
object B : IB
|
||||||
|
|
||||||
|
<info descr="null">public</info> <info descr="null">inline</info> fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
|
||||||
|
|
||||||
|
fun test(a: IA, b: IB) {
|
||||||
|
with(a) lambda1@{
|
||||||
|
with(b) lambda2@{
|
||||||
|
if (this@lambda1 is A && this@lambda2 is B) {
|
||||||
|
<info descr="Extension implicit receiver smart cast to A"><info descr="Extension implicit receiver smart cast to B">foo</info></info>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -928,6 +928,18 @@ public class PsiCheckerTestGenerated extends AbstractPsiCheckerTest {
|
|||||||
doTestWithInfos(fileName);
|
doTestWithInfos(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("threeImplicitReceivers.kt")
|
||||||
|
public void testThreeImplicitReceivers() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/checker/infos/threeImplicitReceivers.kt");
|
||||||
|
doTestWithInfos(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("twoImplicitReceivers.kt")
|
||||||
|
public void testTwoImplicitReceivers() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/checker/infos/twoImplicitReceivers.kt");
|
||||||
|
doTestWithInfos(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("Typos.kt")
|
@TestMetadata("Typos.kt")
|
||||||
public void testTypos() throws Exception {
|
public void testTypos() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/checker/infos/Typos.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/checker/infos/Typos.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user