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 bfecce4b3ca..6ae0e75256b 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java @@ -16,6 +16,7 @@ package org.jetbrains.jet.lang.diagnostics; +import com.google.common.collect.Lists; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiNameIdentifierOwner; @@ -32,15 +33,11 @@ import org.jetbrains.jet.resolve.DescriptorRenderer; import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -import static org.jetbrains.jet.lang.diagnostics.Severity.ERROR; -import static org.jetbrains.jet.lang.diagnostics.Severity.WARNING; +import java.util.*; import static org.jetbrains.jet.lang.diagnostics.Renderers.*; +import static org.jetbrains.jet.lang.diagnostics.Severity.ERROR; +import static org.jetbrains.jet.lang.diagnostics.Severity.WARNING; /** * @author abreslav @@ -374,7 +371,9 @@ public interface Errors { } }, TO_STRING, TO_STRING); - DiagnosticFactory2 OVERRIDING_FINAL_MEMBER = DiagnosticFactory2.create(ERROR, "{0} in {1} is final and cannot be overridden", NAME, NAME); + DiagnosticFactory2 OVERRIDING_FINAL_MEMBER = DiagnosticFactory2.create(ERROR, "''{0}'' in ''{1}'' is final and cannot be overridden", NAME, NAME); + DiagnosticFactory3 CANNOT_WEAKEN_ACCESS_PRIVILEGE = DiagnosticFactory3.create(ERROR, "Cannot weaken access privilege ''{0}'' for ''{1}'' in ''{2}''", PositioningStrategies.POSITION_VISIBILITY_MODIFIER, TO_STRING, NAME, NAME); + DiagnosticFactory3 CANNOT_CHANGE_ACCESS_PRIVILEGE = DiagnosticFactory3.create(ERROR, "Cannot change access privilege ''{0}'' for ''{1}'' in ''{2}''", PositioningStrategies.POSITION_VISIBILITY_MODIFIER, TO_STRING, NAME, NAME); DiagnosticFactory2 RETURN_TYPE_MISMATCH_ON_OVERRIDE = DiagnosticFactory2.create(ERROR, "Return type of {0} is not a subtype of the return type overridden member {1}", PositioningStrategies.POSITION_DECLARATION, DescriptorRenderer.TEXT, DescriptorRenderer.TEXT); 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 54a54ca0904..faef5b871bc 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/PositioningStrategies.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/PositioningStrategies.java @@ -17,6 +17,7 @@ package org.jetbrains.jet.lang.diagnostics; +import com.google.common.collect.Lists; import com.intellij.lang.ASTNode; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; @@ -111,4 +112,20 @@ public class PositioningStrategies { return markElement(element.getIndicesNode()); } }; + + public static PositioningStrategy POSITION_VISIBILITY_MODIFIER = new PositioningStrategy() { + @NotNull + @Override + public List mark(@NotNull JetModifierListOwner element) { + List visibilityTokens = Lists + .newArrayList(JetTokens.PRIVATE_KEYWORD, JetTokens.PROTECTED_KEYWORD, JetTokens.PUBLIC_KEYWORD, JetTokens.INTERNAL_KEYWORD); + List result = Lists.newArrayList(); + for (JetKeywordToken token : visibilityTokens) { + if (element.hasModifier(token)) { + result.add(element.getModifierList().getModifierNode(token).getTextRange()); + } + } + return result; + } + }; } \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/OverrideResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/OverrideResolver.java index 9fa1a5be12e..8aaf3bbb538 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/OverrideResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/OverrideResolver.java @@ -69,6 +69,9 @@ public class OverrideResolver { public void process() { generateOverrides(); checkOverrides(); + //functions and properties visibility can be inherited when overriding, so it can be resolved only after overrides resolve is finished + //also invisible overridden descriptors are removed here + resolveVisibilityForFunctionsAndProperties(); } /** @@ -417,4 +420,73 @@ public class OverrideResolver { } return false; } + + private void resolveVisibilityForFunctionsAndProperties() { + for (Map.Entry entry : context.getFunctions().entrySet()) { + JetNamedFunction function = entry.getKey(); + SimpleFunctionDescriptor functionDescriptor = entry.getValue(); + + Visibility visibility = resolveVisibilityForMember(function, functionDescriptor); + ((SimpleFunctionDescriptorImpl) functionDescriptor).setVisibility(visibility); + } + for (Map.Entry entry : context.getProperties().entrySet()) { + JetProperty property = entry.getKey(); + PropertyDescriptor propertyDescriptor = entry.getValue(); + + Visibility visibility = resolveVisibilityForMember(property, propertyDescriptor); + propertyDescriptor.setVisibility(visibility); + } + } + + private Visibility resolveVisibilityForMember(JetDeclaration member, CallableMemberDescriptor memberDescriptor) { + removeInvisibleOverriddenDescriptors(memberDescriptor); + Visibility defaultVisibility = findMaxVisibility(memberDescriptor.getOverriddenDescriptors()); + Visibility visibility = DescriptorResolver.resolveVisibilityFromModifiers(member.getModifierList(), defaultVisibility); + checkMaxVisibility(visibility, member, memberDescriptor.getOverriddenDescriptors()); + return visibility; + } + + private void removeInvisibleOverriddenDescriptors(@NotNull CallableDescriptor descriptor) { + Set overriddenDescriptors = descriptor.getOverriddenDescriptors(); + for (Iterator iterator = overriddenDescriptors.iterator(); iterator.hasNext(); ) { + CallableDescriptor superDescriptor = iterator.next(); + if (!Visibilities.isVisible(superDescriptor, descriptor)) { + iterator.remove(); + } + } + } + + private Visibility findMaxVisibility(@NotNull Collection descriptors) { + Visibility maxVisibility = null; + for (DeclarationDescriptorWithVisibility descriptor : descriptors) { + Visibility visibility = descriptor.getVisibility(); + if (maxVisibility == null) { + maxVisibility = visibility; + continue; + } + Integer compare = Visibilities.compare(visibility, maxVisibility); + if (compare == null) { + maxVisibility = Visibilities.PUBLIC; //todo error or warning when inference only from incomparable visibilities + continue; + } + if (compare > 0) { + maxVisibility = visibility; + } + } + return maxVisibility != null ? maxVisibility : Visibilities.INTERNAL; + } + + private void checkMaxVisibility(@NotNull Visibility visibility, @NotNull JetModifierListOwner modifierListOwner, @NotNull Collection descriptors) { + for (CallableMemberDescriptor descriptor : descriptors) { + Integer compare = Visibilities.compare(visibility, descriptor.getVisibility()); + if (compare == null) { + trace.report(CANNOT_CHANGE_ACCESS_PRIVILEGE.on(modifierListOwner, descriptor.getVisibility(), descriptor, descriptor.getContainingDeclaration())); + return; + } + else if (compare < 0) { + trace.report(CANNOT_WEAKEN_ACCESS_PRIVILEGE.on(modifierListOwner, descriptor.getVisibility(), descriptor, descriptor.getContainingDeclaration())); + return; + } + } + } } diff --git a/compiler/testData/codegen/regressions/kt1170.kt b/compiler/testData/codegen/regressions/kt1170.kt index aa003bd2a2d..494f3c3e1a3 100644 --- a/compiler/testData/codegen/regressions/kt1170.kt +++ b/compiler/testData/codegen/regressions/kt1170.kt @@ -1,5 +1,5 @@ public abstract class BaseClass() { - protected open val kind : String = "BaseClass " + open val kind : String = "BaseClass " fun getKindValue() : String { return kind diff --git a/compiler/testData/diagnostics/tests/scopes/visibility_inherit_modifier.jet b/compiler/testData/diagnostics/tests/scopes/visibility_inherit_modifier.jet new file mode 100644 index 00000000000..273f4f8c705 --- /dev/null +++ b/compiler/testData/diagnostics/tests/scopes/visibility_inherit_modifier.jet @@ -0,0 +1,85 @@ +package b + +open class A { + internal open fun foo() {} +} + +class B : A() { + protected override fun foo() {} +} + +class C : A() { + internal override fun foo() {} +} + +//------------ +open class D { + private open fun self() : D = this +} + +class E : D() { + internal override fun self() = this + + fun test() { + val s : E = self() + } +} + +//------------ +open class F { + protected open fun protected_fun() {} +} + +class G : F() { + override fun protected_fun() {} +} + +fun test_fun_stays_protected(g: G) { + g.protected_fun() +} + +//------------ +open class H { + internal protected open fun pi_fun() {} +} + +class I : H() { + protected override fun pi_fun() {} +} + +class J : H() { + internal override fun pi_fun() {} +} + +class K : H() { + public override fun pi_fun() {} +} + +//------------- +trait T { + public fun foo() {} +} + +open class L : T { + override fun foo() {} +} + +class M : L() { + internal override fun foo() {} +} +//--------------- +trait R { + internal protected fun foo() {} +} + +trait P : R { + internal override fun foo() {} +} + +trait Q : R { + protected override fun foo() {} +} + +class S : P, Q { + internal override fun foo() {} +}