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()
}