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:
Pavel Kirpichenkov
2019-09-11 14:38:16 +03:00
parent 68bcdaa6c3
commit 55cb9561c2
11 changed files with 59 additions and 44 deletions
@@ -829,7 +829,7 @@ public interface Errors {
DiagnosticFactory0<KtExpression> ITERATOR_ON_NULLABLE = DiagnosticFactory0.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_NONE_APPLICABLE = DiagnosticFactory2.create(ERROR);
DiagnosticFactory3<KtExpression, String, KotlinType, KotlinType> DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH = DiagnosticFactory3.create(ERROR);
@@ -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_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_NONE_APPLICABLE, "Property delegate must have a ''{0}'' method. None of the following functions is suitable: {1}",
STRING, AMBIGUOUS_CALLS);
@@ -195,24 +195,19 @@ class DelegatedPropertyResolver(
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>,
propertyDescriptor: VariableDescriptorWithAccessors,
accessor: VariableAccessorDescriptor,
delegateExpression: KtExpression,
delegateType: KotlinType,
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 resultingCall = result.resultingCall
@@ -230,16 +225,41 @@ class DelegatedPropertyResolver(
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,
delegateOperatorCall: Call,
delegateOperatorResults: OverloadResolutionResults<FunctionDescriptor>,
delegateExpression: KtExpression,
delegateType: KotlinType,
operatorRequired: Boolean = true
delegateExpression: KtExpression
): Boolean {
val expectedFunction = renderCall(delegateOperatorCall, trace.bindingContext)
val resolutionErrorFactory = when {
delegateOperatorResults.isSingleResult ||
delegateOperatorResults.isIncomplete ||
@@ -250,17 +270,12 @@ class DelegatedPropertyResolver(
else -> null
}
if (resolutionErrorFactory != null) {
trace.report(resolutionErrorFactory.on(delegateExpression, expectedFunction, delegateOperatorResults.resultingCalls))
return true
resolutionErrorFactory?.let {
val expectedFunction = renderCall(delegateOperatorCall, trace.bindingContext)
trace.report(it.on(delegateExpression, expectedFunction, delegateOperatorResults.resultingCalls))
}
if (operatorRequired) {
trace.report(DELEGATE_SPECIAL_FUNCTION_MISSING.on(delegateExpression, expectedFunction, delegateType))
return true
}
return false
return resolutionErrorFactory != null
}
private fun resolveProvideDelegateMethod(
@@ -283,8 +298,8 @@ class DelegatedPropertyResolver(
if (!provideDelegateResults.isSuccess) {
val call = traceForProvideDelegate.bindingContext.get(BindingContext.PROVIDE_DELEGATE_CALL, propertyDescriptor)
?: throw AssertionError("'getDelegatedPropertyConventionMethod' didn't record a call")
val shouldCommitTrace = reportDelegateOperatorResolutionError(
traceForProvideDelegate, call, provideDelegateResults, byExpression, byExpressionType, operatorRequired = false
val shouldCommitTrace = reportDelegateErrorIfCandidateExists(
traceForProvideDelegate, call, provideDelegateResults, byExpression
)
if (shouldCommitTrace) {