From 6157ebe3b809ef090ac88f93d77b3cd8d0d4c6f4 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Fri, 15 Jan 2016 10:39:12 +0300 Subject: [PATCH] Separate UNSAFE_IMPLICIT_INVOKE_CALL diagnostics introduced (see KT-8252) --- .../org/jetbrains/kotlin/diagnostics/Errors.java | 1 + .../rendering/DefaultErrorMessages.java | 1 + .../kotlin/resolve/calls/CandidateResolver.kt | 4 ++-- .../calls/tasks/AbstractTracingStrategy.java | 3 +++ .../testData/diagnostics/tests/SafeCallInvoke.kt | 2 +- .../diagnostics/tests/extensions/kt1875.kt | 4 ++-- .../tests/generics/nullability/functionalBound.kt | 2 +- .../platformTypes/nullabilityWarnings/invoke.kt | 2 +- .../tests/resolve/invoke/invokeAndSmartCast.kt | 14 +++++++------- idea/testData/checker/SafeInvoke.kt | 8 ++++++++ .../kotlin/checkers/PsiCheckerTestGenerated.java | 6 ++++++ 11 files changed, 33 insertions(+), 14 deletions(-) create mode 100644 idea/testData/checker/SafeInvoke.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index d764d568c27..3c6b35e03c1 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -646,6 +646,7 @@ public interface Errors { // Nullability DiagnosticFactory1 UNSAFE_CALL = DiagnosticFactory1.create(ERROR); + DiagnosticFactory1 UNSAFE_IMPLICIT_INVOKE_CALL = DiagnosticFactory1.create(ERROR); DiagnosticFactory3 UNSAFE_INFIX_CALL = DiagnosticFactory3.create(ERROR); DiagnosticFactory1 UNNECESSARY_SAFE_CALL = DiagnosticFactory1.create(WARNING); DiagnosticFactory0 UNEXPECTED_SAFE_CALL = DiagnosticFactory0.create(ERROR); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index ece479c8db2..87203602afc 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -469,6 +469,7 @@ public class DefaultErrorMessages { MAP.put(DYNAMIC_SUPERTYPE, "A supertype cannot be dynamic"); MAP.put(REDUNDANT_NULLABLE, "Redundant '?'"); MAP.put(UNSAFE_CALL, "Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type {0}", RENDER_TYPE); + MAP.put(UNSAFE_IMPLICIT_INVOKE_CALL, "Reference has a nullable type {0}, use explicit '?.invoke()' to make function-like call instead", RENDER_TYPE); MAP.put(AMBIGUOUS_LABEL, "Ambiguous label"); MAP.put(UNSUPPORTED, "Unsupported [{0}]", STRING); MAP.put(EXCEPTION_FROM_ANALYZER, "Internal Error occurred while analyzing this expression:\n{0}", THROWABLE); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt index 4c425e3f377..007563f12fa 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt @@ -464,6 +464,7 @@ class CandidateResolver( val dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverArgument, this) val nullability = dataFlowInfo.getPredictableNullability(dataFlowValue) var nullableImplicitInvokeReceiver = false + var receiverArgumentType = receiverArgument.type if (implicitInvokeCheck && call is CallForImplicitInvoke && call.isSafeCall()) { val outerCallReceiver = call.outerCall.explicitReceiver if (outerCallReceiver != call.explicitReceiver && outerCallReceiver is ReceiverValue) { @@ -471,6 +472,7 @@ class CandidateResolver( val outerReceiverNullability = dataFlowInfo.getPredictableNullability(outerReceiverDataFlowValue) if (outerReceiverNullability.canBeNull() && !TypeUtils.isNullableType(expectedReceiverParameterType)) { nullableImplicitInvokeReceiver = true + receiverArgumentType = TypeUtils.makeNullable(receiverArgumentType) } } } @@ -499,8 +501,6 @@ class CandidateResolver( } } - val receiverArgumentType = receiverArgument.type - if (reportUnsafeCall || nullableImplicitInvokeReceiver) { tracing.unsafeCall(trace, receiverArgumentType, implicitInvokeCheck) return UNSAFE_CALL_ERROR diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/AbstractTracingStrategy.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/AbstractTracingStrategy.java index 3b4749bcc9c..6987bd1ef43 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/AbstractTracingStrategy.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/AbstractTracingStrategy.java @@ -180,6 +180,9 @@ public abstract class AbstractTracingStrategy implements TracingStrategy { trace.report(UNSAFE_INFIX_CALL.on(reference, left.getText(), operationString.asString(), right.getText())); } } + else if (isCallForImplicitInvoke) { + trace.report(UNSAFE_IMPLICIT_INVOKE_CALL.on(reference, type)); + } else { trace.report(UNSAFE_CALL.on(reference, type)); } diff --git a/compiler/testData/diagnostics/tests/SafeCallInvoke.kt b/compiler/testData/diagnostics/tests/SafeCallInvoke.kt index 0ec22a660a8..e374ecbdf72 100644 --- a/compiler/testData/diagnostics/tests/SafeCallInvoke.kt +++ b/compiler/testData/diagnostics/tests/SafeCallInvoke.kt @@ -13,7 +13,7 @@ fun foo() { rule?.apply?.invoke() // this should be an error - rule?.apply() + rule?.apply() // these both also ok (with smart cast / unnecessary safe call) if (rule != null) { diff --git a/compiler/testData/diagnostics/tests/extensions/kt1875.kt b/compiler/testData/diagnostics/tests/extensions/kt1875.kt index f7fa2da3b4e..107e9706daf 100644 --- a/compiler/testData/diagnostics/tests/extensions/kt1875.kt +++ b/compiler/testData/diagnostics/tests/extensions/kt1875.kt @@ -9,13 +9,13 @@ interface T { } fun test(t: T) { - t.f(1) //unsafe call error + t.f(1) //unsafe call error t.f?.invoke(1) } fun test1(t: T?) { t.f(1) // todo resolve f as value and report UNSAFE_CALL - t?.f(1) + t?.f(1) t.f?.invoke(1) t?.f?.invoke(1) } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/generics/nullability/functionalBound.kt b/compiler/testData/diagnostics/tests/generics/nullability/functionalBound.kt index d689d5518a0..2944a0a2b31 100644 --- a/compiler/testData/diagnostics/tests/generics/nullability/functionalBound.kt +++ b/compiler/testData/diagnostics/tests/generics/nullability/functionalBound.kt @@ -1,6 +1,6 @@ fun Unit)?> foo(x: E, y: T) { if (x != null) { - y(x) + y(x) } if (y != null) { diff --git a/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/invoke.kt b/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/invoke.kt index f858bc8d56f..a1c251fae35 100644 --- a/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/invoke.kt +++ b/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/invoke.kt @@ -18,6 +18,6 @@ public class J { fun test() { J.staticNN() - J.staticN() + J.staticN() J.staticJ() } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/resolve/invoke/invokeAndSmartCast.kt b/compiler/testData/diagnostics/tests/resolve/invoke/invokeAndSmartCast.kt index 09779e354a1..32e76b75b74 100644 --- a/compiler/testData/diagnostics/tests/resolve/invoke/invokeAndSmartCast.kt +++ b/compiler/testData/diagnostics/tests/resolve/invoke/invokeAndSmartCast.kt @@ -3,18 +3,18 @@ class A(val x: (String.() -> Unit)?) fun test(a: A) { if (a.x != null) { "".(a.x)() - a.x("") // todo + a.x("") // todo (a.x)("") } - "".(a.x)() - a.x("") - (a.x)("") + "".(a.x)() + a.x("") + (a.x)("") with("") { - a.x() - (a.x)() + a.x() + (a.x)() if (a.x != null) { - a.x() // todo + a.x() // todo (a.x)() } } diff --git a/idea/testData/checker/SafeInvoke.kt b/idea/testData/checker/SafeInvoke.kt new file mode 100644 index 00000000000..8cb11f1301d --- /dev/null +++ b/idea/testData/checker/SafeInvoke.kt @@ -0,0 +1,8 @@ +class Rule(val apply: () -> Unit) + +fun foo() { + val rule: Rule? = Rule { } + rule?.apply() + val apply = rule?.apply + apply() +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/checkers/PsiCheckerTestGenerated.java b/idea/tests/org/jetbrains/kotlin/checkers/PsiCheckerTestGenerated.java index 81bd8444568..e3c13c5defc 100644 --- a/idea/tests/org/jetbrains/kotlin/checkers/PsiCheckerTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/checkers/PsiCheckerTestGenerated.java @@ -295,6 +295,12 @@ public class PsiCheckerTestGenerated extends AbstractPsiCheckerTest { doTest(fileName); } + @TestMetadata("SafeInvoke.kt") + public void testSafeInvoke() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/checker/SafeInvoke.kt"); + doTest(fileName); + } + @TestMetadata("Shadowing.kt") public void testShadowing() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/checker/Shadowing.kt");