FIR checkers: report VAL_REASSIGNMENT for assignment operators

Currently VAL_REASSIGNMENT are only reported on direct assignments.
Reassignments in the form of, for example, `+=` are reported as
`VARIABLE_EXPECTED`, which differs from FE1.0.
This commit is contained in:
Tianyu Geng
2021-03-30 16:23:39 -07:00
committed by Mikhail Glukhikh
parent b87a943efd
commit fc8d0e3ee0
17 changed files with 57 additions and 38 deletions
@@ -6,6 +6,6 @@ FILE: fieldPlusAssign.kt
}
public final val y: R|kotlin/Int| = Int(1)
public get(): R|kotlin/Int| {
<Variable expected># = F|/y|.R|kotlin/Int.plus|(Int(1))
<Re-assigning a val variable># = F|/y|.R|kotlin/Int.plus|(Int(1))
^ Int(1)
}
@@ -5,6 +5,6 @@ var x: Int = 1
val y: Int = 1
get() {
<!VARIABLE_EXPECTED!>field<!> += 1
<!VAL_REASSIGNMENT_VIA_BACKING_FIELD_ERROR!>field<!> += 1
return 1
}
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.name.Name
@@ -483,13 +484,13 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
parameter<FirPropertySymbol>("variable")
}
val VAL_REASSIGNMENT by error<FirSourceElement, KtExpression> {
parameter<FirPropertySymbol>("variable")
parameter<FirVariableSymbol<*>>("variable")
}
val VAL_REASSIGNMENT_VIA_BACKING_FIELD by warning<FirSourceElement, KtExpression> {
parameter<FirPropertySymbol>("variable")
parameter<FirPropertySymbol>("property")
}
val VAL_REASSIGNMENT_VIA_BACKING_FIELD_ERROR by error<FirSourceElement, KtExpression> {
parameter<FirPropertySymbol>("variable")
parameter<FirPropertySymbol>("property")
}
val WRONG_INVOCATION_KIND by warning<FirSourceElement, PsiElement> {
parameter<AbstractFirBasedSymbol<*>>("declaration")
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.name.Name
@@ -296,7 +297,7 @@ object FirErrors {
// Control flow diagnostics
val UNINITIALIZED_VARIABLE by error1<FirSourceElement, KtSimpleNameExpression, FirPropertySymbol>()
val VAL_REASSIGNMENT by error1<FirSourceElement, KtExpression, FirPropertySymbol>()
val VAL_REASSIGNMENT by error1<FirSourceElement, KtExpression, FirVariableSymbol<*>>()
val VAL_REASSIGNMENT_VIA_BACKING_FIELD by warning1<FirSourceElement, KtExpression, FirPropertySymbol>()
val VAL_REASSIGNMENT_VIA_BACKING_FIELD_ERROR by error1<FirSourceElement, KtExpression, FirPropertySymbol>()
val WRONG_INVOCATION_KIND by warning3<FirSourceElement, PsiElement, AbstractFirBasedSymbol<*>, EventOccurrencesRange, EventOccurrencesRange>()
@@ -14,12 +14,12 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.FIR
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.FQ_NAMES_IN_TYPES
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.NAME
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.NULLABLE_STRING
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.PROPERTY_NAME
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.RENDER_CLASS_OR_OBJECT
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.RENDER_TYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.SYMBOL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.SYMBOLS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.TO_STRING
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.VARIABLE_NAME
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.VISIBILITY
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.WHEN_MISSING_CASES
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ABSENCE_OF_PRIMARY_CONSTRUCTOR_FOR_INLINE_CLASS
@@ -646,10 +646,10 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
)
// Control flow diagnostics
map.put(UNINITIALIZED_VARIABLE, "{0} must be initialized before access", PROPERTY_NAME)
map.put(VAL_REASSIGNMENT, "Val cannot be reassigned", PROPERTY_NAME)
map.put(VAL_REASSIGNMENT_VIA_BACKING_FIELD, "Reassignment of read-only property via backing field is deprecated", PROPERTY_NAME)
map.put(VAL_REASSIGNMENT_VIA_BACKING_FIELD_ERROR, "Reassignment of read-only property via backing field", PROPERTY_NAME)
map.put(UNINITIALIZED_VARIABLE, "{0} must be initialized before access", VARIABLE_NAME)
map.put(VAL_REASSIGNMENT, "Val cannot be reassigned", VARIABLE_NAME)
map.put(VAL_REASSIGNMENT_VIA_BACKING_FIELD, "Reassignment of read-only property via backing field is deprecated", VARIABLE_NAME)
map.put(VAL_REASSIGNMENT_VIA_BACKING_FIELD_ERROR, "Reassignment of read-only property via backing field", VARIABLE_NAME)
map.put(
WRONG_INVOCATION_KIND,
"{2} wrong invocation kind: given {3} case, but {4} case is possible",
@@ -14,10 +14,7 @@ import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.renderWithType
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.render
@@ -43,7 +40,7 @@ object FirDiagnosticRenderers {
element.toString()
}
val PROPERTY_NAME = Renderer { symbol: FirPropertySymbol ->
val VARIABLE_NAME = Renderer { symbol: FirVariableSymbol<*> ->
symbol.fir.name.asString()
}
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.fir.resolve.calls.NamedArgumentNotAllowed
import org.jetbrains.kotlin.fir.resolve.calls.ResolutionDiagnostic
import org.jetbrains.kotlin.fir.resolve.calls.VarargArgumentOutsideParentheses
import org.jetbrains.kotlin.fir.resolve.diagnostics.*
import org.jetbrains.kotlin.fir.symbols.impl.FirBackingFieldSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
@@ -39,6 +40,10 @@ private fun ConeDiagnostic.toFirDiagnostic(
}
is ConeOperatorAmbiguityError -> FirErrors.ASSIGN_OPERATOR_AMBIGUITY.on(source, this.candidates)
is ConeVariableExpectedError -> FirErrors.VARIABLE_EXPECTED.on(source)
is ConeValReassignmentError -> when (val symbol = this.variable) {
is FirBackingFieldSymbol -> FirErrors.VAL_REASSIGNMENT_VIA_BACKING_FIELD_ERROR.on(source, symbol.fir.symbol)
else -> FirErrors.VAL_REASSIGNMENT.on(source, symbol)
}
is ConeTypeMismatchError -> FirErrors.TYPE_MISMATCH.on(qualifiedAccessSource ?: source, this.expectedType, this.actualType)
is ConeUnexpectedTypeArgumentsError -> FirErrors.TYPE_ARGUMENTS_NOT_ALLOWED.on(this.source.safeAs() ?: source)
is ConeIllegalAnnotationError -> FirErrors.NOT_AN_ANNOTATION_CLASS.on(source, this.name.asString())
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
@@ -59,6 +60,10 @@ class ConeVariableExpectedError : ConeDiagnostic() {
override val reason: String get() = "Variable expected"
}
class ConeValReassignmentError(val variable: FirVariableSymbol<*>) : ConeDiagnostic() {
override val reason: String get() = "Re-assigning a val variable"
}
class ConeTypeMismatchError(val expectedType: ConeKotlinType, val actualType: ConeKotlinType) : ConeDiagnostic() {
override val reason: String
get() = "Type mismatch. Expected: $expectedType, Actual: $actualType"
@@ -385,7 +385,8 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
val operatorIsError = operatorCallReference?.isError ?: true
val lhsReference = leftArgument.toResolvedCallableReference()
val lhsVariable = (lhsReference?.resolvedSymbol as? FirVariableSymbol<*>)?.fir
val lhsSymbol = lhsReference?.resolvedSymbol as? FirVariableSymbol<*>
val lhsVariable = lhsSymbol?.fir
val lhsIsVar = lhsVariable?.isVar == true
return when {
operatorIsError || (!lhsIsVar && !assignIsError) -> {
@@ -409,7 +410,7 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
leftArgument.calleeReference.source
else -> leftArgument.source
}
diagnostic = ConeVariableExpectedError()
diagnostic = if (lhsSymbol == null) ConeVariableExpectedError() else ConeValReassignmentError(lhsSymbol)
}
}
(leftArgument as? FirQualifiedAccess)?.let {
@@ -103,7 +103,7 @@ class Test() {
a += 34
(l@ a) += 34
<!VARIABLE_EXPECTED!>b<!> += 34
<!VAL_REASSIGNMENT!>b<!> += 34
a++
(l@ a)++
@@ -113,9 +113,9 @@ class Test() {
fun testVariables1() {
val b: Int = 34
(l@ <!VARIABLE_EXPECTED!>b<!>) += 34
(l@ <!VAL_REASSIGNMENT!>b<!>) += 34
//repeat for b
(<!VARIABLE_EXPECTED!>b<!>) += 3
(<!VAL_REASSIGNMENT!>b<!>) += 3
}
fun testArrays(a: Array<Int>, ab: Ab) {
@@ -89,18 +89,18 @@ fun t3() {
fun t4() {
val x = 1
<!VARIABLE_EXPECTED!>x<!> += 2
<!VAL_REASSIGNMENT!>x<!> += 2
val y = 3
<!VARIABLE_EXPECTED!>y<!> *= 4
<!VAL_REASSIGNMENT!>y<!> *= 4
var z = 5
z -= y
}
fun t5() {
for (i in 0..2) {
<!VARIABLE_EXPECTED!>i<!> += 1
<!VAL_REASSIGNMENT!>i<!> += 1
fun t5() {
<!VARIABLE_EXPECTED!>i<!> += 3
<!VAL_REASSIGNMENT!>i<!> += 3
}
}
}
@@ -213,7 +213,7 @@ class LocalValsVsProperties(val a: Int, w: Int) : Open(a, w) {
var xx = w
var yy : Int
init {
<!VARIABLE_EXPECTED!>w<!> += 1
<!VAL_REASSIGNMENT!>w<!> += 1
yy = w
}
}
@@ -68,16 +68,16 @@ class Test1 {
init {
inlineMe {
<!VARIABLE_EXPECTED!>a<!> += "allowed"
<!VAL_REASSIGNMENT!>a<!> += "allowed"
}
crossinlineMe {
<!VARIABLE_EXPECTED!>b<!> += "not allowed"
<!VAL_REASSIGNMENT!>b<!> += "not allowed"
}
noinlineMe {
<!VARIABLE_EXPECTED!>c<!> += "not allowed"
<!VAL_REASSIGNMENT!>c<!> += "not allowed"
}
notinline {
<!VARIABLE_EXPECTED!>d<!> += "not allowed"
<!VAL_REASSIGNMENT!>d<!> += "not allowed"
}
}
}
@@ -31,7 +31,7 @@ fun box() : String {
if (c1 != 1) {
return "2"
}
<!VARIABLE_EXPECTED!>a<!> *= 3 // a = a * 3, shouldn't be able to do this on val
<!VAL_REASSIGNMENT!>a<!> *= 3 // a = a * 3, shouldn't be able to do this on val
if (c0 != 2) {
return "3"
}
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.idea.frontend.api.symbols.*
@@ -174,6 +175,11 @@ private object FirToKtConversionCreator {
KtVariableSymbol::class.createType(),
importsToAdd = listOf("org.jetbrains.kotlin.fir.declarations.FirProperty")
),
FirVariableSymbol::class to HLFunctionCallConversion(
"firSymbolBuilder.variableLikeBuilder.buildVariableLikeSymbol({0}.fir)",
KtVariableLikeSymbol::class.createType(),
importsToAdd = listOf("org.jetbrains.kotlin.fir.declarations.FirVariable")
),
)
private val allowedTypesWithoutTypeParams = setOf(
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirTypeParameter
import org.jetbrains.kotlin.fir.declarations.FirVariable
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.psi.KtArrayAccessExpression
import org.jetbrains.kotlin.psi.KtClassOrObject
@@ -1346,7 +1347,7 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
}
add(FirErrors.VAL_REASSIGNMENT) { firDiagnostic ->
ValReassignmentImpl(
firSymbolBuilder.variableLikeBuilder.buildVariableSymbol(firDiagnostic.a.fir as FirProperty),
firSymbolBuilder.variableLikeBuilder.buildVariableLikeSymbol(firDiagnostic.a.fir),
firDiagnostic as FirPsiDiagnostic<*>,
token,
)
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.idea.frontend.api.symbols.KtCallableSymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtClassLikeSymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtSymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtTypeParameterSymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtVariableLikeSymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtVariableSymbol
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
@@ -951,17 +952,17 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
abstract class ValReassignment : KtFirDiagnostic<KtExpression>() {
override val diagnosticClass get() = ValReassignment::class
abstract val variable: KtVariableSymbol
abstract val variable: KtVariableLikeSymbol
}
abstract class ValReassignmentViaBackingField : KtFirDiagnostic<KtExpression>() {
override val diagnosticClass get() = ValReassignmentViaBackingField::class
abstract val variable: KtVariableSymbol
abstract val property: KtVariableSymbol
}
abstract class ValReassignmentViaBackingFieldError : KtFirDiagnostic<KtExpression>() {
override val diagnosticClass get() = ValReassignmentViaBackingFieldError::class
abstract val variable: KtVariableSymbol
abstract val property: KtVariableSymbol
}
abstract class WrongInvocationKind : KtFirDiagnostic<PsiElement>() {
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.idea.frontend.api.symbols.KtCallableSymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtClassLikeSymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtSymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtTypeParameterSymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtVariableLikeSymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtVariableSymbol
import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
@@ -1536,7 +1537,7 @@ internal class UninitializedVariableImpl(
}
internal class ValReassignmentImpl(
override val variable: KtVariableSymbol,
override val variable: KtVariableLikeSymbol,
firDiagnostic: FirPsiDiagnostic<*>,
override val token: ValidityToken,
) : KtFirDiagnostic.ValReassignment(), KtAbstractFirDiagnostic<KtExpression> {
@@ -1544,7 +1545,7 @@ internal class ValReassignmentImpl(
}
internal class ValReassignmentViaBackingFieldImpl(
override val variable: KtVariableSymbol,
override val property: KtVariableSymbol,
firDiagnostic: FirPsiDiagnostic<*>,
override val token: ValidityToken,
) : KtFirDiagnostic.ValReassignmentViaBackingField(), KtAbstractFirDiagnostic<KtExpression> {
@@ -1552,7 +1553,7 @@ internal class ValReassignmentViaBackingFieldImpl(
}
internal class ValReassignmentViaBackingFieldErrorImpl(
override val variable: KtVariableSymbol,
override val property: KtVariableSymbol,
firDiagnostic: FirPsiDiagnostic<*>,
override val token: ValidityToken,
) : KtFirDiagnostic.ValReassignmentViaBackingFieldError(), KtAbstractFirDiagnostic<KtExpression> {