From b5e2c492c6c9e7dec3319e24313dc85192ee4cf7 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Tue, 22 Feb 2022 11:11:45 +0300 Subject: [PATCH] FE 1.0: implement initial label resolve warning for KT-51433 --- ...CompilerTestFE10TestdataTestGenerated.java | 6 ++ ...irOldFrontendDiagnosticsTestGenerated.java | 6 ++ ...DiagnosticsWithLightTreeTestGenerated.java | 6 ++ .../jetbrains/kotlin/diagnostics/Errors.java | 1 + .../rendering/DefaultErrorMessages.java | 2 + .../kotlin/types/expressions/LabelResolver.kt | 77 ++++++++++++++----- .../tests/labels/labeledFunctionLiteral.kt | 4 +- .../testsWithStdLib/labelClashes.fir.kt | 41 ++++++++++ .../testsWithStdLib/labelClashes.kt | 41 ++++++++++ .../testsWithStdLib/labelClashes.txt | 23 ++++++ .../test/runners/DiagnosticTestGenerated.java | 6 ++ 11 files changed, 191 insertions(+), 22 deletions(-) create mode 100644 compiler/testData/diagnostics/testsWithStdLib/labelClashes.fir.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/labelClashes.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/labelClashes.txt diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index 4f496aaf363..4ac13545af8 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -33569,6 +33569,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/testsWithStdLib/kt9985.kt"); } + @Test + @TestMetadata("labelClashes.kt") + public void testLabelClashes() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/labelClashes.kt"); + } + @Test @TestMetadata("outstar.kt") public void testOutstar() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index bcb462053e8..328436aeb37 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -33569,6 +33569,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/testsWithStdLib/kt9985.kt"); } + @Test + @TestMetadata("labelClashes.kt") + public void testLabelClashes() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/labelClashes.kt"); + } + @Test @TestMetadata("outstar.kt") public void testOutstar() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java index a1fd7db1d8e..5053f193540 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java @@ -33569,6 +33569,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/testsWithStdLib/kt9985.kt"); } + @Test + @TestMetadata("labelClashes.kt") + public void testLabelClashes() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/labelClashes.kt"); + } + @Test @TestMetadata("outstar.kt") public void testOutstar() throws Exception { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 3673760e946..f752b4c4e35 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -1025,6 +1025,7 @@ public interface Errors { // Labels DiagnosticFactory0 LABEL_NAME_CLASH = DiagnosticFactory0.create(WARNING); + DiagnosticFactory2 LABEL_RESOLVE_WILL_CHANGE = DiagnosticFactory2.create(WARNING); DiagnosticFactory0 AMBIGUOUS_LABEL = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 BREAK_OR_CONTINUE_OUTSIDE_A_LOOP = DiagnosticFactory0.create(ERROR); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 02dfdb06a9d..a2c76660a58 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -193,6 +193,8 @@ public class DefaultErrorMessages { MAP.put(RETURN_NOT_ALLOWED, "'return' is not allowed here"); MAP.put(PROJECTION_IN_IMMEDIATE_ARGUMENT_TO_SUPERTYPE, "Projections are not allowed for immediate arguments of a supertype"); MAP.put(LABEL_NAME_CLASH, "There is more than one label with such a name in this scope"); + MAP.put(LABEL_RESOLVE_WILL_CHANGE, "This label is now resolved to ''{0}'' but soon it will be resolved to the closest ''{1}''. " + + "Please consider introducing or changing explicit label name", TO_STRING, TO_STRING); MAP.put(EXPRESSION_EXPECTED_PACKAGE_FOUND, "Expression expected, but a package name found"); MAP.put(CANNOT_ALL_UNDER_IMPORT_FROM_SINGLETON, "Cannot import-on-demand from object ''{0}''", NAME); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/LabelResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/LabelResolver.kt index 0286fb6cc19..b0adfd1dbe9 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/LabelResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/LabelResolver.kt @@ -18,12 +18,13 @@ package org.jetbrains.kotlin.types.expressions import com.intellij.psi.PsiElement import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.config.LanguageFeature.ContextReceivers import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.diagnostics.Errors.LABEL_NAME_CLASH -import org.jetbrains.kotlin.diagnostics.Errors.UNRESOLVED_REFERENCE +import org.jetbrains.kotlin.diagnostics.Errors.* import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.checkReservedYield +import org.jetbrains.kotlin.psi.psiUtil.parents import org.jetbrains.kotlin.resolve.* import org.jetbrains.kotlin.resolve.BindingContext.* import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext @@ -150,9 +151,15 @@ object LabelResolver { labelName: Name ): LabeledReceiverResolutionResult { val referenceExpression = expression.instanceReference - val targetLabel = expression.getTargetLabel() ?: error(expression) + val targetLabelExpression = expression.getTargetLabel() ?: error(expression) - val declarationsByLabel = context.scope.getDeclarationsByLabel(labelName) + val scope = context.scope + val declarationsByLabel = scope.getDeclarationsByLabel(labelName) + val elementsByLabel = getElementsByLabelName( + labelName, targetLabelExpression, + classNameLabelsEnabled = expression is KtThisExpression && context.languageVersionSettings.supportsFeature(ContextReceivers) + ) + val trace = context.trace when (declarationsByLabel.size) { 1 -> { val declarationDescriptor = declarationsByLabel.single() @@ -163,13 +170,20 @@ object LabelResolver { else -> throw UnsupportedOperationException("Unsupported descriptor: $declarationDescriptor") // TODO } - val element = DescriptorToSourceUtils.descriptorToDeclaration(declarationDescriptor) + val declarationElement = DescriptorToSourceUtils.descriptorToDeclaration(declarationDescriptor) ?: error("No PSI element for descriptor: $declarationDescriptor") - context.trace.record(LABEL_TARGET, targetLabel, element) - context.trace.record(REFERENCE_TARGET, referenceExpression, declarationDescriptor) + trace.record(LABEL_TARGET, targetLabelExpression, declarationElement) + trace.record(REFERENCE_TARGET, referenceExpression, declarationDescriptor) + val closestElement = elementsByLabel.firstOrNull() + if (closestElement != null && declarationElement in closestElement.parents) { + reportLabelResolveWillChange(trace, targetLabelExpression, declarationElement, closestElement) + } if (declarationDescriptor is ClassDescriptor) { - if (!DescriptorResolver.checkHasOuterClassInstance(context.scope, context.trace, targetLabel, declarationDescriptor)) { + if (!DescriptorResolver.checkHasOuterClassInstance( + scope, trace, targetLabelExpression, declarationDescriptor + ) + ) { return LabeledReceiverResolutionResult.labelResolutionFailed() } } @@ -177,14 +191,15 @@ object LabelResolver { return LabeledReceiverResolutionResult.labelResolutionSuccess(thisReceiver) } 0 -> { - val element = resolveNamedLabel( - labelName, targetLabel, context.trace, - classNameLabelsEnabled = expression is KtThisExpression - && context.languageVersionSettings.supportsFeature(LanguageFeature.ContextReceivers) - ) - val declarationDescriptor = context.trace.bindingContext[DECLARATION_TO_DESCRIPTOR, element] + if (elementsByLabel.size > 1) { + trace.report(LABEL_NAME_CLASH.on(targetLabelExpression)) + } + val element = elementsByLabel.firstOrNull()?.also { + trace.record(LABEL_TARGET, targetLabelExpression, it) + } + val declarationDescriptor = trace.bindingContext[DECLARATION_TO_DESCRIPTOR, element] if (declarationDescriptor is FunctionDescriptor) { - val labelNameToReceiverMap = context.trace.bindingContext[ + val labelNameToReceiverMap = trace.bindingContext[ DESCRIPTOR_TO_CONTEXT_RECEIVER_MAP, if (declarationDescriptor is PropertyAccessorDescriptor) declarationDescriptor.correspondingProperty else declarationDescriptor ] @@ -193,23 +208,45 @@ object LabelResolver { thisReceivers.isNullOrEmpty() -> declarationDescriptor.extensionReceiverParameter thisReceivers.size == 1 -> thisReceivers.single() else -> { - BindingContextUtils.reportAmbiguousLabel(context.trace, targetLabel, declarationsByLabel) + BindingContextUtils.reportAmbiguousLabel(trace, targetLabelExpression, declarationsByLabel) return LabeledReceiverResolutionResult.labelResolutionFailed() } }?.also { - context.trace.record(LABEL_TARGET, targetLabel, element) - context.trace.record(REFERENCE_TARGET, referenceExpression, declarationDescriptor) + trace.record(LABEL_TARGET, targetLabelExpression, element) + trace.record(REFERENCE_TARGET, referenceExpression, declarationDescriptor) } return LabeledReceiverResolutionResult.labelResolutionSuccess(thisReceiver) } else { - context.trace.report(UNRESOLVED_REFERENCE.on(targetLabel, targetLabel)) + trace.report(UNRESOLVED_REFERENCE.on(targetLabelExpression, targetLabelExpression)) } } - else -> BindingContextUtils.reportAmbiguousLabel(context.trace, targetLabel, declarationsByLabel) + else -> BindingContextUtils.reportAmbiguousLabel(trace, targetLabelExpression, declarationsByLabel) } return LabeledReceiverResolutionResult.labelResolutionFailed() } + private fun reportLabelResolveWillChange( + trace: BindingTrace, + target: KtSimpleNameExpression, + declarationElement: PsiElement, + closestElement: KtElement + ) { + val closestDescription = when (closestElement) { + is KtFunctionLiteral -> "anonymous function" + is KtNamedFunction -> "function ${closestElement.name} context receiver" + is KtPropertyAccessor -> "property ${closestElement.property.name} context receiver" + else -> "???" + } + val declarationDescription = when (declarationElement) { + is KtClass -> "class ${declarationElement.name}" + is KtNamedFunction -> "function ${declarationElement.name}" + is KtProperty -> "property ${declarationElement.name}" + is KtNamedDeclaration -> "declaration with name ${declarationElement.name}" + else -> "unknown declaration" + } + trace.report(LABEL_RESOLVE_WILL_CHANGE.on(target, declarationDescription, closestDescription)) + } + class LabeledReceiverResolutionResult private constructor( val code: Code, private val receiverParameterDescriptor: ReceiverParameterDescriptor? diff --git a/compiler/testData/diagnostics/tests/labels/labeledFunctionLiteral.kt b/compiler/testData/diagnostics/tests/labels/labeledFunctionLiteral.kt index c058084c4cb..917da7d61b8 100644 --- a/compiler/testData/diagnostics/tests/labels/labeledFunctionLiteral.kt +++ b/compiler/testData/diagnostics/tests/labels/labeledFunctionLiteral.kt @@ -12,6 +12,6 @@ fun test() { fun lambda() { val funLit = lambda@ fun String.(): String { - return this@lambda + return this@lambda } -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/labelClashes.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/labelClashes.fir.kt new file mode 100644 index 00000000000..50824c20ceb --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/labelClashes.fir.kt @@ -0,0 +1,41 @@ +fun Int.with() { + with("") { + this@with.inc() + } +} + +fun Int.bar() { + with("") bar@{ + this@bar.inc() + } +} + +fun foo(f: with.() -> Unit) {} + +class with { + fun foo() { + with("") { + this@with.foo() + } + + with("") with@{ + this@with.foo() + } + + with("") other@{ + this@with.foo() + } + } +} + +private typealias Extension = TypedThis + +class TypedThis { + fun TypedThis.baz() { + this@TypedThis + } + + fun Extension.bar() { + this@TypedThis + } +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/labelClashes.kt b/compiler/testData/diagnostics/testsWithStdLib/labelClashes.kt new file mode 100644 index 00000000000..fb1bee7efde --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/labelClashes.kt @@ -0,0 +1,41 @@ +fun Int.with() { + with("") { + this@with.inc() + } +} + +fun Int.bar() { + with("") bar@{ + this@bar.inc() + } +} + +fun foo(f: with.() -> Unit) {} + +class with { + fun foo() { + with("") { + this@with.foo() + } + + with("") with@{ + this@with.foo() + } + + with("") other@{ + this@with.foo() + } + } +} + +private typealias Extension = TypedThis + +class TypedThis { + fun TypedThis.baz() { + this@TypedThis + } + + fun Extension.bar() { + this@TypedThis + } +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/labelClashes.txt b/compiler/testData/diagnostics/testsWithStdLib/labelClashes.txt new file mode 100644 index 00000000000..0d4f6312173 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/labelClashes.txt @@ -0,0 +1,23 @@ +package + +public fun foo(/*0*/ f: with.() -> kotlin.Unit): kotlin.Unit +public fun kotlin.Int.bar(): kotlin.Unit +public fun kotlin.Int.with(): kotlin.Unit + +public final class TypedThis { + public constructor TypedThis() + 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 fun Extension /* = TypedThis */.bar(): kotlin.Unit + public final fun TypedThis.baz(): kotlin.Unit +} + +public final class with { + public constructor with() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foo(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} +private typealias Extension = TypedThis diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 3b463e3864a..ca73c6f2fb7 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -33659,6 +33659,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/testsWithStdLib/kt9985.kt"); } + @Test + @TestMetadata("labelClashes.kt") + public void testLabelClashes() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/labelClashes.kt"); + } + @Test @TestMetadata("outstar.kt") public void testOutstar() throws Exception {