diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestWithStdlibGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestWithStdlibGenerated.java index 285422da074..eba1dff4e61 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestWithStdlibGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestWithStdlibGenerated.java @@ -1174,6 +1174,11 @@ public class FirOldFrontendDiagnosticsTestWithStdlibGenerated extends AbstractFi KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); } + @TestMetadata("accessToOuterThis.kt") + public void testAccessToOuterThis() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/accessToOuterThis.kt"); + } + public void testAllFilesPresentInErrors() throws Exception { KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/PsiContractParserDispatcher.kt b/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/PsiContractParserDispatcher.kt index 21a51bc3c42..e5ef97788da 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/PsiContractParserDispatcher.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/contracts/parsing/PsiContractParserDispatcher.kt @@ -133,9 +133,18 @@ internal class PsiContractParserDispatcher( return null } - if (descriptor is ReceiverParameterDescriptor && descriptor.type.constructor.declarationDescriptor?.isFromContractDsl() == true) { - collector.badDescription("only references to parameters are allowed. Did you miss label on ?", expression) - return null + if (descriptor is ReceiverParameterDescriptor) { + if (descriptor.type.constructor.declarationDescriptor?.isFromContractDsl() == true) { + collector.badDescription("only references to parameters are allowed. Did you miss label on ?", expression) + return null + } + val directReceiver = callContext.functionDescriptor.let { + it.extensionReceiverParameter ?: it.dispatchReceiverParameter + } + if (descriptor != directReceiver) { + collector.badDescription("only references to direct are allowed", expression) + return null + } } return if (KotlinBuiltIns.isBoolean(descriptor.type)) diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/accessToOuterThis.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/accessToOuterThis.fir.kt new file mode 100644 index 00000000000..e40ee556980 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/accessToOuterThis.fir.kt @@ -0,0 +1,41 @@ +// !LANGUAGE: +AllowContractsForCustomFunctions +UseReturnsEffect +AllowContractsForNonOverridableMembers +// !USE_EXPERIMENTAL: kotlin.contracts.ExperimentalContracts +// !DIAGNOSTICS: -INVISIBLE_REFERENCE -INVISIBLE_MEMBER -SENSELESS_COMPARISON + +import kotlin.contracts.* + +interface A + +class Foo { + inner class Bar { + fun good() { + contract { + returns() implies (this@Bar != null) + } + } + + fun badOuter() { + contract { + returns() implies (this@Foo != null) + } + } + + fun badInner() { + contract { + returns() implies (this != null) + } + } + + fun A?.goodWithReceiver() { + contract { + returns() implies (this@goodWithReceiver != null) + } + } + + fun A?.badWithReceiver() { + contract { + returns() implies (this@Bar != null) + } + } + } +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/accessToOuterThis.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/accessToOuterThis.kt new file mode 100644 index 00000000000..1abacf2dfa0 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/accessToOuterThis.kt @@ -0,0 +1,41 @@ +// !LANGUAGE: +AllowContractsForCustomFunctions +UseReturnsEffect +AllowContractsForNonOverridableMembers +// !USE_EXPERIMENTAL: kotlin.contracts.ExperimentalContracts +// !DIAGNOSTICS: -INVISIBLE_REFERENCE -INVISIBLE_MEMBER -SENSELESS_COMPARISON + +import kotlin.contracts.* + +interface A + +class Foo { + inner class Bar { + fun good() { + contract { + returns() implies (this@Bar != null) + } + } + + fun badOuter() { + contract { + returns() implies ( are allowed")!>this@Foo != null) + } + } + + fun badInner() { + contract { + returns() implies (?")!>this != null) + } + } + + fun A?.goodWithReceiver() { + contract { + returns() implies (this@goodWithReceiver != null) + } + } + + fun A?.badWithReceiver() { + contract { + returns() implies ( are allowed")!>this@Bar != null) + } + } + } +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/accessToOuterThis.txt b/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/accessToOuterThis.txt new file mode 100644 index 00000000000..221ab154af3 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/accessToOuterThis.txt @@ -0,0 +1,30 @@ +package + +public interface A { + 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 Foo { + public constructor Foo() + 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 inner class Bar { + public constructor Bar() + public final fun badInner(): kotlin.Unit + public final fun badOuter(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun good(): kotlin.Unit + Returns(WILDCARD) -> != null + + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + public final fun A?.badWithReceiver(): kotlin.Unit + public final fun A?.goodWithReceiver(): kotlin.Unit + Returns(WILDCARD) -> != null + + } +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java index 7f1da65a874..334b6186903 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java @@ -1175,6 +1175,11 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); } + @TestMetadata("accessToOuterThis.kt") + public void testAccessToOuterThis() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/accessToOuterThis.kt"); + } + public void testAllFilesPresentInErrors() throws Exception { KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java index e788244c988..b7e6f16270a 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java @@ -1175,6 +1175,11 @@ public class DiagnosticsTestWithStdLibUsingJavacGenerated extends AbstractDiagno KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); } + @TestMetadata("accessToOuterThis.kt") + public void testAccessToOuterThis() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/accessToOuterThis.kt"); + } + public void testAllFilesPresentInErrors() throws Exception { KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); }