Prohibit protected constructor calls that are not super-calls in subtypes
#KT-11649 Fixed
This commit is contained in:
@@ -73,6 +73,8 @@ public interface Errors {
|
||||
DiagnosticFactory3.create(ERROR);
|
||||
DiagnosticFactory3<PsiElement, DeclarationDescriptor, Visibility, DeclarationDescriptor> INVISIBLE_MEMBER = DiagnosticFactory3.create(ERROR, CALL_ELEMENT);
|
||||
|
||||
DiagnosticFactory1<KtExpression, ConstructorDescriptor> PROTECTED_CONSTRUCTOR_NOT_IN_SUPER_CALL = DiagnosticFactory1.create(ERROR);
|
||||
|
||||
// Exposed visibility group
|
||||
DiagnosticFactory3<PsiElement, EffectiveVisibility, DescriptorWithRelation, EffectiveVisibility> EXPOSED_PROPERTY_TYPE = DiagnosticFactory3.create(ERROR);
|
||||
DiagnosticFactory3<PsiElement, EffectiveVisibility, DescriptorWithRelation, EffectiveVisibility> EXPOSED_FUNCTION_RETURN_TYPE = DiagnosticFactory3.create(ERROR);
|
||||
|
||||
+2
@@ -97,6 +97,8 @@ public class DefaultErrorMessages {
|
||||
MAP.put(INVISIBLE_REFERENCE, "Cannot access ''{0}'': it is ''{1}'' in {2}", NAME, TO_STRING, NAME_OF_PARENT_OR_FILE);
|
||||
MAP.put(INVISIBLE_MEMBER, "Cannot access ''{0}'': it is ''{1}'' in {2}", NAME, TO_STRING, NAME_OF_PARENT_OR_FILE);
|
||||
|
||||
MAP.put(PROTECTED_CONSTRUCTOR_NOT_IN_SUPER_CALL, "Protected constructor ''{0}'' from other classes can only be used in super-call", Renderers.SHORT_NAMES_IN_TYPES);
|
||||
|
||||
MAP.put(EXPOSED_PROPERTY_TYPE, "''{0}'' property exposes its ''{2}'' type{1}", TO_STRING, TO_STRING, TO_STRING);
|
||||
MAP.put(EXPOSED_FUNCTION_RETURN_TYPE, "''{0}'' function exposes its ''{2}'' return type{1}", TO_STRING, TO_STRING, TO_STRING);
|
||||
MAP.put(EXPOSED_PARAMETER_TYPE, "''{0}'' function exposes its ''{2}'' parameter type{1}", TO_STRING, TO_STRING, TO_STRING);
|
||||
|
||||
@@ -69,7 +69,7 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf(
|
||||
|
||||
private val DEFAULT_CALL_CHECKERS = listOf(CapturingInClosureChecker(), InlineCheckerWrapper(), ReifiedTypeParameterSubstitutionChecker(),
|
||||
SafeCallChecker(), InvokeConventionChecker(), CallReturnsArrayOfNothingChecker(),
|
||||
ConstructorHeaderCallChecker)
|
||||
ConstructorHeaderCallChecker, ProtectedConstructorCallChecker)
|
||||
private val DEFAULT_TYPE_CHECKERS = emptyList<AdditionalTypeChecker>()
|
||||
private val DEFAULT_VALIDATORS = listOf(DeprecatedSymbolValidator(), OperatorValidator(), InfixValidator())
|
||||
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.calls.checkers
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.psi.KtConstructorCalleeExpression
|
||||
import org.jetbrains.kotlin.psi.KtConstructorDelegationReferenceExpression
|
||||
import org.jetbrains.kotlin.psi.KtSuperTypeCallEntry
|
||||
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf
|
||||
|
||||
object ProtectedConstructorCallChecker : CallChecker {
|
||||
override fun check(resolvedCall: ResolvedCall<*>, context: BasicCallResolutionContext) {
|
||||
val descriptor = resolvedCall.resultingDescriptor as? ConstructorDescriptor ?: return
|
||||
val constructorOwner = descriptor.containingDeclaration.original
|
||||
val scopeOwner = context.scope.ownerDescriptor
|
||||
|
||||
if (descriptor.visibility.normalize() != Visibilities.PROTECTED) return
|
||||
// Error already reported
|
||||
if (!Visibilities.isVisibleWithAnyReceiver(descriptor, scopeOwner)) return
|
||||
|
||||
val calleeExpression = resolvedCall.call.calleeExpression ?: return
|
||||
|
||||
// Permit constructor super-calls
|
||||
when (calleeExpression) {
|
||||
is KtConstructorCalleeExpression -> if (calleeExpression.parent is KtSuperTypeCallEntry) return
|
||||
is KtConstructorDelegationReferenceExpression -> return
|
||||
}
|
||||
|
||||
// Permit calls within class
|
||||
if (scopeOwner.parentsWithSelf.any { it.original === constructorOwner }) return
|
||||
|
||||
// Using FALSE_IF_PROTECTED helps us to check that descriptor doesn't meet conditions of java package/static-protected
|
||||
// (i.e. being in the same package)
|
||||
// And without ProtectedConstructorCallChecker such calls would be allowed only because they are performed within subclass
|
||||
// of constructor owner
|
||||
@Suppress("DEPRECATION")
|
||||
if (Visibilities.findInvisibleMember(Visibilities.FALSE_IF_PROTECTED, descriptor, scopeOwner) == descriptor) {
|
||||
context.trace.report(Errors.PROTECTED_CONSTRUCTOR_NOT_IN_SUPER_CALL.on(calleeExpression, descriptor))
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
|
||||
open class A protected constructor(x: Int) {
|
||||
protected constructor() : this(1)
|
||||
public constructor(x: Double) : this(3)
|
||||
}
|
||||
|
||||
class B4 : A(1) {
|
||||
init {
|
||||
<!PROTECTED_CONSTRUCTOR_NOT_IN_SUPER_CALL!>A<!>()
|
||||
<!PROTECTED_CONSTRUCTOR_NOT_IN_SUPER_CALL!>A<!>(1)
|
||||
A(5.0)
|
||||
}
|
||||
|
||||
fun foo() {
|
||||
<!PROTECTED_CONSTRUCTOR_NOT_IN_SUPER_CALL!>A<!>()
|
||||
<!PROTECTED_CONSTRUCTOR_NOT_IN_SUPER_CALL!>A<!>(1)
|
||||
A(5.0)
|
||||
|
||||
object : A() {}
|
||||
object : A(1) {}
|
||||
object : A(5.0) {}
|
||||
|
||||
class Local : A()
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package
|
||||
|
||||
public open class A {
|
||||
protected constructor A()
|
||||
public constructor A(/*0*/ x: kotlin.Double)
|
||||
protected constructor A(/*0*/ x: kotlin.Int)
|
||||
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
|
||||
}
|
||||
|
||||
public final class B4 : A {
|
||||
public constructor B4()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public 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
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
|
||||
// FILE: abc/A.java
|
||||
package abc;
|
||||
public class A {
|
||||
protected A() {}
|
||||
protected A(int x) {}
|
||||
public A(double x) {}
|
||||
}
|
||||
|
||||
// FILE: main.kt
|
||||
import abc.*
|
||||
|
||||
class B4 : A(1) {
|
||||
init {
|
||||
<!PROTECTED_CONSTRUCTOR_NOT_IN_SUPER_CALL!>A<!>()
|
||||
<!PROTECTED_CONSTRUCTOR_NOT_IN_SUPER_CALL!>A<!>(1)
|
||||
A(5.0)
|
||||
}
|
||||
|
||||
fun foo() {
|
||||
<!PROTECTED_CONSTRUCTOR_NOT_IN_SUPER_CALL!>A<!>()
|
||||
<!PROTECTED_CONSTRUCTOR_NOT_IN_SUPER_CALL!>A<!>(1)
|
||||
A(5.0)
|
||||
|
||||
object : A() {}
|
||||
object : A(1) {}
|
||||
object : A(5.0) {}
|
||||
|
||||
class Local : A()
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package
|
||||
|
||||
public final class B4 : abc.A {
|
||||
public constructor B4()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public 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
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
|
||||
// FILE: A.java
|
||||
public class A {
|
||||
protected A() {}
|
||||
protected A(int x) {}
|
||||
public A(double x) {}
|
||||
}
|
||||
|
||||
// FILE: main.kt
|
||||
|
||||
class B4 : A(1) {
|
||||
init {
|
||||
A()
|
||||
A(1)
|
||||
A(5.0)
|
||||
}
|
||||
|
||||
fun foo() {
|
||||
A()
|
||||
A(1)
|
||||
A(5.0)
|
||||
|
||||
object : A() {}
|
||||
object : A(1) {}
|
||||
object : A(5.0) {}
|
||||
|
||||
class Local : A()
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package
|
||||
|
||||
public open class A {
|
||||
protected/*protected and package*/ constructor A()
|
||||
public constructor A(/*0*/ x: kotlin.Double)
|
||||
protected/*protected and package*/ constructor A(/*0*/ x: kotlin.Int)
|
||||
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
|
||||
}
|
||||
|
||||
public final class B4 : A {
|
||||
public constructor B4()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public 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
|
||||
}
|
||||
@@ -15506,6 +15506,24 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("nonSuperCallConstructor.kt")
|
||||
public void testNonSuperCallConstructor() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/scopes/protectedVisibility/nonSuperCallConstructor.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("nonSuperCallConstructorJavaDifferentPackage.kt")
|
||||
public void testNonSuperCallConstructorJavaDifferentPackage() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/scopes/protectedVisibility/nonSuperCallConstructorJavaDifferentPackage.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("nonSuperCallConstructorJavaSamePackage.kt")
|
||||
public void testNonSuperCallConstructorJavaSamePackage() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/scopes/protectedVisibility/nonSuperCallConstructorJavaSamePackage.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("protectedCallOnSubClass.kt")
|
||||
public void testProtectedCallOnSubClass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/scopes/protectedVisibility/protectedCallOnSubClass.kt");
|
||||
|
||||
@@ -160,6 +160,9 @@ public class Visibilities {
|
||||
@NotNull DeclarationDescriptorWithVisibility whatDeclaration,
|
||||
@NotNull ClassDescriptor fromClass
|
||||
) {
|
||||
//noinspection deprecation
|
||||
if (receiver == FALSE_IF_PROTECTED) return false;
|
||||
|
||||
// Do not check receiver for non-callable declarations
|
||||
if (!(whatDeclaration instanceof CallableMemberDescriptor)) return true;
|
||||
// Constructor accessibility check is performed manually
|
||||
@@ -282,7 +285,6 @@ public class Visibilities {
|
||||
return findInvisibleMember(IRRELEVANT_RECEIVER, what, from) == null;
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
public static DeclarationDescriptorWithVisibility findInvisibleMember(
|
||||
@Nullable ReceiverValue receiver,
|
||||
@@ -362,6 +364,16 @@ public class Visibilities {
|
||||
}
|
||||
};
|
||||
|
||||
// This constant is not intended to use somewhere else from
|
||||
@Deprecated
|
||||
public static final ReceiverValue FALSE_IF_PROTECTED = new ReceiverValue() {
|
||||
@NotNull
|
||||
@Override
|
||||
public KotlinType getType() {
|
||||
throw new IllegalStateException("This method should not be called");
|
||||
}
|
||||
};
|
||||
|
||||
public static boolean isPrivate(@NotNull Visibility visibility) {
|
||||
return visibility == PRIVATE || visibility == PRIVATE_TO_THIS;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user