diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java index a40fbf35d31..f30e26fdad2 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java @@ -23,7 +23,6 @@ import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.*; -import org.jetbrains.jet.lang.descriptors.impl.FunctionDescriptorImpl; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.*; import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; @@ -38,8 +37,6 @@ import org.jetbrains.jet.lang.resolve.calls.results.ResolutionResultsHandler; import org.jetbrains.jet.lang.resolve.calls.tasks.*; import org.jetbrains.jet.lang.resolve.calls.util.CallMaker; import org.jetbrains.jet.lang.resolve.calls.util.DelegatingCall; -import org.jetbrains.jet.lang.resolve.calls.util.ExpressionAsFunctionDescriptor; -import org.jetbrains.jet.lang.resolve.calls.util.JetFakeReference; import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver; @@ -48,7 +45,6 @@ import org.jetbrains.jet.lang.types.expressions.ExpressionTypingContext; import org.jetbrains.jet.lang.types.expressions.ExpressionTypingServices; import org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils; import org.jetbrains.jet.lang.types.expressions.LabelResolver; -import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; import org.jetbrains.jet.lexer.JetTokens; import javax.inject.Inject; @@ -57,7 +53,6 @@ import java.util.Collections; import java.util.List; import java.util.Set; -import static org.jetbrains.jet.lang.descriptors.ReceiverParameterDescriptor.NO_RECEIVER_PARAMETER; import static org.jetbrains.jet.lang.diagnostics.Errors.*; import static org.jetbrains.jet.lang.resolve.BindingContext.NON_DEFAULT_EXPRESSION_DATA_FLOW; import static org.jetbrains.jet.lang.resolve.BindingContext.RESOLUTION_SCOPE; @@ -285,31 +280,20 @@ public class CallResolver { prioritizedTasks = Collections.singletonList(new ResolutionTask(candidates, functionReference, context)); // !! DataFlowInfo.EMPTY } else if (calleeExpression != null) { + // Here we handle the case where the callee expression must be something of type function, e.g. (foo.bar())(1, 2) JetType calleeType = expressionTypingServices.safeGetType(context.scope, calleeExpression, NO_EXPECTED_TYPE, context.dataFlowInfo, context.trace); // We are actually expecting a function, but there seems to be no easy way of expressing this + ExpressionReceiver expressionReceiver = new ExpressionReceiver(calleeExpression, calleeType); - if (!KotlinBuiltIns.getInstance().isFunctionOrExtensionFunctionType(calleeType)) { -// checkTypesWithNoCallee(trace, scope, call); - if (!calleeType.isError()) { - context.trace.report(CALLEE_NOT_A_FUNCTION.on(calleeExpression, calleeType)); - } - return checkArgumentTypesAndFail(context); + Call call = new CallTransformer.CallForImplicitInvoke( + context.call.getExplicitReceiver(), expressionReceiver, context.call); + TracingStrategyForInvoke tracingForInvoke = new TracingStrategyForInvoke(calleeExpression, call); + OverloadResolutionResults invokeResults = + resolveCallForInvoke(context.replaceCall(call), tracingForInvoke); + if (invokeResults.isNothing() || invokeResults.getResultCode() == CANDIDATES_WITH_WRONG_RECEIVER) { + context.trace.report(CALLEE_NOT_A_FUNCTION.on(calleeExpression, calleeType)); } - - FunctionDescriptorImpl functionDescriptor = new ExpressionAsFunctionDescriptor(context.scope.getContainingDeclaration(), Name.special(""), calleeExpression); - FunctionDescriptorUtil.initializeFromFunctionType(functionDescriptor, calleeType, NO_RECEIVER_PARAMETER, Modality.FINAL, - Visibilities.LOCAL); - ResolutionCandidate resolutionCandidate = ResolutionCandidate.create(functionDescriptor, JetPsiUtil.isSafeCall(context.call)); - resolutionCandidate.setReceiverArgument(context.call.getExplicitReceiver()); - resolutionCandidate.setExplicitReceiverKind(ExplicitReceiverKind.RECEIVER_ARGUMENT); - - // strictly speaking, this is a hack: - // we need to pass a reference, but there's no reference in the PSI, - // so we wrap what we have into a fake reference and pass it on (unwrap on the other end) - functionReference = new JetFakeReference(calleeExpression); - - prioritizedTasks = Collections.singletonList(new ResolutionTask( - Collections.singleton(resolutionCandidate), functionReference, context)); + return (OverloadResolutionResultsImpl) invokeResults; //todo } else { // checkTypesWithNoCallee(trace, scope, call); @@ -486,7 +470,9 @@ public class CallResolver { @NotNull TracingStrategy tracing ) { ResolutionDebugInfo.Data debugInfo = ResolutionDebugInfo.create(); - context.trace.record(ResolutionDebugInfo.RESOLUTION_DEBUG_INFO, context.call.getCallElement(), debugInfo); + if (context.call.getCallType() != Call.CallType.INVOKE) { + context.trace.record(ResolutionDebugInfo.RESOLUTION_DEBUG_INFO, context.call.getCallElement(), debugInfo); + } context.trace.record(RESOLUTION_SCOPE, context.call.getCalleeExpression(), context.scope); if (context.dataFlowInfo.hasTypeInfoConstraints()) { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallTransformer.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallTransformer.java index 834e108f738..e9090c61c4c 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallTransformer.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallTransformer.java @@ -39,6 +39,7 @@ import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResultsImp import org.jetbrains.jet.lang.resolve.calls.tasks.ExplicitReceiverKind; import org.jetbrains.jet.lang.resolve.calls.tasks.ResolutionCandidate; import org.jetbrains.jet.lang.resolve.calls.tasks.ResolutionTask; +import org.jetbrains.jet.lang.resolve.calls.tasks.TracingStrategyForInvoke; import org.jetbrains.jet.lang.resolve.calls.util.DelegatingCall; import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver; import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue; @@ -205,8 +206,9 @@ public class CallTransformer { functionCall, context.checkArguments, context.dataFlowInfoForArguments); // 'invoke' call resolve + TracingStrategyForInvoke tracingForInvoke = new TracingStrategyForInvoke(calleeExpression, functionCall); OverloadResolutionResults results = callResolver.resolveCallForInvoke( - basicCallResolutionContext, context.tracing); //todo context.tracing is incorrect + basicCallResolutionContext, tracingForInvoke); Collection> calls = ((OverloadResolutionResultsImpl)results).getResultingCalls(); return Collections2.transform(calls, new Function, ResolvedCallWithTrace>() { @@ -224,7 +226,7 @@ public class CallTransformer { final ExpressionReceiver calleeExpressionAsThisObject; final JetSimpleNameExpression fakeInvokeExpression; - private CallForImplicitInvoke( + public CallForImplicitInvoke( @NotNull ReceiverValue explicitExtensionReceiver, @NotNull ExpressionReceiver calleeExpressionAsThisObject, @NotNull Call call @@ -256,14 +258,7 @@ public class CallTransformer { @NotNull @Override public PsiElement getCallElement() { - if (outerCall.getCallElement() instanceof JetCallElement) { - //to report errors properly - JetValueArgumentList list = ((JetCallElement)outerCall.getCallElement()).getValueArgumentList(); - if (list != null) { - return list; - } - } - return fakeInvokeExpression; + return outerCall.getCallElement(); } @NotNull diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/context/BasicCallResolutionContext.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/context/BasicCallResolutionContext.java index a8de8be4b38..8fa9630c865 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/context/BasicCallResolutionContext.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/context/BasicCallResolutionContext.java @@ -90,4 +90,9 @@ public class BasicCallResolutionContext extends CallResolutionContext slice ) { if (expression == null) return null; - for (CallType callType : Lists.newArrayList(DEFAULT, ARRAY_GET_METHOD, ARRAY_SET_METHOD)) { + for (CallType callType : Lists.newArrayList(DEFAULT, ARRAY_GET_METHOD, ARRAY_SET_METHOD, INVOKE)) { CallKey callKey = CallKey.create(callType, expression); T context = trace.get(slice, callKey); if (context != null) { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/AbstractTracingStrategy.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/AbstractTracingStrategy.java index ccb3a52601a..7e237da7361 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/AbstractTracingStrategy.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/AbstractTracingStrategy.java @@ -46,7 +46,7 @@ import static org.jetbrains.jet.lang.resolve.BindingContext.AMBIGUOUS_REFERENCE_ import static org.jetbrains.jet.lang.types.TypeUtils.noExpectedType; public abstract class AbstractTracingStrategy implements TracingStrategy { - private final JetExpression reference; + protected final JetExpression reference; protected final Call call; protected AbstractTracingStrategy(@NotNull JetExpression reference, @NotNull Call call) { @@ -63,11 +63,6 @@ public abstract class AbstractTracingStrategy implements TracingStrategy { trace.record(AMBIGUOUS_REFERENCE_TARGET, reference, descriptors); } - @Override - public void unresolvedReferenceWrongReceiver(@NotNull BindingTrace trace, @NotNull Collection> candidates) { - trace.report(UNRESOLVED_REFERENCE_WRONG_RECEIVER.on(reference, candidates)); - } - @Override public void noValueForParameter(@NotNull BindingTrace trace, @NotNull ValueParameterDescriptor valueParameter) { JetElement reportOn; diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/TracingStrategyForInvoke.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/TracingStrategyForInvoke.java new file mode 100644 index 00000000000..c641fd6ca59 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/TracingStrategyForInvoke.java @@ -0,0 +1,62 @@ +/* + * Copyright 2010-2014 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.jet.lang.resolve.calls.tasks; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.CallableDescriptor; +import org.jetbrains.jet.lang.psi.Call; +import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.resolve.BindingTrace; +import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallWithTrace; + +import java.util.Collection; + +import static org.jetbrains.jet.lang.resolve.BindingContext.CALL; +import static org.jetbrains.jet.lang.resolve.BindingContext.RESOLVED_CALL; + +public class TracingStrategyForInvoke extends AbstractTracingStrategy { + public TracingStrategyForInvoke( + @NotNull JetExpression reference, + @NotNull Call call + ) { + super(reference, call); + } + + @Override + public void bindReference( + @NotNull BindingTrace trace, @NotNull ResolvedCallWithTrace resolvedCall + ) { + } + + @Override + public void bindResolvedCall( + @NotNull BindingTrace trace, @NotNull ResolvedCallWithTrace resolvedCall + ) { + trace.record(RESOLVED_CALL, reference, resolvedCall); + trace.record(CALL, reference, call); + } + + @Override + public void unresolvedReference(@NotNull BindingTrace trace) { + } + + @Override + public void unresolvedReferenceWrongReceiver( + @NotNull BindingTrace trace, @NotNull Collection> candidates + ) { + } +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/TracingStrategyImpl.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/TracingStrategyImpl.java index 9502d860f73..2ba92448eb1 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/TracingStrategyImpl.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/TracingStrategyImpl.java @@ -26,7 +26,10 @@ import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallWithTrace; import org.jetbrains.jet.lang.resolve.calls.model.VariableAsFunctionResolvedCall; import org.jetbrains.jet.lang.types.ErrorUtils; +import java.util.Collection; + import static org.jetbrains.jet.lang.diagnostics.Errors.UNRESOLVED_REFERENCE; +import static org.jetbrains.jet.lang.diagnostics.Errors.UNRESOLVED_REFERENCE_WRONG_RECEIVER; import static org.jetbrains.jet.lang.resolve.BindingContext.*; public class TracingStrategyImpl extends AbstractTracingStrategy { @@ -64,4 +67,9 @@ public class TracingStrategyImpl extends AbstractTracingStrategy { public void unresolvedReference(@NotNull BindingTrace trace) { trace.report(UNRESOLVED_REFERENCE.on(reference, reference)); } + + @Override + public void unresolvedReferenceWrongReceiver(@NotNull BindingTrace trace, @NotNull Collection> candidates) { + trace.report(UNRESOLVED_REFERENCE_WRONG_RECEIVER.on(reference, candidates)); + } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/util/JetFakeReference.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/util/JetFakeReference.java deleted file mode 100644 index 1909533d8e4..00000000000 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/util/JetFakeReference.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2010-2013 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.jet.lang.resolve.calls.util; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.jet.lang.psi.JetElement; -import org.jetbrains.jet.lang.psi.JetReferenceExpression; - -/** - * - * This class is used to wrap an expression that occurs in a reference position, such as a function literal, into a reference expression - */ -public class JetFakeReference extends JetReferenceExpression { - private final JetElement actualElement; - - public JetFakeReference(@NotNull JetElement actualElement) { - super(actualElement.getNode()); - this.actualElement = actualElement; - } - - public JetElement getActualElement() { - return actualElement; - } -} diff --git a/compiler/testData/cfg/arrays/ArrayOfFunctions.instructions b/compiler/testData/cfg/arrays/ArrayOfFunctions.instructions index dc2395e4dc0..e1af3f6929c 100644 --- a/compiler/testData/cfg/arrays/ArrayOfFunctions.instructions +++ b/compiler/testData/cfg/arrays/ArrayOfFunctions.instructions @@ -14,11 +14,11 @@ L0: r(11) call(array[11], get) r(3) - call(array[11], ) + call(array[11], invoke) L1: - 1 NEXT:[] + 1 NEXT:[] error: - PREV:[] + PREV:[] sink: - PREV:[, ] + PREV:[, ] ===================== diff --git a/compiler/testData/cfg/expressions/expressionAsFunction.instructions b/compiler/testData/cfg/expressions/expressionAsFunction.instructions index d55d17078b1..25f6bee23dc 100644 --- a/compiler/testData/cfg/expressions/expressionAsFunction.instructions +++ b/compiler/testData/cfg/expressions/expressionAsFunction.instructions @@ -11,11 +11,11 @@ L0: mark((f)()) mark((f)) r(f) - call((f), ) + call((f), invoke) L1: - 1 NEXT:[] + 1 NEXT:[] error: - PREV:[] + PREV:[] sink: - PREV:[, ] + PREV:[, ] ===================== diff --git a/compiler/testData/cfg/expressions/thisExpression.instructions b/compiler/testData/cfg/expressions/thisExpression.instructions index 907b4c530e5..9774aeeebf3 100644 --- a/compiler/testData/cfg/expressions/thisExpression.instructions +++ b/compiler/testData/cfg/expressions/thisExpression.instructions @@ -7,12 +7,11 @@ L0: 1 2 mark({ this() }) mark(this()) - r(this) - call(this, ) + call(this, invoke) L1: - 1 NEXT:[] + 1 NEXT:[] error: - PREV:[] + PREV:[] sink: - PREV:[, ] + PREV:[, ] ===================== diff --git a/compiler/testData/codegen/box/functions/invoke/extensionInvokeOnExpr.kt b/compiler/testData/codegen/box/functions/invoke/extensionInvokeOnExpr.kt new file mode 100644 index 00000000000..31473f1bff6 --- /dev/null +++ b/compiler/testData/codegen/box/functions/invoke/extensionInvokeOnExpr.kt @@ -0,0 +1,21 @@ +class A + +class B { + fun A.invoke() = "##" + fun A.invoke(i: Int) = "#${i}" +} + +fun foo() = A() + +fun B.test(): String { + if (A()() != "##") return "fail1" + if (A()(1) != "#1") return "fail2" + if (foo()() != "##") return "fail3" + if (foo()(42) != "#42") return "fail4" + if ((foo())(42) != "#42") return "fail5" + if ({() -> A()}()() != "##") return "fail6" + if ({() -> A()}()(37) != "#37") return "fail7" + return "OK" +} + +fun box(): String = B().test() diff --git a/compiler/testData/codegen/box/functions/invoke/invokeOnExprByConvention.kt b/compiler/testData/codegen/box/functions/invoke/invokeOnExprByConvention.kt new file mode 100644 index 00000000000..e98bc418624 --- /dev/null +++ b/compiler/testData/codegen/box/functions/invoke/invokeOnExprByConvention.kt @@ -0,0 +1,20 @@ +//KT-3217 Invoke convention after function invocation doesn't work +//KT-2728 Can't compile A()() + +class A { + fun invoke() = "##" + fun invoke(i: Int) = "#${i}" +} + +fun foo() = A() + +fun box(): String { + if (A()() != "##") return "fail1" + if (A()(1) != "#1") return "fail2" + if (foo()() != "##") return "fail3" + if (foo()(42) != "#42") return "fail4" + if ((foo())(42) != "#42") return "fail5" + if ({() -> A()}()() != "##") return "fail6" + if ({() -> A()}()(37) != "#37") return "fail7" + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/functions/invoke/kt3450getAndInvoke.kt b/compiler/testData/codegen/box/functions/invoke/kt3450getAndInvoke.kt new file mode 100644 index 00000000000..c77194ff40d --- /dev/null +++ b/compiler/testData/codegen/box/functions/invoke/kt3450getAndInvoke.kt @@ -0,0 +1,13 @@ +//KT-3450 get and invoke are not parsed in one expression + +public class A(val s: String) { + + public fun get(i: Int) : A = A("$s + $i") + + public fun invoke(builder : A.() -> String): String = builder() +} +fun x(y : String) : A = A(y) + +fun foo() = x("aaa")[42] { "$s!!" } + +fun box() = if (foo() == "aaa + 42!!") "OK" else "fail" diff --git a/compiler/testData/codegen/box/functions/invoke/kt3631invokeOnString.kt b/compiler/testData/codegen/box/functions/invoke/kt3631invokeOnString.kt new file mode 100644 index 00000000000..2bb2623e0ac --- /dev/null +++ b/compiler/testData/codegen/box/functions/invoke/kt3631invokeOnString.kt @@ -0,0 +1,5 @@ +//KT-3631 String.invoke doesn't work with literals + +fun String.invoke(i: Int) = "$this$i" + +fun box() = if ("a"(12) == "a12") "OK" else "fail" \ No newline at end of file diff --git a/compiler/testData/codegen/box/functions/invoke/kt3772.kt b/compiler/testData/codegen/box/functions/invoke/kt3772.kt new file mode 100644 index 00000000000..2a81b3c61f0 --- /dev/null +++ b/compiler/testData/codegen/box/functions/invoke/kt3772.kt @@ -0,0 +1,21 @@ +//KT-3772 Invoke and overload resolution ambiguity + +open class A { + fun invoke(f: A.() -> Unit) = 1 +} + +class B { + fun invoke(f: B.() -> Unit) = 2 +} + +open class C +val C.attr = A() + +open class D: C() +val D.attr = B() + + +fun box(): String { + val d = D() + return if (d.attr {} == 2) "OK" else "fail" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/functions/invoke/kt3821invokeOnThis.kt b/compiler/testData/codegen/box/functions/invoke/kt3821invokeOnThis.kt new file mode 100644 index 00000000000..15b2926ab2c --- /dev/null +++ b/compiler/testData/codegen/box/functions/invoke/kt3821invokeOnThis.kt @@ -0,0 +1,8 @@ +//KT-3821 Invoke convention doesn't work for `this` + +class A() { + fun invoke() = 42 + fun foo() = this() // Expecting a function type, but found A +} + +fun box() = if (A().foo() == 42) "OK" else "fail" \ No newline at end of file diff --git a/compiler/testData/codegen/box/functions/invoke/kt3822invokeOnThis.kt b/compiler/testData/codegen/box/functions/invoke/kt3822invokeOnThis.kt new file mode 100644 index 00000000000..3a3067e2e99 --- /dev/null +++ b/compiler/testData/codegen/box/functions/invoke/kt3822invokeOnThis.kt @@ -0,0 +1,9 @@ +//KT-3822 Compiler crashes when use invoke convention with `this` in class which extends Function0 + +class B() : Function0 { + override fun invoke() = true + + fun foo() = this() // Exception +} + +fun box() = if (B().foo()) "OK" else "fail" \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/directInvoke/callableReference.kt b/compiler/testData/codegen/bytecodeText/directInvoke/callableReference.kt index 749b223141e..1a4f2bf1324 100644 --- a/compiler/testData/codegen/bytecodeText/directInvoke/callableReference.kt +++ b/compiler/testData/codegen/bytecodeText/directInvoke/callableReference.kt @@ -7,4 +7,4 @@ class Z{ } } -// 2 invoke \(LZ;I\)V \ No newline at end of file +// 1 invoke \(LZ;I\)V \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/directInvoke/inplaceClosure.kt b/compiler/testData/codegen/bytecodeText/directInvoke/inplaceClosure.kt index 9f79978f7aa..6785206a901 100644 --- a/compiler/testData/codegen/bytecodeText/directInvoke/inplaceClosure.kt +++ b/compiler/testData/codegen/bytecodeText/directInvoke/inplaceClosure.kt @@ -2,4 +2,4 @@ fun test() { 1.{Int.() -> 2}() } -// 2 invoke \(I\)I \ No newline at end of file +// 1 invoke \(I\)I \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/operatorsOverloading/kt3450.kt b/compiler/testData/diagnostics/tests/operatorsOverloading/kt3450.kt new file mode 100644 index 00000000000..e33cf9c167d --- /dev/null +++ b/compiler/testData/diagnostics/tests/operatorsOverloading/kt3450.kt @@ -0,0 +1,23 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER + +public class A { + public fun get(vararg attrs : Pair) : A = this +} +fun String.plus() : A = A() +fun A.div(s : String) : A = A() + +fun test() { + (+"node2" / "node3" / "zzz") ["attr" to "value", "a2" to "v2"] +} + +//--------- +class B { + public fun get(s : String, q : String) : B = this + public fun get(s : Pair) : B = this + public fun invoke(q : B.() -> Unit) : B = this +} +val x = B()["a", "v"]["a" to "b"] {} ["q" to "p"] // does not parses around {} + +//from library +data class Pair (val first: A, val second: B) +fun A.to(that: B) = Pair(this, that) \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/resolve/invoke/errors/ambiguityForInvoke.kt b/compiler/testData/diagnostics/tests/resolve/invoke/errors/ambiguityForInvoke.kt new file mode 100644 index 00000000000..655ac209284 --- /dev/null +++ b/compiler/testData/diagnostics/tests/resolve/invoke/errors/ambiguityForInvoke.kt @@ -0,0 +1,10 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER + +fun Int.invoke(i: Int, a: Any) {} +fun Int.invoke(a: Any, i: Int) {} + +fun foo(i: Int) { + i(1, 1) + + 5(1, 2) +} diff --git a/compiler/testData/diagnostics/tests/resolve/invoke/errors/invisibleInvoke.kt b/compiler/testData/diagnostics/tests/resolve/invoke/errors/invisibleInvoke.kt new file mode 100644 index 00000000000..af608fada30 --- /dev/null +++ b/compiler/testData/diagnostics/tests/resolve/invoke/errors/invisibleInvoke.kt @@ -0,0 +1,10 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER + +class My { + private fun Int.invoke(s: String) {} +} + +fun My.foo(i: Int) { + i("") + 1("") +} diff --git a/compiler/testData/diagnostics/tests/resolve/invoke/errors/typeInferenceErrorForInvoke.kt b/compiler/testData/diagnostics/tests/resolve/invoke/errors/typeInferenceErrorForInvoke.kt new file mode 100644 index 00000000000..acb016330d7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/resolve/invoke/errors/typeInferenceErrorForInvoke.kt @@ -0,0 +1,13 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER + +class A + +fun T.invoke(a: A) {} + +fun foo(s: String, ai: A) { + 1(ai) + + s(ai) + + ""(ai) +} diff --git a/compiler/testData/diagnostics/tests/resolve/invoke/errors/unresolvedInvoke.kt b/compiler/testData/diagnostics/tests/resolve/invoke/errors/unresolvedInvoke.kt new file mode 100644 index 00000000000..707ad370263 --- /dev/null +++ b/compiler/testData/diagnostics/tests/resolve/invoke/errors/unresolvedInvoke.kt @@ -0,0 +1,4 @@ +fun foo(i: Int) { + i() + 1() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/resolve/invoke/errors/unsafeCallWithInvoke.kt b/compiler/testData/diagnostics/tests/resolve/invoke/errors/unsafeCallWithInvoke.kt new file mode 100644 index 00000000000..c45368415e2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/resolve/invoke/errors/unsafeCallWithInvoke.kt @@ -0,0 +1,9 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER + +fun String.invoke(i: Int) {} + +fun foo(s: String?) { + s(1) + + (s ?: null)(1) +} diff --git a/compiler/testData/diagnostics/tests/resolve/invoke/errors/wrongReceiverTypeForInvoke.kt b/compiler/testData/diagnostics/tests/resolve/invoke/errors/wrongReceiverTypeForInvoke.kt new file mode 100644 index 00000000000..31f3f009421 --- /dev/null +++ b/compiler/testData/diagnostics/tests/resolve/invoke/errors/wrongReceiverTypeForInvoke.kt @@ -0,0 +1,9 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER + +fun String.invoke(i: Int) {} + +fun foo(i: Int) { + i(1) + + 1(1) +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java index c6616290bc2..63cf3de56c3 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -5081,6 +5081,11 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest("compiler/testData/diagnostics/tests/operatorsOverloading/kt1028.kt"); } + @TestMetadata("kt3450.kt") + public void testKt3450() throws Exception { + doTest("compiler/testData/diagnostics/tests/operatorsOverloading/kt3450.kt"); + } + } @TestMetadata("compiler/testData/diagnostics/tests/overload") @@ -6041,6 +6046,7 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { } @TestMetadata("compiler/testData/diagnostics/tests/resolve/invoke") + @InnerTestClasses({Invoke.Errors.class}) public static class Invoke extends AbstractJetDiagnosticsTest { public void testAllFilesPresentInInvoke() throws Exception { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/diagnostics/tests/resolve/invoke"), Pattern.compile("^(.+)\\.kt$"), true); @@ -6091,6 +6097,50 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest("compiler/testData/diagnostics/tests/resolve/invoke/valNamedInvoke.kt"); } + @TestMetadata("compiler/testData/diagnostics/tests/resolve/invoke/errors") + public static class Errors extends AbstractJetDiagnosticsTest { + public void testAllFilesPresentInErrors() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/diagnostics/tests/resolve/invoke/errors"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("ambiguityForInvoke.kt") + public void testAmbiguityForInvoke() throws Exception { + doTest("compiler/testData/diagnostics/tests/resolve/invoke/errors/ambiguityForInvoke.kt"); + } + + @TestMetadata("invisibleInvoke.kt") + public void testInvisibleInvoke() throws Exception { + doTest("compiler/testData/diagnostics/tests/resolve/invoke/errors/invisibleInvoke.kt"); + } + + @TestMetadata("typeInferenceErrorForInvoke.kt") + public void testTypeInferenceErrorForInvoke() throws Exception { + doTest("compiler/testData/diagnostics/tests/resolve/invoke/errors/typeInferenceErrorForInvoke.kt"); + } + + @TestMetadata("unresolvedInvoke.kt") + public void testUnresolvedInvoke() throws Exception { + doTest("compiler/testData/diagnostics/tests/resolve/invoke/errors/unresolvedInvoke.kt"); + } + + @TestMetadata("unsafeCallWithInvoke.kt") + public void testUnsafeCallWithInvoke() throws Exception { + doTest("compiler/testData/diagnostics/tests/resolve/invoke/errors/unsafeCallWithInvoke.kt"); + } + + @TestMetadata("wrongReceiverTypeForInvoke.kt") + public void testWrongReceiverTypeForInvoke() throws Exception { + doTest("compiler/testData/diagnostics/tests/resolve/invoke/errors/wrongReceiverTypeForInvoke.kt"); + } + + } + + public static Test innerSuite() { + TestSuite suite = new TestSuite("Invoke"); + suite.addTestSuite(Invoke.class); + suite.addTestSuite(Errors.class); + return suite; + } } @TestMetadata("compiler/testData/diagnostics/tests/resolve/nestedCalls") @@ -6142,7 +6192,7 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { public static Test innerSuite() { TestSuite suite = new TestSuite("Resolve"); suite.addTestSuite(Resolve.class); - suite.addTestSuite(Invoke.class); + suite.addTest(Invoke.innerSuite()); suite.addTestSuite(NestedCalls.class); suite.addTestSuite(SpecialConstructions.class); return suite; diff --git a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java index 9119d80112a..7724824dec7 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java @@ -2878,11 +2878,21 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/codegen/box/functions/invoke"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("extensionInvokeOnExpr.kt") + public void testExtensionInvokeOnExpr() throws Exception { + doTest("compiler/testData/codegen/box/functions/invoke/extensionInvokeOnExpr.kt"); + } + @TestMetadata("invoke.kt") public void testInvoke() throws Exception { doTest("compiler/testData/codegen/box/functions/invoke/invoke.kt"); } + @TestMetadata("invokeOnExprByConvention.kt") + public void testInvokeOnExprByConvention() throws Exception { + doTest("compiler/testData/codegen/box/functions/invoke/invokeOnExprByConvention.kt"); + } + @TestMetadata("kt3189.kt") public void testKt3189() throws Exception { doTest("compiler/testData/codegen/box/functions/invoke/kt3189.kt"); @@ -2898,6 +2908,31 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest("compiler/testData/codegen/box/functions/invoke/kt3297.kt"); } + @TestMetadata("kt3450getAndInvoke.kt") + public void testKt3450getAndInvoke() throws Exception { + doTest("compiler/testData/codegen/box/functions/invoke/kt3450getAndInvoke.kt"); + } + + @TestMetadata("kt3631invokeOnString.kt") + public void testKt3631invokeOnString() throws Exception { + doTest("compiler/testData/codegen/box/functions/invoke/kt3631invokeOnString.kt"); + } + + @TestMetadata("kt3772.kt") + public void testKt3772() throws Exception { + doTest("compiler/testData/codegen/box/functions/invoke/kt3772.kt"); + } + + @TestMetadata("kt3821invokeOnThis.kt") + public void testKt3821invokeOnThis() throws Exception { + doTest("compiler/testData/codegen/box/functions/invoke/kt3821invokeOnThis.kt"); + } + + @TestMetadata("kt3822invokeOnThis.kt") + public void testKt3822invokeOnThis() throws Exception { + doTest("compiler/testData/codegen/box/functions/invoke/kt3822invokeOnThis.kt"); + } + } @TestMetadata("compiler/testData/codegen/box/functions/localFunctions") diff --git a/compiler/tests/org/jetbrains/jet/resolve/calls/AbstractResolvedCallsTest.kt b/compiler/tests/org/jetbrains/jet/resolve/calls/AbstractResolvedCallsTest.kt index 8468886110a..e39c22bd245 100644 --- a/compiler/tests/org/jetbrains/jet/resolve/calls/AbstractResolvedCallsTest.kt +++ b/compiler/tests/org/jetbrains/jet/resolve/calls/AbstractResolvedCallsTest.kt @@ -34,6 +34,7 @@ import java.io.File import java.util.Collections import kotlin.test.assertEquals import kotlin.test.assertTrue +import org.jetbrains.jet.lang.resolve.calls.model.VariableAsFunctionResolvedCall public abstract class AbstractResolvedCallsTest() : JetLiteFixture() { override fun createEnvironment(): JetCoreEnvironment = createEnvironmentWithMockJdk(ConfigurationKind.JDK_ONLY) @@ -72,12 +73,22 @@ public abstract class AbstractResolvedCallsTest() : JetLiteFixture() { var callFound = false for ((element, resolvedCall) in analyzeFileAndGetResolvedCallEntries()) { - if (callName.equals(element.getText())) { + + fun tryCall(resolvedCall: ResolvedCall<*>, actualName: String? = element.getText()) { + if (callName == null || callName != actualName) return callFound = true checkResolvedCall(resolvedCall, element) } + + if (resolvedCall is VariableAsFunctionResolvedCall) { + tryCall(resolvedCall.getFunctionCall(), "invoke") + tryCall(resolvedCall.getVariableCall()) + } + else { + tryCall(resolvedCall) + } } - assertTrue(callFound, "Resolved call for $callName was not found.") + assertTrue(callFound, "Resolved call for $callName was not found") } } diff --git a/idea/src/org/jetbrains/jet/plugin/references/JetInvokeFunctionReference.java b/idea/src/org/jetbrains/jet/plugin/references/JetInvokeFunctionReference.java index ea17f8218b1..c95b5a05ee6 100644 --- a/idea/src/org/jetbrains/jet/plugin/references/JetInvokeFunctionReference.java +++ b/idea/src/org/jetbrains/jet/plugin/references/JetInvokeFunctionReference.java @@ -23,6 +23,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.calls.CallResolverUtil; import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall; import org.jetbrains.jet.lang.resolve.calls.model.VariableAsFunctionResolvedCall; import org.jetbrains.jet.lexer.JetTokens; @@ -32,6 +33,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import static org.jetbrains.jet.lang.resolve.BindingContext.CALL; import static org.jetbrains.jet.lang.resolve.BindingContext.RESOLVED_CALL; class JetInvokeFunctionReference extends JetSimpleReference implements MultiRangeReference { @@ -48,10 +50,15 @@ class JetInvokeFunctionReference extends JetSimpleReference i @Override @NotNull protected Collection getTargetDescriptors(@NotNull BindingContext context) { - ResolvedCall resolvedCall = context.get(RESOLVED_CALL, getExpression().getCalleeExpression()); + JetExpression calleeExpression = getExpression().getCalleeExpression(); + ResolvedCall resolvedCall = context.get(RESOLVED_CALL, calleeExpression); if (resolvedCall instanceof VariableAsFunctionResolvedCall) { return Collections.singleton(((VariableAsFunctionResolvedCall) resolvedCall).getCandidateDescriptor()); } + Call call = context.get(CALL, calleeExpression); + if (call != null && resolvedCall != null && call.getCallType() == Call.CallType.INVOKE) { + return Collections.singleton(resolvedCall.getCandidateDescriptor()); + } return Collections.emptyList(); }