FIR: support separate ASSIGNMENT_IN_EXPRESSION_CONTEXT

This commit is contained in:
Mikhail Glukhikh
2021-04-16 11:06:43 +03:00
parent 9894b97058
commit 6b95bcdbdb
7 changed files with 34 additions and 7 deletions
@@ -50,6 +50,7 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
val ILLEGAL_CONST_EXPRESSION by error<PsiElement>() val ILLEGAL_CONST_EXPRESSION by error<PsiElement>()
val ILLEGAL_UNDERSCORE by error<PsiElement>() val ILLEGAL_UNDERSCORE by error<PsiElement>()
val EXPRESSION_EXPECTED by error<PsiElement>(PositioningStrategy.SELECTOR_BY_QUALIFIED) val EXPRESSION_EXPECTED by error<PsiElement>(PositioningStrategy.SELECTOR_BY_QUALIFIED)
val ASSIGNMENT_IN_EXPRESSION_CONTEXT by error<FirSourceElement, KtBinaryExpression>()
val BREAK_OR_CONTINUE_OUTSIDE_A_LOOP by error<PsiElement>() val BREAK_OR_CONTINUE_OUTSIDE_A_LOOP by error<PsiElement>()
val NOT_A_LOOP_LABEL by error<PsiElement>() val NOT_A_LOOP_LABEL by error<PsiElement>()
val VARIABLE_EXPECTED by error<PsiElement>() val VARIABLE_EXPECTED by error<PsiElement>()
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtArrayAccessExpression import org.jetbrains.kotlin.psi.KtArrayAccessExpression
import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtDeclaration
@@ -76,6 +77,7 @@ object FirErrors {
val ILLEGAL_CONST_EXPRESSION by error0<PsiElement>() val ILLEGAL_CONST_EXPRESSION by error0<PsiElement>()
val ILLEGAL_UNDERSCORE by error0<PsiElement>() val ILLEGAL_UNDERSCORE by error0<PsiElement>()
val EXPRESSION_EXPECTED by error0<PsiElement>(SourceElementPositioningStrategies.SELECTOR_BY_QUALIFIED) val EXPRESSION_EXPECTED by error0<PsiElement>(SourceElementPositioningStrategies.SELECTOR_BY_QUALIFIED)
val ASSIGNMENT_IN_EXPRESSION_CONTEXT by error0<FirSourceElement, KtBinaryExpression>()
val BREAK_OR_CONTINUE_OUTSIDE_A_LOOP by error0<PsiElement>() val BREAK_OR_CONTINUE_OUTSIDE_A_LOOP by error0<PsiElement>()
val NOT_A_LOOP_LABEL by error0<PsiElement>() val NOT_A_LOOP_LABEL by error0<PsiElement>()
val VARIABLE_EXPECTED by error0<PsiElement>() val VARIABLE_EXPECTED by error0<PsiElement>()
@@ -48,9 +48,9 @@ private fun ConeDiagnostic.toFirDiagnostic(
FirErrors.WRONG_NUMBER_OF_TYPE_ARGUMENTS.on(qualifiedAccessSource ?: source, this.desiredCount, this.type) FirErrors.WRONG_NUMBER_OF_TYPE_ARGUMENTS.on(qualifiedAccessSource ?: source, this.desiredCount, this.type)
is ConeNoTypeArgumentsOnRhsError -> is ConeNoTypeArgumentsOnRhsError ->
FirErrors.NO_TYPE_ARGUMENTS_ON_RHS.on(qualifiedAccessSource ?: source, this.desiredCount, this.type) FirErrors.NO_TYPE_ARGUMENTS_ON_RHS.on(qualifiedAccessSource ?: source, this.desiredCount, this.type)
is ConeSimpleDiagnostic -> when { is ConeSimpleDiagnostic -> when (source.kind) {
source.kind is FirFakeSourceElementKind -> null is FirFakeSourceElementKind -> null
else -> this.getFactory().on(qualifiedAccessSource ?: source) else -> this.getFactory(source).on(qualifiedAccessSource ?: source)
} }
is ConeInstanceAccessBeforeSuperCall -> FirErrors.INSTANCE_ACCESS_BEFORE_SUPER_CALL.on(source, this.target) is ConeInstanceAccessBeforeSuperCall -> FirErrors.INSTANCE_ACCESS_BEFORE_SUPER_CALL.on(source, this.target)
is ConeStubDiagnostic -> null is ConeStubDiagnostic -> null
@@ -158,7 +158,7 @@ private fun mapInapplicableCandidateError(
}.ifEmpty { listOf(FirErrors.INAPPLICABLE_CANDIDATE.on(source, diagnostic.candidate.symbol)) } }.ifEmpty { listOf(FirErrors.INAPPLICABLE_CANDIDATE.on(source, diagnostic.candidate.symbol)) }
} }
private fun ConeSimpleDiagnostic.getFactory(): FirDiagnosticFactory0<*> { private fun ConeSimpleDiagnostic.getFactory(source: FirSourceElement): FirDiagnosticFactory0<*> {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
return when (kind) { return when (kind) {
DiagnosticKind.Syntax -> FirErrors.SYNTAX DiagnosticKind.Syntax -> FirErrors.SYNTAX
@@ -174,7 +174,11 @@ private fun ConeSimpleDiagnostic.getFactory(): FirDiagnosticFactory0<*> {
DiagnosticKind.RecursionInImplicitTypes -> FirErrors.RECURSION_IN_IMPLICIT_TYPES DiagnosticKind.RecursionInImplicitTypes -> FirErrors.RECURSION_IN_IMPLICIT_TYPES
DiagnosticKind.Java -> FirErrors.ERROR_FROM_JAVA_RESOLUTION DiagnosticKind.Java -> FirErrors.ERROR_FROM_JAVA_RESOLUTION
DiagnosticKind.SuperNotAllowed -> FirErrors.SUPER_IS_NOT_AN_EXPRESSION DiagnosticKind.SuperNotAllowed -> FirErrors.SUPER_IS_NOT_AN_EXPRESSION
DiagnosticKind.ExpressionExpected -> FirErrors.EXPRESSION_EXPECTED DiagnosticKind.ExpressionExpected -> if (source.elementType == KtNodeTypes.BINARY_EXPRESSION) {
FirErrors.ASSIGNMENT_IN_EXPRESSION_CONTEXT
} else {
FirErrors.EXPRESSION_EXPECTED
}
DiagnosticKind.JumpOutsideLoop -> FirErrors.BREAK_OR_CONTINUE_OUTSIDE_A_LOOP DiagnosticKind.JumpOutsideLoop -> FirErrors.BREAK_OR_CONTINUE_OUTSIDE_A_LOOP
DiagnosticKind.NotLoopLabel -> FirErrors.NOT_A_LOOP_LABEL DiagnosticKind.NotLoopLabel -> FirErrors.NOT_A_LOOP_LABEL
DiagnosticKind.VariableExpected -> FirErrors.VARIABLE_EXPECTED DiagnosticKind.VariableExpected -> FirErrors.VARIABLE_EXPECTED
@@ -149,9 +149,9 @@ fun bar(a: Unit) {}
fun testStatementInExpressionContext() { fun testStatementInExpressionContext() {
var z = 34 var z = 34
val a1: Unit = <!EXPRESSION_EXPECTED!>z = 334<!> val a1: Unit = <!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>z = 334<!>
val f = for (i in 1..10) {} val f = for (i in 1..10) {}
if (true) return <!EXPRESSION_EXPECTED!>z = 34<!> if (true) return <!ASSIGNMENT_IN_EXPRESSION_CONTEXT!>z = 34<!>
return <!EXPRESSION_EXPECTED!>while (true) {}<!> return <!EXPRESSION_EXPECTED!>while (true) {}<!>
} }
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.fir.declarations.FirTypeParameter
import org.jetbrains.kotlin.fir.declarations.FirVariable import org.jetbrains.kotlin.fir.declarations.FirVariable
import org.jetbrains.kotlin.fir.psi import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.psi.KtArrayAccessExpression import org.jetbrains.kotlin.psi.KtArrayAccessExpression
import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtDeclaration
@@ -94,6 +95,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token, token,
) )
} }
add(FirErrors.ASSIGNMENT_IN_EXPRESSION_CONTEXT) { firDiagnostic ->
AssignmentInExpressionContextImpl(
firDiagnostic as FirPsiDiagnostic<*>,
token,
)
}
add(FirErrors.BREAK_OR_CONTINUE_OUTSIDE_A_LOOP) { firDiagnostic -> add(FirErrors.BREAK_OR_CONTINUE_OUTSIDE_A_LOOP) { firDiagnostic ->
BreakOrContinueOutsideALoopImpl( BreakOrContinueOutsideALoopImpl(
firDiagnostic as FirPsiDiagnostic<*>, firDiagnostic as FirPsiDiagnostic<*>,
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.idea.frontend.api.types.KtType
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtArrayAccessExpression import org.jetbrains.kotlin.psi.KtArrayAccessExpression
import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtDeclaration
@@ -87,6 +88,10 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = ExpressionExpected::class override val diagnosticClass get() = ExpressionExpected::class
} }
abstract class AssignmentInExpressionContext : KtFirDiagnostic<KtBinaryExpression>() {
override val diagnosticClass get() = AssignmentInExpressionContext::class
}
abstract class BreakOrContinueOutsideALoop : KtFirDiagnostic<PsiElement>() { abstract class BreakOrContinueOutsideALoop : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = BreakOrContinueOutsideALoop::class override val diagnosticClass get() = BreakOrContinueOutsideALoop::class
} }
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.idea.frontend.api.types.KtType
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtArrayAccessExpression import org.jetbrains.kotlin.psi.KtArrayAccessExpression
import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtDeclaration
@@ -109,6 +110,13 @@ internal class ExpressionExpectedImpl(
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
} }
internal class AssignmentInExpressionContextImpl(
firDiagnostic: FirPsiDiagnostic<*>,
override val token: ValidityToken,
) : KtFirDiagnostic.AssignmentInExpressionContext(), KtAbstractFirDiagnostic<KtBinaryExpression> {
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
}
internal class BreakOrContinueOutsideALoopImpl( internal class BreakOrContinueOutsideALoopImpl(
firDiagnostic: FirPsiDiagnostic<*>, firDiagnostic: FirPsiDiagnostic<*>,
override val token: ValidityToken, override val token: ValidityToken,