Effects: add test on (de)serialization of contracts

- Add ContractDescriptorRenderer
- Add option to dump function contracts in DescriptorRendererOptions
- Add parsing of LANGUAGE_VERSION directive in AbstractLoadJava
- Add tests on serialization-deserializaton identity of contracts

==========
Introduction of EffectSystem: 13/18
This commit is contained in:
Dmitry Savvinov
2017-10-03 15:02:46 +03:00
parent 75ad20f823
commit d20c770a25
29 changed files with 699 additions and 19 deletions
@@ -0,0 +1,96 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.contracts.description
import org.jetbrains.kotlin.contracts.description.expressions.*
class ContractDescriptionRenderer(private val builder: StringBuilder) : ContractDescriptionVisitor<Unit, Unit> {
override fun visitConditionalEffectDeclaration(conditionalEffect: ConditionalEffectDeclaration, data: Unit) {
conditionalEffect.effect.accept(this, data)
builder.append(" -> ")
conditionalEffect.condition.accept(this, data)
}
override fun visitReturnsEffectDeclaration(returnsEffect: ReturnsEffectDeclaration, data: Unit) {
builder.append("Returns(")
returnsEffect.value.accept(this, data)
builder.append(")")
}
override fun visitCallsEffectDeclaration(callsEffect: CallsEffectDeclaration, data: Unit) {
builder.append("CallsInPlace(")
callsEffect.variableReference.accept(this, data)
builder.append(", ${callsEffect.kind})")
}
override fun visitLogicalOr(logicalOr: LogicalOr, data: Unit) {
inBracketsIfNecessary(logicalOr, logicalOr.left) { logicalOr.left.accept(this, data) }
builder.append(" || ")
inBracketsIfNecessary(logicalOr, logicalOr.right) { logicalOr.right.accept(this, data) }
}
override fun visitLogicalAnd(logicalAnd: LogicalAnd, data: Unit) {
inBracketsIfNecessary(logicalAnd, logicalAnd.left) { logicalAnd.left.accept(this, data) }
builder.append(" && ")
inBracketsIfNecessary(logicalAnd, logicalAnd.right) { logicalAnd.right.accept(this, data) }
}
override fun visitLogicalNot(logicalNot: LogicalNot, data: Unit) {
inBracketsIfNecessary(logicalNot, logicalNot.arg) { builder.append("!") }
logicalNot.arg.accept(this, data)
}
override fun visitIsInstancePredicate(isInstancePredicate: IsInstancePredicate, data: Unit) {
isInstancePredicate.arg.accept(this, data)
builder.append(" ${if (isInstancePredicate.isNegated) "!" else ""}is ${isInstancePredicate.type}")
}
override fun visitIsNullPredicate(isNullPredicate: IsNullPredicate, data: Unit) {
isNullPredicate.arg.accept(this, data)
builder.append(" ${if (isNullPredicate.isNegated) "!=" else "=="} null")
}
override fun visitConstantDescriptor(constantReference: ConstantReference, data: Unit) {
builder.append(constantReference.name)
}
override fun visitVariableReference(variableReference: VariableReference, data: Unit) {
builder.append(variableReference.descriptor.name)
}
private fun ContractDescriptionElement.isAtom(): Boolean =
this is VariableReference || this is ConstantReference || this is IsNullPredicate || this is IsInstancePredicate
private fun needsBrackets(parent: ContractDescriptionElement, child: ContractDescriptionElement): Boolean {
if (child.isAtom()) return false
if (parent is LogicalNot) return true
return parent::class != child::class
}
private fun inBracketsIfNecessary(parent: ContractDescriptionElement, child: ContractDescriptionElement, block: () -> Unit) {
if (needsBrackets(parent, child)) {
builder.append("(")
block()
builder.append(")")
}
else {
block()
}
}
}
@@ -0,0 +1,24 @@
// LANGUAGE_VERSION: 1.3
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package test
import kotlin.internal.contracts.*
fun <X, Y, Z, R> callsEffectWithKind(block: (X, Y, Z) -> R) {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
}
inline fun callsEffectImplicitUnknown(x: Int, y: Int, block: () -> Unit) {
contract {
callsInPlace(block)
}
}
inline fun callsEffectExplicitUnknown(x: Int, block: () -> Unit) {
contract {
callsInPlace(block, InvocationKind.UNKNOWN)
}
}
@@ -0,0 +1,10 @@
package test
public inline fun callsEffectExplicitUnknown(/*0*/ x: kotlin.Int, /*1*/ block: () -> kotlin.Unit): kotlin.Unit
CallsInPlace(block, UNKNOWN)
public inline fun callsEffectImplicitUnknown(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int, /*2*/ block: () -> kotlin.Unit): kotlin.Unit
CallsInPlace(block, UNKNOWN)
public fun </*0*/ X, /*1*/ Y, /*2*/ Z, /*3*/ R> callsEffectWithKind(/*0*/ block: (X, Y, Z) -> R): kotlin.Unit
CallsInPlace(block, EXACTLY_ONCE)
@@ -0,0 +1,14 @@
// LANGUAGE_VERSION: 1.3
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package test
import kotlin.internal.contracts.*
class A
fun deeplyNested(x: Any?, y: Any?, b: Boolean, s: String?) {
contract {
returns(true) implies (((x is Int && x is String) || (x is Int && y is A) || b || (!b)) && s != null && (y is A || x is String))
}
}
@@ -0,0 +1,8 @@
package test
public fun deeplyNested(/*0*/ x: kotlin.Any?, /*1*/ y: kotlin.Any?, /*2*/ b: kotlin.Boolean, /*3*/ s: kotlin.String?): kotlin.Unit
Returns(TRUE) -> ((x is Int && x is String) || (x is Int && y is A) || b || (!b)) && s != null && (y is A || x is String)
public final class A {
/*primary*/ public constructor A()
}
@@ -0,0 +1,33 @@
// LANGUAGE_VERSION: 1.3
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package test
import kotlin.internal.contracts.*
// this tests specifically use primitive condition (predicate/value) as the
// first argument of sequence, so that it would be optimized and embedded into message
fun embedVariable(x: Any, b: Boolean) {
contract {
returns() implies (b && x is String)
}
}
fun embedInstancePredicate(x: Any, y: Any?) {
contract {
returns() implies (x is String && y is String)
}
}
fun embedNullCheckPredicate(x: Any?, y: Int?) {
contract {
returns() implies (y != null && x is String)
}
}
fun Boolean.embedReceiverReference(b: Boolean) {
contract {
returns() implies (!this@embedReceiverReference && b)
}
}
@@ -0,0 +1,13 @@
package test
public fun embedInstancePredicate(/*0*/ x: kotlin.Any, /*1*/ y: kotlin.Any?): kotlin.Unit
Returns(WILDCARD) -> x is String && y is String
public fun embedNullCheckPredicate(/*0*/ x: kotlin.Any?, /*1*/ y: kotlin.Int?): kotlin.Unit
Returns(WILDCARD) -> y != null && x is String
public fun embedVariable(/*0*/ x: kotlin.Any, /*1*/ b: kotlin.Boolean): kotlin.Unit
Returns(WILDCARD) -> b && x is String
public fun kotlin.Boolean.embedReceiverReference(/*0*/ b: kotlin.Boolean): kotlin.Unit
Returns(WILDCARD) -> (!<this>) && b
@@ -0,0 +1,65 @@
// LANGUAGE_VERSION: 1.3
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package test
import kotlin.internal.contracts.*
public inline fun <R> run(block: () -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
public inline fun <T, R> T.run(block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return receiver.block()
}
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
public inline fun <T> T.also(block: (T) -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block(this)
return this
}
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? {
contract {
callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
}
return if (predicate(this)) this else null
}
public inline fun repeat(times: Int, action: (Int) -> Unit) {
contract { callsInPlace(action) }
for (index in 0..times - 1) {
action(index)
}
}
@@ -0,0 +1,25 @@
package test
public inline fun repeat(/*0*/ times: kotlin.Int, /*1*/ action: (kotlin.Int) -> kotlin.Unit): kotlin.Unit
CallsInPlace(action, UNKNOWN)
public inline fun </*0*/ R> run(/*0*/ block: () -> R): R
CallsInPlace(block, EXACTLY_ONCE)
public inline fun </*0*/ T, /*1*/ R> with(/*0*/ receiver: T, /*1*/ block: T.() -> R): R
CallsInPlace(block, EXACTLY_ONCE)
public inline fun </*0*/ T> T.also(/*0*/ block: (T) -> kotlin.Unit): T
CallsInPlace(block, EXACTLY_ONCE)
public inline fun </*0*/ T> T.apply(/*0*/ block: T.() -> kotlin.Unit): T
CallsInPlace(block, EXACTLY_ONCE)
public inline fun </*0*/ T, /*1*/ R> T.let(/*0*/ block: (T) -> R): R
CallsInPlace(block, EXACTLY_ONCE)
public inline fun </*0*/ T, /*1*/ R> T.run(/*0*/ block: T.() -> R): R
CallsInPlace(block, EXACTLY_ONCE)
public inline fun </*0*/ T> T.takeIf(/*0*/ predicate: (T) -> kotlin.Boolean): T?
CallsInPlace(predicate, EXACTLY_ONCE)
@@ -0,0 +1,20 @@
// LANGUAGE_VERSION: 1.3
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package test
import kotlin.internal.contracts.*
class A
fun simpleIsInstace(x: Any?) {
contract {
returns(true) implies (x is A)
}
}
fun Any?.receiverIsInstance() {
contract {
returns(true) implies (this@receiverIsInstance is A)
}
}
@@ -0,0 +1,11 @@
package test
public fun simpleIsInstace(/*0*/ x: kotlin.Any?): kotlin.Unit
Returns(TRUE) -> x is A
public fun kotlin.Any?.receiverIsInstance(): kotlin.Unit
Returns(TRUE) -> <this> is A
public final class A {
/*primary*/ public constructor A()
}
@@ -0,0 +1,22 @@
// LANGUAGE_VERSION: 1.3
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package test
import kotlin.internal.contracts.*
fun orSequence(x: Any?, y: Any?, b: Boolean) {
contract {
returns() implies (x is String || y is Int || !b)
}
}
class A
class B
fun andSequence(x: Any?, y: Any?, b:Boolean) {
contract {
returns() implies (x is A && x is B && ((y is A) && b))
}
}
@@ -0,0 +1,15 @@
package test
public fun andSequence(/*0*/ x: kotlin.Any?, /*1*/ y: kotlin.Any?, /*2*/ b: kotlin.Boolean): kotlin.Unit
Returns(WILDCARD) -> x is A && x is B && y is A && b
public fun orSequence(/*0*/ x: kotlin.Any?, /*1*/ y: kotlin.Any?, /*2*/ b: kotlin.Boolean): kotlin.Unit
Returns(WILDCARD) -> x is String || y is Int || (!b)
public final class A {
/*primary*/ public constructor A()
}
public final class B {
/*primary*/ public constructor B()
}
@@ -0,0 +1,44 @@
// LANGUAGE_VERSION: 1.3
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package test
import kotlin.internal.contracts.*
fun twoReturnsValue(b: Boolean) {
contract {
returns(true) implies b
returns(false) implies (!b)
}
}
fun threeReturnsValue(b: Boolean) {
contract {
returnsNotNull() implies (b != null)
returns(true) implies (b)
returns(false) implies (!b)
}
}
fun returnsAndFinished(b: Boolean) {
contract {
returns(true) implies (b)
returns() implies (b != null)
returns(false) implies (!b)
}
}
fun returnsAndCalls(b: Boolean, block: () -> Unit) {
contract {
returns(false) implies (!b)
callsInPlace(block)
returns(true) implies (b)
}
}
fun severalCalls(x: () -> Unit, y: () -> Unit) {
contract {
callsInPlace(x, InvocationKind.AT_MOST_ONCE)
callsInPlace(y, InvocationKind.AT_LEAST_ONCE)
}
}
@@ -0,0 +1,24 @@
package test
public fun returnsAndCalls(/*0*/ b: kotlin.Boolean, /*1*/ block: () -> kotlin.Unit): kotlin.Unit
Returns(FALSE) -> !b
CallsInPlace(block, UNKNOWN)
Returns(TRUE) -> b
public fun returnsAndFinished(/*0*/ b: kotlin.Boolean): kotlin.Unit
Returns(TRUE) -> b
Returns(WILDCARD) -> b != null
Returns(FALSE) -> !b
public fun severalCalls(/*0*/ x: () -> kotlin.Unit, /*1*/ y: () -> kotlin.Unit): kotlin.Unit
CallsInPlace(x, AT_MOST_ONCE)
CallsInPlace(y, AT_LEAST_ONCE)
public fun threeReturnsValue(/*0*/ b: kotlin.Boolean): kotlin.Unit
Returns(NOT_NULL) -> b != null
Returns(TRUE) -> b
Returns(FALSE) -> !b
public fun twoReturnsValue(/*0*/ b: kotlin.Boolean): kotlin.Unit
Returns(TRUE) -> b
Returns(FALSE) -> !b
@@ -0,0 +1,20 @@
// LANGUAGE_VERSION: 1.3
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package test
import kotlin.internal.contracts.*
fun foo(x: Any?): Boolean {
contract {
returns() implies (x is String)
}
return bar(x)
}
fun bar(x: Any?): Boolean {
contract {
returns() implies (x is Int)
}
return foo(x)
}
@@ -0,0 +1,8 @@
package test
public fun bar(/*0*/ x: kotlin.Any?): kotlin.Boolean
Returns(WILDCARD) -> x is Int
public fun foo(/*0*/ x: kotlin.Any?): kotlin.Boolean
Returns(WILDCARD) -> x is String
@@ -0,0 +1,13 @@
// LANGUAGE_VERSION: 1.3
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package test
import kotlin.internal.contracts.*
fun foo(n: Int, x: Any?): Boolean {
contract {
returns(true) implies (x is String)
}
return if (n == 0) x is String else foo(n - 1, x)
}
@@ -0,0 +1,4 @@
package test
public fun foo(/*0*/ n: kotlin.Int, /*1*/ x: kotlin.Any?): kotlin.Boolean
Returns(TRUE) -> x is String
@@ -0,0 +1,13 @@
// LANGUAGE_VERSION: 1.3
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package test
import kotlin.internal.contracts.*
fun Any?.isNotNull(): Boolean {
contract {
returns(true) implies (this@isNotNull != null)
}
return this != null
}
@@ -0,0 +1,5 @@
package test
public fun kotlin.Any?.isNotNull(): kotlin.Boolean
Returns(TRUE) -> <this> != null
@@ -26,9 +26,7 @@ import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles;
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM;
import org.jetbrains.kotlin.cli.jvm.config.JvmContentRootsKt;
import org.jetbrains.kotlin.config.CompilerConfiguration;
import org.jetbrains.kotlin.config.ContentRootsKt;
import org.jetbrains.kotlin.config.JVMConfigurationKeys;
import org.jetbrains.kotlin.config.*;
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.descriptors.ModuleDescriptor;
@@ -120,6 +118,8 @@ public abstract class AbstractLoadJavaTest extends TestCaseWithTmpdir {
if (useTypeTableInSerializer) {
configuration.put(JVMConfigurationKeys.USE_TYPE_TABLE, true);
}
updateConfigurationWithLanguageVersionDirective(ktFile, configuration);
KotlinCoreEnvironment environment =
KotlinCoreEnvironment.createForTests(getTestRootDisposable(), configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES);
registerJavacIfNeeded(environment);
@@ -129,7 +129,8 @@ public abstract class AbstractLoadJavaTest extends TestCaseWithTmpdir {
Assert.assertEquals("test", packageFromSource.getName().asString());
PackageViewDescriptor packageFromBinary = LoadDescriptorUtil.loadTestPackageAndBindingContextFromJavaRoot(
tmpdir, getTestRootDisposable(), getJdkKind(), configurationKind, true, false, useJavacWrapper()
tmpdir, getTestRootDisposable(), getJdkKind(), configurationKind, true, false, useJavacWrapper(),
configuration.get(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS)
).first;
for (DeclarationDescriptor descriptor : DescriptorUtils.getAllDescriptors(packageFromBinary.getMemberScope())) {
@@ -140,10 +141,21 @@ public abstract class AbstractLoadJavaTest extends TestCaseWithTmpdir {
DescriptorValidator.validate(errorTypesForbidden(), packageFromSource);
DescriptorValidator.validate(new DeserializedScopeValidationVisitor(), packageFromBinary);
Configuration comparatorConfiguration = COMPARATOR_CONFIGURATION.checkPrimaryConstructors(true).checkPropertyAccessors(true);
Configuration comparatorConfiguration = COMPARATOR_CONFIGURATION.checkPrimaryConstructors(true).checkPropertyAccessors(true).checkFunctionContracts(true);
compareDescriptors(packageFromSource, packageFromBinary, comparatorConfiguration, txtFile);
}
private static void updateConfigurationWithLanguageVersionDirective(File file, CompilerConfiguration configuration) throws IOException {
String version = InTextDirectivesUtils.findStringWithPrefixes(FileUtil.loadFile(file, true), "// LANGUAGE_VERSION:");
if (version != null) {
LanguageVersion explicitVersion = LanguageVersion.fromVersionString(version);
CommonConfigurationKeysKt.setLanguageVersionSettings(
configuration,
new LanguageVersionSettingsImpl(explicitVersion, ApiVersion.createByLanguageVersion(explicitVersion))
);
}
}
protected boolean useFastClassFilesReading() {
return false;
}
@@ -222,7 +234,7 @@ public abstract class AbstractLoadJavaTest extends TestCaseWithTmpdir {
Pair<PackageViewDescriptor, BindingContext> javaPackageAndContext = loadTestPackageAndBindingContextFromJavaRoot(
tmpdir, getTestRootDisposable(), getJdkKind(), ConfigurationKind.JDK_ONLY, false,
false, useJavacWrapper());
false, useJavacWrapper(), null);
checkJavaPackage(
expectedFile, javaPackageAndContext.first, javaPackageAndContext.second,
@@ -267,7 +279,7 @@ public abstract class AbstractLoadJavaTest extends TestCaseWithTmpdir {
) throws IOException {
compileJavaWithAnnotationsJar(javaFiles, outDir);
return loadTestPackageAndBindingContextFromJavaRoot(outDir, myTestRootDisposable, getJdkKind(), configurationKind, true,
useFastClassFilesReading(), useJavacWrapper());
useFastClassFilesReading(), useJavacWrapper(), null);
}
private static void checkJavaPackage(
@@ -25,6 +25,7 @@ import kotlin.io.FilesKt;
import kotlin.sequences.SequencesKt;
import kotlin.text.Charsets;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.analyzer.AnalysisResult;
import org.jetbrains.kotlin.cli.common.output.outputUtils.OutputUtilsKt;
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles;
@@ -32,8 +33,10 @@ import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
import org.jetbrains.kotlin.codegen.GenerationUtils;
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.config.CommonConfigurationKeys;
import org.jetbrains.kotlin.config.CompilerConfiguration;
import org.jetbrains.kotlin.config.JVMConfigurationKeys;
import org.jetbrains.kotlin.config.LanguageVersionSettings;
import org.jetbrains.kotlin.descriptors.ModuleDescriptor;
import org.jetbrains.kotlin.descriptors.PackageViewDescriptor;
import org.jetbrains.kotlin.jvm.compiler.javac.JavacRegistrarForTests;
@@ -80,7 +83,8 @@ public class LoadDescriptorUtil {
@NotNull ConfigurationKind configurationKind,
boolean isBinaryRoot,
boolean useFastClassReading,
boolean useJavacWrapper
boolean useJavacWrapper,
@Nullable LanguageVersionSettings explicitLanguageVersionSettings
) {
List<File> javaBinaryRoots = new ArrayList<>();
javaBinaryRoots.add(KotlinTestUtils.getAnnotationsJar());
@@ -97,6 +101,9 @@ public class LoadDescriptorUtil {
KotlinTestUtils.newConfiguration(configurationKind, testJdkKind, javaBinaryRoots, javaSourceRoots);
configuration.put(JVMConfigurationKeys.USE_FAST_CLASS_FILES_READING, useFastClassReading);
configuration.put(JVMConfigurationKeys.USE_JAVAC, useJavacWrapper);
if (explicitLanguageVersionSettings != null) {
configuration.put(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS, explicitLanguageVersionSettings);
}
KotlinCoreEnvironment environment =
KotlinCoreEnvironment.createForTests(disposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES);
if (useJavacWrapper) {
@@ -97,6 +97,7 @@ abstract class AbstractJvmRuntimeDescriptorLoaderTest : TestCaseWithTmpdir() {
/* checkPropertyAccessors = */ true,
/* includeMethodsOfKotlinAny = */ false,
/* renderDeclarationsFromOtherModules = */ true,
/* checkFunctionContract = */ false,
// Skip Java annotation constructors because order of their parameters is not retained at runtime
{ descriptor -> !descriptor!!.isJavaAnnotationConstructor() },
errorTypesForbidden(), renderer
@@ -109,7 +110,7 @@ abstract class AbstractJvmRuntimeDescriptorLoaderTest : TestCaseWithTmpdir() {
}
val expected = LoadDescriptorUtil.loadTestPackageAndBindingContextFromJavaRoot(
tmpdir, testRootDisposable, jdkKind, ConfigurationKind.ALL, true, false, false
tmpdir, testRootDisposable, jdkKind, ConfigurationKind.ALL, true, false, false, null
).first
RecursiveDescriptorComparator.validateAndCompareDescriptors(expected, actual, comparatorConfiguration, null)
@@ -22,6 +22,7 @@ import kotlin.Unit;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.contracts.description.*;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.impl.SubpackagesScope;
import org.jetbrains.kotlin.jvm.compiler.ExpectedLoadErrorsUtil;
@@ -63,12 +64,12 @@ public class RecursiveDescriptorComparator {
);
public static final Configuration DONT_INCLUDE_METHODS_OF_OBJECT = new Configuration(false, false, false, false,
descriptor -> true, errorTypesForbidden(), DEFAULT_RENDERER);
false, descriptor -> true, errorTypesForbidden(), DEFAULT_RENDERER);
public static final Configuration RECURSIVE = new Configuration(false, false, true, false,
descriptor -> true, errorTypesForbidden(), DEFAULT_RENDERER);
false, descriptor -> true, errorTypesForbidden(), DEFAULT_RENDERER);
public static final Configuration RECURSIVE_ALL = new Configuration(true, true, true, false,
descriptor -> true, errorTypesForbidden(), DEFAULT_RENDERER);
true, descriptor -> true, errorTypesForbidden(), DEFAULT_RENDERER);
public static final Predicate<DeclarationDescriptor> SKIP_BUILT_INS_PACKAGES = descriptor -> {
if (descriptor instanceof PackageViewDescriptor) {
@@ -111,6 +112,11 @@ public class RecursiveDescriptorComparator {
boolean isPrimaryConstructor = descriptor instanceof ConstructorDescriptor && ((ConstructorDescriptor) descriptor).isPrimary();
printer.print(isPrimaryConstructor && conf.checkPrimaryConstructors ? "/*primary*/ " : "", conf.renderer.render(descriptor));
if (descriptor instanceof FunctionDescriptor && conf.checkFunctionContracts) {
FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor;
printEffectsIfAny(functionDescriptor, printer);
}
if (isClassOrPackage) {
if (!topLevel) {
printer.printlnWithNoIndent(" {").pushIndent();
@@ -171,6 +177,24 @@ public class RecursiveDescriptorComparator {
}
}
private void printEffectsIfAny(FunctionDescriptor functionDescriptor, Printer printer) {
LazyContractProvider contractProvider = functionDescriptor.getUserData(ContractProviderKey.INSTANCE);
if (contractProvider == null) return;
ContractDescription contractDescription = contractProvider.getContractDescription();
if (contractDescription == null || contractDescription.getEffects().isEmpty()) return;
printer.println();
printer.pushIndent();
for (EffectDeclaration effect : contractDescription.getEffects()) {
StringBuilder sb = new StringBuilder();
ContractDescriptionRenderer renderer = new ContractDescriptionRenderer(sb);
effect.accept(renderer, Unit.INSTANCE);
printer.println(sb.toString());
}
printer.popIndent();
}
@NotNull
private MemberScope getPackageScopeInModule(@NotNull PackageViewDescriptor descriptor, @NotNull ModuleDescriptor module) {
// See LazyPackageViewDescriptorImpl#memberScope
@@ -298,6 +322,7 @@ public class RecursiveDescriptorComparator {
private final boolean checkPropertyAccessors;
private final boolean includeMethodsOfKotlinAny;
private final boolean renderDeclarationsFromOtherModules;
private final boolean checkFunctionContracts;
private final Predicate<DeclarationDescriptor> recursiveFilter;
private final DescriptorRenderer renderer;
private final DescriptorValidator.ValidationVisitor validationStrategy;
@@ -307,6 +332,7 @@ public class RecursiveDescriptorComparator {
boolean checkPropertyAccessors,
boolean includeMethodsOfKotlinAny,
boolean renderDeclarationsFromOtherModules,
boolean checkFunctionContracts,
Predicate<DeclarationDescriptor> recursiveFilter,
DescriptorValidator.ValidationVisitor validationStrategy,
DescriptorRenderer renderer
@@ -315,6 +341,7 @@ public class RecursiveDescriptorComparator {
this.checkPropertyAccessors = checkPropertyAccessors;
this.includeMethodsOfKotlinAny = includeMethodsOfKotlinAny;
this.renderDeclarationsFromOtherModules = renderDeclarationsFromOtherModules;
this.checkFunctionContracts = checkFunctionContracts;
this.recursiveFilter = recursiveFilter;
this.validationStrategy = validationStrategy;
this.renderer = renderer;
@@ -322,38 +349,43 @@ public class RecursiveDescriptorComparator {
public Configuration filterRecursion(@NotNull Predicate<DeclarationDescriptor> stepIntoFilter) {
return new Configuration(checkPrimaryConstructors, checkPropertyAccessors, includeMethodsOfKotlinAny,
renderDeclarationsFromOtherModules, stepIntoFilter,
renderDeclarationsFromOtherModules, checkFunctionContracts, stepIntoFilter,
validationStrategy.withStepIntoFilter(stepIntoFilter), renderer);
}
public Configuration checkPrimaryConstructors(boolean checkPrimaryConstructors) {
return new Configuration(checkPrimaryConstructors, checkPropertyAccessors, includeMethodsOfKotlinAny,
renderDeclarationsFromOtherModules, recursiveFilter, validationStrategy, renderer);
renderDeclarationsFromOtherModules, checkFunctionContracts, recursiveFilter, validationStrategy, renderer);
}
public Configuration checkPropertyAccessors(boolean checkPropertyAccessors) {
return new Configuration(checkPrimaryConstructors, checkPropertyAccessors, includeMethodsOfKotlinAny,
renderDeclarationsFromOtherModules, recursiveFilter, validationStrategy, renderer);
renderDeclarationsFromOtherModules, checkFunctionContracts, recursiveFilter, validationStrategy, renderer);
}
public Configuration checkFunctionContracts(boolean checkFunctionContracts) {
return new Configuration(checkPrimaryConstructors, checkPropertyAccessors, includeMethodsOfKotlinAny,
renderDeclarationsFromOtherModules, checkFunctionContracts, recursiveFilter, validationStrategy, renderer);
}
public Configuration includeMethodsOfKotlinAny(boolean includeMethodsOfKotlinAny) {
return new Configuration(checkPrimaryConstructors, checkPropertyAccessors, includeMethodsOfKotlinAny,
renderDeclarationsFromOtherModules, recursiveFilter, validationStrategy, renderer);
renderDeclarationsFromOtherModules, checkFunctionContracts, recursiveFilter, validationStrategy, renderer);
}
public Configuration renderDeclarationsFromOtherModules(boolean renderDeclarationsFromOtherModules) {
return new Configuration(checkPrimaryConstructors, checkPropertyAccessors, includeMethodsOfKotlinAny,
renderDeclarationsFromOtherModules, recursiveFilter, validationStrategy, renderer);
renderDeclarationsFromOtherModules, checkFunctionContracts, recursiveFilter, validationStrategy, renderer);
}
public Configuration withValidationStrategy(@NotNull DescriptorValidator.ValidationVisitor validationStrategy) {
return new Configuration(checkPrimaryConstructors, checkPropertyAccessors, includeMethodsOfKotlinAny,
renderDeclarationsFromOtherModules, recursiveFilter, validationStrategy, renderer);
renderDeclarationsFromOtherModules, checkFunctionContracts, recursiveFilter, validationStrategy, renderer);
}
public Configuration withRenderer(@NotNull DescriptorRenderer renderer) {
return new Configuration(checkPrimaryConstructors, checkPropertyAccessors, includeMethodsOfKotlinAny,
renderDeclarationsFromOtherModules, recursiveFilter, validationStrategy, renderer);
renderDeclarationsFromOtherModules, checkFunctionContracts, recursiveFilter, validationStrategy, renderer);
}
}
}
@@ -4976,6 +4976,75 @@ public class LoadJavaTestGenerated extends AbstractLoadJavaTest {
}
}
@TestMetadata("compiler/testData/loadJava/compiledKotlinWithStdlib/contracts")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Contracts extends AbstractLoadJavaTest {
public void testAllFilesPresentInContracts() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/loadJava/compiledKotlinWithStdlib/contracts"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("callsEffect.kt")
public void testCallsEffect() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlinWithStdlib/contracts/callsEffect.kt");
doTestCompiledKotlinWithStdlib(fileName);
}
@TestMetadata("deeplyNestedExpression.kt")
public void testDeeplyNestedExpression() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlinWithStdlib/contracts/deeplyNestedExpression.kt");
doTestCompiledKotlinWithStdlib(fileName);
}
@TestMetadata("embedding.kt")
public void testEmbedding() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlinWithStdlib/contracts/embedding.kt");
doTestCompiledKotlinWithStdlib(fileName);
}
@TestMetadata("fromStandardKt.kt")
public void testFromStandardKt() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlinWithStdlib/contracts/fromStandardKt.kt");
doTestCompiledKotlinWithStdlib(fileName);
}
@TestMetadata("isInstancePredicate.kt")
public void testIsInstancePredicate() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlinWithStdlib/contracts/isInstancePredicate.kt");
doTestCompiledKotlinWithStdlib(fileName);
}
@TestMetadata("logicOperators.kt")
public void testLogicOperators() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlinWithStdlib/contracts/logicOperators.kt");
doTestCompiledKotlinWithStdlib(fileName);
}
@TestMetadata("multieffectContracts.kt")
public void testMultieffectContracts() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlinWithStdlib/contracts/multieffectContracts.kt");
doTestCompiledKotlinWithStdlib(fileName);
}
@TestMetadata("mutualRecursion.kt")
public void testMutualRecursion() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlinWithStdlib/contracts/mutualRecursion.kt");
doTestCompiledKotlinWithStdlib(fileName);
}
@TestMetadata("recursion.kt")
public void testRecursion() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlinWithStdlib/contracts/recursion.kt");
doTestCompiledKotlinWithStdlib(fileName);
}
@TestMetadata("withReceiver.kt")
public void testWithReceiver() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlinWithStdlib/contracts/withReceiver.kt");
doTestCompiledKotlinWithStdlib(fileName);
}
}
@TestMetadata("compiler/testData/loadJava/compiledKotlinWithStdlib/coroutines")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -4976,6 +4976,75 @@ public class LoadJavaUsingJavacTestGenerated extends AbstractLoadJavaUsingJavacT
}
}
@TestMetadata("compiler/testData/loadJava/compiledKotlinWithStdlib/contracts")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Contracts extends AbstractLoadJavaUsingJavacTest {
public void testAllFilesPresentInContracts() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/loadJava/compiledKotlinWithStdlib/contracts"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("callsEffect.kt")
public void testCallsEffect() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlinWithStdlib/contracts/callsEffect.kt");
doTestCompiledKotlinWithStdlib(fileName);
}
@TestMetadata("deeplyNestedExpression.kt")
public void testDeeplyNestedExpression() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlinWithStdlib/contracts/deeplyNestedExpression.kt");
doTestCompiledKotlinWithStdlib(fileName);
}
@TestMetadata("embedding.kt")
public void testEmbedding() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlinWithStdlib/contracts/embedding.kt");
doTestCompiledKotlinWithStdlib(fileName);
}
@TestMetadata("fromStandardKt.kt")
public void testFromStandardKt() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlinWithStdlib/contracts/fromStandardKt.kt");
doTestCompiledKotlinWithStdlib(fileName);
}
@TestMetadata("isInstancePredicate.kt")
public void testIsInstancePredicate() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlinWithStdlib/contracts/isInstancePredicate.kt");
doTestCompiledKotlinWithStdlib(fileName);
}
@TestMetadata("logicOperators.kt")
public void testLogicOperators() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlinWithStdlib/contracts/logicOperators.kt");
doTestCompiledKotlinWithStdlib(fileName);
}
@TestMetadata("multieffectContracts.kt")
public void testMultieffectContracts() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlinWithStdlib/contracts/multieffectContracts.kt");
doTestCompiledKotlinWithStdlib(fileName);
}
@TestMetadata("mutualRecursion.kt")
public void testMutualRecursion() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlinWithStdlib/contracts/mutualRecursion.kt");
doTestCompiledKotlinWithStdlib(fileName);
}
@TestMetadata("recursion.kt")
public void testRecursion() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlinWithStdlib/contracts/recursion.kt");
doTestCompiledKotlinWithStdlib(fileName);
}
@TestMetadata("withReceiver.kt")
public void testWithReceiver() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlinWithStdlib/contracts/withReceiver.kt");
doTestCompiledKotlinWithStdlib(fileName);
}
}
@TestMetadata("compiler/testData/loadJava/compiledKotlinWithStdlib/coroutines")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -222,6 +222,7 @@ interface DescriptorRendererOptions {
var renderUnabbreviatedType: Boolean
var includeAdditionalModifiers: Boolean
var parameterNamesInFunctionalTypes: Boolean
var renderFunctionContracts: Boolean
}
object ExcludedTypeAnnotations {
@@ -109,4 +109,6 @@ internal class DescriptorRendererOptionsImpl : DescriptorRendererOptions {
override var includeAdditionalModifiers: Boolean by property(true)
override var parameterNamesInFunctionalTypes: Boolean by property(true)
override var renderFunctionContracts: Boolean by property(false)
}