Prohibit using references to this from outer scope in contract declarations

This commit is contained in:
Dmitriy Novozhilov
2019-12-26 13:15:52 +03:00
parent f083edfac2
commit 9c1b68f839
7 changed files with 139 additions and 3 deletions
@@ -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);
}
@@ -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 <this>?", 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 <this>?", expression)
return null
}
val directReceiver = callContext.functionDescriptor.let {
it.extensionReceiverParameter ?: it.dispatchReceiverParameter
}
if (descriptor != directReceiver) {
collector.badDescription("only references to direct <this> are allowed", expression)
return null
}
}
return if (KotlinBuiltIns.isBoolean(descriptor.type))
@@ -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)
}
}
}
}
@@ -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 (<!ERROR_IN_CONTRACT_DESCRIPTION("only references to direct <this> are allowed")!>this@Foo<!> != null)
}
}
fun badInner() {
contract {
returns() implies (<!ERROR_IN_CONTRACT_DESCRIPTION("only references to parameters are allowed. Did you miss label on <this>?")!>this<!> != null)
}
}
fun A?.goodWithReceiver() {
contract {
returns() implies (this@goodWithReceiver != null)
}
}
fun A?.badWithReceiver() {
contract {
returns() implies (<!ERROR_IN_CONTRACT_DESCRIPTION("only references to direct <this> are allowed")!>this@Bar<!> != null)
}
}
}
}
@@ -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) -> <this> != 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) -> <this> != null
}
}
@@ -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);
}
@@ -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);
}