Provide better error messages for read-only delegate
Split error reporting into two parts for incorrect and missing candidates. Missing function error is not reported on provideDelegate. Update error factory and default message for error. Update error texts in quick fix test data. #KT-16526 Fixed
This commit is contained in:
@@ -829,7 +829,7 @@ public interface Errors {
|
|||||||
DiagnosticFactory0<KtExpression> ITERATOR_ON_NULLABLE = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<KtExpression> ITERATOR_ON_NULLABLE = DiagnosticFactory0.create(ERROR);
|
||||||
DiagnosticFactory1<PsiElement, Collection<? extends ResolvedCall<?>>> ITERATOR_AMBIGUITY = DiagnosticFactory1.create(ERROR);
|
DiagnosticFactory1<PsiElement, Collection<? extends ResolvedCall<?>>> ITERATOR_AMBIGUITY = DiagnosticFactory1.create(ERROR);
|
||||||
|
|
||||||
DiagnosticFactory2<KtExpression, String, KotlinType> DELEGATE_SPECIAL_FUNCTION_MISSING = DiagnosticFactory2.create(ERROR);
|
DiagnosticFactory3<KtExpression, String, KotlinType, String> DELEGATE_SPECIAL_FUNCTION_MISSING = DiagnosticFactory3.create(ERROR);
|
||||||
DiagnosticFactory2<KtExpression, String, Collection<? extends ResolvedCall<?>>> DELEGATE_SPECIAL_FUNCTION_AMBIGUITY = DiagnosticFactory2.create(ERROR);
|
DiagnosticFactory2<KtExpression, String, Collection<? extends ResolvedCall<?>>> DELEGATE_SPECIAL_FUNCTION_AMBIGUITY = DiagnosticFactory2.create(ERROR);
|
||||||
DiagnosticFactory2<KtExpression, String, Collection<? extends ResolvedCall<?>>> DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE = DiagnosticFactory2.create(ERROR);
|
DiagnosticFactory2<KtExpression, String, Collection<? extends ResolvedCall<?>>> DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE = DiagnosticFactory2.create(ERROR);
|
||||||
DiagnosticFactory3<KtExpression, String, KotlinType, KotlinType> DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH = DiagnosticFactory3.create(ERROR);
|
DiagnosticFactory3<KtExpression, String, KotlinType, KotlinType> DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH = DiagnosticFactory3.create(ERROR);
|
||||||
|
|||||||
+1
-1
@@ -476,7 +476,7 @@ public class DefaultErrorMessages {
|
|||||||
MAP.put(ITERATOR_ON_NULLABLE, "Not nullable value required to call an 'iterator()' method on for-loop range");
|
MAP.put(ITERATOR_ON_NULLABLE, "Not nullable value required to call an 'iterator()' method on for-loop range");
|
||||||
MAP.put(ITERATOR_AMBIGUITY, "Method ''iterator()'' is ambiguous for this expression: {0}", AMBIGUOUS_CALLS);
|
MAP.put(ITERATOR_AMBIGUITY, "Method ''iterator()'' is ambiguous for this expression: {0}", AMBIGUOUS_CALLS);
|
||||||
|
|
||||||
MAP.put(DELEGATE_SPECIAL_FUNCTION_MISSING, "Missing ''{0}'' method on delegate of type ''{1}''", STRING, RENDER_TYPE);
|
MAP.put(DELEGATE_SPECIAL_FUNCTION_MISSING, "Type ''{1}'' has no method ''{0}'' and thus it cannot serve as a {2}", STRING, RENDER_TYPE, STRING);
|
||||||
MAP.put(DELEGATE_SPECIAL_FUNCTION_AMBIGUITY, "Overload resolution ambiguity on method ''{0}'': {1}", STRING, AMBIGUOUS_CALLS);
|
MAP.put(DELEGATE_SPECIAL_FUNCTION_AMBIGUITY, "Overload resolution ambiguity on method ''{0}'': {1}", STRING, AMBIGUOUS_CALLS);
|
||||||
MAP.put(DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE, "Property delegate must have a ''{0}'' method. None of the following functions is suitable: {1}",
|
MAP.put(DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE, "Property delegate must have a ''{0}'' method. None of the following functions is suitable: {1}",
|
||||||
STRING, AMBIGUOUS_CALLS);
|
STRING, AMBIGUOUS_CALLS);
|
||||||
|
|||||||
@@ -195,24 +195,19 @@ class DelegatedPropertyResolver(
|
|||||||
isGet = isGet, isComplete = true
|
isGet = isGet, isComplete = true
|
||||||
)
|
)
|
||||||
|
|
||||||
reportAndRecordDelegateOperatorResults(functionResults, propertyDescriptor, accessor, delegateExpression, delegateType, trace)
|
if (functionResults.isSuccess) {
|
||||||
|
recordDelegateOperatorResults(functionResults, propertyDescriptor, accessor, trace)
|
||||||
|
} else {
|
||||||
|
reportGetSetValueResolutionError(functionResults, accessor, delegateExpression, delegateType, trace, isGet)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reportAndRecordDelegateOperatorResults(
|
private fun recordDelegateOperatorResults(
|
||||||
result: OverloadResolutionResults<FunctionDescriptor>,
|
result: OverloadResolutionResults<FunctionDescriptor>,
|
||||||
propertyDescriptor: VariableDescriptorWithAccessors,
|
propertyDescriptor: VariableDescriptorWithAccessors,
|
||||||
accessor: VariableAccessorDescriptor,
|
accessor: VariableAccessorDescriptor,
|
||||||
delegateExpression: KtExpression,
|
|
||||||
delegateType: KotlinType,
|
|
||||||
trace: BindingTrace
|
trace: BindingTrace
|
||||||
) {
|
) {
|
||||||
if (!result.isSuccess) {
|
|
||||||
val call = trace.bindingContext.get(DELEGATED_PROPERTY_CALL, accessor)
|
|
||||||
?: throw AssertionError("'getDelegatedPropertyConventionMethod' didn't record a call")
|
|
||||||
reportDelegateOperatorResolutionError(trace, call, result, delegateExpression, delegateType)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val resultingDescriptor = result.resultingDescriptor
|
val resultingDescriptor = result.resultingDescriptor
|
||||||
val resultingCall = result.resultingCall
|
val resultingCall = result.resultingCall
|
||||||
|
|
||||||
@@ -230,16 +225,41 @@ class DelegatedPropertyResolver(
|
|||||||
trace.record(DELEGATED_PROPERTY_RESOLVED_CALL, accessor, resultingCall)
|
trace.record(DELEGATED_PROPERTY_RESOLVED_CALL, accessor, resultingCall)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reportDelegateOperatorResolutionError(
|
private fun reportGetSetValueResolutionError(
|
||||||
|
result: OverloadResolutionResults<FunctionDescriptor>,
|
||||||
|
accessor: VariableAccessorDescriptor,
|
||||||
|
delegateExpression: KtExpression,
|
||||||
|
delegateType: KotlinType,
|
||||||
|
trace: BindingTrace,
|
||||||
|
isGet: Boolean
|
||||||
|
) {
|
||||||
|
val call = trace.bindingContext.get(DELEGATED_PROPERTY_CALL, accessor)
|
||||||
|
?: throw AssertionError("'getDelegatedPropertyConventionMethod' didn't record a call")
|
||||||
|
|
||||||
|
val errorReportedForCandidate = reportDelegateErrorIfCandidateExists(trace, call, result, delegateExpression)
|
||||||
|
if (!errorReportedForCandidate) {
|
||||||
|
reportDelegateFunctionMissing(call, delegateExpression, delegateType, trace, isGet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun reportDelegateFunctionMissing(
|
||||||
|
delegateOperatorCall: Call,
|
||||||
|
delegateExpression: KtExpression,
|
||||||
|
delegateType: KotlinType,
|
||||||
|
trace: BindingTrace,
|
||||||
|
isGet: Boolean
|
||||||
|
) {
|
||||||
|
val expectedFunction = renderCall(delegateOperatorCall, trace.bindingContext)
|
||||||
|
val delegateKind = if (isGet) "delegate" else "delegate for var (read-write property)"
|
||||||
|
trace.report(DELEGATE_SPECIAL_FUNCTION_MISSING.on(delegateExpression, expectedFunction, delegateType, delegateKind))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun reportDelegateErrorIfCandidateExists(
|
||||||
trace: BindingTrace,
|
trace: BindingTrace,
|
||||||
delegateOperatorCall: Call,
|
delegateOperatorCall: Call,
|
||||||
delegateOperatorResults: OverloadResolutionResults<FunctionDescriptor>,
|
delegateOperatorResults: OverloadResolutionResults<FunctionDescriptor>,
|
||||||
delegateExpression: KtExpression,
|
delegateExpression: KtExpression
|
||||||
delegateType: KotlinType,
|
|
||||||
operatorRequired: Boolean = true
|
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val expectedFunction = renderCall(delegateOperatorCall, trace.bindingContext)
|
|
||||||
|
|
||||||
val resolutionErrorFactory = when {
|
val resolutionErrorFactory = when {
|
||||||
delegateOperatorResults.isSingleResult ||
|
delegateOperatorResults.isSingleResult ||
|
||||||
delegateOperatorResults.isIncomplete ||
|
delegateOperatorResults.isIncomplete ||
|
||||||
@@ -250,17 +270,12 @@ class DelegatedPropertyResolver(
|
|||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resolutionErrorFactory != null) {
|
resolutionErrorFactory?.let {
|
||||||
trace.report(resolutionErrorFactory.on(delegateExpression, expectedFunction, delegateOperatorResults.resultingCalls))
|
val expectedFunction = renderCall(delegateOperatorCall, trace.bindingContext)
|
||||||
return true
|
trace.report(it.on(delegateExpression, expectedFunction, delegateOperatorResults.resultingCalls))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (operatorRequired) {
|
return resolutionErrorFactory != null
|
||||||
trace.report(DELEGATE_SPECIAL_FUNCTION_MISSING.on(delegateExpression, expectedFunction, delegateType))
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun resolveProvideDelegateMethod(
|
private fun resolveProvideDelegateMethod(
|
||||||
@@ -283,8 +298,8 @@ class DelegatedPropertyResolver(
|
|||||||
if (!provideDelegateResults.isSuccess) {
|
if (!provideDelegateResults.isSuccess) {
|
||||||
val call = traceForProvideDelegate.bindingContext.get(BindingContext.PROVIDE_DELEGATE_CALL, propertyDescriptor)
|
val call = traceForProvideDelegate.bindingContext.get(BindingContext.PROVIDE_DELEGATE_CALL, propertyDescriptor)
|
||||||
?: throw AssertionError("'getDelegatedPropertyConventionMethod' didn't record a call")
|
?: throw AssertionError("'getDelegatedPropertyConventionMethod' didn't record a call")
|
||||||
val shouldCommitTrace = reportDelegateOperatorResolutionError(
|
val shouldCommitTrace = reportDelegateErrorIfCandidateExists(
|
||||||
traceForProvideDelegate, call, provideDelegateResults, byExpression, byExpressionType, operatorRequired = false
|
traceForProvideDelegate, call, provideDelegateResults, byExpression
|
||||||
)
|
)
|
||||||
|
|
||||||
if (shouldCommitTrace) {
|
if (shouldCommitTrace) {
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
val a: Int by <!DELEGATE_SPECIAL_FUNCTION_MISSING("getValue(Nothing?, KProperty<*>)", "A")!>A()<!>
|
val a: Int by <!DELEGATE_SPECIAL_FUNCTION_MISSING("getValue(Nothing?, KProperty<*>)", "A", "delegate")!>A()<!>
|
||||||
|
|
||||||
class A
|
class A
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
var a: Int by <!DELEGATE_SPECIAL_FUNCTION_MISSING("setValue(Nothing?, KProperty<*>, Int)", "A")!>A()<!>
|
var a: Int by <!DELEGATE_SPECIAL_FUNCTION_MISSING("setValue(Nothing?, KProperty<*>, Int)", "A", "delegate for var (read-write property)")!>A()<!>
|
||||||
|
|
||||||
class A {
|
class A {
|
||||||
operator fun getValue(t: Any?, p: KProperty<*>): Int {
|
operator fun getValue(t: Any?, p: KProperty<*>): Int {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// FILE: first.before.kt
|
// FILE: first.before.kt
|
||||||
// "Import" "true"
|
// "Import" "true"
|
||||||
// ERROR: Missing 'getValue(BigTest, KProperty<*>)' method on delegate of type 'DelegateImpl<Int>'
|
// ERROR: Type 'DelegateImpl<Int>' has no method 'getValue(BigTest, KProperty<*>)' and thus it cannot serve as a delegate
|
||||||
// ERROR: Missing 'setValue(BigTest, KProperty<*>, [ERROR : Type from delegate])' method on delegate of type 'DelegateImpl<Int>'
|
// ERROR: Type 'DelegateImpl<Int>' has no method 'setValue(BigTest, KProperty<*>, [ERROR : Type from delegate])' and thus it cannot serve as a delegate for var (read-write property)
|
||||||
|
|
||||||
package testing
|
package testing
|
||||||
|
|
||||||
@@ -27,8 +27,8 @@ public operator fun <T> DelegateImpl<T>.setValue(thisRef: Any, property: KProper
|
|||||||
|
|
||||||
// FILE: first.after.kt
|
// FILE: first.after.kt
|
||||||
// "Import" "true"
|
// "Import" "true"
|
||||||
// ERROR: Missing 'getValue(BigTest, KProperty<*>)' method on delegate of type 'DelegateImpl<Int>'
|
// ERROR: Type 'DelegateImpl<Int>' has no method 'getValue(BigTest, KProperty<*>)' and thus it cannot serve as a delegate
|
||||||
// ERROR: Missing 'setValue(BigTest, KProperty<*>, [ERROR : Type from delegate])' method on delegate of type 'DelegateImpl<Int>'
|
// ERROR: Type 'DelegateImpl<Int>' has no method 'setValue(BigTest, KProperty<*>, [ERROR : Type from delegate])' and thus it cannot serve as a delegate for var (read-write property)
|
||||||
|
|
||||||
package testing
|
package testing
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// FILE: first.before.kt
|
// FILE: first.before.kt
|
||||||
// "Import" "true"
|
// "Import" "true"
|
||||||
// ERROR: Missing 'getValue(BigTest, KProperty<*>)' method on delegate of type 'DelegateImpl<Int>'
|
// ERROR: Type 'DelegateImpl<Int>' has no method 'getValue(BigTest, KProperty<*>)' and thus it cannot serve as a delegate
|
||||||
|
|
||||||
package testing
|
package testing
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ public operator fun <T> DelegateImpl<T>.getValue(thisRef: Any?, property: KPrope
|
|||||||
|
|
||||||
// FILE: first.after.kt
|
// FILE: first.after.kt
|
||||||
// "Import" "true"
|
// "Import" "true"
|
||||||
// ERROR: Missing 'getValue(BigTest, KProperty<*>)' method on delegate of type 'DelegateImpl<Int>'
|
// ERROR: Type 'DelegateImpl<Int>' has no method 'getValue(BigTest, KProperty<*>)' and thus it cannot serve as a delegate
|
||||||
|
|
||||||
package testing
|
package testing
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// FILE: first.before.kt
|
// FILE: first.before.kt
|
||||||
// "Import" "true"
|
// "Import" "true"
|
||||||
// ERROR: Missing 'setValue(BigTest, KProperty<*>, Int)' method on delegate of type 'DelegateImpl<Int>'
|
// ERROR: Type 'DelegateImpl<Int>' has no method 'setValue(BigTest, KProperty<*>, Int)' and thus it cannot serve as a delegate for var (read-write property)
|
||||||
|
|
||||||
package testing
|
package testing
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ operator fun <T> DelegateImpl<T>.setValue(thisRef: Any, property: KProperty<*>,
|
|||||||
|
|
||||||
// FILE: first.after.kt
|
// FILE: first.after.kt
|
||||||
// "Import" "true"
|
// "Import" "true"
|
||||||
// ERROR: Missing 'setValue(BigTest, KProperty<*>, Int)' method on delegate of type 'DelegateImpl<Int>'
|
// ERROR: Type 'DelegateImpl<Int>' has no method 'setValue(BigTest, KProperty<*>, Int)' and thus it cannot serve as a delegate for var (read-write property)
|
||||||
|
|
||||||
package testing
|
package testing
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// FILE: first.before.kt
|
// FILE: first.before.kt
|
||||||
// "class org.jetbrains.kotlin.idea.quickfix.ImportFix" "false"
|
// "class org.jetbrains.kotlin.idea.quickfix.ImportFix" "false"
|
||||||
// ERROR: Missing 'getValue(Nothing?, KProperty<*>)' method on delegate of type 'String'
|
// ERROR: Type 'String' has no method 'getValue(Nothing?, KProperty<*>)' and thus it cannot serve as a delegate
|
||||||
// ACTION: To raw string literal
|
// ACTION: To raw string literal
|
||||||
|
|
||||||
package b
|
package b
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
// ACTION: Create extension function 'Delegate.getValue'
|
// ACTION: Create extension function 'Delegate.getValue'
|
||||||
// ACTION: Create member function 'Delegate.getValue'
|
// ACTION: Create member function 'Delegate.getValue'
|
||||||
// ACTION: Introduce import alias
|
// ACTION: Introduce import alias
|
||||||
// ERROR: Missing 'getValue(Nothing?, KProperty<*>)' method on delegate of type 'Delegate'
|
// ERROR: Type 'Delegate' has no method 'getValue(Nothing?, KProperty<*>)' and thus it cannot serve as a delegate
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
fun test() {
|
fun test() {
|
||||||
|
|||||||
+2
-2
@@ -2,8 +2,8 @@
|
|||||||
// ACTION: Create extension function 'Delegate.getValue', function 'Delegate.setValue'
|
// ACTION: Create extension function 'Delegate.getValue', function 'Delegate.setValue'
|
||||||
// ACTION: Create member function 'Delegate.getValue', function 'Delegate.setValue'
|
// ACTION: Create member function 'Delegate.getValue', function 'Delegate.setValue'
|
||||||
// ACTION: Introduce import alias
|
// ACTION: Introduce import alias
|
||||||
// ERROR: Missing 'getValue(Nothing?, KProperty<*>)' method on delegate of type 'Delegate'
|
// ERROR: Type 'Delegate' has no method 'getValue(Nothing?, KProperty<*>)' and thus it cannot serve as a delegate
|
||||||
// ERROR: Missing 'setValue(Nothing?, KProperty<*>, String)' method on delegate of type 'Delegate'
|
// ERROR: Type 'Delegate' has no method 'setValue(Nothing?, KProperty<*>, String)' and thus it cannot serve as a delegate for var (read-write property)
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
fun test() {
|
fun test() {
|
||||||
|
|||||||
Reference in New Issue
Block a user