KT-2397 Prohibit final methods in traits with no implementation
#KT-2397 fixed
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
+2
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user