Separate UNSAFE_IMPLICIT_INVOKE_CALL diagnostics introduced (see KT-8252)

This commit is contained in:
Mikhail Glukhikh
2016-01-15 10:39:12 +03:00
parent fe11b5a473
commit 6157ebe3b8
11 changed files with 33 additions and 14 deletions
@@ -646,6 +646,7 @@ public interface Errors {
// Nullability
DiagnosticFactory1<PsiElement, KotlinType> UNSAFE_CALL = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, KotlinType> UNSAFE_IMPLICIT_INVOKE_CALL = DiagnosticFactory1.create(ERROR);
DiagnosticFactory3<KtExpression, String, String, String> UNSAFE_INFIX_CALL = DiagnosticFactory3.create(ERROR);
DiagnosticFactory1<PsiElement, KotlinType> UNNECESSARY_SAFE_CALL = DiagnosticFactory1.create(WARNING);
DiagnosticFactory0<PsiElement> UNEXPECTED_SAFE_CALL = DiagnosticFactory0.create(ERROR);
@@ -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);
@@ -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
@@ -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));
}
+1 -1
View File
@@ -13,7 +13,7 @@ fun foo() {
rule?.apply?.invoke()
// this should be an error
rule?.<!UNSAFE_CALL!>apply<!>()
rule?.<!UNSAFE_IMPLICIT_INVOKE_CALL!>apply<!>()
// these both also ok (with smart cast / unnecessary safe call)
if (rule != null) {
+2 -2
View File
@@ -9,13 +9,13 @@ interface T {
}
fun test(t: T) {
t.<!UNSAFE_CALL!>f<!>(1) //unsafe call error
t.<!UNSAFE_IMPLICIT_INVOKE_CALL!>f<!>(1) //unsafe call error
t.f?.invoke(1)
}
fun test1(t: T?) {
t<!UNSAFE_CALL!>.<!><!FUNCTION_EXPECTED!>f<!>(1) // todo resolve f as value and report UNSAFE_CALL
t?.<!UNSAFE_CALL!>f<!>(1)
t?.<!UNSAFE_IMPLICIT_INVOKE_CALL!>f<!>(1)
t<!UNSAFE_CALL!>.<!>f?.invoke(1)
t?.f?.invoke(1)
}
@@ -1,6 +1,6 @@
fun <E : String?, T : ((CharSequence) -> Unit)?> foo(x: E, y: T) {
if (x != null) {
<!UNSAFE_CALL!>y<!>(<!DEBUG_INFO_SMARTCAST!>x<!>)
<!UNSAFE_IMPLICIT_INVOKE_CALL!>y<!>(<!DEBUG_INFO_SMARTCAST!>x<!>)
}
if (y != null) {
@@ -18,6 +18,6 @@ public class J {
fun test() {
J.staticNN()
J.<!UNSAFE_CALL!>staticN<!>()
J.<!UNSAFE_IMPLICIT_INVOKE_CALL!>staticN<!>()
J.staticJ()
}
@@ -3,18 +3,18 @@ class A(val x: (String.() -> Unit)?)
fun test(a: A) {
if (a.x != null) {
"".<!DEBUG_INFO_SMARTCAST!>(a.x)<!>()
a.<!UNSAFE_CALL!>x<!>("") // todo
a.<!UNSAFE_IMPLICIT_INVOKE_CALL!>x<!>("") // todo
<!DEBUG_INFO_SMARTCAST!>(a.x)<!>("")
}
"".<!UNSAFE_CALL!>(a.x)<!>()
a.<!UNSAFE_CALL!>x<!>("")
<!UNSAFE_CALL!>(a.x)<!>("")
"".<!UNSAFE_IMPLICIT_INVOKE_CALL!>(a.x)<!>()
a.<!UNSAFE_IMPLICIT_INVOKE_CALL!>x<!>("")
<!UNSAFE_IMPLICIT_INVOKE_CALL!>(a.x)<!>("")
with("") {
a.<!UNSAFE_CALL!>x<!>(<!NO_VALUE_FOR_PARAMETER!>)<!>
<!UNSAFE_CALL!>(a.x)<!>()
a.<!UNSAFE_IMPLICIT_INVOKE_CALL!>x<!>(<!NO_VALUE_FOR_PARAMETER!>)<!>
<!UNSAFE_IMPLICIT_INVOKE_CALL!>(a.x)<!>()
if (a.x != null) {
a.<!UNSAFE_CALL!>x<!>(<!NO_VALUE_FOR_PARAMETER!>)<!> // todo
a.<!UNSAFE_IMPLICIT_INVOKE_CALL!>x<!>(<!NO_VALUE_FOR_PARAMETER!>)<!> // todo
<!DEBUG_INFO_SMARTCAST!>(a.x)<!>()
}
}
+8
View File
@@ -0,0 +1,8 @@
class Rule(val apply: () -> Unit)
fun foo() {
val rule: Rule? = Rule { }
rule?.<error descr="[UNSAFE_IMPLICIT_INVOKE_CALL] Reference has a nullable type (() -> kotlin.Unit)?, use explicit ?.invoke() to make function-like call instead">apply</error>()
val apply = rule?.apply
<error descr="[UNSAFE_IMPLICIT_INVOKE_CALL] Reference has a nullable type (() -> kotlin.Unit)?, use explicit ?.invoke() to make function-like call instead">apply</error>()
}
@@ -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");