diff --git a/js/js.tests/test/org/jetbrains/k2js/test/semantics/CastTest.java b/js/js.tests/test/org/jetbrains/k2js/test/semantics/CastTest.java index 7dee4df742c..74cab0ed722 100644 --- a/js/js.tests/test/org/jetbrains/k2js/test/semantics/CastTest.java +++ b/js/js.tests/test/org/jetbrains/k2js/test/semantics/CastTest.java @@ -31,4 +31,12 @@ public final class CastTest extends AbstractExpressionTest { public void testCastToNotNullType() throws Exception { checkFooBoxIsOk(); } + + public void testSmartCastInExtensionFunction() throws Exception { + checkFooBoxIsOk(); + } + + public void testSmartCastInFunction() throws Exception { + checkFooBoxIsOk(); + } } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/JsDescriptorUtils.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/JsDescriptorUtils.java index 8d178667eb4..49b51891e79 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/JsDescriptorUtils.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/utils/JsDescriptorUtils.java @@ -24,8 +24,10 @@ import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.psi.JetExpression; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.DescriptorUtils; +import org.jetbrains.jet.lang.resolve.calls.autocasts.AutoCastReceiver; import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue; +import org.jetbrains.jet.lang.resolve.scopes.receivers.ThisReceiver; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.expressions.OperatorConventions; import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; @@ -112,12 +114,20 @@ public final class JsDescriptorUtils { } @NotNull - public static DeclarationDescriptor getDeclarationDescriptorForReceiver - (@NotNull ReceiverValue receiverParameter) { - DeclarationDescriptor declarationDescriptor = - receiverParameter.getType().getConstructor().getDeclarationDescriptor(); - //TODO: WHY assert? - assert declarationDescriptor != null; + public static DeclarationDescriptor getDeclarationDescriptorForReceiver(@NotNull ReceiverValue receiverParameter) { + DeclarationDescriptor declarationDescriptor; + + if (receiverParameter instanceof ThisReceiver) { + declarationDescriptor = ((ThisReceiver) receiverParameter).getDeclarationDescriptor(); + } + else if (receiverParameter instanceof AutoCastReceiver) { + AutoCastReceiver autoCastReceiver = ((AutoCastReceiver) receiverParameter); + declarationDescriptor = getDeclarationDescriptorForReceiver(autoCastReceiver.getOriginal()); + } + else { + throw new UnsupportedOperationException("Unsupported receiver type: " + receiverParameter); + } + return declarationDescriptor.getOriginal(); } diff --git a/js/js.translator/testFiles/expression/cast/cases/smartCastInExtensionFunction.kt b/js/js.translator/testFiles/expression/cast/cases/smartCastInExtensionFunction.kt new file mode 100644 index 00000000000..d452aba6877 --- /dev/null +++ b/js/js.translator/testFiles/expression/cast/cases/smartCastInExtensionFunction.kt @@ -0,0 +1,59 @@ +package foo + +class A { + fun foo(a: Int) = "A.foo($a)" +} + +fun Any.bar() = "Any.bar()" +fun A.bar() = "A.bar()" + +fun boo(a: Any) = "boo(Any)" +fun boo(a: A) = "boo(A)" + +fun assert(expected: T, actual: T, caseName: String) { + if (expected != actual) throw Exception("Filed on $caseName, expected: $expected, actual: $actual") +} + +fun Any.testInTopLevel() { + assert(bar(), "Any.bar()", "bar()") + assert(this.bar(), "Any.bar()", "this.bar()") + assert(boo(this), "boo(Any)", "boo(this)") + + if (this is A) { + assert(foo(47), "A.foo(47)", "foo(47)") + assert(bar(), "A.bar()", "bar()") + + assert(this.foo(47), "A.foo(47)", "this.foo(47)") + assert(this.bar(), "A.bar()", "this.bar()") + + assert(boo(this), "boo(A)", "boo(this: A)") + } +} + +class B { + fun Any.test() { + assert(bar(), "Any.bar()", "bar()") + assert(this.bar(), "Any.bar()", "this.bar()") + assert(boo(this), "boo(Any)", "boo(this)") + + if (this is A) { + assert(foo(47), "A.foo(47)", "foo(47)") + assert(bar(), "A.bar()", "bar()") + + assert(this.foo(47), "A.foo(47)", "this.foo(47)") + assert(this.bar(), "A.bar()", "this.bar()") + + assert(boo(this), "boo(A)", "boo(this: A)") + } + } + + fun testInClass() { + A().test() + } +} + +fun box(): String { + A().testInTopLevel() + B().testInClass() + return "OK" +} diff --git a/js/js.translator/testFiles/expression/cast/cases/smartCastInFunction.kt b/js/js.translator/testFiles/expression/cast/cases/smartCastInFunction.kt new file mode 100644 index 00000000000..94029bb71de --- /dev/null +++ b/js/js.translator/testFiles/expression/cast/cases/smartCastInFunction.kt @@ -0,0 +1,49 @@ +package foo + +class A { + fun foo(a: Int) = "A.foo($a)" +} + +fun Any.bar() = "Any.bar()" +fun A.bar() = "A.bar()" + +fun boo(a: Any) = "boo(Any)" +fun boo(a: A) = "boo(A)" + +fun assert(expected: T, actual: T, caseName: String) { + if (expected != actual) throw Exception("Filed on $caseName, expected: $expected, actual: $actual") +} + +fun testInTopLevel(a: Any) { + assert(a.bar(), "Any.bar()", "bar()") + assert(a.bar(), "Any.bar()", "this.bar()") + assert(boo(a), "boo(Any)", "boo(this)") + + if (a is A) { + assert(a.foo(47), "A.foo(47)", "a.foo(47)") + assert(a.bar(), "A.bar()", "a.bar()") + assert(boo(a), "boo(A)", "boo(a: A)") + } +} + +class B { + fun testInClass(a: Any) { + assert(a.bar(), "Any.bar()", "bar()") + assert(a.bar(), "Any.bar()", "this.bar()") + assert(boo(a), "boo(Any)", "boo(this)") + + if (a is A) { + assert(a.foo(47), "A.foo(47)", "a.foo(47)") + assert(a.bar(), "A.bar()", "a.bar()") + assert(boo(a), "boo(A)", "boo(a: A)") + } + } + +} + +fun box(): String { + testInTopLevel(A()) + B().testInClass(A()) + + return "OK" +}