KT-2397 Prohibit final methods in traits with no implementation

#KT-2397 fixed
This commit is contained in:
Svetlana Isakova
2012-12-29 20:07:58 +04:00
parent 9bd4ed2a8d
commit 0b53bd25bd
7 changed files with 44 additions and 6 deletions
@@ -250,6 +250,7 @@ public interface Errors {
SimpleDiagnosticFactory<JetExpression> PROPERTY_INITIALIZER_NO_BACKING_FIELD = SimpleDiagnosticFactory.create(ERROR);
SimpleDiagnosticFactory<JetExpression> PROPERTY_INITIALIZER_IN_TRAIT = SimpleDiagnosticFactory.create(ERROR);
SimpleDiagnosticFactory<JetProperty> FINAL_PROPERTY_IN_TRAIT = SimpleDiagnosticFactory.create(ERROR, FINAL_MODIFIER);
SimpleDiagnosticFactory<JetProperty> BACKING_FIELD_IN_TRAIT = SimpleDiagnosticFactory.create(ERROR, NAMED_ELEMENT);
DiagnosticFactory2<JetModifierListOwner, String, ClassDescriptor> ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS = DiagnosticFactory2.create(ERROR, ABSTRACT_MODIFIER);
@@ -266,6 +267,7 @@ public interface Errors {
DiagnosticFactory1<JetFunction, SimpleFunctionDescriptor> ABSTRACT_FUNCTION_WITH_BODY = DiagnosticFactory1.create(ERROR, ABSTRACT_MODIFIER);
DiagnosticFactory1<JetFunction, SimpleFunctionDescriptor> NON_ABSTRACT_FUNCTION_WITH_NO_BODY = DiagnosticFactory1.create(ERROR, NAMED_ELEMENT);
DiagnosticFactory1<JetFunction, SimpleFunctionDescriptor> FINAL_FUNCTION_WITH_NO_BODY = DiagnosticFactory1.create(ERROR, FINAL_MODIFIER);
DiagnosticFactory1<JetFunction, SimpleFunctionDescriptor> NON_MEMBER_FUNCTION_NO_BODY = DiagnosticFactory1.create(ERROR, NAMED_ELEMENT);
@@ -165,6 +165,8 @@ public class PositioningStrategies {
public static final PositioningStrategy<JetModifierListOwner> OVERRIDE_MODIFIER = modifierSetPosition(JetTokens.OVERRIDE_KEYWORD);
public static final PositioningStrategy<JetModifierListOwner> FINAL_MODIFIER = modifierSetPosition(JetTokens.FINAL_KEYWORD);
public static final PositioningStrategy<JetModifierListOwner> VARIANCE_MODIFIER = modifierSetPosition(JetTokens.IN_KEYWORD,
JetTokens.OUT_KEYWORD);
@@ -135,11 +135,13 @@ public class DefaultErrorMessages {
MAP.put(MUST_BE_INITIALIZED, "Property must be initialized");
MAP.put(MUST_BE_INITIALIZED_OR_BE_ABSTRACT, "Property must be initialized or be abstract");
MAP.put(PROPERTY_INITIALIZER_IN_TRAIT, "Property initializers are not allowed in traits");
MAP.put(FINAL_PROPERTY_IN_TRAIT, "Abstract property in trait cannot be final");
MAP.put(PROPERTY_INITIALIZER_NO_BACKING_FIELD, "Initializer is not allowed here because this property has no backing field");
MAP.put(ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS, "Abstract property ''{0}'' in non-abstract class ''{1}''", NAME, NAME);
MAP.put(ABSTRACT_FUNCTION_IN_NON_ABSTRACT_CLASS, "Abstract function ''{0}'' in non-abstract class ''{1}''", NAME, NAME);
MAP.put(ABSTRACT_FUNCTION_WITH_BODY, "A function ''{0}'' with body cannot be abstract", NAME);
MAP.put(NON_ABSTRACT_FUNCTION_WITH_NO_BODY, "Function ''{0}'' without a body must be abstract", NAME);
MAP.put(FINAL_FUNCTION_WITH_NO_BODY, "Function ''{0}'' without body cannot be final", NAME);
MAP.put(NON_MEMBER_FUNCTION_NO_BODY, "Function ''{0}'' must have a body", NAME);
MAP.put(NON_FINAL_MEMBER_IN_FINAL_CLASS, "\"open\" has no effect in a final class");
@@ -179,7 +179,6 @@ public class DeclarationsChecker {
if (abstractNode != null) { //has abstract modifier
if (!(classDescriptor.getModality() == Modality.ABSTRACT) && classDescriptor.getKind() != ClassKind.ENUM_CLASS) {
JetClass classElement = (JetClass) BindingContextUtils.classDescriptorToDeclaration(trace.getBindingContext(), classDescriptor);
String name = property.getName();
trace.report(ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS.on(property, name != null ? name : "", classDescriptor));
return;
@@ -246,6 +245,9 @@ public class DeclarationsChecker {
if (!error && property.getTypeRef() == null) {
trace.report(PROPERTY_WITH_NO_TYPE_NO_INITIALIZER.on(property));
}
if (inTrait && property.hasModifier(JetTokens.FINAL_KEYWORD) && backingFieldRequired) {
trace.report(FINAL_PROPERTY_IN_TRAIT.on(property));
}
return;
}
if (inTrait) {
@@ -266,17 +268,20 @@ public class DeclarationsChecker {
boolean inTrait = classDescriptor.getKind() == ClassKind.TRAIT;
boolean inEnum = classDescriptor.getKind() == ClassKind.ENUM_CLASS;
boolean inAbstractClass = classDescriptor.getModality() == Modality.ABSTRACT;
if (hasAbstractModifier && !inAbstractClass && !inTrait && !inEnum) {
JetClass classElement = (JetClass) BindingContextUtils.classDescriptorToDeclaration(trace.getBindingContext(), classDescriptor);
if (hasAbstractModifier && !inAbstractClass && !inEnum) {
trace.report(ABSTRACT_FUNCTION_IN_NON_ABSTRACT_CLASS.on(function, functionDescriptor.getName().getName(), classDescriptor));
}
if (hasAbstractModifier && inTrait) {
trace.report(ABSTRACT_MODIFIER_IN_TRAIT.on(function));
}
if (function.getBodyExpression() != null && hasAbstractModifier) {
boolean hasBody = function.getBodyExpression() != null;
if (hasBody && hasAbstractModifier) {
trace.report(ABSTRACT_FUNCTION_WITH_BODY.on(function, functionDescriptor));
}
if (function.getBodyExpression() == null && !hasAbstractModifier && !inTrait) {
if (!hasBody && function.hasModifier(JetTokens.FINAL_KEYWORD) && inTrait) {
trace.report(FINAL_FUNCTION_WITH_NO_BODY.on(function, functionDescriptor));
}
if (!hasBody && !hasAbstractModifier && !inTrait) {
trace.report(NON_ABSTRACT_FUNCTION_WITH_NO_BODY.on(function, functionDescriptor));
}
return;
@@ -0,0 +1,17 @@
//KT-2397 Prohibit final methods in traits with no implementation
package a
trait T {
<!FINAL_FUNCTION_WITH_NO_BODY!>final<!> fun foo()
<!FINAL_PROPERTY_IN_TRAIT!>final<!> val b : Int
final fun bar() {}
final val c : Int
get() = 42
final val d = <!PROPERTY_INITIALIZER_IN_TRAIT!>1<!>
}
class A {
<!NON_ABSTRACT_FUNCTION_WITH_NO_BODY!>final fun foo()<!>
}
@@ -1475,6 +1475,11 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
doTest("compiler/testData/diagnostics/tests/declarationChecks/kt2142.kt");
}
@TestMetadata("kt2397.kt")
public void testKt2397() throws Exception {
doTest("compiler/testData/diagnostics/tests/declarationChecks/kt2397.kt");
}
@TestMetadata("kt2631_MultipleDeclaration.kt")
public void testKt2631_MultipleDeclaration() throws Exception {
doTest("compiler/testData/diagnostics/tests/declarationChecks/kt2631_MultipleDeclaration.kt");
@@ -63,6 +63,8 @@ public class QuickFixes {
factories.put(MUST_BE_INITIALIZED_OR_BE_ABSTRACT, addAbstractModifierFactory);
JetIntentionActionFactory removeFinalModifierFactory = RemoveModifierFix.createRemoveModifierFromListOwnerFactory(FINAL_KEYWORD);
JetIntentionActionFactory addAbstractToClassFactory = AddModifierFix.createFactory(ABSTRACT_KEYWORD, JetClass.class);
factories.put(ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS, removeAbstractModifierFactory);
factories.put(ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS, addAbstractToClassFactory);
@@ -74,6 +76,9 @@ public class QuickFixes {
factories.put(ABSTRACT_FUNCTION_WITH_BODY, removeAbstractModifierFactory);
factories.put(ABSTRACT_FUNCTION_WITH_BODY, removeFunctionBodyFactory);
factories.put(FINAL_PROPERTY_IN_TRAIT, removeFinalModifierFactory);
factories.put(FINAL_FUNCTION_WITH_NO_BODY, removeFinalModifierFactory);
JetIntentionActionFactory addFunctionBodyFactory = AddFunctionBodyFix.createFactory();
factories.put(NON_ABSTRACT_FUNCTION_WITH_NO_BODY, addAbstractModifierFactory);
factories.put(NON_ABSTRACT_FUNCTION_WITH_NO_BODY, addFunctionBodyFactory);
@@ -96,7 +101,7 @@ public class QuickFixes {
factories.put(REDUNDANT_MODIFIER, removeRedundantModifierFactory);
factories.put(ABSTRACT_MODIFIER_IN_TRAIT, RemoveModifierFix.createRemoveModifierFromListOwnerFactory(ABSTRACT_KEYWORD, true));
factories.put(OPEN_MODIFIER_IN_TRAIT, RemoveModifierFix.createRemoveModifierFromListOwnerFactory(OPEN_KEYWORD, true));
factories.put(TRAIT_CAN_NOT_BE_FINAL, RemoveModifierFix.createRemoveModifierFromListOwnerFactory(FINAL_KEYWORD));
factories.put(TRAIT_CAN_NOT_BE_FINAL, removeFinalModifierFactory);
JetIntentionActionFactory removeOpenModifierFactory = RemoveModifierFix.createRemoveModifierFromListOwnerFactory(OPEN_KEYWORD);
factories.put(NON_FINAL_MEMBER_IN_FINAL_CLASS, AddModifierFix.createFactory(OPEN_KEYWORD, JetClass.class));