diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java index ad98340fe9a..4deedeb76ff 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java @@ -165,6 +165,9 @@ public interface Errors { DiagnosticFactory0 DELEGATION_IN_TRAIT = DiagnosticFactory0.create(ERROR); + DiagnosticFactory2 UNMET_TRAIT_REQUIREMENT = + DiagnosticFactory2.create(ERROR, PositioningStrategies.NAMED_ELEMENT); + // Enum-specific DiagnosticFactory0 ILLEGAL_ENUM_ANNOTATION = DiagnosticFactory0 diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/DefaultErrorMessages.java index d2dedeb6dd4..20ba396d42f 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/DefaultErrorMessages.java @@ -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"); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DeclarationResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DeclarationResolver.java index 16edbfa14d5..70057320445 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DeclarationResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DeclarationResolver.java @@ -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); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/checkTraitRequirements.kt b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/checkTraitRequirements.kt new file mode 100644 index 00000000000..11f82723ff3 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/checkTraitRequirements.kt @@ -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, 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 { + [suppress("UNCHECKED_CAST")] + return TypeUtils.getAllSupertypes(descriptor.getDefaultType()) + .map { supertype -> supertype.getConstructor().getDeclarationDescriptor() } + .filter { superClass -> DescriptorUtils.isTrait(superClass) } as List +} + +private fun getSuperClassesReachableByClassInheritance( + descriptor: ClassDescriptor, + result: MutableSet = hashSetOf() +): Set { + 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() +} + diff --git a/compiler/testData/codegen/box/traits/withRequired/simple.kt b/compiler/testData/codegen/box/traits/withRequired/simple.kt index e53c4bbecd7..580498cbb68 100644 --- a/compiler/testData/codegen/box/traits/withRequired/simple.kt +++ b/compiler/testData/codegen/box/traits/withRequired/simple.kt @@ -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" } diff --git a/compiler/testData/diagnostics/tests/traitWithRequired/abstractClass.kt b/compiler/testData/diagnostics/tests/traitWithRequired/abstractClass.kt new file mode 100644 index 00000000000..e30b00ec9c3 --- /dev/null +++ b/compiler/testData/diagnostics/tests/traitWithRequired/abstractClass.kt @@ -0,0 +1,7 @@ +open class Required + +trait Trait : Required + +abstract class Abstract : Trait + +abstract class AbstractDerived : Abstract() diff --git a/compiler/testData/diagnostics/tests/traitWithRequired/anonymousObjectExtendsTraitWithRequired.kt b/compiler/testData/diagnostics/tests/traitWithRequired/anonymousObjectExtendsTraitWithRequired.kt new file mode 100644 index 00000000000..ae8dbeffa34 --- /dev/null +++ b/compiler/testData/diagnostics/tests/traitWithRequired/anonymousObjectExtendsTraitWithRequired.kt @@ -0,0 +1,6 @@ +open class Required + +trait A : Required + +val a = object : A {} +val b: A = object : A, Required() {} diff --git a/compiler/testData/diagnostics/tests/traitWithRequired/differentGenericArguments.kt b/compiler/testData/diagnostics/tests/traitWithRequired/differentGenericArguments.kt new file mode 100644 index 00000000000..183ac1d3386 --- /dev/null +++ b/compiler/testData/diagnostics/tests/traitWithRequired/differentGenericArguments.kt @@ -0,0 +1,8 @@ +open class Generic + +trait A : Generic + +trait B : Generic + +class Y : A, B +class Z : A, B, Generic() diff --git a/compiler/testData/diagnostics/tests/traitWithRequired/kt3006.kt b/compiler/testData/diagnostics/tests/traitWithRequired/kt3006.kt new file mode 100644 index 00000000000..88ecbff53c9 --- /dev/null +++ b/compiler/testData/diagnostics/tests/traitWithRequired/kt3006.kt @@ -0,0 +1,13 @@ +open class Base { +} + +trait Derived: Base { + fun foo() { + f1(this@Derived) + } +} + +class DerivedImpl(): Derived {} +object ObjectImpl: Derived {} + +fun f1(b: Base) = b diff --git a/compiler/testData/diagnostics/tests/traitWithRequired/manyRequirementsDisallowed.kt b/compiler/testData/diagnostics/tests/traitWithRequired/manyRequirementsDisallowed.kt new file mode 100644 index 00000000000..fd2290e4b9f --- /dev/null +++ b/compiler/testData/diagnostics/tests/traitWithRequired/manyRequirementsDisallowed.kt @@ -0,0 +1,4 @@ +open class A +open class B + +trait C : A, B diff --git a/compiler/testData/diagnostics/tests/traitWithRequired/manyTraitsRequireSameClass.kt b/compiler/testData/diagnostics/tests/traitWithRequired/manyTraitsRequireSameClass.kt new file mode 100644 index 00000000000..1c0115281cd --- /dev/null +++ b/compiler/testData/diagnostics/tests/traitWithRequired/manyTraitsRequireSameClass.kt @@ -0,0 +1,14 @@ +open class Required + +trait A : Required + +trait B : A, Required + +trait C : Required + +trait D : B, Required + +class W : D +class X : D, Required() +class Y : C, D +class Z : D, C, Required() diff --git a/compiler/testData/diagnostics/tests/traitWithRequired/requirementFulfilledBySubclass.kt b/compiler/testData/diagnostics/tests/traitWithRequired/requirementFulfilledBySubclass.kt new file mode 100644 index 00000000000..64013802cce --- /dev/null +++ b/compiler/testData/diagnostics/tests/traitWithRequired/requirementFulfilledBySubclass.kt @@ -0,0 +1,8 @@ +open class RequiredBase + +trait Trait : RequiredBase + +open class RequiredDerived : RequiredBase() + +class A : Trait +class B : Trait, RequiredDerived() diff --git a/compiler/testData/diagnostics/tests/traitWithRequired/traitExtendsTraitWithRequired.kt b/compiler/testData/diagnostics/tests/traitWithRequired/traitExtendsTraitWithRequired.kt new file mode 100644 index 00000000000..4d35afc516a --- /dev/null +++ b/compiler/testData/diagnostics/tests/traitWithRequired/traitExtendsTraitWithRequired.kt @@ -0,0 +1,8 @@ +open class Required(val value: String) + +trait First : Required + +trait Second : First + +class Y : Second +class Z : Second, Required(":o)") diff --git a/compiler/testData/diagnostics/tests/traitWithRequired/traitRequiresAny.kt b/compiler/testData/diagnostics/tests/traitWithRequired/traitRequiresAny.kt new file mode 100644 index 00000000000..85f253285d0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/traitWithRequired/traitRequiresAny.kt @@ -0,0 +1,5 @@ +trait AnyTrait : Any + +class Foo : AnyTrait + +class Bar : AnyTrait, Any() diff --git a/compiler/testData/diagnostics/tests/TraitSupertypeList.kt b/compiler/testData/diagnostics/tests/traitWithRequired/traitSupertypeList.kt similarity index 100% rename from compiler/testData/diagnostics/tests/TraitSupertypeList.kt rename to compiler/testData/diagnostics/tests/traitWithRequired/traitSupertypeList.kt diff --git a/compiler/testData/diagnostics/tests/traitWithRequired/uninheritableTraitDifferentGenericArguments.kt b/compiler/testData/diagnostics/tests/traitWithRequired/uninheritableTraitDifferentGenericArguments.kt new file mode 100644 index 00000000000..56a6fc4e30e --- /dev/null +++ b/compiler/testData/diagnostics/tests/traitWithRequired/uninheritableTraitDifferentGenericArguments.kt @@ -0,0 +1,7 @@ +open class Generic + +trait A : Generic + +trait B : Generic + +trait C : A, B diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java index 7a2b4ca264c..139d80332e1 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -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); diff --git a/core/descriptors/src/org/jetbrains/jet/lang/resolve/DescriptorUtils.java b/core/descriptors/src/org/jetbrains/jet/lang/resolve/DescriptorUtils.java index 9c97aa89957..7579d9f41af 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/resolve/DescriptorUtils.java +++ b/core/descriptors/src/org/jetbrains/jet/lang/resolve/DescriptorUtils.java @@ -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); } diff --git a/core/descriptors/src/org/jetbrains/jet/lang/types/lang/KotlinBuiltIns.java b/core/descriptors/src/org/jetbrains/jet/lang/types/lang/KotlinBuiltIns.java index c5589605509..99db82af859 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/types/lang/KotlinBuiltIns.java +++ b/core/descriptors/src/org/jetbrains/jet/lang/types/lang/KotlinBuiltIns.java @@ -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();