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 e7f5750638e..07b00557761 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java @@ -250,6 +250,7 @@ public interface Errors { SimpleDiagnosticFactory PROPERTY_INITIALIZER_NO_BACKING_FIELD = SimpleDiagnosticFactory.create(ERROR); SimpleDiagnosticFactory PROPERTY_INITIALIZER_IN_TRAIT = SimpleDiagnosticFactory.create(ERROR); + SimpleDiagnosticFactory FINAL_PROPERTY_IN_TRAIT = SimpleDiagnosticFactory.create(ERROR, FINAL_MODIFIER); SimpleDiagnosticFactory BACKING_FIELD_IN_TRAIT = SimpleDiagnosticFactory.create(ERROR, NAMED_ELEMENT); DiagnosticFactory2 ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS = DiagnosticFactory2.create(ERROR, ABSTRACT_MODIFIER); @@ -266,6 +267,7 @@ public interface Errors { DiagnosticFactory1 ABSTRACT_FUNCTION_WITH_BODY = DiagnosticFactory1.create(ERROR, ABSTRACT_MODIFIER); DiagnosticFactory1 NON_ABSTRACT_FUNCTION_WITH_NO_BODY = DiagnosticFactory1.create(ERROR, NAMED_ELEMENT); + DiagnosticFactory1 FINAL_FUNCTION_WITH_NO_BODY = DiagnosticFactory1.create(ERROR, FINAL_MODIFIER); DiagnosticFactory1 NON_MEMBER_FUNCTION_NO_BODY = DiagnosticFactory1.create(ERROR, NAMED_ELEMENT); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/PositioningStrategies.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/PositioningStrategies.java index c0c83208774..a0dcc436be3 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/PositioningStrategies.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/PositioningStrategies.java @@ -165,6 +165,8 @@ public class PositioningStrategies { public static final PositioningStrategy OVERRIDE_MODIFIER = modifierSetPosition(JetTokens.OVERRIDE_KEYWORD); + public static final PositioningStrategy FINAL_MODIFIER = modifierSetPosition(JetTokens.FINAL_KEYWORD); + public static final PositioningStrategy VARIANCE_MODIFIER = modifierSetPosition(JetTokens.IN_KEYWORD, JetTokens.OUT_KEYWORD); 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 2e2e3211bec..5b7adc54bc9 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 @@ -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"); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DeclarationsChecker.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DeclarationsChecker.java index 6d5c989cc4e..518345d6c70 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DeclarationsChecker.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DeclarationsChecker.java @@ -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; diff --git a/compiler/testData/diagnostics/tests/declarationChecks/kt2397.kt b/compiler/testData/diagnostics/tests/declarationChecks/kt2397.kt new file mode 100644 index 00000000000..0b5e602d432 --- /dev/null +++ b/compiler/testData/diagnostics/tests/declarationChecks/kt2397.kt @@ -0,0 +1,17 @@ +//KT-2397 Prohibit final methods in traits with no implementation +package a + +trait T { + final fun foo() + final val b : Int + + final fun bar() {} + final val c : Int + get() = 42 + + final val d = 1 +} + +class A { + final fun foo() +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java index c00b50dc8b5..abb775a4cc8 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -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"); diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixes.java b/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixes.java index 1be6b61d9e8..03c32c160ca 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixes.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixes.java @@ -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));