diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index 24ef38180fd..638c70b6019 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -1018,6 +1018,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val ILLEGAL_DECLARATION_IN_WHEN_SUBJECT by error { parameter("illegalReason") } + val COMMA_IN_WHEN_CONDITION_WITHOUT_ARGUMENT by error(PositioningStrategy.COMMAS) } val CONTEXT_TRACKING by object : DiagnosticGroup("Context tracking") { diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/RegularDiagnosticData.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/RegularDiagnosticData.kt index 645f64e252d..533bf940dc2 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/RegularDiagnosticData.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/RegularDiagnosticData.kt @@ -102,6 +102,8 @@ enum class PositioningStrategy(private val strategy: String? = null) { INLINE_PARAMETER_MODIFIER, ABSTRACT_MODIFIER, LABEL, + COMMAS, + ; val expressionToCreate get() = "SourceElementPositioningStrategies.${strategy ?: name}" diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index 81bc9d0387b..303b1676ecc 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -542,6 +542,7 @@ object FirErrors { val INVALID_IF_AS_EXPRESSION by error0(SourceElementPositioningStrategies.IF_EXPRESSION) val ELSE_MISPLACED_IN_WHEN by error0(SourceElementPositioningStrategies.ELSE_ENTRY) val ILLEGAL_DECLARATION_IN_WHEN_SUBJECT by error1() + val COMMA_IN_WHEN_CONDITION_WITHOUT_ARGUMENT by error0(SourceElementPositioningStrategies.COMMAS) // Context tracking val TYPE_PARAMETER_IS_NOT_AN_EXPRESSION by error1() diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt index 4c27673813d..4ed17aa62ad 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt @@ -68,6 +68,7 @@ object CommonExpressionCheckers : ExpressionCheckers() { FirExhaustiveWhenChecker, FirWhenConditionChecker, FirWhenSubjectChecker, + FirCommaInWhenConditionChecker, ) override val loopExpressionCheckers: Set diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCommaInWhenConditionChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCommaInWhenConditionChecker.kt new file mode 100644 index 00000000000..077e5102255 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCommaInWhenConditionChecker.kt @@ -0,0 +1,45 @@ +/* + * 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 com.intellij.psi.PsiElement +import org.jetbrains.kotlin.KtNodeTypes +import org.jetbrains.kotlin.fir.FirSourceElement +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.syntax.FirExpressionSyntaxChecker +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.analysis.getChild +import org.jetbrains.kotlin.fir.expressions.FirWhenBranch +import org.jetbrains.kotlin.fir.expressions.FirWhenExpression +import org.jetbrains.kotlin.fir.expressions.impl.FirElseIfTrueCondition +import org.jetbrains.kotlin.lexer.KtTokens + +object FirCommaInWhenConditionChecker : FirExpressionSyntaxChecker() { + override fun isApplicable(element: FirWhenExpression, source: FirSourceElement): Boolean { + return element.subject == null + } + + override fun checkLightTree( + element: FirWhenExpression, + source: FirSourceElement, + context: CheckerContext, + reporter: DiagnosticReporter + ) { + for (branch in element.branches) { + if (branch.condition is FirElseIfTrueCondition) continue + checkCommaInBranchCondition(branch, context, reporter) + } + } + + private fun checkCommaInBranchCondition(branch: FirWhenBranch, context: CheckerContext, reporter: DiagnosticReporter) { + val source = branch.source + if (source?.elementType == KtNodeTypes.WHEN_ENTRY && source?.getChild(KtTokens.COMMA, depth = 1) != null) { + reporter.reportOn(source, FirErrors.COMMA_IN_WHEN_CONDITION_WITHOUT_ARGUMENT, context) + } + } +} \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt index 0c35f8a1f3f..1b9a995bcc4 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt @@ -7,8 +7,8 @@ package org.jetbrains.kotlin.fir.analysis.diagnostics import org.jetbrains.kotlin.diagnostics.rendering.LanguageFeatureMessageRenderer import org.jetbrains.kotlin.diagnostics.rendering.Renderers.RENDER_POSITION_VARIANCE -import org.jetbrains.kotlin.diagnostics.rendering.Renderers.commaSeparated import org.jetbrains.kotlin.diagnostics.rendering.Renderers.STRING +import org.jetbrains.kotlin.diagnostics.rendering.Renderers.commaSeparated import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.AMBIGUOUS_CALLS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.DECLARATION_NAME import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.FIR @@ -87,6 +87,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CATCH_PARAMETER_W import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CLASS_CANNOT_BE_EXTENDED_DIRECTLY import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CLASS_IN_SUPERTYPE_FOR_ENUM import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CLASS_LITERAL_LHS_NOT_A_CLASS +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.COMMA_IN_WHEN_CONDITION_WITHOUT_ARGUMENT import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.COMPONENT_FUNCTION_AMBIGUITY import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.COMPONENT_FUNCTION_MISSING import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.COMPONENT_FUNCTION_ON_NULLABLE @@ -1371,6 +1372,7 @@ class FirDefaultErrorMessages { TO_STRING, WHEN_MISSING_CASES ) + map.put(COMMA_IN_WHEN_CONDITION_WITHOUT_ARGUMENT, "Deprecated syntax. Use '||' instead of commas in when-condition for 'when' without argument") // Context tracking map.put(TYPE_PARAMETER_IS_NOT_AN_EXPRESSION, "Type parameter ''{0}'' is not an expression", SYMBOL) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/LightTreePositioningStrategies.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/LightTreePositioningStrategies.kt index 15b7a3ccbac..1a3728ab3d7 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/LightTreePositioningStrategies.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/LightTreePositioningStrategies.kt @@ -910,6 +910,26 @@ object LightTreePositioningStrategies { return markElement(nodeToMark, startOffset, endOffset, tree, node) } } + + @OptIn(ExperimentalStdlibApi::class) + val COMMAS: LightTreePositioningStrategy = object : LightTreePositioningStrategy() { + override fun mark( + node: LighterASTNode, + startOffset: Int, + endOffset: Int, + tree: FlyweightCapableTreeStructure + ): List { + return buildList { + val childrenRef = Ref>() + tree.getChildren(node, childrenRef) + for (child in childrenRef.get()) { + if (child != null && child.tokenType == KtTokens.COMMA) { + add(markSingleElement(child, child, startOffset, endOffset, tree, node)) + } + } + } + } + } } fun FirSourceElement.hasValOrVar(): Boolean = diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategies.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategies.kt index 53118620a67..633eeab63ac 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategies.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategies.kt @@ -272,6 +272,10 @@ object SourceElementPositioningStrategies { LightTreePositioningStrategies.DECLARATION_WITH_BODY, PositioningStrategies.DECLARATION_WITH_BODY ) + val COMMAS = SourceElementPositioningStrategy( + LightTreePositioningStrategies.COMMAS, + PositioningStrategies.COMMAS + ) val UNREACHABLE_CODE = SourceElementPositioningStrategy( LightTreePositioningStrategies.UNREACHABLE_CODE, diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt index 20f36e102e3..c162c7d1518 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt @@ -7,7 +7,6 @@ package org.jetbrains.kotlin.diagnostics import com.intellij.openapi.util.TextRange import com.intellij.psi.* -import com.intellij.psi.impl.source.tree.LeafPsiElement import com.intellij.psi.tree.TokenSet import org.jetbrains.kotlin.KtNodeTypes import org.jetbrains.kotlin.cfg.UnreachableCode @@ -946,6 +945,19 @@ object PositioningStrategies { } } + @OptIn(ExperimentalStdlibApi::class) + val COMMAS: PositioningStrategy = object : PositioningStrategy() { + override fun mark(element: PsiElement): List { + return buildList { + for (child in element.allChildren) { + if (child.node.elementType == KtTokens.COMMA) { + add(markSingleElement(child)) + } + } + } + } + } + /** * @param locateReferencedName whether to remove any nested parentheses while locating the reference element. This is useful for * diagnostics on super and unresolved references. For example, with the following, only the part inside the parentheses should be diff --git a/compiler/testData/diagnostics/tests/smartCasts/kt6819.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/kt6819.fir.kt index 0428d84174b..48922f7d8fb 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/kt6819.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/kt6819.fir.kt @@ -1,7 +1,7 @@ public fun test(o: String?): Boolean { return when { // Data flow info should propagate from o == null to o.length - o == null, o.length == 0 -> false + o == null, o.length == 0 -> false else -> true } -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/tests/when/CommaInWhenConditionWithoutArgument.fir.kt b/compiler/testData/diagnostics/tests/when/CommaInWhenConditionWithoutArgument.fir.kt deleted file mode 100644 index 67d01c6de6a..00000000000 --- a/compiler/testData/diagnostics/tests/when/CommaInWhenConditionWithoutArgument.fir.kt +++ /dev/null @@ -1,18 +0,0 @@ -/* - * KOTLIN DIAGNOSTICS SPEC TEST (POSITIVE) - * - * SPEC VERSION: 0.1-313 - * PRIMARY LINKS: expressions, when-expression -> paragraph 2 -> sentence 1 - * expressions, when-expression -> paragraph 5 -> sentence 1 - */ -fun foo(x: Int, y: Int): Int = - when { - x > 0, y > 0,, x < 0 -> 1 - else -> 0 - } - -fun bar(x: Int): Int = - when (x) { - 0 -> 0 - else -> 1 - } diff --git a/compiler/testData/diagnostics/tests/when/CommaInWhenConditionWithoutArgument.kt b/compiler/testData/diagnostics/tests/when/CommaInWhenConditionWithoutArgument.kt index a666f056153..63ae791875d 100644 --- a/compiler/testData/diagnostics/tests/when/CommaInWhenConditionWithoutArgument.kt +++ b/compiler/testData/diagnostics/tests/when/CommaInWhenConditionWithoutArgument.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL /* * KOTLIN DIAGNOSTICS SPEC TEST (POSITIVE) * diff --git a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-2/neg/2.2.fir.kt b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-2/neg/2.2.fir.kt index ee8b9b50ac4..d21e26e8aa5 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-2/neg/2.2.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-2/neg/2.2.fir.kt @@ -3,9 +3,9 @@ // TESTCASE NUMBER: 1 fun case_1(value_1: TypesProvider) { when { - getBoolean(), value_1.getBoolean() -> return - value_1.getBoolean() && getBoolean(), getLong() == 1000L -> return - Out(), getLong(), {}, Any(), throw Exception() -> return + getBoolean(), value_1.getBoolean() -> return + value_1.getBoolean() && getBoolean(), getLong() == 1000L -> return + Out(), getLong(), {}, Any(), throw Exception() -> return } return diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt index bf71dbf2d0f..53694359aa5 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -2803,6 +2803,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.COMMA_IN_WHEN_CONDITION_WITHOUT_ARGUMENT) { firDiagnostic -> + CommaInWhenConditionWithoutArgumentImpl( + firDiagnostic as FirPsiDiagnostic, + token, + ) + } add(FirErrors.TYPE_PARAMETER_IS_NOT_AN_EXPRESSION) { firDiagnostic -> TypeParameterIsNotAnExpressionImpl( firSymbolBuilder.classifierBuilder.buildTypeParameterSymbol(firDiagnostic.a.fir), diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt index 3cc11438c94..718345087c8 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt @@ -1958,6 +1958,10 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { abstract val illegalReason: String } + abstract class CommaInWhenConditionWithoutArgument : KtFirDiagnostic() { + override val diagnosticClass get() = CommaInWhenConditionWithoutArgument::class + } + abstract class TypeParameterIsNotAnExpression : KtFirDiagnostic() { override val diagnosticClass get() = TypeParameterIsNotAnExpression::class abstract val typeParameter: KtTypeParameterSymbol diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 5fbb65fd5a1..6630837d7a9 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -3156,6 +3156,13 @@ internal class IllegalDeclarationInWhenSubjectImpl( override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) } +internal class CommaInWhenConditionWithoutArgumentImpl( + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.CommaInWhenConditionWithoutArgument(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} + internal class TypeParameterIsNotAnExpressionImpl( override val typeParameter: KtTypeParameterSymbol, firDiagnostic: FirPsiDiagnostic,