[FIR] Forbid multiple labels per statement

^KT-53629: Fixed
This commit is contained in:
vladislav.grechko
2024-01-25 17:24:27 +01:00
committed by Space Team
parent cd5b38b958
commit d27adf6677
29 changed files with 201 additions and 34 deletions
@@ -4957,6 +4957,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirErrors.MULTIPLE_LABELS_ARE_FORBIDDEN) { firDiagnostic ->
MultipleLabelsAreForbiddenImpl(
firDiagnostic as KtPsiDiagnostic,
token,
)
}
add(FirErrors.DEPRECATED_ACCESS_TO_ENUM_ENTRY_COMPANION_PROPERTY) { firDiagnostic ->
DeprecatedAccessToEnumEntryCompanionPropertyImpl(
firDiagnostic as KtPsiDiagnostic,
@@ -3450,6 +3450,10 @@ sealed interface KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = RedundantLabelWarning::class
}
interface MultipleLabelsAreForbidden : KtFirDiagnostic<KtLabelReferenceExpression> {
override val diagnosticClass get() = MultipleLabelsAreForbidden::class
}
interface DeprecatedAccessToEnumEntryCompanionProperty : KtFirDiagnostic<PsiElement> {
override val diagnosticClass get() = DeprecatedAccessToEnumEntryCompanionProperty::class
}
@@ -4161,6 +4161,11 @@ internal class RedundantLabelWarningImpl(
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<KtLabelReferenceExpression>(firDiagnostic, token), KtFirDiagnostic.RedundantLabelWarning
internal class MultipleLabelsAreForbiddenImpl(
firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<KtLabelReferenceExpression>(firDiagnostic, token), KtFirDiagnostic.MultipleLabelsAreForbidden
internal class DeprecatedAccessToEnumEntryCompanionPropertyImpl(
firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken,
@@ -23901,6 +23901,12 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia
public void testLabelsMustBeNamed() throws Exception {
runTest("compiler/testData/diagnostics/tests/labels/labelsMustBeNamed.kt");
}
@Test
@TestMetadata("multipleLabels.kt")
public void testMultipleLabels() throws Exception {
runTest("compiler/testData/diagnostics/tests/labels/multipleLabels.kt");
}
}
@Nested
@@ -23901,6 +23901,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated
public void testLabelsMustBeNamed() throws Exception {
runTest("compiler/testData/diagnostics/tests/labels/labelsMustBeNamed.kt");
}
@Test
@TestMetadata("multipleLabels.kt")
public void testMultipleLabels() throws Exception {
runTest("compiler/testData/diagnostics/tests/labels/multipleLabels.kt");
}
}
@Nested
@@ -23895,6 +23895,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir
public void testLabelsMustBeNamed() throws Exception {
runTest("compiler/testData/diagnostics/tests/labels/labelsMustBeNamed.kt");
}
@Test
@TestMetadata("multipleLabels.kt")
public void testMultipleLabels() throws Exception {
runTest("compiler/testData/diagnostics/tests/labels/multipleLabels.kt");
}
}
@Nested
@@ -23901,6 +23901,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia
public void testLabelsMustBeNamed() throws Exception {
runTest("compiler/testData/diagnostics/tests/labels/labelsMustBeNamed.kt");
}
@Test
@TestMetadata("multipleLabels.kt")
public void testMultipleLabels() throws Exception {
runTest("compiler/testData/diagnostics/tests/labels/multipleLabels.kt");
}
}
@Nested
@@ -1751,6 +1751,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val LABEL by object : DiagnosticGroup("label") {
val REDUNDANT_LABEL_WARNING by warning<KtLabelReferenceExpression>(PositioningStrategy.LABEL)
val MULTIPLE_LABELS_ARE_FORBIDDEN by error<KtLabelReferenceExpression>(PositioningStrategy.LABEL)
}
val ENUM_ENTRIES_DEPRECATIONS by object : DiagnosticGroup("Enum.entries resolve deprecations") {
@@ -872,6 +872,7 @@ object FirErrors {
// label
val REDUNDANT_LABEL_WARNING: KtDiagnosticFactory0 by warning0<KtLabelReferenceExpression>(SourceElementPositioningStrategies.LABEL)
val MULTIPLE_LABELS_ARE_FORBIDDEN: KtDiagnosticFactory0 by error0<KtLabelReferenceExpression>(SourceElementPositioningStrategies.LABEL)
// Enum.entries resolve deprecations
val DEPRECATED_ACCESS_TO_ENUM_ENTRY_COMPANION_PROPERTY: KtDiagnosticFactory0 by warning0<PsiElement>()
@@ -572,6 +572,7 @@ val FIR_NON_SUPPRESSIBLE_ERROR_NAMES: Set<String> = setOf(
"RETURN_FOR_BUILT_IN_SUSPEND",
"MIXING_SUSPEND_AND_NON_SUSPEND_SUPERTYPES",
"MIXING_FUNCTIONAL_KINDS_IN_SUPERTYPES",
"MULTIPLE_LABELS_ARE_FORBIDDEN",
"INCOMPATIBLE_CLASS",
"PRE_RELEASE_CLASS",
"IR_WITH_UNSTABLE_ABI_COMPILED_CLASS",
@@ -501,6 +501,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PACKAGE_CANNOT_BE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PACKAGE_OR_CLASSIFIER_REDECLARATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PLATFORM_CLASS_MAPPED_TO_KOTLIN
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.COMPILER_REQUIRED_ANNOTATION_AMBIGUITY
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MULTIPLE_LABELS_ARE_FORBIDDEN
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.POTENTIALLY_NON_REPORTED_ANNOTATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PRE_RELEASE_CLASS
@@ -2538,6 +2539,8 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
"Label is redundant, because it cannot be referenced in a 'break', 'continue', or 'return' expression."
)
map.put(MULTIPLE_LABELS_ARE_FORBIDDEN, "Multiple labels per statement are forbidden.")
// Enum entries deprecations
map.put(
DEPRECATED_ACCESS_TO_ENUM_ENTRY_COMPANION_PROPERTY,
@@ -194,6 +194,7 @@ private fun ConeDiagnostic.toKtDiagnostic(
is ConeAmbiguousFunctionTypeKinds -> FirErrors.AMBIGUOUS_FUNCTION_TYPE_KIND.createOn(source, kinds)
is ConeUnsupportedClassLiteralsWithEmptyLhs -> FirErrors.UNSUPPORTED_CLASS_LITERALS_WITH_EMPTY_LHS.createOn(source)
is ConeMissingConstructorKeyword -> FirErrors.MISSING_CONSTRUCTOR_KEYWORD.createOn(source)
is ConeMultipleLabelsAreForbidden -> FirErrors.MULTIPLE_LABELS_ARE_FORBIDDEN.createOn(this.source)
else -> throw IllegalArgumentException("Unsupported diagnostic type: ${this.javaClass}")
}
@@ -397,19 +397,19 @@ class LightTreeRawFirExpressionBuilder(
*/
private fun convertLabeledExpression(labeledExpression: LighterASTNode): FirElement {
var firExpression: FirElement? = null
var errorLabelSource: KtSourceElement? = null
var labelSource: KtSourceElement? = null
var forbiddenLabelKind: ForbiddenLabelKind? = null
val isRepetitiveLabel = labeledExpression.getLabeledExpression()?.tokenType == LABELED_EXPRESSION
labeledExpression.forEachChildren {
context.setNewLabelUserNode(it)
when (it.tokenType) {
LABEL_QUALIFIER -> {
val rawName = it.toString()
val pair = buildLabelAndErrorSource(
rawName.substring(0, rawName.length - 1),
it.getChildNodesByType(LABEL).single().toFirSourceElement()
)
context.addNewLabel(pair.first)
errorLabelSource = pair.second
val name = it.asText.dropLast(1)
labelSource = it.getChildNodesByType(LABEL).single().toFirSourceElement()
context.addNewLabel(buildLabel(name, labelSource!!))
forbiddenLabelKind = getForbiddenLabelKind(name, isRepetitiveLabel)
}
BLOCK -> firExpression = declarationBuilder.convertBlock(it)
PROPERTY -> firExpression = declarationBuilder.convertPropertyDeclaration(it)
@@ -419,7 +419,7 @@ class LightTreeRawFirExpressionBuilder(
context.dropLastLabel()
return buildExpressionWithErrorLabel(firExpression, errorLabelSource, labeledExpression.toFirSourceElement())
return buildExpressionHandlingErrors(firExpression, labeledExpression.toFirSourceElement(), forbiddenLabelKind, labelSource)
}
/**
@@ -51,7 +51,10 @@ import org.jetbrains.kotlin.types.ConstantValueKind
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.addToStdlib.*
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.kotlin.utils.addToStdlib.runIf
import org.jetbrains.kotlin.utils.addToStdlib.runUnless
import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled
import org.jetbrains.kotlin.utils.exceptions.errorWithAttachment
import org.jetbrains.kotlin.utils.exceptions.requireWithAttachment
import org.jetbrains.kotlin.utils.exceptions.withPsiEntry
@@ -3135,20 +3138,24 @@ open class PsiRawFirBuilder(
override fun visitLabeledExpression(expression: KtLabeledExpression, data: FirElement?): FirElement {
val label = expression.getTargetLabel()
var errorLabelSource: KtSourceElement? = null
var labelSource: KtSourceElement? = null
var forbiddenLabelKind: ForbiddenLabelKind? = null
val isRepetitiveLabel = expression.baseExpression is KtLabeledExpression
val result = if (label != null) {
val rawName = label.getReferencedNameElement().node!!.text
val labelAndErrorSource = buildLabelAndErrorSource(rawName, label.toKtPsiSourceElement())
errorLabelSource = labelAndErrorSource.second
context.withNewLabel(labelAndErrorSource.first, expression.baseExpression) {
val name = label.getReferencedNameElement().node!!.text
forbiddenLabelKind = getForbiddenLabelKind(name, isRepetitiveLabel)
labelSource = label.toKtPsiSourceElement()
val firLabel = buildLabel(name, labelSource)
context.withNewLabel(firLabel, expression.baseExpression) {
expression.baseExpression?.accept(this, data)
}
} else {
expression.baseExpression?.accept(this, data)
}
return buildExpressionWithErrorLabel(result, errorLabelSource, expression.toFirSourceElement())
return buildExpressionHandlingErrors(result, expression.toFirSourceElement(), forbiddenLabelKind, labelSource)
}
override fun visitAnnotatedExpression(expression: KtAnnotatedExpression, data: FirElement?): FirElement {
@@ -1044,26 +1044,41 @@ abstract class AbstractRawFirBuilder<T>(val baseSession: FirSession, val context
initContainingClassAttr(context)
}
protected fun buildLabelAndErrorSource(rawName: String, source: KtSourceElement): Pair<FirLabel, KtSourceElement?> {
protected fun buildLabel(rawName: String, source: KtSourceElement): FirLabel {
val firLabel = buildLabel {
name = KtPsiUtil.unquoteIdentifier(rawName)
this.source = source
}
return Pair(firLabel, if (rawName.isUnderscore) firLabel.source else null)
return firLabel
}
protected fun buildExpressionWithErrorLabel(
protected fun getForbiddenLabelKind(rawName: String, isMultipleLabel: Boolean): ForbiddenLabelKind? = when {
rawName.isUnderscore -> ForbiddenLabelKind.UNDERSCORE_IS_RESERVED
isMultipleLabel -> ForbiddenLabelKind.MULTIPLE_LABEL
else -> null
}
protected enum class ForbiddenLabelKind {
UNDERSCORE_IS_RESERVED, MULTIPLE_LABEL
}
protected fun buildExpressionHandlingErrors(
element: FirElement?,
errorLabelSource: KtSourceElement?,
elementSource: KtSourceElement,
forbiddenLabelKind: ForbiddenLabelKind?,
forbiddenLabelSource: KtSourceElement?,
): FirElement {
return if (element != null) {
if (errorLabelSource != null) {
if (forbiddenLabelKind != null) {
require(forbiddenLabelSource != null)
buildErrorExpression {
this.source = element.source
this.expression = element as? FirExpression
diagnostic = ConeUnderscoreIsReserved(errorLabelSource)
diagnostic = when (forbiddenLabelKind) {
ForbiddenLabelKind.UNDERSCORE_IS_RESERVED -> ConeUnderscoreIsReserved(forbiddenLabelSource)
ForbiddenLabelKind.MULTIPLE_LABEL -> ConeMultipleLabelsAreForbidden(forbiddenLabelSource)
}
}
} else {
element
@@ -27,6 +27,10 @@ class ConeUnderscoreIsReserved(source: KtSourceElement) : ConeDiagnosticWithSour
override val reason: String get() = "Names _, __, ___, ..., are reserved in Kotlin"
}
class ConeMultipleLabelsAreForbidden(source: KtSourceElement) : ConeDiagnosticWithSource(source) {
override val reason: String get() = "Multiple labels per statement are forbidden"
}
class ConeCannotInferTypeParameterType(
val typeParameter: FirTypeParameterSymbol,
override val reason: String = "Cannot infer type for parameter ${typeParameter.name}"
@@ -102,7 +102,7 @@ fun <T : FirStatement> FirBlock.replaceFirstStatement(factory: (T) -> FirStateme
}
fun FirExpression.unwrapErrorExpression(): FirExpression? =
if (this is FirErrorExpression) expression?.run { unwrapErrorExpression() } else this
if (this is FirErrorExpression) expression?.unwrapErrorExpression() else this
fun FirExpression.unwrapArgument(): FirExpression = (this as? FirWrappedArgumentExpression)?.expression ?: this
@@ -0,0 +1,10 @@
// K2: See KT-65342
fun test() {
<!MULTIPLE_LABELS_ARE_FORBIDDEN!>a@<!> b@ while(true) {
val f = {
return@a
}
break@b
}
}
@@ -1,4 +1,5 @@
// FIR_IDENTICAL
// K2: See KT-65342
fun test() {
a@ b@ while(true) {
val f = {
@@ -30,12 +30,12 @@ fun testAnnotatedLambdaLabel() =
}
fun testLambdaMultipleLabels1() =
lambda1@ lambda2@ {
<!MULTIPLE_LABELS_ARE_FORBIDDEN!>lambda1@<!> lambda2@ {
<!NOT_A_FUNCTION_LABEL!>return@lambda1<!>
}
fun testLambdaMultipleLabels2() =
lambda1@ lambda2@ {
<!MULTIPLE_LABELS_ARE_FORBIDDEN!>lambda1@<!> lambda2@ {
return@lambda2
}
@@ -62,7 +62,7 @@ fun testHighOrderFunctionCallLabelInReturn() {
}
fun testMultipleLabelsWithNestedLambda() {
l1@ l2@{
<!MULTIPLE_LABELS_ARE_FORBIDDEN!>l1@<!> l2@{
{
<!NOT_A_FUNCTION_LABEL!>return@l1<!>
}
@@ -30,12 +30,12 @@ fun testAnnotatedLambdaLabel() =
}
fun testLambdaMultipleLabels1() =
lambda1@ lambda2@ {
<!MULTIPLE_LABELS_ARE_FORBIDDEN!>lambda1@<!> lambda2@ {
<!NOT_A_FUNCTION_LABEL!>return@lambda1<!>
}
fun testLambdaMultipleLabels2() =
lambda1@ lambda2@ {
<!MULTIPLE_LABELS_ARE_FORBIDDEN!>lambda1@<!> lambda2@ {
return@lambda2
}
@@ -62,7 +62,7 @@ fun testHighOrderFunctionCallLabelInReturn() {
}
fun testMultipleLabelsWithNestedLambda() {
l1@ l2@{
<!MULTIPLE_LABELS_ARE_FORBIDDEN!>l1@<!> l2@{
{
<!NOT_A_FUNCTION_LABEL!>return@l1<!>
}
@@ -1,4 +1,4 @@
// !DIAGNOSTICS: -REDUNDANT_LABEL_WARNING -UNDERSCORE_IS_RESERVED
// !DIAGNOSTICS: -REDUNDANT_LABEL_WARNING -UNDERSCORE_IS_RESERVED -MULTIPLE_LABELS_ARE_FORBIDDEN
// See KT-65337
// !DIAGNOSTICS: -UNRESOLVED_REFERENCE
@@ -1,4 +1,4 @@
// !DIAGNOSTICS: -REDUNDANT_LABEL_WARNING -UNDERSCORE_IS_RESERVED
// !DIAGNOSTICS: -REDUNDANT_LABEL_WARNING -UNDERSCORE_IS_RESERVED -MULTIPLE_LABELS_ARE_FORBIDDEN
// See KT-65337
// !DIAGNOSTICS: -UNRESOLVED_REFERENCE
@@ -23,5 +23,5 @@ inline fun inlineFunWithInvoke2(s: (p: Int) -> Unit) {
inline fun propagation(s: (p: Int) -> Unit) {
inlineFunWithInvoke((label@ s))
inlineFunWithInvoke((label2@ label@ s))
inlineFunWithInvoke((<!MULTIPLE_LABELS_ARE_FORBIDDEN!>label2@<!> label@ s))
}
@@ -0,0 +1,37 @@
// !DIAGNOSTICS: -REDUNDANT_LABEL_WARNING
fun foo1() {
<!MULTIPLE_LABELS_ARE_FORBIDDEN!>l2@<!> l1@ do {
} while (true)
}
fun foo2() {
<!MULTIPLE_LABELS_ARE_FORBIDDEN!>l4@<!> <!UNDERSCORE_IS_RESERVED!>_<!>@ <!MULTIPLE_LABELS_ARE_FORBIDDEN!>l3@<!> <!UNDERSCORE_IS_RESERVED!>__<!>@ <!MULTIPLE_LABELS_ARE_FORBIDDEN!>l2@<!> l1@ while (true) {
}
}
fun foo3() {
<!MULTIPLE_LABELS_ARE_FORBIDDEN!>l2@<!> l1@ 42
}
fun foo4() {
l1@ do {
l4@ { false }
} while (true)
}
fun foo5() {
<!MULTIPLE_LABELS_ARE_FORBIDDEN!>l2@<!> l1@ do {
l4@ { false }
} while (true)
}
fun foo6() {
<!MULTIPLE_LABELS_ARE_FORBIDDEN!>l2@<!> l1@ do {
l4@ l3@{ true }
} while (true)
}
fun foo7() {
<!MULTIPLE_LABELS_ARE_FORBIDDEN!>l3@<!> <!MULTIPLE_LABELS_ARE_FORBIDDEN!>l2@<!> l1@ fun bar() {}
}
@@ -0,0 +1,37 @@
// !DIAGNOSTICS: -REDUNDANT_LABEL_WARNING
fun foo1() {
l2@ l1@ do {
} while (true)
}
fun foo2() {
l4@ <!UNDERSCORE_IS_RESERVED!>_<!>@ l3@ <!UNDERSCORE_IS_RESERVED!>__<!>@ l2@ l1@ while (true) {
}
}
fun foo3() {
l2@ l1@ 42
}
fun foo4() {
l1@ do {
l4@ { false }
} while (true)
}
fun foo5() {
l2@ l1@ do {
l4@ { false }
} while (true)
}
fun foo6() {
l2@ l1@ do {
l4@ l3@{ true }
} while (true)
}
fun foo7() {
l3@ l2@ l1@ fun bar() {}
}
@@ -1,3 +1,5 @@
// K2: See KT-65342
fun main() {
val a : Int? = null;
var v = 1
@@ -9,7 +11,7 @@ fun main() {
val h1 : String = <!INITIALIZER_TYPE_MISMATCH!>--v<!>;
val i : String = <!INITIALIZER_TYPE_MISMATCH, TYPE_MISMATCH!>!true<!>;
val j : String = <!INITIALIZER_TYPE_MISMATCH!>foo@ true<!>;
val k : String = <!INITIALIZER_TYPE_MISMATCH!>foo@ bar@ true<!>;
val k : String = <!MULTIPLE_LABELS_ARE_FORBIDDEN!>foo@<!> bar@ true;
val l : String = <!INITIALIZER_TYPE_MISMATCH!>-1<!>;
val m : String = <!INITIALIZER_TYPE_MISMATCH!>+1<!>;
}
@@ -1,3 +1,5 @@
// K2: See KT-65342
fun main() {
val a : Int? = null;
var v = 1
@@ -23901,6 +23901,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
public void testLabelsMustBeNamed() throws Exception {
runTest("compiler/testData/diagnostics/tests/labels/labelsMustBeNamed.kt");
}
@Test
@TestMetadata("multipleLabels.kt")
public void testMultipleLabels() throws Exception {
runTest("compiler/testData/diagnostics/tests/labels/multipleLabels.kt");
}
}
@Nested