Separate UNSAFE_IMPLICIT_INVOKE_CALL diagnostics introduced (see KT-8252)
This commit is contained in:
@@ -646,6 +646,7 @@ public interface Errors {
|
|||||||
// Nullability
|
// Nullability
|
||||||
|
|
||||||
DiagnosticFactory1<PsiElement, KotlinType> UNSAFE_CALL = DiagnosticFactory1.create(ERROR);
|
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);
|
DiagnosticFactory3<KtExpression, String, String, String> UNSAFE_INFIX_CALL = DiagnosticFactory3.create(ERROR);
|
||||||
DiagnosticFactory1<PsiElement, KotlinType> UNNECESSARY_SAFE_CALL = DiagnosticFactory1.create(WARNING);
|
DiagnosticFactory1<PsiElement, KotlinType> UNNECESSARY_SAFE_CALL = DiagnosticFactory1.create(WARNING);
|
||||||
DiagnosticFactory0<PsiElement> UNEXPECTED_SAFE_CALL = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<PsiElement> UNEXPECTED_SAFE_CALL = DiagnosticFactory0.create(ERROR);
|
||||||
|
|||||||
+1
@@ -469,6 +469,7 @@ public class DefaultErrorMessages {
|
|||||||
MAP.put(DYNAMIC_SUPERTYPE, "A supertype cannot be dynamic");
|
MAP.put(DYNAMIC_SUPERTYPE, "A supertype cannot be dynamic");
|
||||||
MAP.put(REDUNDANT_NULLABLE, "Redundant '?'");
|
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_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(AMBIGUOUS_LABEL, "Ambiguous label");
|
||||||
MAP.put(UNSUPPORTED, "Unsupported [{0}]", STRING);
|
MAP.put(UNSUPPORTED, "Unsupported [{0}]", STRING);
|
||||||
MAP.put(EXCEPTION_FROM_ANALYZER, "Internal Error occurred while analyzing this expression:\n{0}", THROWABLE);
|
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 dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverArgument, this)
|
||||||
val nullability = dataFlowInfo.getPredictableNullability(dataFlowValue)
|
val nullability = dataFlowInfo.getPredictableNullability(dataFlowValue)
|
||||||
var nullableImplicitInvokeReceiver = false
|
var nullableImplicitInvokeReceiver = false
|
||||||
|
var receiverArgumentType = receiverArgument.type
|
||||||
if (implicitInvokeCheck && call is CallForImplicitInvoke && call.isSafeCall()) {
|
if (implicitInvokeCheck && call is CallForImplicitInvoke && call.isSafeCall()) {
|
||||||
val outerCallReceiver = call.outerCall.explicitReceiver
|
val outerCallReceiver = call.outerCall.explicitReceiver
|
||||||
if (outerCallReceiver != call.explicitReceiver && outerCallReceiver is ReceiverValue) {
|
if (outerCallReceiver != call.explicitReceiver && outerCallReceiver is ReceiverValue) {
|
||||||
@@ -471,6 +472,7 @@ class CandidateResolver(
|
|||||||
val outerReceiverNullability = dataFlowInfo.getPredictableNullability(outerReceiverDataFlowValue)
|
val outerReceiverNullability = dataFlowInfo.getPredictableNullability(outerReceiverDataFlowValue)
|
||||||
if (outerReceiverNullability.canBeNull() && !TypeUtils.isNullableType(expectedReceiverParameterType)) {
|
if (outerReceiverNullability.canBeNull() && !TypeUtils.isNullableType(expectedReceiverParameterType)) {
|
||||||
nullableImplicitInvokeReceiver = true
|
nullableImplicitInvokeReceiver = true
|
||||||
|
receiverArgumentType = TypeUtils.makeNullable(receiverArgumentType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -499,8 +501,6 @@ class CandidateResolver(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val receiverArgumentType = receiverArgument.type
|
|
||||||
|
|
||||||
if (reportUnsafeCall || nullableImplicitInvokeReceiver) {
|
if (reportUnsafeCall || nullableImplicitInvokeReceiver) {
|
||||||
tracing.unsafeCall(trace, receiverArgumentType, implicitInvokeCheck)
|
tracing.unsafeCall(trace, receiverArgumentType, implicitInvokeCheck)
|
||||||
return UNSAFE_CALL_ERROR
|
return UNSAFE_CALL_ERROR
|
||||||
|
|||||||
+3
@@ -180,6 +180,9 @@ public abstract class AbstractTracingStrategy implements TracingStrategy {
|
|||||||
trace.report(UNSAFE_INFIX_CALL.on(reference, left.getText(), operationString.asString(), right.getText()));
|
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 {
|
else {
|
||||||
trace.report(UNSAFE_CALL.on(reference, type));
|
trace.report(UNSAFE_CALL.on(reference, type));
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -13,7 +13,7 @@ fun foo() {
|
|||||||
rule?.apply?.invoke()
|
rule?.apply?.invoke()
|
||||||
|
|
||||||
// this should be an error
|
// 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)
|
// these both also ok (with smart cast / unnecessary safe call)
|
||||||
if (rule != null) {
|
if (rule != null) {
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ interface T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun test(t: 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)
|
t.f?.invoke(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun test1(t: T?) {
|
fun test1(t: T?) {
|
||||||
t<!UNSAFE_CALL!>.<!><!FUNCTION_EXPECTED!>f<!>(1) // todo resolve f as value and report UNSAFE_CALL
|
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<!UNSAFE_CALL!>.<!>f?.invoke(1)
|
||||||
t?.f?.invoke(1)
|
t?.f?.invoke(1)
|
||||||
}
|
}
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
fun <E : String?, T : ((CharSequence) -> Unit)?> foo(x: E, y: T) {
|
fun <E : String?, T : ((CharSequence) -> Unit)?> foo(x: E, y: T) {
|
||||||
if (x != null) {
|
if (x != null) {
|
||||||
<!UNSAFE_CALL!>y<!>(<!DEBUG_INFO_SMARTCAST!>x<!>)
|
<!UNSAFE_IMPLICIT_INVOKE_CALL!>y<!>(<!DEBUG_INFO_SMARTCAST!>x<!>)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (y != null) {
|
if (y != null) {
|
||||||
|
|||||||
+1
-1
@@ -18,6 +18,6 @@ public class J {
|
|||||||
|
|
||||||
fun test() {
|
fun test() {
|
||||||
J.staticNN()
|
J.staticNN()
|
||||||
J.<!UNSAFE_CALL!>staticN<!>()
|
J.<!UNSAFE_IMPLICIT_INVOKE_CALL!>staticN<!>()
|
||||||
J.staticJ()
|
J.staticJ()
|
||||||
}
|
}
|
||||||
@@ -3,18 +3,18 @@ class A(val x: (String.() -> Unit)?)
|
|||||||
fun test(a: A) {
|
fun test(a: A) {
|
||||||
if (a.x != null) {
|
if (a.x != null) {
|
||||||
"".<!DEBUG_INFO_SMARTCAST!>(a.x)<!>()
|
"".<!DEBUG_INFO_SMARTCAST!>(a.x)<!>()
|
||||||
a.<!UNSAFE_CALL!>x<!>("") // todo
|
a.<!UNSAFE_IMPLICIT_INVOKE_CALL!>x<!>("") // todo
|
||||||
<!DEBUG_INFO_SMARTCAST!>(a.x)<!>("")
|
<!DEBUG_INFO_SMARTCAST!>(a.x)<!>("")
|
||||||
}
|
}
|
||||||
"".<!UNSAFE_CALL!>(a.x)<!>()
|
"".<!UNSAFE_IMPLICIT_INVOKE_CALL!>(a.x)<!>()
|
||||||
a.<!UNSAFE_CALL!>x<!>("")
|
a.<!UNSAFE_IMPLICIT_INVOKE_CALL!>x<!>("")
|
||||||
<!UNSAFE_CALL!>(a.x)<!>("")
|
<!UNSAFE_IMPLICIT_INVOKE_CALL!>(a.x)<!>("")
|
||||||
|
|
||||||
with("") {
|
with("") {
|
||||||
a.<!UNSAFE_CALL!>x<!>(<!NO_VALUE_FOR_PARAMETER!>)<!>
|
a.<!UNSAFE_IMPLICIT_INVOKE_CALL!>x<!>(<!NO_VALUE_FOR_PARAMETER!>)<!>
|
||||||
<!UNSAFE_CALL!>(a.x)<!>()
|
<!UNSAFE_IMPLICIT_INVOKE_CALL!>(a.x)<!>()
|
||||||
if (a.x != null) {
|
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)<!>()
|
<!DEBUG_INFO_SMARTCAST!>(a.x)<!>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+8
@@ -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);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("SafeInvoke.kt")
|
||||||
|
public void testSafeInvoke() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/checker/SafeInvoke.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("Shadowing.kt")
|
@TestMetadata("Shadowing.kt")
|
||||||
public void testShadowing() throws Exception {
|
public void testShadowing() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/checker/Shadowing.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/checker/Shadowing.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user