[FIR] Add lValueTypeRef to FirVariableAssignment, fix tests with ASSIGNMENT_TYPE_MISMATCH

This commit is contained in:
Ivan Kochurkin
2021-05-13 17:06:04 +03:00
committed by TeamCityServer
parent e57108d4e8
commit c3b8f3e859
28 changed files with 127 additions and 87 deletions
@@ -2,7 +2,7 @@ class SomeClass {
var foo: Int = 0
set(value: <!WRONG_SETTER_PARAMETER_TYPE!>String<!>){
field = value
field = <!ASSIGNMENT_TYPE_MISMATCH!>value<!>
}
}
@@ -18,5 +18,5 @@ FILE: hashSet.kt
public final fun foo(): R|kotlin/Unit| {
lvar c: R|kotlin/collections/MutableSet<kotlin/String>?| = Null(null)
R|<local>/c| = R|java/util/HashSet.HashSet|<R|ft<kotlin/String, kotlin/String?>|>()
R|<local>/c|!!.R|/d| = R|/produce|<R|T?|>()
R|<local>/c|!!.R|/d| = R|/produce|<R|kotlin/String?|>()
}
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.overrideModifier
import org.jetbrains.kotlin.fir.analysis.diagnostics.visibilityModifier
import org.jetbrains.kotlin.fir.analysis.getChild
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.expressions.impl.FirEmptyExpressionBlock
@@ -521,16 +522,34 @@ private val FirSimpleFunction.matchesToStringSignature: Boolean
get() = valueParameters.isEmpty()
fun checkTypeMismatch(
lValueType: ConeKotlinType,
lValueOriginalType: ConeKotlinType,
rValue: FirExpression,
context: CheckerContext,
source: FirSourceElement,
reporter: DiagnosticReporter,
isInitializer: Boolean
) {
val rValueType = rValue.typeRef.coneType
var lValueType = lValueOriginalType
var rValueType = rValue.typeRef.coneType
val typeContext = context.session.typeContext
val diagnosticFactory = when {
isInitializer -> {
FirErrors.INITIALIZER_TYPE_MISMATCH
}
source.kind is FirFakeSourceElementKind.DesugaredIncrementOrDecrement -> {
if (!lValueType.isNullable && rValueType.isNullable) {
val tempType = rValueType
rValueType = lValueType
lValueType = tempType
}
FirErrors.RESULT_TYPE_MISMATCH
}
else -> {
FirErrors.ASSIGNMENT_TYPE_MISMATCH
}
}
if (!isSubtypeForTypeMismatch(typeContext, subtype = rValueType, supertype = lValueType)) {
if (rValueType is ConeClassLikeType &&
rValueType.lookupTag.classId == StandardClassIds.Int &&
@@ -548,15 +567,6 @@ fun checkTypeMismatch(
if (rValue.isNullLiteral && lValueType.nullability == ConeNullability.NOT_NULL) {
reporter.reportOn(rValue.source, FirErrors.NULL_FOR_NONNULL_TYPE, context)
} else {
val diagnosticFactory = when {
isInitializer ->
FirErrors.INITIALIZER_TYPE_MISMATCH
source.kind is FirFakeSourceElementKind.DesugaredIncrementOrDecrement ->
FirErrors.RESULT_TYPE_MISMATCH
else ->
FirErrors.ASSIGNMENT_TYPE_MISMATCH
}
reporter.report(diagnosticFactory.on(source, lValueType, rValueType), context)
}
}
@@ -9,9 +9,9 @@ import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.fir.FirRealSourceElementKind
import org.jetbrains.kotlin.fir.analysis.checkers.checkTypeMismatch
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.isComponentCall
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.expressions.FirComponentCall
import org.jetbrains.kotlin.fir.types.coneType
object FirInitializerTypeMismatchChecker : FirPropertyChecker() {
@@ -9,17 +9,12 @@ import org.jetbrains.kotlin.fir.analysis.checkers.checkTypeMismatch
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.types.coneType
object FirAssignmentTypeMismatchChecker : FirVariableAssignmentChecker() {
override fun check(expression: FirVariableAssignment, context: CheckerContext, reporter: DiagnosticReporter) {
val source = expression.rValue.source ?: return
val lValueType =
((expression.lValue as? FirResolvedNamedReference)?.resolvedSymbol as? FirPropertySymbol)?.fir?.returnTypeRef?.coneType
?: return
checkTypeMismatch(lValueType, expression.rValue, context, source, reporter, false)
val coneType = expression.lValueTypeRef.coneType
checkTypeMismatch(coneType, expression.rValue, context, source, reporter, false)
}
}
}
@@ -43,6 +43,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ARGUMENT_PASSED_T
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ARGUMENT_TYPE_MISMATCH
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ARRAY_EQUALITY_OPERATOR_CAN_BE_REPLACED_WITH_EQUALS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ASSIGNED_VALUE_IS_NEVER_READ
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ASSIGNMENT_TYPE_MISMATCH
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ASSIGN_OPERATOR_AMBIGUITY
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.BACKING_FIELD_IN_INTERFACE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER
@@ -248,6 +249,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REIFIED_TYPE_PARA
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REPEATED_BOUND
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REPEATED_MODIFIER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RESERVED_MEMBER_INSIDE_INLINE_CLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RESULT_TYPE_MISMATCH
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RETURN_NOT_ALLOWED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RETURN_TYPE_MISMATCH
@@ -522,7 +524,14 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
map.put(NAMED_PARAMETER_NOT_FOUND, "Cannot find a parameter with this name: {0}", TO_STRING)
map.put(MANY_LAMBDA_EXPRESSION_ARGUMENTS, "Only one lambda expression is allowed outside a parenthesized argument list")
map.put(ARGUMENT_TYPE_MISMATCH, "Argument type mismatch: actual type is {1} but {0} was expected", TO_STRING, TO_STRING)
map.put(ARGUMENT_TYPE_MISMATCH, "Argument type mismatch: actual type is {1} but {0} was expected", RENDER_TYPE, RENDER_TYPE)
map.put(ASSIGNMENT_TYPE_MISMATCH, "Assignment type mismatch: actual type is {1} but {0} was expected", RENDER_TYPE, RENDER_TYPE)
map.put(
RESULT_TYPE_MISMATCH,
"Function return type mismatch: actual type is {1} but {0} was expected",
RENDER_TYPE,
RENDER_TYPE
)
// Ambiguity
map.put(OVERLOAD_RESOLUTION_AMBIGUITY, "Overload resolution ambiguity between candidates: {0}", SYMBOLS)
@@ -834,7 +843,10 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
)
map.put(INITIALIZER_TYPE_MISMATCH, "Initializer type mismatch: expected {0}, actual {1}", RENDER_TYPE, RENDER_TYPE)
map.put(GETTER_VISIBILITY_DIFFERS_FROM_PROPERTY_VISIBILITY, "Getter visibility must be the same as property visibility")
map.put(SETTER_VISIBILITY_INCONSISTENT_WITH_PROPERTY_VISIBILITY, "Setter visibility must be the same or less permissive than property visibility")
map.put(
SETTER_VISIBILITY_INCONSISTENT_WITH_PROPERTY_VISIBILITY,
"Setter visibility must be the same or less permissive than property visibility"
)
map.put(WRONG_SETTER_RETURN_TYPE, "Setter return type must be Unit")
map.put(
WRONG_GETTER_RETURN_TYPE,
@@ -469,7 +469,6 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
if (unwrappedArgument == null) {
return buildErrorExpression {
source = unwrappedArgument
diagnostic = ConeSimpleDiagnostic("Inc/dec without operand", DiagnosticKind.Syntax)
}
}
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.fir.declarations.builder.buildValueParameter
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirResolvable
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
import org.jetbrains.kotlin.fir.resolve.calls.Candidate
import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate
@@ -56,7 +57,12 @@ class FirCallCompleter(
where T : FirResolvable, T : FirStatement {
val typeRef = components.typeFromCallee(call)
if (call is FirVariableAssignment) {
call.replaceLValueTypeRef(typeRef)
}
val reference = call.calleeReference as? FirNamedReferenceWithCandidate ?: return CompletionResult(call, true)
val candidate = reference.candidate
val initialType = components.initialTypeOfCandidate(candidate, call)
@@ -66,7 +66,7 @@ class FirCallCompletionResultsWriterTransformer(
}
}
private fun <T : FirQualifiedAccessExpression> prepareQualifiedTransform(
private fun <T : FirQualifiedAccess> prepareQualifiedTransform(
qualifiedAccessExpression: T, calleeReference: FirNamedReferenceWithCandidate
): T {
val subCandidate = calleeReference.candidate
@@ -111,7 +111,13 @@ class FirCallCompletionResultsWriterTransformer(
)
.transformDispatchReceiver(StoreReceiver, subCandidate.dispatchReceiverExpression())
.transformExtensionReceiver(StoreReceiver, subCandidate.extensionReceiverExpression()) as T
result.replaceTypeRef(typeRef)
if (result is FirQualifiedAccessExpression) {
result.replaceTypeRef(typeRef)
} else if (result is FirVariableAssignment) {
result.replaceLValueTypeRef(typeRef)
}
if (declaration !is FirErrorFunction) {
result.replaceTypeArguments(typeArguments)
}
@@ -354,13 +360,19 @@ class FirCallCompletionResultsWriterTransformer(
): FirStatement {
val calleeReference = variableAssignment.calleeReference as? FirNamedReferenceWithCandidate
?: return variableAssignment
val typeArguments = computeTypeArguments(variableAssignment, calleeReference.candidate)
// Initialize lValueTypeRef
val qualifiedTransform = prepareQualifiedTransform(variableAssignment, calleeReference)
val lValueTypeRef = qualifiedTransform.lValueTypeRef as FirResolvedTypeRef
val resultLValueType = lValueTypeRef.substituteTypeRef(calleeReference.candidate)
resultLValueType.ensureResolvedTypeDeclaration(session)
variableAssignment.replaceLValueTypeRef(resultLValueType)
session.lookupTracker?.recordTypeResolveAsLookup(resultLValueType, variableAssignment.lValue.source, null)
return variableAssignment.transformCalleeReference(
StoreCalleeReference,
calleeReference.toResolvedReference(),
).apply {
replaceTypeArguments(typeArguments)
}
)
}
private inner class TypeUpdaterForDelegateArguments : FirTransformer<Any?>() {
@@ -677,8 +677,7 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
val resolvedAssignment = callResolver.resolveVariableAccessAndSelectCandidate(variableAssignment)
val result = if (resolvedAssignment is FirVariableAssignment) {
val completeAssignment = callCompleter.completeCall(resolvedAssignment, noExpectedType).result // TODO: check
val expectedType = components.typeFromCallee(completeAssignment)
completeAssignment.transformRValue(transformer, withExpectedType(expectedType))
completeAssignment.transformRValue(transformer, withExpectedType(variableAssignment.lValueTypeRef))
} else {
// This can happen in erroneous code only
resolvedAssignment
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.fir.FirPureAbstractElement
import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.references.FirReference
import org.jetbrains.kotlin.fir.types.FirTypeProjection
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.fir.visitors.*
import org.jetbrains.kotlin.fir.FirImplementationDetail
@@ -27,6 +28,7 @@ abstract class FirVariableAssignment : FirPureAbstractElement(), FirQualifiedAcc
abstract override val extensionReceiver: FirExpression
abstract override val source: FirSourceElement?
abstract val lValue: FirReference
abstract val lValueTypeRef: FirTypeRef
abstract val rValue: FirExpression
override fun <R, D> accept(visitor: FirVisitor<R, D>, data: D): R = visitor.visitVariableAssignment(this, data)
@@ -44,6 +46,8 @@ abstract class FirVariableAssignment : FirPureAbstractElement(), FirQualifiedAcc
@FirImplementationDetail
abstract override fun replaceSource(newSource: FirSourceElement?)
abstract fun replaceLValueTypeRef(newLValueTypeRef: FirTypeRef)
abstract override fun <D> transformCalleeReference(transformer: FirTransformer<D>, data: D): FirVariableAssignment
abstract override fun <D> transformAnnotations(transformer: FirTransformer<D>, data: D): FirVariableAssignment
@@ -18,6 +18,8 @@ import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression
import org.jetbrains.kotlin.fir.expressions.impl.FirVariableAssignmentImpl
import org.jetbrains.kotlin.fir.references.FirReference
import org.jetbrains.kotlin.fir.types.FirTypeProjection
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.fir.types.impl.FirImplicitTypeRefImpl
import org.jetbrains.kotlin.fir.visitors.*
/*
@@ -11,6 +11,8 @@ import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
import org.jetbrains.kotlin.fir.references.FirReference
import org.jetbrains.kotlin.fir.types.FirTypeProjection
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.fir.types.impl.FirImplicitTypeRefImpl
import org.jetbrains.kotlin.fir.visitors.*
import org.jetbrains.kotlin.fir.FirImplementationDetail
@@ -34,6 +36,7 @@ internal class FirVariableAssignmentImpl(
set(value) {
calleeReference = value
}
override var lValueTypeRef: FirTypeRef = FirImplicitTypeRefImpl(null)
override fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D) {
calleeReference.accept(visitor, data)
@@ -46,6 +49,7 @@ internal class FirVariableAssignmentImpl(
if (extensionReceiver !== explicitReceiver && extensionReceiver !== dispatchReceiver) {
extensionReceiver.accept(visitor, data)
}
lValueTypeRef.accept(visitor, data)
rValue.accept(visitor, data)
}
@@ -60,6 +64,7 @@ internal class FirVariableAssignmentImpl(
if (extensionReceiver !== explicitReceiver && extensionReceiver !== dispatchReceiver) {
extensionReceiver = extensionReceiver.transform(transformer, data)
}
lValueTypeRef = lValueTypeRef.transform(transformer, data)
transformRValue(transformer, data)
return this
}
@@ -116,4 +121,8 @@ internal class FirVariableAssignmentImpl(
override fun replaceSource(newSource: FirSourceElement?) {
source = newSource
}
override fun replaceLValueTypeRef(newLValueTypeRef: FirTypeRef) {
lValueTypeRef = newLValueTypeRef
}
}
@@ -495,5 +495,14 @@ object ImplementationConfigurator : AbstractFirTreeImplementationConfigurator()
default(it, "FirImplicitTypeRefImpl(null)")
useTypes(implicitTypeRefType)
}
configureFieldInAllImplementations(
field = "lValueTypeRef",
implementationPredicate = { it.type in "FirVariableAssignmentImpl" },
fieldPredicate = { it.defaultValueInImplementation == null }
) {
default(it, "FirImplicitTypeRefImpl(null)")
useTypes(implicitTypeRefType)
}
}
}
@@ -521,6 +521,7 @@ object NodeConfigurator : AbstractFieldConfigurator<FirTreeBuilder>(FirTreeBuild
variableAssignment.configure {
+field("lValue", reference)
+field("lValueTypeRef", typeRef).withReplace()
+field("rValue", expression).withTransform()
}