[TEST] Extract compiler-specific test utils from :tests-common to new module
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testCompile(kotlinStdlib("jdk8"))
|
||||
testCompile(project(":kotlin-scripting-compiler"))
|
||||
testCompile(project(":core:descriptors"))
|
||||
testCompile(project(":core:descriptors.jvm"))
|
||||
testCompile(project(":core:deserialization"))
|
||||
testCompile(project(":compiler:util"))
|
||||
testCompile(project(":compiler:tests-mutes"))
|
||||
testCompile(project(":compiler:backend"))
|
||||
testCompile(project(":compiler:ir.ir2cfg"))
|
||||
testCompile(project(":compiler:frontend"))
|
||||
testCompile(project(":compiler:frontend.java"))
|
||||
testCompile(project(":compiler:util"))
|
||||
testCompile(project(":compiler:psi"))
|
||||
testCompile(project(":compiler:cli-common"))
|
||||
testCompile(project(":compiler:cli"))
|
||||
testCompile(project(":compiler:cli-js"))
|
||||
testCompile(project(":compiler:serialization"))
|
||||
testCompile(projectTests(":compiler:test-infrastructure-utils"))
|
||||
testCompileOnly(intellijCoreDep()) { includeJars("intellij-core") }
|
||||
|
||||
testCompile(intellijDep()) {
|
||||
includeJars(
|
||||
"jps-model",
|
||||
"extensions",
|
||||
"util",
|
||||
"platform-api",
|
||||
"platform-impl",
|
||||
"idea",
|
||||
"idea_rt",
|
||||
"guava",
|
||||
"trove4j",
|
||||
"asm-all",
|
||||
"log4j",
|
||||
"jdom",
|
||||
"streamex",
|
||||
"bootstrap",
|
||||
rootProject = rootProject
|
||||
)
|
||||
isTransitive = false
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { none() }
|
||||
"test" { projectDefault() }
|
||||
}
|
||||
|
||||
testsJar {}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.fir
|
||||
|
||||
import org.jetbrains.kotlin.fir.references.FirControlFlowGraphReference
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.FirControlFlowGraphReferenceImpl
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.CFGNode
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraph
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.EdgeKind
|
||||
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
|
||||
import org.jetbrains.kotlin.test.Assertions
|
||||
|
||||
class FirCfgConsistencyChecker(private val assertions: Assertions) : FirVisitorVoid() {
|
||||
override fun visitElement(element: FirElement) {
|
||||
element.acceptChildren(this)
|
||||
}
|
||||
|
||||
override fun visitControlFlowGraphReference(controlFlowGraphReference: FirControlFlowGraphReference) {
|
||||
val graph = (controlFlowGraphReference as? FirControlFlowGraphReferenceImpl)?.controlFlowGraph ?: return
|
||||
assertions.assertEquals(ControlFlowGraph.State.Completed, graph.state)
|
||||
checkConsistency(graph)
|
||||
checkOrder(graph)
|
||||
}
|
||||
|
||||
private fun checkConsistency(graph: ControlFlowGraph) {
|
||||
for (node in graph.nodes) {
|
||||
for (to in node.followingNodes) {
|
||||
checkEdge(node, to)
|
||||
}
|
||||
for (from in node.previousNodes) {
|
||||
checkEdge(from, node)
|
||||
}
|
||||
if (node.followingNodes.isEmpty() && node.previousNodes.isEmpty()) {
|
||||
throw AssertionError("Unconnected CFG node: $node")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val cfgKinds = listOf(EdgeKind.DeadForward, EdgeKind.CfgForward, EdgeKind.DeadBackward, EdgeKind.CfgBackward)
|
||||
|
||||
private fun checkEdge(from: CFGNode<*>, to: CFGNode<*>) {
|
||||
assertions.assertContainsElements(from.followingNodes, to)
|
||||
assertions.assertContainsElements(to.previousNodes, from)
|
||||
val fromKind = from.outgoingEdges.getValue(to).kind
|
||||
val toKind = to.incomingEdges.getValue(from).kind
|
||||
assertions.assertEquals(fromKind, toKind)
|
||||
if (from.isDead && to.isDead) {
|
||||
assertions.assertContainsElements(cfgKinds, toKind)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkOrder(graph: ControlFlowGraph) {
|
||||
val visited = mutableSetOf<CFGNode<*>>()
|
||||
for (node in graph.nodes) {
|
||||
for (previousNode in node.previousNodes) {
|
||||
if (previousNode.owner != graph) continue
|
||||
if (!node.incomingEdges.getValue(previousNode).kind.isBack) {
|
||||
assertions.assertTrue(previousNode in visited)
|
||||
}
|
||||
}
|
||||
visited += node
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.fir
|
||||
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
|
||||
fun ConeKotlinType.renderForDebugInfo(): String {
|
||||
val nullabilitySuffix = if (this !is ConeKotlinErrorType && this !is ConeClassErrorType) nullability.suffix else ""
|
||||
return when (this) {
|
||||
is ConeTypeVariableType -> "TypeVariable(${this.lookupTag.name})"
|
||||
is ConeDefinitelyNotNullType -> "${original.renderForDebugInfo()}!!"
|
||||
is ConeClassErrorType -> "ERROR CLASS: ${diagnostic.reason}"
|
||||
is ConeCapturedType -> "CapturedType(${constructor.projection.renderForDebugInfo()})"
|
||||
is ConeClassLikeType -> {
|
||||
buildString {
|
||||
append(lookupTag.classId.asSingleFqName().asString())
|
||||
if (typeArguments.isNotEmpty()) {
|
||||
append(typeArguments.joinToString(prefix = "<", postfix = ">") {
|
||||
it.renderForDebugInfo()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
is ConeLookupTagBasedType -> {
|
||||
lookupTag.name.asString()
|
||||
}
|
||||
is ConeFlexibleType -> {
|
||||
buildString {
|
||||
append(lowerBound.renderForDebugInfo())
|
||||
append("..")
|
||||
append(upperBound.renderForDebugInfo())
|
||||
}
|
||||
}
|
||||
is ConeIntersectionType -> {
|
||||
intersectedTypes.joinToString(
|
||||
separator = " & ",
|
||||
) { it.renderForDebugInfo() }
|
||||
}
|
||||
is ConeStubType -> "Stub: $variable"
|
||||
is ConeIntegerLiteralType -> "ILT: $value"
|
||||
} + nullabilitySuffix
|
||||
}
|
||||
|
||||
private fun ConeTypeProjection.renderForDebugInfo(): String {
|
||||
return when (this) {
|
||||
ConeStarProjection -> "*"
|
||||
is ConeKotlinTypeProjectionIn -> "in ${type.renderForDebugInfo()}"
|
||||
is ConeKotlinTypeProjectionOut -> "out ${type.renderForDebugInfo()}"
|
||||
is ConeKotlinType -> renderForDebugInfo()
|
||||
}
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.jvm.compiler;
|
||||
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.impl.DeclarationDescriptorVisitorEmptyBodies;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmBindingContextSlices;
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope;
|
||||
import org.jetbrains.kotlin.test.Assertions;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class ExpectedLoadErrorsUtil {
|
||||
public static final String ANNOTATION_CLASS_NAME = "org.jetbrains.kotlin.jvm.compiler.annotation.ExpectLoadError";
|
||||
|
||||
public static void checkForLoadErrors(
|
||||
@NotNull PackageViewDescriptor packageFromJava,
|
||||
@NotNull BindingContext bindingContext,
|
||||
@NotNull Assertions assertions
|
||||
) {
|
||||
Map<SourceElement, List<String>> expectedErrors = getExpectedLoadErrors(packageFromJava);
|
||||
Map<SourceElement, List<String>> actualErrors = getActualLoadErrors(bindingContext);
|
||||
|
||||
for (SourceElement source : ContainerUtil.union(expectedErrors.keySet(), actualErrors.keySet())) {
|
||||
List<String> actual = actualErrors.get(source);
|
||||
List<String> expected = expectedErrors.get(source);
|
||||
|
||||
assertions.assertNotNull(expected, () -> "Unexpected load error(s):\n" + actual + "\ncontainer:" + source);
|
||||
assertions.assertNotNull(actual, () -> "Missing load error(s):\n" + expected + "\ncontainer:" + source);
|
||||
|
||||
assertions.assertSameElements(actual, expected, () -> "Unexpected/missing load error(s)\ncontainer:" + source);
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<SourceElement, List<String>> getExpectedLoadErrors(@NotNull PackageViewDescriptor packageFromJava) {
|
||||
Map<SourceElement, List<String>> map = new HashMap<>();
|
||||
|
||||
packageFromJava.acceptVoid(new DeclarationDescriptorVisitorEmptyBodies<Void, Void>() {
|
||||
@Override
|
||||
public Void visitPackageViewDescriptor(PackageViewDescriptor descriptor, Void data) {
|
||||
return visitDeclarationRecursively(descriptor, descriptor.getMemberScope());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitClassDescriptor(ClassDescriptor descriptor, Void data) {
|
||||
return visitDeclarationRecursively(descriptor, descriptor.getDefaultType().getMemberScope());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitFunctionDescriptor(FunctionDescriptor descriptor, Void data) {
|
||||
return visitDeclaration(descriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitPropertyDescriptor(PropertyDescriptor descriptor, Void data) {
|
||||
return visitDeclaration(descriptor);
|
||||
}
|
||||
|
||||
private Void visitDeclaration(@NotNull DeclarationDescriptor descriptor) {
|
||||
AnnotationDescriptor annotation = descriptor.getAnnotations().findAnnotation(new FqName(ANNOTATION_CLASS_NAME));
|
||||
if (annotation == null) return null;
|
||||
|
||||
// we expect exactly one annotation argument
|
||||
ConstantValue<?> argument = annotation.getAllValueArguments().values().iterator().next();
|
||||
|
||||
String error = (String) argument.getValue();
|
||||
//noinspection ConstantConditions
|
||||
List<String> errors = Arrays.asList(error.split("\\|"));
|
||||
|
||||
putError(map, descriptor, errors);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Void visitDeclarationRecursively(@NotNull DeclarationDescriptor descriptor, @NotNull MemberScope memberScope) {
|
||||
for (DeclarationDescriptor member : DescriptorUtils.getAllDescriptors(memberScope)) {
|
||||
member.acceptVoid(this);
|
||||
}
|
||||
|
||||
return visitDeclaration(descriptor);
|
||||
}
|
||||
});
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
private static Map<SourceElement, List<String>> getActualLoadErrors(@NotNull BindingContext bindingContext) {
|
||||
Map<SourceElement, List<String>> result = new HashMap<>();
|
||||
|
||||
Collection<DeclarationDescriptor> descriptors = bindingContext.getKeys(JvmBindingContextSlices.LOAD_FROM_JAVA_SIGNATURE_ERRORS);
|
||||
for (DeclarationDescriptor descriptor : descriptors) {
|
||||
List<String> errors = bindingContext.get(JvmBindingContextSlices.LOAD_FROM_JAVA_SIGNATURE_ERRORS, descriptor);
|
||||
if (errors == null) continue;
|
||||
|
||||
putError(result, descriptor, errors);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void putError(
|
||||
@NotNull Map<SourceElement, List<String>> result,
|
||||
@NotNull DeclarationDescriptor descriptor,
|
||||
@NotNull List<String> errors
|
||||
) {
|
||||
assert descriptor.getOriginal() instanceof DeclarationDescriptorWithSource
|
||||
: "Signature errors should be reported only on declarations with source, but " + descriptor + " found";
|
||||
result.put(((DeclarationDescriptorWithSource) descriptor.getOriginal()).getSource(), errors);
|
||||
}
|
||||
|
||||
private ExpectedLoadErrorsUtil() {
|
||||
}
|
||||
}
|
||||
+560
@@ -0,0 +1,560 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.util;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
import org.jetbrains.kotlin.types.KotlinTypeKt;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class DescriptorValidator {
|
||||
|
||||
public static void validate(@NotNull ValidationVisitor validationStrategy, DeclarationDescriptor descriptor) {
|
||||
DiagnosticCollectorForTests collector = new DiagnosticCollectorForTests();
|
||||
validate(validationStrategy, descriptor, collector);
|
||||
collector.done();
|
||||
}
|
||||
|
||||
public static void validate(
|
||||
@NotNull ValidationVisitor validator,
|
||||
@NotNull DeclarationDescriptor descriptor,
|
||||
@NotNull DiagnosticCollector collector
|
||||
) {
|
||||
RecursiveDescriptorProcessor.process(descriptor, collector, validator);
|
||||
}
|
||||
|
||||
private static void report(@NotNull DiagnosticCollector collector, @NotNull DeclarationDescriptor descriptor, @NotNull String message) {
|
||||
collector.report(new ValidationDiagnostic(descriptor, message));
|
||||
}
|
||||
|
||||
public interface DiagnosticCollector {
|
||||
void report(@NotNull ValidationDiagnostic diagnostic);
|
||||
}
|
||||
|
||||
public static class ValidationVisitor implements DeclarationDescriptorVisitor<Boolean, DiagnosticCollector> {
|
||||
public static ValidationVisitor errorTypesForbidden() {
|
||||
return new ValidationVisitor();
|
||||
}
|
||||
|
||||
public static ValidationVisitor errorTypesAllowed() {
|
||||
return new ValidationVisitor().allowErrorTypes();
|
||||
}
|
||||
|
||||
private boolean allowErrorTypes = false;
|
||||
private Predicate<DeclarationDescriptor> recursiveFilter = descriptor -> true;
|
||||
|
||||
protected ValidationVisitor() {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ValidationVisitor withStepIntoFilter(@NotNull Predicate<DeclarationDescriptor> filter) {
|
||||
this.recursiveFilter = filter;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ValidationVisitor allowErrorTypes() {
|
||||
this.allowErrorTypes = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
protected void validateScope(DeclarationDescriptor scopeOwner, @NotNull MemberScope scope, @NotNull DiagnosticCollector collector) {
|
||||
for (DeclarationDescriptor descriptor : DescriptorUtils.getAllDescriptors(scope)) {
|
||||
if (recursiveFilter.test(descriptor)) {
|
||||
descriptor.accept(new ScopeValidatorVisitor(collector), scope);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void validateType(
|
||||
@NotNull DeclarationDescriptor descriptor,
|
||||
@Nullable KotlinType type,
|
||||
@NotNull DiagnosticCollector collector
|
||||
) {
|
||||
if (type == null) {
|
||||
report(collector, descriptor, "No type");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!allowErrorTypes && KotlinTypeKt.isError(type)) {
|
||||
report(collector, descriptor, "Error type: " + type);
|
||||
return;
|
||||
}
|
||||
|
||||
validateScope(descriptor, type.getMemberScope(), collector);
|
||||
}
|
||||
|
||||
private void validateReturnType(CallableDescriptor descriptor, DiagnosticCollector collector) {
|
||||
validateType(descriptor, descriptor.getReturnType(), collector);
|
||||
}
|
||||
|
||||
private static void validateTypeParameters(DiagnosticCollector collector, List<TypeParameterDescriptor> parameters) {
|
||||
for (int i = 0; i < parameters.size(); i++) {
|
||||
TypeParameterDescriptor typeParameterDescriptor = parameters.get(i);
|
||||
if (typeParameterDescriptor.getIndex() != i) {
|
||||
report(collector, typeParameterDescriptor, "Incorrect index: " + typeParameterDescriptor.getIndex() + " but must be " + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void validateValueParameters(DiagnosticCollector collector, List<ValueParameterDescriptor> parameters) {
|
||||
for (int i = 0; i < parameters.size(); i++) {
|
||||
ValueParameterDescriptor valueParameterDescriptor = parameters.get(i);
|
||||
if (valueParameterDescriptor.getIndex() != i) {
|
||||
report(collector, valueParameterDescriptor, "Incorrect index: " + valueParameterDescriptor.getIndex() + " but must be " + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void validateTypes(
|
||||
DeclarationDescriptor descriptor,
|
||||
DiagnosticCollector collector,
|
||||
Collection<KotlinType> types
|
||||
) {
|
||||
for (KotlinType type : types) {
|
||||
validateType(descriptor, type, collector);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateCallable(CallableDescriptor descriptor, DiagnosticCollector collector) {
|
||||
validateReturnType(descriptor, collector);
|
||||
validateTypeParameters(collector, descriptor.getTypeParameters());
|
||||
validateValueParameters(collector, descriptor.getValueParameters());
|
||||
}
|
||||
|
||||
private static <T> void assertEquals(
|
||||
DeclarationDescriptor descriptor,
|
||||
DiagnosticCollector collector,
|
||||
String name,
|
||||
T expected,
|
||||
T actual
|
||||
) {
|
||||
if (!expected.equals(actual)) {
|
||||
report(collector, descriptor, "Wrong " + name + ": " + actual + " must be " + expected);
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> void assertEqualTypes(
|
||||
DeclarationDescriptor descriptor,
|
||||
DiagnosticCollector collector,
|
||||
String name,
|
||||
KotlinType expected,
|
||||
KotlinType actual
|
||||
) {
|
||||
if (KotlinTypeKt.isError(expected) && KotlinTypeKt.isError(actual)) {
|
||||
assertEquals(descriptor, collector, name, expected.toString(), actual.toString());
|
||||
}
|
||||
else if (!expected.equals(actual)) {
|
||||
report(collector, descriptor, "Wrong " + name + ": " + actual + " must be " + expected);
|
||||
}
|
||||
}
|
||||
|
||||
private static void validateAccessor(
|
||||
PropertyDescriptor descriptor,
|
||||
DiagnosticCollector collector,
|
||||
PropertyAccessorDescriptor accessor,
|
||||
String name
|
||||
) {
|
||||
// TODO: fix the discrepancies in descriptor construction and enable these checks
|
||||
//assertEquals(accessor, collector, name + " visibility", descriptor.getVisibility(), accessor.getVisibility());
|
||||
//assertEquals(accessor, collector, name + " modality", descriptor.getModality(), accessor.getModality());
|
||||
assertEquals(accessor, collector, "corresponding property", descriptor, accessor.getCorrespondingProperty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitPackageFragmentDescriptor(
|
||||
PackageFragmentDescriptor descriptor, DiagnosticCollector collector
|
||||
) {
|
||||
validateScope(descriptor, descriptor.getMemberScope(), collector);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitPackageViewDescriptor(PackageViewDescriptor descriptor, DiagnosticCollector collector) {
|
||||
if (!recursiveFilter.test(descriptor)) return false;
|
||||
|
||||
validateScope(descriptor, descriptor.getMemberScope(), collector);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitVariableDescriptor(
|
||||
VariableDescriptor descriptor, DiagnosticCollector collector
|
||||
) {
|
||||
validateReturnType(descriptor, collector);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitFunctionDescriptor(
|
||||
FunctionDescriptor descriptor, DiagnosticCollector collector
|
||||
) {
|
||||
validateCallable(descriptor, collector);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitTypeParameterDescriptor(
|
||||
TypeParameterDescriptor descriptor, DiagnosticCollector collector
|
||||
) {
|
||||
validateTypes(descriptor, collector, descriptor.getUpperBounds());
|
||||
|
||||
validateType(descriptor, descriptor.getDefaultType(), collector);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitClassDescriptor(
|
||||
ClassDescriptor descriptor, DiagnosticCollector collector
|
||||
) {
|
||||
validateTypeParameters(collector, descriptor.getDeclaredTypeParameters());
|
||||
|
||||
Collection<KotlinType> supertypes = descriptor.getTypeConstructor().getSupertypes();
|
||||
if (supertypes.isEmpty() && descriptor.getKind() != ClassKind.INTERFACE
|
||||
&& !KotlinBuiltIns.isSpecialClassWithNoSupertypes(descriptor)) {
|
||||
report(collector, descriptor, "No supertypes for non-trait");
|
||||
}
|
||||
validateTypes(descriptor, collector, supertypes);
|
||||
|
||||
validateType(descriptor, descriptor.getDefaultType(), collector);
|
||||
|
||||
validateScope(descriptor, descriptor.getUnsubstitutedInnerClassesScope(), collector);
|
||||
|
||||
List<ConstructorDescriptor> primary = Lists.newArrayList();
|
||||
for (ConstructorDescriptor constructorDescriptor : descriptor.getConstructors()) {
|
||||
if (constructorDescriptor.isPrimary()) {
|
||||
primary.add(constructorDescriptor);
|
||||
}
|
||||
}
|
||||
if (primary.size() > 1) {
|
||||
report(collector, descriptor, "Many primary constructors: " + primary);
|
||||
}
|
||||
|
||||
ConstructorDescriptor primaryConstructor = descriptor.getUnsubstitutedPrimaryConstructor();
|
||||
if (primaryConstructor != null) {
|
||||
if (!descriptor.getConstructors().contains(primaryConstructor)) {
|
||||
report(collector, primaryConstructor,
|
||||
"Primary constructor not in getConstructors() result: " + descriptor.getConstructors());
|
||||
}
|
||||
}
|
||||
|
||||
ClassDescriptor companionObjectDescriptor = descriptor.getCompanionObjectDescriptor();
|
||||
if (companionObjectDescriptor != null && !companionObjectDescriptor.isCompanionObject()) {
|
||||
report(collector, companionObjectDescriptor, "Companion object should be marked as such");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitTypeAliasDescriptor(
|
||||
TypeAliasDescriptor descriptor, DiagnosticCollector data
|
||||
) {
|
||||
// TODO typealias
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitModuleDeclaration(
|
||||
ModuleDescriptor descriptor, DiagnosticCollector collector
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitConstructorDescriptor(
|
||||
ConstructorDescriptor constructorDescriptor, DiagnosticCollector collector
|
||||
) {
|
||||
visitFunctionDescriptor(constructorDescriptor, collector);
|
||||
|
||||
assertEqualTypes(constructorDescriptor, collector,
|
||||
"return type",
|
||||
constructorDescriptor.getContainingDeclaration().getDefaultType(),
|
||||
constructorDescriptor.getReturnType());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitScriptDescriptor(
|
||||
ScriptDescriptor scriptDescriptor, DiagnosticCollector collector
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitPropertyDescriptor(
|
||||
PropertyDescriptor descriptor, DiagnosticCollector collector
|
||||
) {
|
||||
validateCallable(descriptor, collector);
|
||||
|
||||
PropertyGetterDescriptor getter = descriptor.getGetter();
|
||||
if (getter != null) {
|
||||
assertEqualTypes(getter, collector, "getter return type", descriptor.getType(), getter.getReturnType());
|
||||
validateAccessor(descriptor, collector, getter, "getter");
|
||||
}
|
||||
|
||||
PropertySetterDescriptor setter = descriptor.getSetter();
|
||||
if (setter != null) {
|
||||
assertEquals(setter, collector, "setter parameter count", 1, setter.getValueParameters().size());
|
||||
assertEqualTypes(setter, collector, "setter parameter type", descriptor.getType(), setter.getValueParameters().get(0).getType());
|
||||
assertEquals(setter, collector, "corresponding property", descriptor, setter.getCorrespondingProperty());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitValueParameterDescriptor(
|
||||
ValueParameterDescriptor descriptor, DiagnosticCollector collector
|
||||
) {
|
||||
return visitVariableDescriptor(descriptor, collector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitPropertyGetterDescriptor(
|
||||
PropertyGetterDescriptor descriptor, DiagnosticCollector collector
|
||||
) {
|
||||
return visitFunctionDescriptor(descriptor, collector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitPropertySetterDescriptor(
|
||||
PropertySetterDescriptor descriptor, DiagnosticCollector collector
|
||||
) {
|
||||
return visitFunctionDescriptor(descriptor, collector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitReceiverParameterDescriptor(
|
||||
ReceiverParameterDescriptor descriptor, DiagnosticCollector collector
|
||||
) {
|
||||
validateType(descriptor, descriptor.getType(), collector);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class ScopeValidatorVisitor implements DeclarationDescriptorVisitor<Void, MemberScope> {
|
||||
private final DiagnosticCollector collector;
|
||||
|
||||
public ScopeValidatorVisitor(DiagnosticCollector collector) {
|
||||
this.collector = collector;
|
||||
}
|
||||
|
||||
private void report(DeclarationDescriptor expected, String message) {
|
||||
DescriptorValidator.report(collector, expected, message);
|
||||
}
|
||||
|
||||
private void assertFound(
|
||||
@NotNull MemberScope scope,
|
||||
@NotNull DeclarationDescriptor expected,
|
||||
@Nullable DeclarationDescriptor found,
|
||||
boolean shouldBeSame
|
||||
) {
|
||||
if (found == null) {
|
||||
report(expected, "Not found in " + scope);
|
||||
}
|
||||
if (shouldBeSame ? expected != found : !expected.equals(found)) {
|
||||
report(expected, "Lookup error in " + scope + ": " + found);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertFound(
|
||||
@NotNull MemberScope scope,
|
||||
@NotNull DeclarationDescriptor expected,
|
||||
@NotNull Collection<? extends DeclarationDescriptor> found
|
||||
) {
|
||||
if (!found.contains(expected)) {
|
||||
report(expected, "Not found in " + scope + ": " + found);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitPackageFragmentDescriptor(
|
||||
PackageFragmentDescriptor descriptor, MemberScope scope
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitPackageViewDescriptor(
|
||||
PackageViewDescriptor descriptor, MemberScope scope
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitVariableDescriptor(
|
||||
VariableDescriptor descriptor, MemberScope scope
|
||||
) {
|
||||
assertFound(scope, descriptor, scope.getContributedVariables(descriptor.getName(), NoLookupLocation.FROM_TEST));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitFunctionDescriptor(
|
||||
FunctionDescriptor descriptor, MemberScope scope
|
||||
) {
|
||||
assertFound(scope, descriptor, scope.getContributedFunctions(descriptor.getName(), NoLookupLocation.FROM_TEST));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitTypeParameterDescriptor(
|
||||
TypeParameterDescriptor descriptor, MemberScope scope
|
||||
) {
|
||||
assertFound(scope, descriptor, scope.getContributedClassifier(descriptor.getName(), NoLookupLocation.FROM_TEST), true);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitClassDescriptor(
|
||||
ClassDescriptor descriptor, MemberScope scope
|
||||
) {
|
||||
assertFound(scope, descriptor, scope.getContributedClassifier(descriptor.getName(), NoLookupLocation.FROM_TEST), true);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitTypeAliasDescriptor(TypeAliasDescriptor descriptor, MemberScope data) {
|
||||
// TODO typealias
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitModuleDeclaration(
|
||||
ModuleDescriptor descriptor, MemberScope scope
|
||||
) {
|
||||
report(descriptor, "Module found in scope: " + scope);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitConstructorDescriptor(
|
||||
ConstructorDescriptor descriptor, MemberScope scope
|
||||
) {
|
||||
report(descriptor, "Constructor found in scope: " + scope);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitScriptDescriptor(
|
||||
ScriptDescriptor descriptor, MemberScope scope
|
||||
) {
|
||||
report(descriptor, "Script found in scope: " + scope);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitPropertyDescriptor(
|
||||
PropertyDescriptor descriptor, MemberScope scope
|
||||
) {
|
||||
return visitVariableDescriptor(descriptor, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitValueParameterDescriptor(
|
||||
ValueParameterDescriptor descriptor, MemberScope scope
|
||||
) {
|
||||
return visitVariableDescriptor(descriptor, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitPropertyGetterDescriptor(
|
||||
PropertyGetterDescriptor descriptor, MemberScope scope
|
||||
) {
|
||||
report(descriptor, "Getter found in scope: " + scope);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitPropertySetterDescriptor(
|
||||
PropertySetterDescriptor descriptor, MemberScope scope
|
||||
) {
|
||||
report(descriptor, "Setter found in scope: " + scope);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitReceiverParameterDescriptor(
|
||||
ReceiverParameterDescriptor descriptor, MemberScope scope
|
||||
) {
|
||||
report(descriptor, "Receiver parameter found in scope: " + scope);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ValidationDiagnostic {
|
||||
|
||||
private final DeclarationDescriptor descriptor;
|
||||
private final String message;
|
||||
private final Throwable stackTrace;
|
||||
|
||||
private ValidationDiagnostic(@NotNull DeclarationDescriptor descriptor, @NotNull String message) {
|
||||
this.descriptor = descriptor;
|
||||
this.message = message;
|
||||
this.stackTrace = new Throwable();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DeclarationDescriptor getDescriptor() {
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Throwable getStackTrace() {
|
||||
return stackTrace;
|
||||
}
|
||||
|
||||
public void printStackTrace(@NotNull PrintStream out) {
|
||||
out.println(descriptor);
|
||||
out.println(message);
|
||||
stackTrace.printStackTrace(out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return descriptor + " > " + message;
|
||||
}
|
||||
}
|
||||
|
||||
private static class DiagnosticCollectorForTests implements DiagnosticCollector {
|
||||
private boolean errorsFound = false;
|
||||
|
||||
@Override
|
||||
public void report(@NotNull ValidationDiagnostic diagnostic) {
|
||||
diagnostic.printStackTrace(System.err);
|
||||
errorsFound = true;
|
||||
}
|
||||
|
||||
public void done() {
|
||||
if (errorsFound) {
|
||||
throw new AssertionError("Descriptor validation failed (see messages above)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DescriptorValidator() {}
|
||||
}
|
||||
+457
@@ -0,0 +1,457 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.util;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Lists;
|
||||
import kotlin.Unit;
|
||||
import kotlin.text.StringsKt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.builtins.StandardNames;
|
||||
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;
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor;
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.platform.TargetPlatformKt;
|
||||
import org.jetbrains.kotlin.renderer.*;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
import org.jetbrains.kotlin.resolve.MemberComparator;
|
||||
import org.jetbrains.kotlin.resolve.scopes.ChainedMemberScope;
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope;
|
||||
import org.jetbrains.kotlin.test.Assertions;
|
||||
import org.jetbrains.kotlin.utils.Printer;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumEntry;
|
||||
import static org.jetbrains.kotlin.test.util.DescriptorValidator.ValidationVisitor.errorTypesForbidden;
|
||||
|
||||
public class RecursiveDescriptorComparator {
|
||||
|
||||
private static final DescriptorRenderer DEFAULT_RENDERER = DescriptorRenderer.Companion.withOptions(
|
||||
options -> {
|
||||
options.setWithDefinedIn(false);
|
||||
options.setExcludedAnnotationClasses(Collections.singleton(new FqName(ExpectedLoadErrorsUtil.ANNOTATION_CLASS_NAME)));
|
||||
options.setOverrideRenderingPolicy(OverrideRenderingPolicy.RENDER_OPEN_OVERRIDE);
|
||||
options.setIncludePropertyConstant(true);
|
||||
options.setClassifierNamePolicy(ClassifierNamePolicy.FULLY_QUALIFIED.INSTANCE);
|
||||
options.setVerbose(true);
|
||||
options.setAnnotationArgumentsRenderingPolicy(AnnotationArgumentsRenderingPolicy.UNLESS_EMPTY);
|
||||
options.setModifiers(DescriptorRendererModifier.ALL);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
);
|
||||
|
||||
public static final Configuration DONT_INCLUDE_METHODS_OF_OBJECT = new Configuration(false, false, false, false,
|
||||
false, descriptor -> true, errorTypesForbidden(), DEFAULT_RENDERER);
|
||||
public static final Configuration RECURSIVE = new Configuration(false, false, true, false,
|
||||
false, descriptor -> true, errorTypesForbidden(), DEFAULT_RENDERER);
|
||||
|
||||
public static final Configuration RECURSIVE_ALL = new Configuration(true, true, true, false,
|
||||
true, descriptor -> true, errorTypesForbidden(), DEFAULT_RENDERER);
|
||||
|
||||
public static final Predicate<DeclarationDescriptor> SKIP_BUILT_INS_PACKAGES = descriptor -> {
|
||||
if (descriptor instanceof PackageViewDescriptor) {
|
||||
return !StandardNames.BUILT_INS_PACKAGE_FQ_NAME.equals(((PackageViewDescriptor) descriptor).getFqName());
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
private static final ImmutableSet<String> KOTLIN_ANY_METHOD_NAMES = ImmutableSet.of("equals", "hashCode", "toString");
|
||||
|
||||
private final Configuration conf;
|
||||
|
||||
public RecursiveDescriptorComparator(@NotNull Configuration conf) {
|
||||
this.conf = conf;
|
||||
}
|
||||
|
||||
public String serializeRecursively(@NotNull DeclarationDescriptor declarationDescriptor) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
appendDeclarationRecursively(declarationDescriptor, DescriptorUtils.getContainingModule(declarationDescriptor),
|
||||
new Printer(result, 1), true);
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private void appendDeclarationRecursively(
|
||||
@NotNull DeclarationDescriptor descriptor,
|
||||
@NotNull ModuleDescriptor module,
|
||||
@NotNull Printer printer,
|
||||
boolean topLevel
|
||||
) {
|
||||
if (!isFromModule(descriptor, module)) return;
|
||||
|
||||
boolean isEnumEntry = isEnumEntry(descriptor);
|
||||
boolean isClassOrPackage =
|
||||
(descriptor instanceof ClassOrPackageFragmentDescriptor || descriptor instanceof PackageViewDescriptor) && !isEnumEntry;
|
||||
|
||||
StringBuilder content = new StringBuilder();
|
||||
if (isClassOrPackage) {
|
||||
Printer child = new Printer(content, printer);
|
||||
|
||||
if (!topLevel) {
|
||||
child.pushIndent();
|
||||
}
|
||||
|
||||
if (descriptor instanceof ClassDescriptor) {
|
||||
ClassDescriptor klass = (ClassDescriptor) descriptor;
|
||||
appendSubDescriptors(descriptor, module, klass.getDefaultType().getMemberScope(), klass.getConstructors(), child);
|
||||
MemberScope staticScope = klass.getStaticScope();
|
||||
if (!DescriptorUtils.getAllDescriptors(staticScope).isEmpty()) {
|
||||
child.println();
|
||||
child.println("// Static members");
|
||||
appendSubDescriptors(descriptor, module, staticScope, Collections.emptyList(), child);
|
||||
}
|
||||
}
|
||||
else if (descriptor instanceof PackageFragmentDescriptor) {
|
||||
appendSubDescriptors(descriptor, module,
|
||||
((PackageFragmentDescriptor) descriptor).getMemberScope(),
|
||||
Collections.emptyList(), child);
|
||||
}
|
||||
else if (descriptor instanceof PackageViewDescriptor) {
|
||||
appendSubDescriptors(descriptor, module,
|
||||
getPackageScopeInModule((PackageViewDescriptor) descriptor, module),
|
||||
Collections.emptyList(), child);
|
||||
}
|
||||
|
||||
if (!topLevel) {
|
||||
if (child.isEmpty() && (
|
||||
descriptor instanceof PackageFragmentDescriptor || descriptor instanceof PackageViewDescriptor
|
||||
)) {
|
||||
return;
|
||||
}
|
||||
|
||||
printer.println();
|
||||
}
|
||||
}
|
||||
|
||||
printDescriptor(descriptor, printer);
|
||||
|
||||
if (isClassOrPackage) {
|
||||
if (!topLevel) {
|
||||
printer.printlnWithNoIndent(" {");
|
||||
printer.printWithNoIndent(content);
|
||||
printer.println("}");
|
||||
}
|
||||
else {
|
||||
printer.println();
|
||||
printer.println();
|
||||
printer.printWithNoIndent(StringsKt.trimStart(content, Printer.LINE_SEPARATOR.toCharArray()));
|
||||
}
|
||||
}
|
||||
else if (conf.checkPropertyAccessors && descriptor instanceof PropertyDescriptor) {
|
||||
printer.printlnWithNoIndent();
|
||||
printer.pushIndent();
|
||||
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
|
||||
PropertyGetterDescriptor getter = propertyDescriptor.getGetter();
|
||||
if (getter != null) {
|
||||
printer.println(conf.renderer.render(getter));
|
||||
}
|
||||
|
||||
PropertySetterDescriptor setter = propertyDescriptor.getSetter();
|
||||
if (setter != null) {
|
||||
printer.println(conf.renderer.render(setter));
|
||||
}
|
||||
|
||||
printer.popIndent();
|
||||
}
|
||||
else {
|
||||
printer.printlnWithNoIndent();
|
||||
}
|
||||
|
||||
if (isEnumEntry) {
|
||||
printer.println();
|
||||
}
|
||||
}
|
||||
|
||||
private void printDescriptor(
|
||||
@NotNull DeclarationDescriptor descriptor,
|
||||
@NotNull Printer printer
|
||||
) {
|
||||
boolean isPrimaryConstructor = conf.checkPrimaryConstructors &&
|
||||
descriptor instanceof ConstructorDescriptor && ((ConstructorDescriptor) descriptor).isPrimary();
|
||||
|
||||
boolean isRecord = descriptor instanceof JavaClassDescriptor && ((JavaClassDescriptor) descriptor).isRecord();
|
||||
boolean isRecordComponent = descriptor instanceof JavaMethodDescriptor && ((JavaMethodDescriptor) descriptor).isForRecordComponent();
|
||||
|
||||
StringBuilder prefix = new StringBuilder();
|
||||
|
||||
if (isPrimaryConstructor) {
|
||||
prefix.append("/*primary*/ ");
|
||||
}
|
||||
|
||||
if (isRecord) {
|
||||
prefix.append("/*record*/ ");
|
||||
}
|
||||
|
||||
if (isRecordComponent) {
|
||||
prefix.append("/*record component*/ ");
|
||||
}
|
||||
|
||||
printer.print(prefix.toString(), conf.renderer.render(descriptor));
|
||||
|
||||
if (descriptor instanceof FunctionDescriptor && conf.checkFunctionContracts) {
|
||||
printEffectsIfAny((FunctionDescriptor) descriptor, printer);
|
||||
}
|
||||
}
|
||||
|
||||
private static void printEffectsIfAny(FunctionDescriptor functionDescriptor, Printer printer) {
|
||||
AbstractContractProvider 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
|
||||
List<MemberScope> scopes = new ArrayList<>();
|
||||
for (PackageFragmentDescriptor fragment : descriptor.getFragments()) {
|
||||
if (isFromModule(fragment, module)) {
|
||||
scopes.add(fragment.getMemberScope());
|
||||
}
|
||||
}
|
||||
scopes.add(new SubpackagesScope(module, descriptor.getFqName()));
|
||||
return ChainedMemberScope.Companion.create("test", scopes);
|
||||
}
|
||||
|
||||
private boolean isFromModule(@NotNull DeclarationDescriptor descriptor, @NotNull ModuleDescriptor module) {
|
||||
if (conf.renderDeclarationsFromOtherModules) return true;
|
||||
|
||||
if (descriptor instanceof PackageViewDescriptor) {
|
||||
// PackageViewDescriptor does not belong to any module, so we check if one of its containing fragments is in our module
|
||||
for (PackageFragmentDescriptor fragment : ((PackageViewDescriptor) descriptor).getFragments()) {
|
||||
if (module.equals(DescriptorUtils.getContainingModule(fragment))) return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 'expected' declarations do not belong to the platform-specific module, even though they participate in the analysis
|
||||
if (descriptor instanceof MemberDescriptor && ((MemberDescriptor) descriptor).isExpect() &&
|
||||
!TargetPlatformKt.isCommon(module.getPlatform())) return false;
|
||||
|
||||
return module.equals(DescriptorUtils.getContainingModule(descriptor));
|
||||
}
|
||||
|
||||
private boolean shouldSkip(@NotNull DeclarationDescriptor subDescriptor) {
|
||||
boolean isFunctionFromAny = subDescriptor.getContainingDeclaration() instanceof ClassDescriptor
|
||||
&& subDescriptor instanceof FunctionDescriptor
|
||||
&& KOTLIN_ANY_METHOD_NAMES.contains(subDescriptor.getName().asString());
|
||||
return (isFunctionFromAny && !conf.includeMethodsOfKotlinAny) || !conf.recursiveFilter.test(subDescriptor);
|
||||
}
|
||||
|
||||
private void appendSubDescriptors(
|
||||
@NotNull DeclarationDescriptor descriptor,
|
||||
@NotNull ModuleDescriptor module,
|
||||
@NotNull MemberScope memberScope,
|
||||
@NotNull Collection<? extends DeclarationDescriptor> extraSubDescriptors,
|
||||
@NotNull Printer printer
|
||||
) {
|
||||
if (!isFromModule(descriptor, module)) return;
|
||||
|
||||
List<DeclarationDescriptor> subDescriptors = Lists.newArrayList();
|
||||
|
||||
subDescriptors.addAll(DescriptorUtils.getAllDescriptors(memberScope));
|
||||
subDescriptors.addAll(extraSubDescriptors);
|
||||
|
||||
subDescriptors.sort(MemberComparator.INSTANCE);
|
||||
|
||||
for (DeclarationDescriptor subDescriptor : subDescriptors) {
|
||||
if (!shouldSkip(subDescriptor)) {
|
||||
appendDeclarationRecursively(subDescriptor, module, printer, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void compareDescriptorWithFile(
|
||||
@NotNull DeclarationDescriptor actual,
|
||||
@NotNull Configuration configuration,
|
||||
@NotNull File txtFile,
|
||||
@NotNull Assertions assertions
|
||||
) {
|
||||
doCompareDescriptors(null, actual, configuration, txtFile, assertions);
|
||||
}
|
||||
|
||||
public static void compareDescriptors(
|
||||
@NotNull DeclarationDescriptor expected,
|
||||
@NotNull DeclarationDescriptor actual,
|
||||
@NotNull Configuration configuration,
|
||||
@Nullable File txtFile,
|
||||
@NotNull Assertions assertions
|
||||
) {
|
||||
if (expected == actual) {
|
||||
throw new IllegalArgumentException("Don't invoke this method with expected == actual." +
|
||||
"Invoke compareDescriptorWithFile() instead.");
|
||||
}
|
||||
doCompareDescriptors(expected, actual, configuration, txtFile, assertions);
|
||||
}
|
||||
|
||||
public static void validateAndCompareDescriptorWithFile(
|
||||
@NotNull DeclarationDescriptor actual,
|
||||
@NotNull Configuration configuration,
|
||||
@NotNull File txtFile,
|
||||
@NotNull Assertions assertions
|
||||
) {
|
||||
DescriptorValidator.validate(configuration.validationStrategy, actual);
|
||||
compareDescriptorWithFile(actual, configuration, txtFile, assertions);
|
||||
}
|
||||
|
||||
public static void validateAndCompareDescriptors(
|
||||
@NotNull DeclarationDescriptor expected,
|
||||
@NotNull DeclarationDescriptor actual,
|
||||
@NotNull Configuration configuration,
|
||||
@Nullable File txtFile,
|
||||
@NotNull Assertions assertions
|
||||
) {
|
||||
DescriptorValidator.validate(configuration.validationStrategy, expected);
|
||||
DescriptorValidator.validate(configuration.validationStrategy, actual);
|
||||
compareDescriptors(expected, actual, configuration, txtFile, assertions);
|
||||
}
|
||||
|
||||
private static void doCompareDescriptors(
|
||||
@Nullable DeclarationDescriptor expected,
|
||||
@NotNull DeclarationDescriptor actual,
|
||||
@NotNull Configuration configuration,
|
||||
@Nullable File txtFile,
|
||||
@NotNull Assertions assertions
|
||||
) {
|
||||
RecursiveDescriptorComparator comparator = new RecursiveDescriptorComparator(configuration);
|
||||
|
||||
String actualSerialized = comparator.serializeRecursively(actual);
|
||||
|
||||
if (expected != null) {
|
||||
String expectedSerialized = comparator.serializeRecursively(expected);
|
||||
|
||||
assertions.assertEquals(expectedSerialized, actualSerialized, () -> "Expected and actual descriptors differ");
|
||||
}
|
||||
|
||||
if (txtFile != null) {
|
||||
assertions.assertEqualsToFile(txtFile, actualSerialized, (s) -> s);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Configuration {
|
||||
private final boolean checkPrimaryConstructors;
|
||||
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;
|
||||
|
||||
public Configuration(
|
||||
boolean checkPrimaryConstructors,
|
||||
boolean checkPropertyAccessors,
|
||||
boolean includeMethodsOfKotlinAny,
|
||||
boolean renderDeclarationsFromOtherModules,
|
||||
boolean checkFunctionContracts,
|
||||
Predicate<DeclarationDescriptor> recursiveFilter,
|
||||
DescriptorValidator.ValidationVisitor validationStrategy,
|
||||
DescriptorRenderer renderer
|
||||
) {
|
||||
this.checkPrimaryConstructors = checkPrimaryConstructors;
|
||||
this.checkPropertyAccessors = checkPropertyAccessors;
|
||||
this.includeMethodsOfKotlinAny = includeMethodsOfKotlinAny;
|
||||
this.renderDeclarationsFromOtherModules = renderDeclarationsFromOtherModules;
|
||||
this.checkFunctionContracts = checkFunctionContracts;
|
||||
this.recursiveFilter = recursiveFilter;
|
||||
this.validationStrategy = validationStrategy;
|
||||
this.renderer = rendererWithPropertyAccessors(renderer, checkPropertyAccessors);
|
||||
}
|
||||
|
||||
public Configuration filterRecursion(@NotNull Predicate<DeclarationDescriptor> stepIntoFilter) {
|
||||
return new Configuration(checkPrimaryConstructors, checkPropertyAccessors, includeMethodsOfKotlinAny,
|
||||
renderDeclarationsFromOtherModules, checkFunctionContracts, stepIntoFilter,
|
||||
validationStrategy.withStepIntoFilter(stepIntoFilter), renderer);
|
||||
}
|
||||
|
||||
public Configuration checkPrimaryConstructors(boolean checkPrimaryConstructors) {
|
||||
return new Configuration(checkPrimaryConstructors, checkPropertyAccessors, includeMethodsOfKotlinAny,
|
||||
renderDeclarationsFromOtherModules, checkFunctionContracts, recursiveFilter, validationStrategy, renderer);
|
||||
}
|
||||
|
||||
public Configuration checkPropertyAccessors(boolean checkPropertyAccessors) {
|
||||
return new Configuration(
|
||||
checkPrimaryConstructors, checkPropertyAccessors, includeMethodsOfKotlinAny, renderDeclarationsFromOtherModules,
|
||||
checkFunctionContracts, recursiveFilter, validationStrategy,
|
||||
rendererWithPropertyAccessors(renderer, checkPropertyAccessors)
|
||||
);
|
||||
}
|
||||
|
||||
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, checkFunctionContracts, recursiveFilter, validationStrategy, renderer);
|
||||
}
|
||||
|
||||
public Configuration renderDeclarationsFromOtherModules(boolean renderDeclarationsFromOtherModules) {
|
||||
return new Configuration(checkPrimaryConstructors, checkPropertyAccessors, includeMethodsOfKotlinAny,
|
||||
renderDeclarationsFromOtherModules, checkFunctionContracts, recursiveFilter, validationStrategy, renderer);
|
||||
}
|
||||
|
||||
public Configuration withValidationStrategy(@NotNull DescriptorValidator.ValidationVisitor validationStrategy) {
|
||||
return new Configuration(checkPrimaryConstructors, checkPropertyAccessors, includeMethodsOfKotlinAny,
|
||||
renderDeclarationsFromOtherModules, checkFunctionContracts, recursiveFilter, validationStrategy, renderer);
|
||||
}
|
||||
|
||||
public Configuration withRenderer(@NotNull DescriptorRenderer renderer) {
|
||||
return new Configuration(checkPrimaryConstructors, checkPropertyAccessors, includeMethodsOfKotlinAny,
|
||||
renderDeclarationsFromOtherModules, checkFunctionContracts, recursiveFilter, validationStrategy, renderer);
|
||||
}
|
||||
|
||||
public Configuration withRendererOptions(@NotNull Consumer<DescriptorRendererOptions> configure) {
|
||||
return new Configuration(
|
||||
checkPrimaryConstructors, checkPropertyAccessors, includeMethodsOfKotlinAny,
|
||||
renderDeclarationsFromOtherModules, checkFunctionContracts, recursiveFilter, validationStrategy,
|
||||
newRenderer(renderer, configure));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static DescriptorRenderer rendererWithPropertyAccessors(
|
||||
@NotNull DescriptorRenderer renderer, boolean checkPropertyAccessors
|
||||
) {
|
||||
return newRenderer(renderer, options ->
|
||||
options.setPropertyAccessorRenderingPolicy(
|
||||
checkPropertyAccessors ? PropertyAccessorRenderingPolicy.DEBUG : PropertyAccessorRenderingPolicy.NONE
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static DescriptorRenderer newRenderer(
|
||||
@NotNull DescriptorRenderer renderer, @NotNull Consumer<DescriptorRendererOptions> configure
|
||||
) {
|
||||
return renderer.withOptions(options -> {
|
||||
configure.accept(options);
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.test.util;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class RecursiveDescriptorProcessor {
|
||||
|
||||
public static <D> boolean process(
|
||||
@NotNull DeclarationDescriptor descriptor,
|
||||
D data,
|
||||
@NotNull DeclarationDescriptorVisitor<Boolean, D> visitor
|
||||
) {
|
||||
return descriptor.accept(new RecursiveVisitor<>(visitor), data);
|
||||
}
|
||||
|
||||
private static class RecursiveVisitor<D> implements DeclarationDescriptorVisitor<Boolean, D> {
|
||||
|
||||
private final DeclarationDescriptorVisitor<Boolean, D> worker;
|
||||
|
||||
private RecursiveVisitor(@NotNull DeclarationDescriptorVisitor<Boolean, D> worker) {
|
||||
this.worker = worker;
|
||||
}
|
||||
|
||||
private boolean visitChildren(Collection<? extends DeclarationDescriptor> descriptors, D data) {
|
||||
for (DeclarationDescriptor descriptor : descriptors) {
|
||||
if (!descriptor.accept(this, data)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean visitChildren(@Nullable DeclarationDescriptor descriptor, D data) {
|
||||
if (descriptor == null) return true;
|
||||
|
||||
return descriptor.accept(this, data);
|
||||
}
|
||||
|
||||
private boolean applyWorker(@NotNull DeclarationDescriptor descriptor, D data) {
|
||||
return descriptor.accept(worker, data);
|
||||
}
|
||||
|
||||
private boolean processCallable(CallableDescriptor descriptor, D data) {
|
||||
return applyWorker(descriptor, data)
|
||||
&& visitChildren(descriptor.getTypeParameters(), data)
|
||||
&& visitChildren(descriptor.getExtensionReceiverParameter(), data)
|
||||
&& visitChildren(descriptor.getValueParameters(), data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitPackageFragmentDescriptor(PackageFragmentDescriptor descriptor, D data) {
|
||||
return applyWorker(descriptor, data)
|
||||
&& visitChildren(DescriptorUtils.getAllDescriptors(descriptor.getMemberScope()), data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitPackageViewDescriptor(PackageViewDescriptor descriptor, D data) {
|
||||
return applyWorker(descriptor, data)
|
||||
&& visitChildren(DescriptorUtils.getAllDescriptors(descriptor.getMemberScope()), data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitVariableDescriptor(VariableDescriptor descriptor, D data) {
|
||||
return processCallable(descriptor, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitPropertyDescriptor(PropertyDescriptor descriptor, D data) {
|
||||
return processCallable(descriptor, data)
|
||||
&& visitChildren(descriptor.getGetter(), data)
|
||||
&& visitChildren(descriptor.getSetter(), data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitFunctionDescriptor(FunctionDescriptor descriptor, D data) {
|
||||
return processCallable(descriptor, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitTypeParameterDescriptor(TypeParameterDescriptor descriptor, D data) {
|
||||
return applyWorker(descriptor, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitClassDescriptor(ClassDescriptor descriptor, D data) {
|
||||
return applyWorker(descriptor, data)
|
||||
&& visitChildren(descriptor.getThisAsReceiverParameter(), data)
|
||||
&& visitChildren(descriptor.getConstructors(), data)
|
||||
&& visitChildren(descriptor.getTypeConstructor().getParameters(), data)
|
||||
&& visitChildren(DescriptorUtils.getAllDescriptors(descriptor.getDefaultType().getMemberScope()), data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitTypeAliasDescriptor(TypeAliasDescriptor descriptor, D data) {
|
||||
return applyWorker(descriptor, data)
|
||||
&& visitChildren(descriptor.getDeclaredTypeParameters(), data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitModuleDeclaration(ModuleDescriptor descriptor, D data) {
|
||||
return applyWorker(descriptor, data)
|
||||
&& visitChildren(descriptor.getPackage(FqName.ROOT), data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitConstructorDescriptor(ConstructorDescriptor constructorDescriptor, D data) {
|
||||
return visitFunctionDescriptor(constructorDescriptor, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitScriptDescriptor(ScriptDescriptor scriptDescriptor, D data) {
|
||||
return visitClassDescriptor(scriptDescriptor, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitValueParameterDescriptor(ValueParameterDescriptor descriptor, D data) {
|
||||
return visitVariableDescriptor(descriptor, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitPropertyGetterDescriptor(PropertyGetterDescriptor descriptor, D data) {
|
||||
return visitFunctionDescriptor(descriptor, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitPropertySetterDescriptor(PropertySetterDescriptor descriptor, D data) {
|
||||
return visitFunctionDescriptor(descriptor, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitReceiverParameterDescriptor(ReceiverParameterDescriptor descriptor, D data) {
|
||||
return applyWorker(descriptor, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user