FIR checker: add convention check for ++ and +=, etc
This commit is contained in:
committed by
TeamCityServer
parent
68e7476765
commit
64ebddcbc6
+5
@@ -1115,6 +1115,11 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
||||
parameter<ConeKotlinType>("leftType")
|
||||
parameter<ConeKotlinType>("rightType")
|
||||
}
|
||||
val INC_DEC_SHOULD_NOT_RETURN_UNIT by error<KtExpression>(PositioningStrategy.OPERATOR)
|
||||
val ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT by error<KtExpression>(PositioningStrategy.OPERATOR) {
|
||||
parameter<FirNamedFunctionSymbol>("functionSymbol")
|
||||
parameter<String>("operator")
|
||||
}
|
||||
}
|
||||
|
||||
val TYPE_ALIAS by object : DiagnosticGroup("Type alias") {
|
||||
|
||||
@@ -577,6 +577,8 @@ object FirErrors {
|
||||
val EQUALITY_NOT_APPLICABLE by error3<KtBinaryExpression, String, ConeKotlinType, ConeKotlinType>()
|
||||
val EQUALITY_NOT_APPLICABLE_WARNING by warning3<KtBinaryExpression, String, ConeKotlinType, ConeKotlinType>()
|
||||
val INCOMPATIBLE_ENUM_COMPARISON_ERROR by error2<KtElement, ConeKotlinType, ConeKotlinType>()
|
||||
val INC_DEC_SHOULD_NOT_RETURN_UNIT by error0<KtExpression>(SourceElementPositioningStrategies.OPERATOR)
|
||||
val ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT by error2<KtExpression, FirNamedFunctionSymbol, String>(SourceElementPositioningStrategies.OPERATOR)
|
||||
|
||||
// Type alias
|
||||
val TOPLEVEL_TYPEALIASES_ONLY by error0<KtTypeAlias>()
|
||||
|
||||
+2
-1
@@ -50,7 +50,8 @@ object CommonExpressionCheckers : ExpressionCheckers() {
|
||||
FirConventionFunctionCallChecker,
|
||||
FirDivisionByZeroChecker,
|
||||
FirConstructorCallChecker,
|
||||
FirSpreadOfNullableChecker
|
||||
FirSpreadOfNullableChecker,
|
||||
FirAssignmentOperatorCallChecker,
|
||||
)
|
||||
|
||||
override val tryExpressionCheckers: Set<FirTryExpressionChecker>
|
||||
|
||||
@@ -16,8 +16,11 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.*
|
||||
import org.jetbrains.kotlin.fir.analysis.getChild
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.*
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
|
||||
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
|
||||
import org.jetbrains.kotlin.fir.expressions.impl.FirEmptyExpressionBlock
|
||||
import org.jetbrains.kotlin.fir.expressions.toResolvedCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.resolve.SessionHolder
|
||||
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||
import org.jetbrains.kotlin.fir.resolve.inference.isBuiltinFunctionalType
|
||||
@@ -578,7 +581,11 @@ fun checkTypeMismatch(
|
||||
rValueType = lValueType
|
||||
lValueType = tempType
|
||||
}
|
||||
reporter.reportOn(source, FirErrors.RESULT_TYPE_MISMATCH, lValueType, rValueType, context)
|
||||
if (rValueType.isUnit) {
|
||||
reporter.reportOn(source, FirErrors.INC_DEC_SHOULD_NOT_RETURN_UNIT, context)
|
||||
} else {
|
||||
reporter.reportOn(source, FirErrors.RESULT_TYPE_MISMATCH, lValueType, rValueType, context)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
reporter.reportOn(source, FirErrors.ASSIGNMENT_TYPE_MISMATCH, lValueType, rValueType, context)
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers.expression
|
||||
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
|
||||
import org.jetbrains.kotlin.fir.expressions.FirFunctionCallOrigin
|
||||
import org.jetbrains.kotlin.fir.expressions.FirOperationNameConventions
|
||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.types.coneType
|
||||
import org.jetbrains.kotlin.fir.types.isUnit
|
||||
|
||||
object FirAssignmentOperatorCallChecker : FirFunctionCallChecker() {
|
||||
override fun check(expression: FirFunctionCall, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
val resolvedCallee = expression.calleeReference as? FirResolvedNamedReference ?: return
|
||||
val resolvedCalleeSymbol = resolvedCallee.resolvedSymbol as? FirNamedFunctionSymbol ?: return
|
||||
val resolvedCalleeName = resolvedCalleeSymbol.name
|
||||
if (expression.origin != FirFunctionCallOrigin.Operator ||
|
||||
resolvedCalleeName !in FirOperationNameConventions.ASSIGNMENT_NAMES
|
||||
) {
|
||||
return
|
||||
}
|
||||
if (!expression.typeRef.coneType.isUnit) {
|
||||
reporter.reportOn(
|
||||
expression.source,
|
||||
FirErrors.ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT,
|
||||
resolvedCalleeSymbol,
|
||||
FirOperationNameConventions.ASSIGNMENT_NAMES[resolvedCalleeName]!!.operator,
|
||||
context
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
-2
@@ -64,6 +64,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_OPERATOR_SHOULD_RETURN_UNIT
|
||||
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
|
||||
@@ -219,6 +220,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCOMPATIBLE_TYPE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCOMPATIBLE_TYPES_WARNING
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCONSISTENT_TYPE_PARAMETER_BOUNDS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCONSISTENT_TYPE_PARAMETER_VALUES
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INC_DEC_SHOULD_NOT_RETURN_UNIT
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INFERENCE_ERROR
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INFIX_MODIFIER_REQUIRED
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INITIALIZATION_BEFORE_DECLARATION
|
||||
@@ -238,8 +240,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INNER_CLASS_INSID
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INNER_CLASS_OF_GENERIC_THROWABLE_SUBCLASS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INSTANCE_ACCESS_BEFORE_SUPER_CALL
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INTERFACE_WITH_SUPERCLASS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INVALID_DEFAULT_FUNCTIONAL_PARAMETER_FOR_INLINE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INVALID_CHARACTERS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INVALID_DEFAULT_FUNCTIONAL_PARAMETER_FOR_INLINE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INVALID_IF_AS_EXPRESSION
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INVALID_TYPE_OF_ANNOTATION_MEMBER
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INVISIBLE_REFERENCE
|
||||
@@ -290,9 +292,9 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_A_LOOP_LABEL
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_A_SUPERTYPE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_NULL_ASSERTION_ON_CALLABLE_REFERENCE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_NULL_ASSERTION_ON_LAMBDA_EXPRESSION
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_YET_SUPPORTED_IN_INLINE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_ACTUAL_FOR_EXPECT
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_YET_SUPPORTED_IN_INLINE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_COMPANION_OBJECT
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_ELSE_IN_WHEN
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_GET_METHOD
|
||||
@@ -1471,6 +1473,13 @@ class FirDefaultErrorMessages {
|
||||
RENDER_TYPE,
|
||||
RENDER_TYPE
|
||||
)
|
||||
map.put(INC_DEC_SHOULD_NOT_RETURN_UNIT, "Functions inc(), dec() shouldn't return Unit to be used by operators ++, --")
|
||||
map.put(
|
||||
ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT,
|
||||
"Function ''{0}'' should return Unit to be used by corresponding operator ''{1}''",
|
||||
SYMBOL,
|
||||
TO_STRING
|
||||
)
|
||||
|
||||
// Type alias
|
||||
map.put(TOPLEVEL_TYPEALIASES_ONLY, "Nested and local type aliases are not supported")
|
||||
|
||||
@@ -60,6 +60,7 @@ object FirOperationNameConventions {
|
||||
FirOperation.REM_ASSIGN to OperatorNameConventions.REM_ASSIGN
|
||||
)
|
||||
)
|
||||
val ASSIGNMENT_NAMES = ASSIGNMENTS.map { (k, v) -> v to k }.toMap()
|
||||
|
||||
val ASSIGNMENTS_TO_SIMPLE_OPERATOR: Map<FirOperation, Name> = EnumMap(
|
||||
mapOf(
|
||||
|
||||
+8
-8
@@ -35,12 +35,12 @@ class UnitIncDec() {
|
||||
|
||||
fun testUnitIncDec() {
|
||||
var x = UnitIncDec()
|
||||
<!RESULT_TYPE_MISMATCH!>x++<!>
|
||||
<!RESULT_TYPE_MISMATCH!>++x<!>
|
||||
<!RESULT_TYPE_MISMATCH!>x--<!>
|
||||
<!RESULT_TYPE_MISMATCH!>--x<!>
|
||||
x = <!RESULT_TYPE_MISMATCH!>x++<!>
|
||||
x = <!RESULT_TYPE_MISMATCH!>x--<!>
|
||||
x = <!RESULT_TYPE_MISMATCH!>++x<!>
|
||||
x = <!RESULT_TYPE_MISMATCH!>--x<!>
|
||||
x<!INC_DEC_SHOULD_NOT_RETURN_UNIT!>++<!>
|
||||
<!INC_DEC_SHOULD_NOT_RETURN_UNIT!>++<!>x
|
||||
x<!INC_DEC_SHOULD_NOT_RETURN_UNIT!>--<!>
|
||||
<!INC_DEC_SHOULD_NOT_RETURN_UNIT!>--<!>x
|
||||
x = x<!INC_DEC_SHOULD_NOT_RETURN_UNIT!>++<!>
|
||||
x = x<!INC_DEC_SHOULD_NOT_RETURN_UNIT!>--<!>
|
||||
x = <!INC_DEC_SHOULD_NOT_RETURN_UNIT!>++<!>x
|
||||
x = <!INC_DEC_SHOULD_NOT_RETURN_UNIT!>--<!>x
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ class Test()
|
||||
fun test()
|
||||
{
|
||||
val control = Control()
|
||||
control.MouseMoved += { it.X } // here
|
||||
control.MouseMoved <!ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT!>+=<!> { it.X } // here
|
||||
control.MouseMoved.plusAssign( { it.X } ) // ok
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,9 @@ class B() {
|
||||
|
||||
fun main() {
|
||||
var c = B()
|
||||
c += B()
|
||||
c *= B()
|
||||
c /= B()
|
||||
c -= B()
|
||||
c %= B()
|
||||
}
|
||||
c <!ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT!>+=<!> B()
|
||||
c <!ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT!>*=<!> B()
|
||||
c <!ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT!>/=<!> B()
|
||||
c <!ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT!>-=<!> B()
|
||||
c <!ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT!>%=<!> B()
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ class IncDec {
|
||||
|
||||
fun foo(): IncDec {
|
||||
var x = IncDec()
|
||||
x = <!RESULT_TYPE_MISMATCH!>x++<!>
|
||||
<!RESULT_TYPE_MISMATCH!>x++<!>
|
||||
x = x<!INC_DEC_SHOULD_NOT_RETURN_UNIT!>++<!>
|
||||
x<!INC_DEC_SHOULD_NOT_RETURN_UNIT!>++<!>
|
||||
return x
|
||||
}
|
||||
|
||||
+14
@@ -3027,6 +3027,20 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.INC_DEC_SHOULD_NOT_RETURN_UNIT) { firDiagnostic ->
|
||||
IncDecShouldNotReturnUnitImpl(
|
||||
firDiagnostic as FirPsiDiagnostic,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT) { firDiagnostic ->
|
||||
AssignmentOperatorShouldReturnUnitImpl(
|
||||
firSymbolBuilder.functionLikeBuilder.buildFunctionSymbol(firDiagnostic.a.fir),
|
||||
firDiagnostic.b,
|
||||
firDiagnostic as FirPsiDiagnostic,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.TOPLEVEL_TYPEALIASES_ONLY) { firDiagnostic ->
|
||||
ToplevelTypealiasesOnlyImpl(
|
||||
firDiagnostic as FirPsiDiagnostic,
|
||||
|
||||
+10
@@ -2113,6 +2113,16 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
|
||||
abstract val rightType: KtType
|
||||
}
|
||||
|
||||
abstract class IncDecShouldNotReturnUnit : KtFirDiagnostic<KtExpression>() {
|
||||
override val diagnosticClass get() = IncDecShouldNotReturnUnit::class
|
||||
}
|
||||
|
||||
abstract class AssignmentOperatorShouldReturnUnit : KtFirDiagnostic<KtExpression>() {
|
||||
override val diagnosticClass get() = AssignmentOperatorShouldReturnUnit::class
|
||||
abstract val functionSymbol: KtFunctionLikeSymbol
|
||||
abstract val operator: String
|
||||
}
|
||||
|
||||
abstract class ToplevelTypealiasesOnly : KtFirDiagnostic<KtTypeAlias>() {
|
||||
override val diagnosticClass get() = ToplevelTypealiasesOnly::class
|
||||
}
|
||||
|
||||
+16
@@ -3401,6 +3401,22 @@ internal class IncompatibleEnumComparisonErrorImpl(
|
||||
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class IncDecShouldNotReturnUnitImpl(
|
||||
firDiagnostic: FirPsiDiagnostic,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.IncDecShouldNotReturnUnit(), KtAbstractFirDiagnostic<KtExpression> {
|
||||
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class AssignmentOperatorShouldReturnUnitImpl(
|
||||
override val functionSymbol: KtFunctionLikeSymbol,
|
||||
override val operator: String,
|
||||
firDiagnostic: FirPsiDiagnostic,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.AssignmentOperatorShouldReturnUnit(), KtAbstractFirDiagnostic<KtExpression> {
|
||||
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class ToplevelTypealiasesOnlyImpl(
|
||||
firDiagnostic: FirPsiDiagnostic,
|
||||
override val token: ValidityToken,
|
||||
|
||||
Reference in New Issue
Block a user