Published-api checker refactoring, support java package-protected visibility

This commit is contained in:
Mikhael Bogdanov
2016-12-19 13:58:38 +01:00
parent 0fa2c54ae8
commit 6ca1d47207
4 changed files with 108 additions and 19 deletions
@@ -20,7 +20,6 @@ import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.FunctionTypesKt;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.diagnostics.Errors;
import org.jetbrains.kotlin.lexer.KtToken;
@@ -51,13 +50,13 @@ import static org.jetbrains.kotlin.resolve.inline.InlineUtil.checkNonLocalReturn
class InlineChecker implements CallChecker {
private final FunctionDescriptor descriptor;
private final Set<CallableDescriptor> inlinableParameters = new LinkedHashSet<CallableDescriptor>();
private final boolean isEffectivelyPublicOrPublishedApiFunction;
private final EffectiveVisibility inlineFunEffectiveVisibility;
private final boolean isEffectivelyPrivateApiFunction;
public InlineChecker(@NotNull FunctionDescriptor descriptor) {
assert InlineUtil.isInline(descriptor) : "This extension should be created only for inline functions: " + descriptor;
this.descriptor = descriptor;
this.isEffectivelyPublicOrPublishedApiFunction = isEffectivelyPublicOrPublishedApi(descriptor);
this.inlineFunEffectiveVisibility = EffectiveVisibilityKt.effectiveVisibility(descriptor, descriptor.getVisibility(), true);
this.isEffectivelyPrivateApiFunction = DescriptorUtilsKt.isEffectivelyPrivateApi(descriptor);
for (ValueParameterDescriptor param : descriptor.getValueParameters()) {
if (isInlinableParameter(param)) {
@@ -239,31 +238,33 @@ class InlineChecker implements CallChecker {
}
private void checkVisibilityAndAccess(
@NotNull CallableDescriptor declarationDescriptor,
@NotNull CallableDescriptor calledDescriptor,
@NotNull KtElement expression,
@NotNull CallCheckerContext context
) {
boolean declarationDescriptorIsPublicOrPublishedApi = isEffectivelyPublicOrPublishedApi(declarationDescriptor) ||
isDefinedInInlineFunction(declarationDescriptor);
if (isEffectivelyPublicOrPublishedApiFunction &&
!declarationDescriptorIsPublicOrPublishedApi &&
declarationDescriptor.getVisibility() != Visibilities.LOCAL) {
context.getTrace().report(Errors.NON_PUBLIC_CALL_FROM_PUBLIC_INLINE.on(expression, declarationDescriptor, descriptor));
EffectiveVisibility calledFunEffectiveVisibility =
isDefinedInInlineFunction(calledDescriptor) ?
EffectiveVisibility.Public.INSTANCE :
EffectiveVisibilityKt.effectiveVisibility(calledDescriptor, calledDescriptor.getVisibility(), true);
boolean isCalledFunPublicOrPublishedApi = calledFunEffectiveVisibility.getPublicApi();
boolean isInlineFunPublicOrPublishedApi = inlineFunEffectiveVisibility.getPublicApi();
if (isInlineFunPublicOrPublishedApi &&
!isCalledFunPublicOrPublishedApi &&
calledDescriptor.getVisibility() != Visibilities.LOCAL) {
context.getTrace().report(Errors.NON_PUBLIC_CALL_FROM_PUBLIC_INLINE.on(expression, calledDescriptor, descriptor));
}
else {
checkPrivateClassMemberAccess(declarationDescriptor, expression, context);
checkPrivateClassMemberAccess(calledDescriptor, expression, context);
}
if (isEffectivelyPublicOrPublishedApiFunction && declarationDescriptor.getVisibility() == Visibilities.PROTECTED) {
context.getTrace().report(Errors.PROTECTED_CALL_FROM_PUBLIC_INLINE.on(expression, declarationDescriptor));
if (isInlineFunPublicOrPublishedApi &&
inlineFunEffectiveVisibility.toVisibility() != Visibilities.PROTECTED &&
calledFunEffectiveVisibility.toVisibility() == Visibilities.PROTECTED) {
context.getTrace().report(Errors.PROTECTED_CALL_FROM_PUBLIC_INLINE.on(expression, calledDescriptor));
}
}
private static boolean isEffectivelyPublicOrPublishedApi(@NotNull CallableDescriptor descriptor) {
EffectiveVisibility visibility = EffectiveVisibilityKt.effectiveVisibility(descriptor, descriptor.getVisibility(), true);
return visibility.getPublicApi();
}
private void checkPrivateClassMemberAccess(
@NotNull DeclarationDescriptor declarationDescriptor,
@NotNull KtElement expression,
@@ -1,5 +1,13 @@
// !DIAGNOSTICS: -EXPOSED_PARAMETER_TYPE -NOTHING_TO_INLINE
// FILE: JavaClass.java
public abstract class JavaClass {
protected void bind() {}
}
// FILE: main.kt
open class A {
protected fun test() {}
@@ -28,6 +36,13 @@ open class A {
zVar
<!PROTECTED_CALL_FROM_PUBLIC_INLINE!>zVar<!> = "123"
}
protected inline fun callFromProtected() {
test()
zVar
zVar = "123"
}
}
class B : A() {
@@ -36,6 +51,25 @@ class B : A() {
}
}
class C : JavaClass() {
inline fun call() {
<!PROTECTED_CALL_FROM_PUBLIC_INLINE!>bind<!>()
}
internal inline fun callFromInternal() {
bind()
}
protected inline fun callFromProtected() {
bind()
}
@PublishedApi
internal inline fun callFromPublished() {
<!PROTECTED_CALL_FROM_PUBLIC_INLINE!>bind<!>()
}
}
internal class AInternal {
protected fun test() {}
@@ -54,4 +88,14 @@ internal class AInternal {
internal inline fun call2() {
test()
}
}
private class X {
public class Z : A() {
public inline fun effictivelyNonPublic() {
test()
}
}
}
@@ -6,6 +6,7 @@ public open class A {
public final var zVar: kotlin.String
public final inline fun call(): kotlin.Unit
internal final inline fun callFromInternal(): kotlin.Unit
protected final inline fun callFromProtected(): kotlin.Unit
@kotlin.PublishedApi internal final inline fun callFromPublished(): 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
@@ -31,6 +32,7 @@ public final class B : A {
public final override /*1*/ /*fake_override*/ var zVar: kotlin.String
public final inline override /*1*/ /*fake_override*/ fun call(): kotlin.Unit
internal final inline override /*1*/ /*fake_override*/ fun callFromInternal(): kotlin.Unit
protected final inline override /*1*/ /*fake_override*/ fun callFromProtected(): kotlin.Unit
@kotlin.PublishedApi internal final inline override /*1*/ /*fake_override*/ fun callFromPublished(): 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
@@ -38,3 +40,45 @@ public final class B : A {
public final inline fun testB(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class C : JavaClass {
public constructor C()
protected/*protected and package*/ open override /*1*/ /*fake_override*/ fun bind(): kotlin.Unit
public final inline fun call(): kotlin.Unit
internal final inline fun callFromInternal(): kotlin.Unit
protected final inline fun callFromProtected(): kotlin.Unit
@kotlin.PublishedApi internal final inline fun callFromPublished(): 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
}
public abstract class JavaClass {
public constructor JavaClass()
protected/*protected and package*/ open fun bind(): 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 final class X {
public constructor X()
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 Z : A {
public constructor Z()
protected final override /*1*/ /*fake_override*/ val z: kotlin.String
public final override /*1*/ /*fake_override*/ var zVar: kotlin.String
public final inline override /*1*/ /*fake_override*/ fun call(): kotlin.Unit
internal final inline override /*1*/ /*fake_override*/ fun callFromInternal(): kotlin.Unit
protected final inline override /*1*/ /*fake_override*/ fun callFromProtected(): kotlin.Unit
@kotlin.PublishedApi internal final inline override /*1*/ /*fake_override*/ fun callFromPublished(): kotlin.Unit
public final inline fun effictivelyNonPublic(): 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
protected final override /*1*/ /*fake_override*/ fun test(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
@@ -15,7 +15,7 @@ fun <T> javaClass(): Class<T> = null!!
public class AppServiceModule : AbstractModule<String>() {
inline fun <reified T> AbstractModule<Int>.bind() {
val x = bind(javaClass<T>())
val x = <!PROTECTED_CALL_FROM_PUBLIC_INLINE!>bind<!>(javaClass<T>())
x checkType { _<String>() } // check that Class receiver is used instead of extension one
}