Forbidden old invokeExtension convention.

This commit is contained in:
Stanislav Erokhin
2015-10-16 21:25:25 +03:00
parent fc4b0a8121
commit 7d7d37719b
26 changed files with 157 additions and 126 deletions
@@ -453,8 +453,6 @@ public interface Errors {
DiagnosticFactory1<JetExpression, JetType> MISSING_RECEIVER = DiagnosticFactory1.create(ERROR);
DiagnosticFactory0<JetExpression> NO_RECEIVER_ALLOWED = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<JetExpression> FREE_FUNCTION_CALLED_AS_EXTENSION = DiagnosticFactory0.create(ERROR);
// Call resolution
DiagnosticFactory1<JetExpression, String> ILLEGAL_SELECTOR = DiagnosticFactory1.create(ERROR);
@@ -475,6 +473,7 @@ public interface Errors {
DiagnosticFactory1<PsiElement, Collection<? extends ResolvedCall<?>>> NONE_APPLICABLE = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, Collection<? extends ResolvedCall<?>>> CANNOT_COMPLETE_RESOLVE = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, Collection<? extends ResolvedCall<?>>> UNRESOLVED_REFERENCE_WRONG_RECEIVER = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, JetExpression> INVOKE_EXTENSION_ON_NOT_EXTENSION_FUNCTION = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, TypeParameterDescriptor> TYPE_PARAMETER_AS_REIFIED = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, JetType> REIFIED_TYPE_FORBIDDEN_SUBSTITUTION = DiagnosticFactory1.create(ERROR);
@@ -591,13 +591,12 @@ public class DefaultErrorMessages {
MAP.put(NONE_APPLICABLE, "None of the following functions can be called with the arguments supplied: {0}", AMBIGUOUS_CALLS);
MAP.put(CANNOT_COMPLETE_RESOLVE, "Cannot choose among the following candidates without completing type inference: {0}", AMBIGUOUS_CALLS);
MAP.put(UNRESOLVED_REFERENCE_WRONG_RECEIVER, "Unresolved reference. None of the following candidates is applicable because of receiver type mismatch: {0}", AMBIGUOUS_CALLS);
MAP.put(INVOKE_EXTENSION_ON_NOT_EXTENSION_FUNCTION, "Impossible call as extension because {0} is not an extension function.", ELEMENT_TEXT);
MAP.put(NO_VALUE_FOR_PARAMETER, "No value passed for parameter {0}", NAME);
MAP.put(MISSING_RECEIVER, "A receiver of type {0} is required", RENDER_TYPE);
MAP.put(NO_RECEIVER_ALLOWED, "No receiver can be passed to this function or property");
MAP.put(FREE_FUNCTION_CALLED_AS_EXTENSION, "The function cannot be called as an extension function");
MAP.put(CREATING_AN_INSTANCE_OF_ABSTRACT_CLASS, "Cannot create an instance of an abstract class");
MAP.put(TYPE_INFERENCE_CONFLICTING_SUBSTITUTIONS, "Type inference failed: {0}", TYPE_INFERENCE_CONFLICTING_SUBSTITUTIONS_RENDERER);
@@ -267,14 +267,18 @@ public class CandidateResolver(
}
private fun CallCandidateResolutionContext<*>.checkNonExtensionCalledWithReceiver() = checkAndReport {
if (isSynthesizedInvoke(candidateCall.getCandidateDescriptor())
&& !KotlinBuiltIns.isExtensionFunctionType(candidateCall.getDispatchReceiver().getType())
val call = candidateCall.call
if (call is CallTransformer.CallForImplicitInvoke && candidateCall.extensionReceiver.exists()
&& candidateCall.dispatchReceiver.exists()
) {
tracing.freeFunctionCalledAsExtension(trace)
OTHER_ERROR
} else {
SUCCESS
if (call.dispatchReceiver == candidateCall.dispatchReceiver
&& !KotlinBuiltIns.isExactExtensionFunctionType(call.dispatchReceiver.type)
) {
tracing.nonExtensionFunctionCalledAsExtension(trace)
return@checkAndReport OTHER_ERROR
}
}
SUCCESS
}
private fun getReceiverSuper(receiver: ReceiverValue): JetSuperExpression? {
@@ -253,7 +253,7 @@ public abstract class AbstractTracingStrategy implements TracingStrategy {
}
@Override
public void freeFunctionCalledAsExtension(@NotNull BindingTrace trace) {
trace.report(FREE_FUNCTION_CALLED_AS_EXTENSION.on(reference));
public void nonExtensionFunctionCalledAsExtension(@NotNull BindingTrace trace) {
trace.report(INVOKE_EXTENSION_ON_NOT_EXTENSION_FUNCTION.on(reference, reference));
}
}
@@ -101,7 +101,7 @@ public interface TracingStrategy {
public void typeInferenceFailed(@NotNull BindingTrace trace, @NotNull InferenceErrorData inferenceErrorData) {}
@Override
public void freeFunctionCalledAsExtension(@NotNull BindingTrace trace) { }
public void nonExtensionFunctionCalledAsExtension(@NotNull BindingTrace trace) { }
};
void bindCall(@NotNull BindingTrace trace, @NotNull Call call);
@@ -153,5 +153,5 @@ public interface TracingStrategy {
void typeInferenceFailed(@NotNull BindingTrace trace, @NotNull InferenceErrorData inferenceErrorData);
void freeFunctionCalledAsExtension(@NotNull BindingTrace trace);
void nonExtensionFunctionCalledAsExtension(@NotNull BindingTrace trace);
}
@@ -534,7 +534,7 @@ public class ControlStructureTypingUtils {
}
@Override
public void freeFunctionCalledAsExtension(@NotNull BindingTrace trace) {
public void nonExtensionFunctionCalledAsExtension(@NotNull BindingTrace trace) {
logError();
}
}
+2 -15
View File
@@ -1,16 +1,4 @@
class A() {
fun A.invoke(a: A) = "$this ${this@A} $a"
}
fun test1() {
val a = A()
val b = A()
val c = A()
a.b(c)
a b c
}
fun test2() {
fun test() {
val a: (@Extension Function2<*, *, *>).(@Extension Function2<*, *, *>)->Unit = {}
val b: (@Extension Function2<*, *, *>).(@Extension Function2<*, *, *>)->Unit = {"$this $it"}
val c: (@Extension Function2<*, *, *>).(@Extension Function2<*, *, *>)->Unit = {}
@@ -22,8 +10,7 @@ fun Int.foo(a: Int) = this * a
val boo = fun Int.(a: Int): Int = this + a
fun box(): String {
test1()
test2()
test()
1 foo 2
3 boo 4
return "OK"
@@ -1,5 +1,5 @@
fun foo(a: (String) -> Unit) {
"".<!FREE_FUNCTION_CALLED_AS_EXTENSION!>a<!>()
"".<!INVOKE_EXTENSION_ON_NOT_EXTENSION_FUNCTION!>a<!>()
}
@@ -9,5 +9,5 @@ interface A : (String) -> Unit {}
fun foo(a: @Extension A) {
// @Extension annotation on an unrelated type shouldn't have any effect on this diagnostic.
// Only kotlin.Function{n} type annotated with @Extension should
"".<!FREE_FUNCTION_CALLED_AS_EXTENSION!>a<!>()
}
"".<!INVOKE_EXTENSION_ON_NOT_EXTENSION_FUNCTION!>a<!>()
}
@@ -1,13 +1,13 @@
fun <E : String?, T : ((CharSequence).() -> Unit)?> foo(x: E, y: T) {
if (x != null) {
<!DEBUG_INFO_SMARTCAST!>x<!>.<!UNSAFE_CALL!>y<!>()
<!DEBUG_INFO_SMARTCAST!>x<!>.<!UNSAFE_CALL, INVOKE_EXTENSION_ON_NOT_EXTENSION_FUNCTION!>y<!>()
}
if (y != null) {
x<!UNSAFE_CALL!>.<!><!DEBUG_INFO_SMARTCAST!>y<!>()
x<!UNSAFE_CALL!>.<!><!INVOKE_EXTENSION_ON_NOT_EXTENSION_FUNCTION, DEBUG_INFO_SMARTCAST!>y<!>()
}
if (x != null && y != null) {
<!DEBUG_INFO_SMARTCAST!>x<!>.<!DEBUG_INFO_SMARTCAST!>y<!>()
<!DEBUG_INFO_SMARTCAST!>x<!>.<!INVOKE_EXTENSION_ON_NOT_EXTENSION_FUNCTION, DEBUG_INFO_SMARTCAST!>y<!>()
}
}
}
@@ -5,7 +5,7 @@ fun test1(f: String.() -> Unit) {
}
fun test2(f: (Int) -> Int) {
1.<!FREE_FUNCTION_CALLED_AS_EXTENSION!>f<!>(<!TOO_MANY_ARGUMENTS!>2<!>)
1.<!INVOKE_EXTENSION_ON_NOT_EXTENSION_FUNCTION!>f<!>(<!TOO_MANY_ARGUMENTS!>2<!>)
2.<!FREE_FUNCTION_CALLED_AS_EXTENSION!>(f)<!>(<!TOO_MANY_ARGUMENTS!>2<!>)
}
2.<!INVOKE_EXTENSION_ON_NOT_EXTENSION_FUNCTION!>(f)<!>(<!TOO_MANY_ARGUMENTS!>2<!>)
}
@@ -0,0 +1,35 @@
class B
class A {
operator fun B.invoke() = 4
}
class X {
operator fun invoke() = 3
}
fun test(a: A, b: B) {
with (a) {
b()
(b)()
}
X()()
val x = X()
x()
(x)()
}
fun test(c: () -> String, e: Int.() -> String) {
c()
(c)()
3.e()
3.(e)()
with(3) {
e()
(e)()
}
}
fun <T, R> with(receiver: T, f: T.() -> R): R = receiver.f()
@@ -0,0 +1,28 @@
package
public fun test(/*0*/ c: () -> kotlin.String, /*1*/ e: kotlin.Int.() -> kotlin.String): kotlin.Unit
public fun test(/*0*/ a: A, /*1*/ b: B): kotlin.Unit
public fun </*0*/ T, /*1*/ R> with(/*0*/ receiver: T, /*1*/ f: T.() -> R): R
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 operator fun B.invoke(): kotlin.Int
}
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
}
public final class X {
public constructor X()
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 operator fun invoke(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -4,7 +4,7 @@ interface Foo {
}
fun test(a: A, foo: Foo) {
a.foo()
a.<!INVOKE_EXTENSION_ON_NOT_EXTENSION_FUNCTION!>foo<!>()
}
fun test(a: Int, foo: Int.()->Unit) {
@@ -0,0 +1,20 @@
class B
class A {
operator fun B.invoke() {}
}
val B.a: () -> Int get() = { 5 }
fun test(a: A, b: B) {
val <!UNUSED_VARIABLE!>x<!>: Int = b.a()
b.<!INVOKE_EXTENSION_ON_NOT_EXTENSION_FUNCTION!>(a)<!>()
with(b) {
val <!UNUSED_VARIABLE!>y<!>: Int = a()
<!MISSING_RECEIVER!>(a)<!>()
}
}
fun <T, R> with(receiver: T, f: T.() -> R): R = receiver.f()
@@ -0,0 +1,20 @@
package
public val B.a: () -> kotlin.Int
public fun test(/*0*/ a: A, /*1*/ b: B): kotlin.Unit
public fun </*0*/ T, /*1*/ R> with(/*0*/ receiver: T, /*1*/ f: T.() -> R): R
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 operator fun B.invoke(): kotlin.Unit
}
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,9 +1,5 @@
class Foo() {
fun Int.invoke() {}
}
fun bar(f: Foo, i: Int) {
fun bar(f: Int.() -> Unit, i: Int) {
with (i) {
f<caret>()
}
}
}
@@ -1,8 +1,4 @@
class Foo() {
fun Int.invoke() {}
}
fun bar(f: Foo, i: Int) {
fun bar(f: Int.() -> Unit, i: Int) {
with (i) {
f<caret>()
}
@@ -11,8 +7,8 @@ fun bar(f: Foo, i: Int) {
Resolved call:
Resulting descriptor: fun Int.invoke(): Unit defined in Foo
Resulting descriptor: operator fun Int.invoke(): Unit defined in kotlin.Function1
Explicit receiver kind = DISPATCH_RECEIVER
Dispatch receiver = f {Foo}
Dispatch receiver = f {[@kotlin.Extension] Function1<Int, Unit>}
Extension receiver = IntExt{fun Int.<anonymous>(): Unit defined in bar}
@@ -12815,6 +12815,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/resolve/invoke"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("implicitInvoke.kt")
public void testImplicitInvoke() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/resolve/invoke/implicitInvoke.kt");
doTest(fileName);
}
@TestMetadata("invokeAsExtension.kt")
public void testInvokeAsExtension() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/resolve/invoke/invokeAsExtension.kt");
@@ -12881,6 +12887,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("wrongInvokeExtension.kt")
public void testWrongInvokeExtension() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/resolve/invoke/wrongInvokeExtension.kt");
doTest(fileName);
}
@TestMetadata("compiler/testData/diagnostics/tests/resolve/invoke/errors")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
+1 -5
View File
@@ -1,9 +1,5 @@
class A {
fun test(b: B) {
fun test(b: A.() -> Unit) {
<selection>this.b()</selection>
}
}
class B() {
fun A.invoke() {}
}
@@ -1,9 +1,5 @@
class A {
fun test(b: B) {
fun test(b: A.() -> Unit) {
b()
}
}
class B() {
fun A.invoke() {}
}
@@ -1,14 +0,0 @@
class Bar {
init {
Foo()()
}
}
class Foo() {
fun Bar.invoke() {}
}
fun foobar(f: Foo) {
<selection>Bar().f()</selection>
Bar().f()
}
@@ -1,3 +0,0 @@
Bar().f()
Bar().f()
@@ -365,12 +365,6 @@ public class JetPsiUnifierTestGenerated extends AbstractJetPsiUnifierTest {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/unifier/equivalence/expressions/calls"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("bothReceivers.kt")
public void testBothReceivers() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/unifier/equivalence/expressions/calls/bothReceivers.kt");
doTest(fileName);
}
@TestMetadata("callAndCalleeRuntime.kt")
public void testCallAndCalleeRuntime() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/unifier/equivalence/expressions/calls/callAndCalleeRuntime.kt");
@@ -1,20 +1,7 @@
// KT-3998 Infix call doesn't work for function literals and another classes which implements invoke convention (in JS backend)
package foo
class A(val s: String) {
fun A.invoke(a: A) = "${this.s} ${this@A.s} ${a.s}"
}
fun test1(): String {
val a = A("a")
val b = A("b")
val c = A("c")
val f = a.b(c) // works
val s = a b c //compiler crashes
return "$f | $s"
}
fun test2(): String {
fun test(): String {
val a: (@Extension Function2<*, *, *>).(@Extension Function2<*, *, *>)->String = { "a" }
val b: (@Extension Function2<*, *, *>).(@Extension Function2<*, *, *>)->String = {
val aa = this as @Extension Function2<Any?, Any?, Any?>;
@@ -29,8 +16,7 @@ fun test2(): String {
}
fun box(): String {
assertEquals("a b c | a b c", test1())
assertEquals("a b c | a b c", test2())
assertEquals("a b c | a b c", test())
return "OK"
}
@@ -4,6 +4,7 @@
* This led to runtime errors (see KT-7692), so the test is temporarily disabled.
*
* TODO: support inheritance from function types and re-enable this test
* NOTE: inheritance from extension function is forbidden now
*/
package foo
@@ -16,42 +17,24 @@ class Baz/* : Function2<Int, Boolean, String>*/ {
fun invoke(i: Int, b: Boolean) = "Baz.invoke($i, $b)"
}
class ExtBar/* : ExtensionFunction0<String, String>*/ {
fun String.invoke() = "ExtBar.invoke($this)"
}
class ExtBaz/* : ExtensionFunction2<String, Int, Boolean, String>*/ {
fun String.invoke(i: Int, b: Boolean) = "ExtBaz.invoke($this, $i, $b)"
}
class Mixed/* :
Function1<Int, String>,
Function2<Int, Boolean, String>,
ExtensionFunction1<Int, Boolean, String>,
ExtensionFunction2<Int, Int, Boolean, String>*/
Function2<Int, Boolean, String>*/
{
fun invoke(i: Int) = "Mixed.invoke($i)"
fun invoke(i: Int, b: Boolean) = "Mixed.invoke($i, $b)"
fun Int.invoke(b: Boolean) = "ext Mixed.invoke($this, $b)"
fun Int.invoke(i: Int, b: Boolean) = "ext Mixed.invoke($this, $i, $b)"
}
fun box(): String {
val bar = Bar()
val baz = Baz()
val extBar = ExtBar()
val extBaz = ExtBaz()
val mixed = Mixed()
assertEquals("Bar.invoke()", bar())
assertEquals("Baz.invoke(2, false)", baz(2, false))
assertEquals("ExtBar.invoke(2e2)", "2e2".extBar())
assertEquals("ExtBaz.invoke(29, 34, true)", "29".extBaz(34, true))
assertEquals("Mixed.invoke(45)", mixed(45))
assertEquals("Mixed.invoke(552, true)", mixed(552, true))
assertEquals("ext Mixed.invoke(21, true)", 21.mixed(true))
assertEquals("ext Mixed.invoke(29, 304, false)", 29.mixed(304, false))
return "OK"
}
@@ -1,12 +1,9 @@
package foo
class A
class B {
fun A.invoke(i: Int) = i
}
fun box(): Boolean {
val a = A()
val b = B()
val b = fun A.(i: Int) = i
return a.(b)(1) == 1
}