Resolve control label locally when label name clashes with fun name
#KT-5354 Fixed #KT-15085 Fixed
This commit is contained in:
@@ -102,31 +102,29 @@ object LabelResolver {
|
|||||||
val labelName = expression.getLabelNameAsName()
|
val labelName = expression.getLabelNameAsName()
|
||||||
if (labelElement == null || labelName == null) return null
|
if (labelElement == null || labelName == null) return null
|
||||||
|
|
||||||
|
val element = resolveNamedLabel(labelName, labelElement, context.trace)
|
||||||
|
if (element != null) return element
|
||||||
|
|
||||||
val declarationsByLabel = context.scope.getDeclarationsByLabel(labelName)
|
val declarationsByLabel = context.scope.getDeclarationsByLabel(labelName)
|
||||||
val size = declarationsByLabel.size
|
when (declarationsByLabel.size) {
|
||||||
|
0 -> {
|
||||||
if (size > 1) {
|
|
||||||
BindingContextUtils.reportAmbiguousLabel(context.trace, labelElement, declarationsByLabel)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
if (size == 0) {
|
|
||||||
val element = resolveNamedLabel(labelName, labelElement, context.trace)
|
|
||||||
if (element == null) {
|
|
||||||
context.trace.report(UNRESOLVED_REFERENCE.on(labelElement, labelElement))
|
context.trace.report(UNRESOLVED_REFERENCE.on(labelElement, labelElement))
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
return element
|
1 -> {
|
||||||
}
|
val declarationDescriptor = declarationsByLabel.single()
|
||||||
|
if (declarationDescriptor is FunctionDescriptor || declarationDescriptor is ClassDescriptor) {
|
||||||
val declarationDescriptor = declarationsByLabel.iterator().next()
|
val declarationElement = DescriptorToSourceUtils.descriptorToDeclaration(declarationDescriptor)
|
||||||
if (declarationDescriptor is FunctionDescriptor || declarationDescriptor is ClassDescriptor) {
|
if (declarationElement is KtElement) {
|
||||||
val element = DescriptorToSourceUtils.descriptorToDeclaration(declarationDescriptor)
|
context.trace.record(LABEL_TARGET, labelElement, declarationElement)
|
||||||
if (element is KtElement) {
|
return declarationElement
|
||||||
context.trace.record(LABEL_TARGET, labelElement, element)
|
}
|
||||||
return element
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
BindingContextUtils.reportAmbiguousLabel(context.trace, labelElement, declarationsByLabel)
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw UnsupportedOperationException(declarationDescriptor.javaClass.toString()) // TODO
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
@@ -144,7 +142,7 @@ object LabelResolver {
|
|||||||
trace.report(LABEL_NAME_CLASH.on(labelExpression))
|
trace.report(LABEL_NAME_CLASH.on(labelExpression))
|
||||||
}
|
}
|
||||||
|
|
||||||
return list.single().also { trace.record(LABEL_TARGET, labelExpression, it) }
|
return list.first().also { trace.record(LABEL_TARGET, labelExpression, it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun resolveThisOrSuperLabel(
|
fun resolveThisOrSuperLabel(
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
fun test1(): Boolean {
|
||||||
|
test1@ for(i in 1..2) {
|
||||||
|
continue@test1
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test2(): Boolean {
|
||||||
|
test2@ while (true) {
|
||||||
|
break@test2
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
if (!test1()) return "fail test1"
|
||||||
|
if (!test2()) return "fail test2"
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
Vendored
+53
@@ -0,0 +1,53 @@
|
|||||||
|
fun test1() {
|
||||||
|
test1@ for(i in 1..2) {
|
||||||
|
continue<!LABEL_NAME_CLASH!>@test1<!>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test2() {
|
||||||
|
test2@ while (true) {
|
||||||
|
break<!LABEL_NAME_CLASH!>@test2<!>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Test3 {
|
||||||
|
fun Test3() {
|
||||||
|
Test3@ while (true) {
|
||||||
|
break<!LABEL_NAME_CLASH!>@Test3<!>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test4() {
|
||||||
|
<!NOT_A_LOOP_LABEL!>break@test4<!>
|
||||||
|
}
|
||||||
|
|
||||||
|
class Test5 {
|
||||||
|
fun Test5<!SYNTAX!><!> {
|
||||||
|
return@Test5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Test6 {
|
||||||
|
fun Test6() {
|
||||||
|
Test6@ while (true) {
|
||||||
|
break<!LABEL_NAME_CLASH!>@Test6<!>
|
||||||
|
}
|
||||||
|
|
||||||
|
Test6@ while (true) {
|
||||||
|
break<!LABEL_NAME_CLASH!>@Test6<!>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Test7 {
|
||||||
|
fun Test7() {
|
||||||
|
Test8@ while (true) {
|
||||||
|
<!NOT_A_LOOP_LABEL!>break@Test7<!>
|
||||||
|
}
|
||||||
|
|
||||||
|
<!UNREACHABLE_CODE!>Test7@ while (true) {
|
||||||
|
<!NOT_A_LOOP_LABEL!>break<!UNRESOLVED_REFERENCE!>@Test8<!><!>
|
||||||
|
}<!>
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+37
@@ -0,0 +1,37 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public fun test1(): kotlin.Unit
|
||||||
|
public fun test2(): kotlin.Unit
|
||||||
|
public fun test4(): kotlin.Unit
|
||||||
|
|
||||||
|
public final class Test3 {
|
||||||
|
public constructor Test3()
|
||||||
|
public final fun Test3(): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class Test5 {
|
||||||
|
public constructor Test5()
|
||||||
|
public final fun Test5(): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class Test6 {
|
||||||
|
public constructor Test6()
|
||||||
|
public final fun Test6(): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class Test7 {
|
||||||
|
public constructor Test7()
|
||||||
|
public final fun Test7(): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
+6
@@ -10064,6 +10064,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/labels"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/labels"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("controlLabelClashesWithFuncitonName.kt")
|
||||||
|
public void testControlLabelClashesWithFuncitonName() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/labels/controlLabelClashesWithFuncitonName.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("labeledDeclarations.kt")
|
@TestMetadata("labeledDeclarations.kt")
|
||||||
public void testLabeledDeclarations() throws Exception {
|
public void testLabeledDeclarations() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/labels/labeledDeclarations.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/labels/labeledDeclarations.kt");
|
||||||
|
|||||||
@@ -4402,6 +4402,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("continueAndBreakLabelWithSameFunctionName.kt")
|
||||||
|
public void testContinueAndBreakLabelWithSameFunctionName() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/continueAndBreakLabelWithSameFunctionName.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("emptyIf.kt")
|
@TestMetadata("emptyIf.kt")
|
||||||
public void testEmptyIf() throws Exception {
|
public void testEmptyIf() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/emptyIf.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/emptyIf.kt");
|
||||||
|
|||||||
@@ -10064,6 +10064,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/labels"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/labels"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("controlLabelClashesWithFuncitonName.kt")
|
||||||
|
public void testControlLabelClashesWithFuncitonName() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/labels/controlLabelClashesWithFuncitonName.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("labeledDeclarations.kt")
|
@TestMetadata("labeledDeclarations.kt")
|
||||||
public void testLabeledDeclarations() throws Exception {
|
public void testLabeledDeclarations() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/labels/labeledDeclarations.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/labels/labeledDeclarations.kt");
|
||||||
|
|||||||
@@ -10064,6 +10064,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/labels"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/labels"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("controlLabelClashesWithFuncitonName.kt")
|
||||||
|
public void testControlLabelClashesWithFuncitonName() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/labels/controlLabelClashesWithFuncitonName.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("labeledDeclarations.kt")
|
@TestMetadata("labeledDeclarations.kt")
|
||||||
public void testLabeledDeclarations() throws Exception {
|
public void testLabeledDeclarations() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/labels/labeledDeclarations.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/labels/labeledDeclarations.kt");
|
||||||
|
|||||||
@@ -11595,6 +11595,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
|||||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/labels"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true);
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/labels"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("controlLabelClashesWithFuncitonName.kt")
|
||||||
|
public void testControlLabelClashesWithFuncitonName() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/labels/controlLabelClashesWithFuncitonName.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("labeledDeclarations.kt")
|
@TestMetadata("labeledDeclarations.kt")
|
||||||
public void testLabeledDeclarations() throws Exception {
|
public void testLabeledDeclarations() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/labels/labeledDeclarations.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/labels/labeledDeclarations.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user