diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 5b41ea5d57d..c8cab080019 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -71,6 +71,7 @@ public interface Errors { DiagnosticFactory3 INVISIBLE_REFERENCE = DiagnosticFactory3.create(ERROR); DiagnosticFactory3 INVISIBLE_MEMBER = DiagnosticFactory3.create(ERROR, CALL_ELEMENT); + DiagnosticFactory3 ACCESS_TO_PRIVATE_TOP_LEVEL_FROM_ANOTHER_FILE = DiagnosticFactory3.create(WARNING, CALL_ELEMENT); DiagnosticFactory1> PLATFORM_CLASS_MAPPED_TO_KOTLIN = DiagnosticFactory1.create(WARNING); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index a30a2d21c20..bf205164418 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -624,6 +624,7 @@ public class DefaultErrorMessages { MAP.put(NON_LOCAL_RETURN_NOT_ALLOWED, "Can''t inline ''{0}'' here: it may contain non-local returns. Annotate parameter declaration ''{0}'' with ''inlineOptions(ONLY_LOCAL_RETURN)''", ELEMENT_TEXT, SHORT_NAMES_IN_TYPES, SHORT_NAMES_IN_TYPES); MAP.put(INLINE_CALL_CYCLE, "The ''{0}'' invocation is a part of inline cycle", NAME); MAP.put(NON_LOCAL_RETURN_IN_DISABLED_INLINE, "Non-local returns are not allowed with inlining disabled"); + MAP.put(ACCESS_TO_PRIVATE_TOP_LEVEL_FROM_ANOTHER_FILE, "Access to private top-level declarations from another file will soon be forbidden: ''{0}'' is ''{1}'' in ''{2}''", NAME, TO_STRING, RENDER_FILE); MAP.setImmutable(); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/Renderers.kt b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/Renderers.kt index 13ec5c20b30..7fa90560531 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/Renderers.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/Renderers.kt @@ -27,6 +27,7 @@ import org.jetbrains.kotlin.diagnostics.rendering.TabledDescriptorRenderer.newTa import org.jetbrains.kotlin.diagnostics.rendering.TabledDescriptorRenderer.newText import org.jetbrains.kotlin.psi.JetClass import org.jetbrains.kotlin.psi.JetClassOrObject +import org.jetbrains.kotlin.psi.JetFile import org.jetbrains.kotlin.psi.JetNamedDeclaration import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.renderer.Renderer @@ -87,6 +88,8 @@ public object Renderers { public val RENDER_TYPE: Renderer = Renderer { DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(it) } + public val RENDER_FILE: Renderer = Renderer { it.name } + public val RENDER_POSITION_VARIANCE: Renderer = Renderer { variance: Variance -> when (variance) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt index 3470368a91b..91f29b65675 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.resolve import org.jetbrains.kotlin.container.StorageComponentContainer import org.jetbrains.kotlin.container.useInstance import org.jetbrains.kotlin.resolve.calls.checkers.* +import org.jetbrains.kotlin.resolve.validation.AccessToPrivateTopLevelSymbolValidator import org.jetbrains.kotlin.resolve.validation.DeprecatedSymbolValidator import org.jetbrains.kotlin.resolve.validation.SymbolUsageValidator import org.jetbrains.kotlin.types.DynamicTypesSettings @@ -40,7 +41,7 @@ public abstract class TargetPlatform( private val DEFAULT_DECLARATION_CHECKERS = listOf(DataClassAnnotationChecker()) private val DEFAULT_CALL_CHECKERS = listOf(CapturingInClosureChecker(), InlineCheckerWrapper(), ReifiedTypeParameterSubstitutionChecker()) private val DEFAULT_TYPE_CHECKERS = emptyList() -private val DEFAULT_VALIDATORS = listOf(DeprecatedSymbolValidator()) +private val DEFAULT_VALIDATORS = listOf(DeprecatedSymbolValidator(), AccessToPrivateTopLevelSymbolValidator()) public open class PlatformConfigurator( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/validation/AccessToPrivateTopLevelSymbolValidator.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/validation/AccessToPrivateTopLevelSymbolValidator.kt new file mode 100644 index 00000000000..f005dcc6b54 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/validation/AccessToPrivateTopLevelSymbolValidator.kt @@ -0,0 +1,66 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.validation + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.psi.JetElement +import org.jetbrains.kotlin.psi.JetFile +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.BindingTrace +import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils +import org.jetbrains.kotlin.resolve.DescriptorUtils + +public class AccessToPrivateTopLevelSymbolValidator : SymbolUsageValidator { + override fun validateCall(targetDescriptor: CallableDescriptor, trace: BindingTrace, element: PsiElement) { + val descriptor = + if (DescriptorUtils.isTopLevelDeclaration(targetDescriptor)) targetDescriptor + else DescriptorUtils.getContainingClass(targetDescriptor) + + descriptor?.let { + reportIfNeeded(it, trace, element) + } + } + + override fun validateTypeUsage(targetDescriptor: ClassifierDescriptor, trace: BindingTrace, element: PsiElement) = + reportIfNeeded(targetDescriptor, trace, element) + + private fun reportIfNeeded(descriptor: DeclarationDescriptor, trace: BindingTrace, element: PsiElement) { + if (descriptor !is DeclarationDescriptorWithVisibility || + descriptor.visibility != Visibilities.PRIVATE || + !DescriptorUtils.isTopLevelDeclaration(descriptor)) return + + if (descriptor is PropertySetterDescriptor && descriptor.correspondingProperty.visibility == Visibilities.PRIVATE) return + + if (element !is JetElement) return + + DescriptorToSourceUtils.getContainingFile(descriptor)?.let { + val jetFile = element.containingFile as? JetFile ?: return + + val currentPackageViewDescriptor = trace.bindingContext.get(BindingContext.FILE_TO_PACKAGE_FRAGMENT, jetFile) + if (currentPackageViewDescriptor != null && !DescriptorUtils.areInSameModule(currentPackageViewDescriptor, descriptor)) return + + val packageFqName = DescriptorUtils.getParentOfType(descriptor, PackageFragmentDescriptor::class.java)?.fqName + if (packageFqName != currentPackageViewDescriptor?.fqName) return + + if (jetFile != it) { + trace.report(Errors.ACCESS_TO_PRIVATE_TOP_LEVEL_FROM_ANOTHER_FILE.on(element, descriptor, descriptor.visibility, it)) + } + } + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/multimodule/packagePrivate.kt b/compiler/testData/diagnostics/tests/multimodule/packagePrivate.kt index 3c9aaa57c95..87f62aa6424 100644 --- a/compiler/testData/diagnostics/tests/multimodule/packagePrivate.kt +++ b/compiler/testData/diagnostics/tests/multimodule/packagePrivate.kt @@ -9,7 +9,7 @@ private val a = 1 package p -val b = a // same package, same module +val b = a // same package, same module // MODULE: m2(m1) // FILE: c.kt diff --git a/compiler/testData/diagnostics/tests/privateInFile/visibility.kt b/compiler/testData/diagnostics/tests/privateInFile/visibility.kt new file mode 100644 index 00000000000..b856fde0823 --- /dev/null +++ b/compiler/testData/diagnostics/tests/privateInFile/visibility.kt @@ -0,0 +1,54 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE + +//FILE:file1.kt +package a + +private open class A { + fun bar() {} +} + +private var x: Int = 10 + +var xx: Int = 20 + private set(value: Int) {} + +private fun foo() {} + +private fun bar() { + val y = x + x = 20 + xx = 30 +} + +fun makeA() = A() + +private object PO {} + +//FILE:file2.kt +package a + +fun test() { + val y = makeA() + y.bar() + foo() + + val u : A = A() + + val z = x + x = 30 + + val po = PO + + val v = xx + xx = 40 +} + +class B : A() {} + +class Q { + class W { + fun foo() { + val y = makeA() //assure that 'makeA' is visible + } + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/privateInFile/visibility.txt b/compiler/testData/diagnostics/tests/privateInFile/visibility.txt new file mode 100644 index 00000000000..be93aaff5d1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/privateInFile/visibility.txt @@ -0,0 +1,48 @@ +package + +package a { + private var x: kotlin.Int + internal var xx: kotlin.Int + private fun bar(): kotlin.Unit + private fun foo(): kotlin.Unit + internal fun makeA(): a.A + internal fun test(): kotlin.Unit + + private open class A { + public constructor A() + internal final fun bar(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + internal final class B : a.A { + public constructor B() + internal final override /*1*/ /*fake_override*/ fun bar(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + private object PO { + private constructor PO() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + internal final class Q { + public constructor Q() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + internal final class W { + public constructor W() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + internal final fun foo(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + } +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java index f64d3b07890..33c81548583 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -10918,6 +10918,21 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { } } + @TestMetadata("compiler/testData/diagnostics/tests/privateInFile") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class PrivateInFile extends AbstractJetDiagnosticsTest { + public void testAllFilesPresentInPrivateInFile() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/privateInFile"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("visibility.kt") + public void testVisibility() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/privateInFile/visibility.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/diagnostics/tests/reassignment") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)