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_NO_BACKING_FIELD = SimpleDiagnosticFactory.create(ERROR);
|
||||||
SimpleDiagnosticFactory<JetExpression> PROPERTY_INITIALIZER_IN_TRAIT = 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);
|
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);
|
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> 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> 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);
|
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> 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,
|
public static final PositioningStrategy<JetModifierListOwner> VARIANCE_MODIFIER = modifierSetPosition(JetTokens.IN_KEYWORD,
|
||||||
JetTokens.OUT_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, "Property must be initialized");
|
||||||
MAP.put(MUST_BE_INITIALIZED_OR_BE_ABSTRACT, "Property must be initialized or be abstract");
|
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(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(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_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_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(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(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_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");
|
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 (abstractNode != null) { //has abstract modifier
|
||||||
if (!(classDescriptor.getModality() == Modality.ABSTRACT) && classDescriptor.getKind() != ClassKind.ENUM_CLASS) {
|
if (!(classDescriptor.getModality() == Modality.ABSTRACT) && classDescriptor.getKind() != ClassKind.ENUM_CLASS) {
|
||||||
JetClass classElement = (JetClass) BindingContextUtils.classDescriptorToDeclaration(trace.getBindingContext(), classDescriptor);
|
|
||||||
String name = property.getName();
|
String name = property.getName();
|
||||||
trace.report(ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS.on(property, name != null ? name : "", classDescriptor));
|
trace.report(ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS.on(property, name != null ? name : "", classDescriptor));
|
||||||
return;
|
return;
|
||||||
@@ -246,6 +245,9 @@ public class DeclarationsChecker {
|
|||||||
if (!error && property.getTypeRef() == null) {
|
if (!error && property.getTypeRef() == null) {
|
||||||
trace.report(PROPERTY_WITH_NO_TYPE_NO_INITIALIZER.on(property));
|
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;
|
return;
|
||||||
}
|
}
|
||||||
if (inTrait) {
|
if (inTrait) {
|
||||||
@@ -266,17 +268,20 @@ public class DeclarationsChecker {
|
|||||||
boolean inTrait = classDescriptor.getKind() == ClassKind.TRAIT;
|
boolean inTrait = classDescriptor.getKind() == ClassKind.TRAIT;
|
||||||
boolean inEnum = classDescriptor.getKind() == ClassKind.ENUM_CLASS;
|
boolean inEnum = classDescriptor.getKind() == ClassKind.ENUM_CLASS;
|
||||||
boolean inAbstractClass = classDescriptor.getModality() == Modality.ABSTRACT;
|
boolean inAbstractClass = classDescriptor.getModality() == Modality.ABSTRACT;
|
||||||
if (hasAbstractModifier && !inAbstractClass && !inTrait && !inEnum) {
|
if (hasAbstractModifier && !inAbstractClass && !inEnum) {
|
||||||
JetClass classElement = (JetClass) BindingContextUtils.classDescriptorToDeclaration(trace.getBindingContext(), classDescriptor);
|
|
||||||
trace.report(ABSTRACT_FUNCTION_IN_NON_ABSTRACT_CLASS.on(function, functionDescriptor.getName().getName(), classDescriptor));
|
trace.report(ABSTRACT_FUNCTION_IN_NON_ABSTRACT_CLASS.on(function, functionDescriptor.getName().getName(), classDescriptor));
|
||||||
}
|
}
|
||||||
if (hasAbstractModifier && inTrait) {
|
if (hasAbstractModifier && inTrait) {
|
||||||
trace.report(ABSTRACT_MODIFIER_IN_TRAIT.on(function));
|
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));
|
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));
|
trace.report(NON_ABSTRACT_FUNCTION_WITH_NO_BODY.on(function, functionDescriptor));
|
||||||
}
|
}
|
||||||
return;
|
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");
|
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")
|
@TestMetadata("kt2631_MultipleDeclaration.kt")
|
||||||
public void testKt2631_MultipleDeclaration() throws Exception {
|
public void testKt2631_MultipleDeclaration() throws Exception {
|
||||||
doTest("compiler/testData/diagnostics/tests/declarationChecks/kt2631_MultipleDeclaration.kt");
|
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);
|
factories.put(MUST_BE_INITIALIZED_OR_BE_ABSTRACT, addAbstractModifierFactory);
|
||||||
|
|
||||||
|
JetIntentionActionFactory removeFinalModifierFactory = RemoveModifierFix.createRemoveModifierFromListOwnerFactory(FINAL_KEYWORD);
|
||||||
|
|
||||||
JetIntentionActionFactory addAbstractToClassFactory = AddModifierFix.createFactory(ABSTRACT_KEYWORD, JetClass.class);
|
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, removeAbstractModifierFactory);
|
||||||
factories.put(ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS, addAbstractToClassFactory);
|
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, removeAbstractModifierFactory);
|
||||||
factories.put(ABSTRACT_FUNCTION_WITH_BODY, removeFunctionBodyFactory);
|
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();
|
JetIntentionActionFactory addFunctionBodyFactory = AddFunctionBodyFix.createFactory();
|
||||||
factories.put(NON_ABSTRACT_FUNCTION_WITH_NO_BODY, addAbstractModifierFactory);
|
factories.put(NON_ABSTRACT_FUNCTION_WITH_NO_BODY, addAbstractModifierFactory);
|
||||||
factories.put(NON_ABSTRACT_FUNCTION_WITH_NO_BODY, addFunctionBodyFactory);
|
factories.put(NON_ABSTRACT_FUNCTION_WITH_NO_BODY, addFunctionBodyFactory);
|
||||||
@@ -96,7 +101,7 @@ public class QuickFixes {
|
|||||||
factories.put(REDUNDANT_MODIFIER, removeRedundantModifierFactory);
|
factories.put(REDUNDANT_MODIFIER, removeRedundantModifierFactory);
|
||||||
factories.put(ABSTRACT_MODIFIER_IN_TRAIT, RemoveModifierFix.createRemoveModifierFromListOwnerFactory(ABSTRACT_KEYWORD, true));
|
factories.put(ABSTRACT_MODIFIER_IN_TRAIT, RemoveModifierFix.createRemoveModifierFromListOwnerFactory(ABSTRACT_KEYWORD, true));
|
||||||
factories.put(OPEN_MODIFIER_IN_TRAIT, RemoveModifierFix.createRemoveModifierFromListOwnerFactory(OPEN_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);
|
JetIntentionActionFactory removeOpenModifierFactory = RemoveModifierFix.createRemoveModifierFromListOwnerFactory(OPEN_KEYWORD);
|
||||||
factories.put(NON_FINAL_MEMBER_IN_FINAL_CLASS, AddModifierFix.createFactory(OPEN_KEYWORD, JetClass.class));
|
factories.put(NON_FINAL_MEMBER_IN_FINAL_CLASS, AddModifierFix.createFactory(OPEN_KEYWORD, JetClass.class));
|
||||||
|
|||||||
Reference in New Issue
Block a user