FIR: check DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH
This commit is contained in:
committed by
Mikhail Glukhikh
parent
84e67da2af
commit
b6bd4ae8e6
+6
@@ -706,6 +706,12 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
|
|||||||
parameter<String>("expectedFunctionSignature")
|
parameter<String>("expectedFunctionSignature")
|
||||||
parameter<Collection<AbstractFirBasedSymbol<*>>>("candidates")
|
parameter<Collection<AbstractFirBasedSymbol<*>>>("candidates")
|
||||||
}
|
}
|
||||||
|
val DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH by error<KtExpression> {
|
||||||
|
parameter<String>("delegateFunction")
|
||||||
|
parameter<ConeKotlinType>("expected")
|
||||||
|
parameter<ConeKotlinType>("actual")
|
||||||
|
}
|
||||||
|
|
||||||
val UNDERSCORE_IS_RESERVED by error<KtExpression>(PositioningStrategy.RESERVED_UNDERSCORE)
|
val UNDERSCORE_IS_RESERVED by error<KtExpression>(PositioningStrategy.RESERVED_UNDERSCORE)
|
||||||
val UNDERSCORE_USAGE_WITHOUT_BACKTICKS by error<KtExpression>(PositioningStrategy.RESERVED_UNDERSCORE)
|
val UNDERSCORE_USAGE_WITHOUT_BACKTICKS by error<KtExpression>(PositioningStrategy.RESERVED_UNDERSCORE)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -415,6 +415,7 @@ object FirErrors {
|
|||||||
val DELEGATE_SPECIAL_FUNCTION_MISSING by error3<KtExpression, String, ConeKotlinType, String>()
|
val DELEGATE_SPECIAL_FUNCTION_MISSING by error3<KtExpression, String, ConeKotlinType, String>()
|
||||||
val DELEGATE_SPECIAL_FUNCTION_AMBIGUITY by error2<KtExpression, String, Collection<AbstractFirBasedSymbol<*>>>()
|
val DELEGATE_SPECIAL_FUNCTION_AMBIGUITY by error2<KtExpression, String, Collection<AbstractFirBasedSymbol<*>>>()
|
||||||
val DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE by error2<KtExpression, String, Collection<AbstractFirBasedSymbol<*>>>()
|
val DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE by error2<KtExpression, String, Collection<AbstractFirBasedSymbol<*>>>()
|
||||||
|
val DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH by error3<KtExpression, String, ConeKotlinType, ConeKotlinType>()
|
||||||
val UNDERSCORE_IS_RESERVED by error0<KtExpression>(SourceElementPositioningStrategies.RESERVED_UNDERSCORE)
|
val UNDERSCORE_IS_RESERVED by error0<KtExpression>(SourceElementPositioningStrategies.RESERVED_UNDERSCORE)
|
||||||
val UNDERSCORE_USAGE_WITHOUT_BACKTICKS by error0<KtExpression>(SourceElementPositioningStrategies.RESERVED_UNDERSCORE)
|
val UNDERSCORE_USAGE_WITHOUT_BACKTICKS by error0<KtExpression>(SourceElementPositioningStrategies.RESERVED_UNDERSCORE)
|
||||||
|
|
||||||
|
|||||||
+32
-3
@@ -21,18 +21,23 @@ import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeAmbiguityError
|
|||||||
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeInapplicableCandidateError
|
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeInapplicableCandidateError
|
||||||
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedNameError
|
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedNameError
|
||||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.typeContext
|
||||||
import org.jetbrains.kotlin.fir.types.ConeKotlinErrorType
|
import org.jetbrains.kotlin.fir.types.ConeKotlinErrorType
|
||||||
import org.jetbrains.kotlin.fir.types.coneType
|
import org.jetbrains.kotlin.fir.types.coneType
|
||||||
import org.jetbrains.kotlin.fir.types.render
|
import org.jetbrains.kotlin.fir.types.render
|
||||||
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
|
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
|
||||||
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
|
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
|
||||||
import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
|
import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
|
||||||
|
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||||
|
|
||||||
object FirDelegatedPropertyChecker : FirPropertyChecker() {
|
object FirDelegatedPropertyChecker : FirPropertyChecker() {
|
||||||
override fun check(declaration: FirProperty, context: CheckerContext, reporter: DiagnosticReporter) {
|
override fun check(declaration: FirProperty, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||||
val delegate = declaration.delegate ?: return
|
val delegate = declaration.delegate ?: return
|
||||||
val delegateType = delegate.typeRef.coneType
|
val delegateType = delegate.typeRef.coneType
|
||||||
|
|
||||||
|
// TODO: Also suppress delegate issue if type inference failed. For example, in
|
||||||
|
// compiler/testData/diagnostics/tests/delegatedProperty/inference/differentDelegatedExpressions.fir.kt, no delegate issues are
|
||||||
|
// reported due to the inference issue.
|
||||||
if (delegateType is ConeKotlinErrorType) {
|
if (delegateType is ConeKotlinErrorType) {
|
||||||
val delegateSource = delegate.source
|
val delegateSource = delegate.source
|
||||||
// Implicit recursion type is not reported since the type ref does not have a real source.
|
// Implicit recursion type is not reported since the type ref does not have a real source.
|
||||||
@@ -47,8 +52,13 @@ object FirDelegatedPropertyChecker : FirPropertyChecker() {
|
|||||||
override fun visitElement(element: FirElement) = element.acceptChildren(this)
|
override fun visitElement(element: FirElement) = element.acceptChildren(this)
|
||||||
|
|
||||||
override fun visitFunctionCall(functionCall: FirFunctionCall) {
|
override fun visitFunctionCall(functionCall: FirFunctionCall) {
|
||||||
val errorNamedReference = functionCall.calleeReference as? FirErrorNamedReference ?: return
|
val hasReferenceError = hasFunctionReferenceErrors(functionCall)
|
||||||
if (errorNamedReference.source?.kind != FirFakeSourceElementKind.DelegatedPropertyAccessor) return
|
if (isGet && !hasReferenceError) checkReturnType(functionCall)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hasFunctionReferenceErrors(functionCall: FirFunctionCall): Boolean {
|
||||||
|
val errorNamedReference = functionCall.calleeReference as? FirErrorNamedReference ?: return false
|
||||||
|
if (errorNamedReference.source?.kind != FirFakeSourceElementKind.DelegatedPropertyAccessor) return false
|
||||||
val expectedFunctionSignature =
|
val expectedFunctionSignature =
|
||||||
(if (isGet) "getValue" else "setValue") + "(${functionCall.arguments.joinToString(", ") { it.typeRef.coneType.render() }})"
|
(if (isGet) "getValue" else "setValue") + "(${functionCall.arguments.joinToString(", ") { it.typeRef.coneType.render() }})"
|
||||||
val delegateDescription = if (isGet) "delegate" else "delegate for var (read-write property)"
|
val delegateDescription = if (isGet) "delegate" else "delegate for var (read-write property)"
|
||||||
@@ -77,7 +87,7 @@ object FirDelegatedPropertyChecker : FirPropertyChecker() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
when (val diagnostic = errorNamedReference.diagnostic) {
|
return when (val diagnostic = errorNamedReference.diagnostic) {
|
||||||
is ConeUnresolvedNameError -> {
|
is ConeUnresolvedNameError -> {
|
||||||
reporter.reportOn(
|
reporter.reportOn(
|
||||||
errorNamedReference.source,
|
errorNamedReference.source,
|
||||||
@@ -87,6 +97,7 @@ object FirDelegatedPropertyChecker : FirPropertyChecker() {
|
|||||||
delegateDescription,
|
delegateDescription,
|
||||||
context
|
context
|
||||||
)
|
)
|
||||||
|
true
|
||||||
}
|
}
|
||||||
is ConeAmbiguityError -> {
|
is ConeAmbiguityError -> {
|
||||||
if (diagnostic.applicability.isSuccess) {
|
if (diagnostic.applicability.isSuccess) {
|
||||||
@@ -101,10 +112,28 @@ object FirDelegatedPropertyChecker : FirPropertyChecker() {
|
|||||||
} else {
|
} else {
|
||||||
reportInapplicableDiagnostics(diagnostic.applicability, diagnostic.candidates.map { it.symbol })
|
reportInapplicableDiagnostics(diagnostic.applicability, diagnostic.candidates.map { it.symbol })
|
||||||
}
|
}
|
||||||
|
true
|
||||||
}
|
}
|
||||||
is ConeInapplicableCandidateError -> {
|
is ConeInapplicableCandidateError -> {
|
||||||
reportInapplicableDiagnostics(diagnostic.applicability, listOf(diagnostic.candidate.symbol))
|
reportInapplicableDiagnostics(diagnostic.applicability, listOf(diagnostic.candidate.symbol))
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkReturnType(functionCall: FirFunctionCall) {
|
||||||
|
val returnType = functionCall.typeRef.coneType
|
||||||
|
val propertyType = declaration.returnTypeRef.coneType
|
||||||
|
if (!AbstractTypeChecker.isSubtypeOf(context.session.typeContext, returnType, propertyType)) {
|
||||||
|
reporter.reportOn(
|
||||||
|
delegate.source,
|
||||||
|
FirErrors.DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH,
|
||||||
|
"getValue",
|
||||||
|
propertyType,
|
||||||
|
returnType,
|
||||||
|
context
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
@@ -82,6 +82,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATED_PROPERT
|
|||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATE_SPECIAL_FUNCTION_AMBIGUITY
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATE_SPECIAL_FUNCTION_AMBIGUITY
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATE_SPECIAL_FUNCTION_MISSING
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATE_SPECIAL_FUNCTION_MISSING
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATION_IN_INTERFACE
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATION_IN_INTERFACE
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATION_SUPER_CALL_IN_ENUM_CONSTRUCTOR
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATION_SUPER_CALL_IN_ENUM_CONSTRUCTOR
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DEPRECATED_MODIFIER_PAIR
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DEPRECATED_MODIFIER_PAIR
|
||||||
@@ -913,6 +914,13 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
|
|||||||
TO_STRING,
|
TO_STRING,
|
||||||
SYMBOLS
|
SYMBOLS
|
||||||
)
|
)
|
||||||
|
map.put(
|
||||||
|
DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH,
|
||||||
|
"The ''{0}'' function of property delegate is expected to return ''{1}'', but returns ''{2}''",
|
||||||
|
TO_STRING,
|
||||||
|
RENDER_TYPE,
|
||||||
|
RENDER_TYPE,
|
||||||
|
)
|
||||||
|
|
||||||
// Type alias
|
// Type alias
|
||||||
map.put(TOPLEVEL_TYPEALIASES_ONLY, "Nested and local type aliases are not supported")
|
map.put(TOPLEVEL_TYPEALIASES_ONLY, "Nested and local type aliases are not supported")
|
||||||
|
|||||||
compiler/testData/diagnostics/tests/delegatedProperty/inference/differentDelegatedExpressions.fir.kt
Vendored
+2
-2
@@ -12,8 +12,8 @@ class A(outer: Outer) {
|
|||||||
|
|
||||||
var b: String by foo(getMyProperty())
|
var b: String by foo(getMyProperty())
|
||||||
var r: String by foo(outer.getContainer().getMyProperty())
|
var r: String by foo(outer.getContainer().getMyProperty())
|
||||||
var e: String by <!DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE!>+ foo(getMyProperty())<!>
|
var e: String by <!DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE, DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH!>+ foo(getMyProperty())<!>
|
||||||
var f: String by <!DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE!>foo(getMyProperty()) - 1<!>
|
var f: String by <!DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE, DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH!>foo(getMyProperty()) - 1<!>
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <A, B> foo(a: Any?) = MyProperty<A, B>()
|
fun <A, B> foo(a: Any?) = MyProperty<A, B>()
|
||||||
|
|||||||
Vendored
+1
-1
@@ -25,4 +25,4 @@ class C<R>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var c1: Int by C()
|
var c1: Int by C()
|
||||||
var c2: Int by <!DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE!>C<Number>()<!>
|
var c2: Int by <!DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE, DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH!>C<Number>()<!>
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
val c: Int by Delegate()
|
val c: Int by <!DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH!>Delegate()<!>
|
||||||
|
|
||||||
class Delegate {
|
class Delegate {
|
||||||
operator fun getValue(t: Any?, p: KProperty<*>): String {
|
operator fun getValue(t: Any?, p: KProperty<*>): String {
|
||||||
|
|||||||
+9
@@ -1985,6 +1985,15 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
|||||||
token,
|
token,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
add(FirErrors.DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH) { firDiagnostic ->
|
||||||
|
DelegateSpecialFunctionReturnTypeMismatchImpl(
|
||||||
|
firDiagnostic.a,
|
||||||
|
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.b),
|
||||||
|
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.c),
|
||||||
|
firDiagnostic as FirPsiDiagnostic<*>,
|
||||||
|
token,
|
||||||
|
)
|
||||||
|
}
|
||||||
add(FirErrors.UNDERSCORE_IS_RESERVED) { firDiagnostic ->
|
add(FirErrors.UNDERSCORE_IS_RESERVED) { firDiagnostic ->
|
||||||
UnderscoreIsReservedImpl(
|
UnderscoreIsReservedImpl(
|
||||||
firDiagnostic as FirPsiDiagnostic<*>,
|
firDiagnostic as FirPsiDiagnostic<*>,
|
||||||
|
|||||||
+7
@@ -1388,6 +1388,13 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
|
|||||||
abstract val candidates: List<KtSymbol>
|
abstract val candidates: List<KtSymbol>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract class DelegateSpecialFunctionReturnTypeMismatch : KtFirDiagnostic<KtExpression>() {
|
||||||
|
override val diagnosticClass get() = DelegateSpecialFunctionReturnTypeMismatch::class
|
||||||
|
abstract val delegateFunction: String
|
||||||
|
abstract val expected: KtType
|
||||||
|
abstract val actual: KtType
|
||||||
|
}
|
||||||
|
|
||||||
abstract class UnderscoreIsReserved : KtFirDiagnostic<KtExpression>() {
|
abstract class UnderscoreIsReserved : KtFirDiagnostic<KtExpression>() {
|
||||||
override val diagnosticClass get() = UnderscoreIsReserved::class
|
override val diagnosticClass get() = UnderscoreIsReserved::class
|
||||||
}
|
}
|
||||||
|
|||||||
+10
@@ -2253,6 +2253,16 @@ internal class DelegateSpecialFunctionNoneApplicableImpl(
|
|||||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class DelegateSpecialFunctionReturnTypeMismatchImpl(
|
||||||
|
override val delegateFunction: String,
|
||||||
|
override val expected: KtType,
|
||||||
|
override val actual: KtType,
|
||||||
|
firDiagnostic: FirPsiDiagnostic<*>,
|
||||||
|
override val token: ValidityToken,
|
||||||
|
) : KtFirDiagnostic.DelegateSpecialFunctionReturnTypeMismatch(), KtAbstractFirDiagnostic<KtExpression> {
|
||||||
|
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||||
|
}
|
||||||
|
|
||||||
internal class UnderscoreIsReservedImpl(
|
internal class UnderscoreIsReservedImpl(
|
||||||
firDiagnostic: FirPsiDiagnostic<*>,
|
firDiagnostic: FirPsiDiagnostic<*>,
|
||||||
override val token: ValidityToken,
|
override val token: ValidityToken,
|
||||||
|
|||||||
Reference in New Issue
Block a user