Report unmet trait requirements

#KT-3006 Fixed
This commit is contained in:
Alexander Udalov
2014-08-11 12:26:17 +04:00
parent 18ca935558
commit 47d5f83d04
19 changed files with 234 additions and 14 deletions
@@ -165,6 +165,9 @@ public interface Errors {
DiagnosticFactory0<JetDelegatorByExpressionSpecifier> DELEGATION_IN_TRAIT = DiagnosticFactory0.create(ERROR);
DiagnosticFactory2<PsiNameIdentifierOwner, ClassDescriptor, ClassDescriptor> UNMET_TRAIT_REQUIREMENT =
DiagnosticFactory2.create(ERROR, PositioningStrategies.NAMED_ELEMENT);
// Enum-specific
DiagnosticFactory0<JetModifierListOwner> ILLEGAL_ENUM_ANNOTATION = DiagnosticFactory0
@@ -218,6 +218,7 @@ public class DefaultErrorMessages {
MAP.put(CLASS_OBJECT_NOT_ALLOWED, "A class object is not allowed here");
MAP.put(DELEGATION_IN_TRAIT, "Traits cannot use delegation");
MAP.put(DELEGATION_NOT_TO_TRAIT, "Only traits can be delegated to");
MAP.put(UNMET_TRAIT_REQUIREMENT, "Super trait ''{0}'' requires subclasses to extend ''{1}''", NAME, NAME);
MAP.put(NO_CONSTRUCTOR, "This class does not have a constructor");
MAP.put(NOT_A_CLASS, "Not a class");
MAP.put(ILLEGAL_ESCAPE_SEQUENCE, "Illegal escape sequence");
@@ -32,6 +32,7 @@ import org.jetbrains.jet.lang.descriptors.impl.MutablePackageFragmentDescriptor;
import org.jetbrains.jet.lang.descriptors.impl.PackageLikeBuilder;
import org.jetbrains.jet.lang.diagnostics.Errors;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.calls.CallsPackage;
import org.jetbrains.jet.lang.resolve.lazy.KotlinCodeAnalyzer;
import org.jetbrains.jet.lang.resolve.lazy.descriptors.LazyPackageDescriptor;
import org.jetbrains.jet.lang.resolve.name.FqName;
@@ -90,6 +91,7 @@ public class DeclarationResolver {
createFunctionsForDataClasses(c);
importsResolver.processMembersImports(c);
CallsPackage.checkTraitRequirements(c.getDeclaredClasses(), trace);
checkRedeclarationsInPackages(c);
checkRedeclarationsInInnerClassNames(c);
}
@@ -0,0 +1,72 @@
/*
* Copyright 2010-2014 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.jet.lang.resolve.calls
import org.jetbrains.jet.lang.descriptors.ClassDescriptor
import org.jetbrains.jet.lang.resolve.DescriptorUtils
import org.jetbrains.jet.lang.types.TypeUtils
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
import org.jetbrains.jet.lang.diagnostics.Errors
import org.jetbrains.jet.lang.psi.JetClassOrObject
import org.jetbrains.jet.lang.descriptors.ClassDescriptorWithResolutionScopes
import org.jetbrains.jet.lang.resolve.BindingTrace
fun checkTraitRequirements(c: Map<JetClassOrObject, ClassDescriptorWithResolutionScopes>, trace: BindingTrace) {
for ((classOrObject, descriptor) in c.entrySet()) {
if (DescriptorUtils.isTrait(descriptor)) continue
val satisfiedRequirements = getSuperClassesReachableByClassInheritance(descriptor)
for (superTrait in getAllSuperTraits(descriptor)) {
for (traitSupertype in superTrait.getDefaultType().getConstructor().getSupertypes()) {
val traitSuperClass = traitSupertype.getConstructor().getDeclarationDescriptor()
if (DescriptorUtils.isClass(traitSuperClass) && traitSuperClass !in satisfiedRequirements) {
trace.report(Errors.UNMET_TRAIT_REQUIREMENT.on(classOrObject, superTrait, traitSuperClass as ClassDescriptor))
}
}
}
}
}
private fun getAllSuperTraits(descriptor: ClassDescriptor): List<ClassDescriptor> {
[suppress("UNCHECKED_CAST")]
return TypeUtils.getAllSupertypes(descriptor.getDefaultType())
.map { supertype -> supertype.getConstructor().getDeclarationDescriptor() }
.filter { superClass -> DescriptorUtils.isTrait(superClass) } as List<ClassDescriptor>
}
private fun getSuperClassesReachableByClassInheritance(
descriptor: ClassDescriptor,
result: MutableSet<ClassDescriptor> = hashSetOf()
): Set<ClassDescriptor> {
val superClass = getSuperClass(descriptor)
result.add(superClass)
if (!KotlinBuiltIns.getInstance().isAny(superClass)) {
getSuperClassesReachableByClassInheritance(superClass, result)
}
return result
}
private fun getSuperClass(descriptor: ClassDescriptor): ClassDescriptor {
for (supertype in descriptor.getDefaultType().getConstructor().getSupertypes()) {
val superClassifier = supertype.getConstructor().getDeclarationDescriptor()
if (DescriptorUtils.isClass(superClassifier) || DescriptorUtils.isEnumClass(superClassifier)) {
return superClassifier as ClassDescriptor
}
}
return KotlinBuiltIns.getInstance().getAny()
}
@@ -1,12 +1,11 @@
trait SimpleClass : java.lang.Object {
fun foo() : String = "239 " + toString ()
trait Trait : java.lang.Object {
fun foo(): String = "239 " + toString()
}
class SimpleClassImpl() : SimpleClass {
override fun toString() = "SimpleClassImpl"
class Impl : Trait, java.lang.Object() {
override fun toString() = "Impl"
}
fun box() : String {
val c = SimpleClassImpl()
return if("239 SimpleClassImpl" == c.foo()) "OK" else "fail"
fun box(): String {
return if ("239 Impl" == Impl().foo()) "OK" else "Fail"
}
@@ -0,0 +1,7 @@
open class Required
trait Trait : Required
abstract class <!UNMET_TRAIT_REQUIREMENT!>Abstract<!> : Trait
abstract class <!UNMET_TRAIT_REQUIREMENT!>AbstractDerived<!> : Abstract()
@@ -0,0 +1,6 @@
open class Required
trait A : Required
val a = <!UNMET_TRAIT_REQUIREMENT!>object<!> : A {}
val b: A = object : A, Required() {}
@@ -0,0 +1,8 @@
open class Generic<T>
trait A : Generic<Int>
trait B : Generic<String>
class <!UNMET_TRAIT_REQUIREMENT, UNMET_TRAIT_REQUIREMENT!>Y<!> : <!INCONSISTENT_TYPE_PARAMETER_VALUES!>A, B<!>
class Z : <!INCONSISTENT_TYPE_PARAMETER_VALUES!>A, B, Generic<Int>()<!>
@@ -0,0 +1,13 @@
open class Base {
}
trait Derived: Base {
fun foo() {
f1(this@Derived)
}
}
class <!UNMET_TRAIT_REQUIREMENT!>DerivedImpl()<!>: Derived {}
object <!UNMET_TRAIT_REQUIREMENT!>ObjectImpl<!>: Derived {}
fun f1(b: Base) = b
@@ -0,0 +1,4 @@
open class A
open class B
trait C : A, <!MANY_CLASSES_IN_SUPERTYPE_LIST!>B<!>
@@ -0,0 +1,14 @@
open class Required
trait A : Required
trait B : A, Required
trait C : Required
trait D : B, Required
class <!UNMET_TRAIT_REQUIREMENT, UNMET_TRAIT_REQUIREMENT, UNMET_TRAIT_REQUIREMENT!>W<!> : D
class X : D, Required()
class <!UNMET_TRAIT_REQUIREMENT, UNMET_TRAIT_REQUIREMENT, UNMET_TRAIT_REQUIREMENT, UNMET_TRAIT_REQUIREMENT!>Y<!> : C, D
class Z : D, C, Required()
@@ -0,0 +1,8 @@
open class RequiredBase
trait Trait : RequiredBase
open class RequiredDerived : RequiredBase()
class <!UNMET_TRAIT_REQUIREMENT!>A<!> : Trait
class B : Trait, RequiredDerived()
@@ -0,0 +1,8 @@
open class Required(val value: String)
trait First : Required
trait Second : First
class <!UNMET_TRAIT_REQUIREMENT!>Y<!> : Second
class Z : Second, Required(":o)")
@@ -0,0 +1,5 @@
trait AnyTrait : Any
class Foo : AnyTrait
class Bar : AnyTrait, Any()
@@ -0,0 +1,7 @@
open class Generic<T>
trait A : Generic<String>
trait B : Generic<Int>
trait C : <!INCONSISTENT_TYPE_PARAMETER_VALUES!>A, B<!>
@@ -33,7 +33,7 @@ import org.jetbrains.jet.checkers.AbstractJetDiagnosticsTest;
@InnerTestClasses({JetDiagnosticsTestGenerated.Tests.class, JetDiagnosticsTestGenerated.Script.class, JetDiagnosticsTestGenerated.TailRecursion.class})
public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
@TestMetadata("compiler/testData/diagnostics/tests")
@InnerTestClasses({Tests.Annotations.class, Tests.BackingField.class, Tests.Cast.class, Tests.CheckArguments.class, Tests.ClassObjects.class, Tests.ControlFlowAnalysis.class, Tests.ControlStructures.class, Tests.CyclicHierarchy.class, Tests.DataClasses.class, Tests.DataFlow.class, Tests.DataFlowInfoTraversal.class, Tests.DeclarationChecks.class, Tests.DelegatedProperty.class, Tests.Deparenthesize.class, Tests.DuplicateJvmSignature.class, Tests.Enum.class, Tests.Evaluate.class, Tests.Extensions.class, Tests.FunctionLiterals.class, Tests.Generics.class, Tests.Imports.class, Tests.IncompleteCode.class, Tests.Inference.class, Tests.Infos.class, Tests.Inline.class, Tests.Inner.class, Tests.J_k.class, Tests.Jdk_annotations.class, Tests.Labels.class, Tests.Library.class, Tests.Multimodule.class, Tests.NamedArguments.class, Tests.NullabilityAndAutoCasts.class, Tests.NullableTypes.class, Tests.Numbers.class, Tests.Objects.class, Tests.OperatorsOverloading.class, Tests.Overload.class, Tests.Override.class, Tests.Recovery.class, Tests.Redeclarations.class, Tests.Regressions.class, Tests.Resolve.class, Tests.Scopes.class, Tests.SenselessComparison.class, Tests.Shadowing.class, Tests.SmartCasts.class, Tests.Substitutions.class, Tests.Subtyping.class, Tests.Suppress.class, Tests.ThisAndSuper.class, Tests.Typedefs.class, Tests.Unit.class, Tests.Varargs.class, Tests.When.class})
@InnerTestClasses({Tests.Annotations.class, Tests.BackingField.class, Tests.Cast.class, Tests.CheckArguments.class, Tests.ClassObjects.class, Tests.ControlFlowAnalysis.class, Tests.ControlStructures.class, Tests.CyclicHierarchy.class, Tests.DataClasses.class, Tests.DataFlow.class, Tests.DataFlowInfoTraversal.class, Tests.DeclarationChecks.class, Tests.DelegatedProperty.class, Tests.Deparenthesize.class, Tests.DuplicateJvmSignature.class, Tests.Enum.class, Tests.Evaluate.class, Tests.Extensions.class, Tests.FunctionLiterals.class, Tests.Generics.class, Tests.Imports.class, Tests.IncompleteCode.class, Tests.Inference.class, Tests.Infos.class, Tests.Inline.class, Tests.Inner.class, Tests.J_k.class, Tests.Jdk_annotations.class, Tests.Labels.class, Tests.Library.class, Tests.Multimodule.class, Tests.NamedArguments.class, Tests.NullabilityAndAutoCasts.class, Tests.NullableTypes.class, Tests.Numbers.class, Tests.Objects.class, Tests.OperatorsOverloading.class, Tests.Overload.class, Tests.Override.class, Tests.Recovery.class, Tests.Redeclarations.class, Tests.Regressions.class, Tests.Resolve.class, Tests.Scopes.class, Tests.SenselessComparison.class, Tests.Shadowing.class, Tests.SmartCasts.class, Tests.Substitutions.class, Tests.Subtyping.class, Tests.Suppress.class, Tests.ThisAndSuper.class, Tests.TraitWithRequired.class, Tests.Typedefs.class, Tests.Unit.class, Tests.Varargs.class, Tests.When.class})
public static class Tests extends AbstractJetDiagnosticsTest {
@TestMetadata("Abstract.kt")
public void testAbstract() throws Exception {
@@ -444,11 +444,6 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
doTest("compiler/testData/diagnostics/tests/TraitOverrideObjectMethods.kt");
}
@TestMetadata("TraitSupertypeList.kt")
public void testTraitSupertypeList() throws Exception {
doTest("compiler/testData/diagnostics/tests/TraitSupertypeList.kt");
}
@TestMetadata("TypeInference.kt")
public void testTypeInference() throws Exception {
doTest("compiler/testData/diagnostics/tests/TypeInference.kt");
@@ -7924,6 +7919,69 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
}
@TestMetadata("compiler/testData/diagnostics/tests/traitWithRequired")
public static class TraitWithRequired extends AbstractJetDiagnosticsTest {
@TestMetadata("abstractClass.kt")
public void testAbstractClass() throws Exception {
doTest("compiler/testData/diagnostics/tests/traitWithRequired/abstractClass.kt");
}
public void testAllFilesPresentInTraitWithRequired() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/diagnostics/tests/traitWithRequired"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("anonymousObjectExtendsTraitWithRequired.kt")
public void testAnonymousObjectExtendsTraitWithRequired() throws Exception {
doTest("compiler/testData/diagnostics/tests/traitWithRequired/anonymousObjectExtendsTraitWithRequired.kt");
}
@TestMetadata("differentGenericArguments.kt")
public void testDifferentGenericArguments() throws Exception {
doTest("compiler/testData/diagnostics/tests/traitWithRequired/differentGenericArguments.kt");
}
@TestMetadata("kt3006.kt")
public void testKt3006() throws Exception {
doTest("compiler/testData/diagnostics/tests/traitWithRequired/kt3006.kt");
}
@TestMetadata("manyRequirementsDisallowed.kt")
public void testManyRequirementsDisallowed() throws Exception {
doTest("compiler/testData/diagnostics/tests/traitWithRequired/manyRequirementsDisallowed.kt");
}
@TestMetadata("manyTraitsRequireSameClass.kt")
public void testManyTraitsRequireSameClass() throws Exception {
doTest("compiler/testData/diagnostics/tests/traitWithRequired/manyTraitsRequireSameClass.kt");
}
@TestMetadata("requirementFulfilledBySubclass.kt")
public void testRequirementFulfilledBySubclass() throws Exception {
doTest("compiler/testData/diagnostics/tests/traitWithRequired/requirementFulfilledBySubclass.kt");
}
@TestMetadata("traitExtendsTraitWithRequired.kt")
public void testTraitExtendsTraitWithRequired() throws Exception {
doTest("compiler/testData/diagnostics/tests/traitWithRequired/traitExtendsTraitWithRequired.kt");
}
@TestMetadata("traitRequiresAny.kt")
public void testTraitRequiresAny() throws Exception {
doTest("compiler/testData/diagnostics/tests/traitWithRequired/traitRequiresAny.kt");
}
@TestMetadata("traitSupertypeList.kt")
public void testTraitSupertypeList() throws Exception {
doTest("compiler/testData/diagnostics/tests/traitWithRequired/traitSupertypeList.kt");
}
@TestMetadata("uninheritableTraitDifferentGenericArguments.kt")
public void testUninheritableTraitDifferentGenericArguments() throws Exception {
doTest("compiler/testData/diagnostics/tests/traitWithRequired/uninheritableTraitDifferentGenericArguments.kt");
}
}
@TestMetadata("compiler/testData/diagnostics/tests/typedefs")
public static class Typedefs extends AbstractJetDiagnosticsTest {
public void testAllFilesPresentInTypedefs() throws Exception {
@@ -8120,6 +8178,7 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
suite.addTestSuite(Subtyping.class);
suite.addTest(Suppress.innerSuite());
suite.addTestSuite(ThisAndSuper.class);
suite.addTestSuite(TraitWithRequired.class);
suite.addTestSuite(Typedefs.class);
suite.addTestSuite(Unit.class);
suite.addTestSuite(Varargs.class);
@@ -226,7 +226,7 @@ public class DescriptorUtils {
return false;
}
public static boolean isEnumClass(@NotNull DeclarationDescriptor descriptor) {
public static boolean isEnumClass(@Nullable DeclarationDescriptor descriptor) {
return isKindOf(descriptor, ClassKind.ENUM_CLASS);
}
@@ -822,6 +822,10 @@ public class KotlinBuiltIns {
return fqNames.any.equals(fqName) || fqNames.nothing.equals(fqName);
}
public boolean isAny(@NotNull ClassDescriptor descriptor) {
return fqNames.any.equals(DescriptorUtils.getFqName(descriptor));
}
public boolean isNothing(@NotNull JetType type) {
return isNothingOrNullableNothing(type)
&& !type.isNullable();