From 7ea29d33972b8a4d48d6963a28c6ba5cc234b981 Mon Sep 17 00:00:00 2001 From: Mikhael Bogdanov Date: Thu, 1 Dec 2016 16:29:17 +0100 Subject: [PATCH] Wrong @PublishedApi usage diagnostic --- .../jetbrains/kotlin/diagnostics/Errors.java | 1 + .../rendering/DefaultErrorMessages.java | 1 + .../kotlin/resolve/ModifiersChecker.java | 2 + .../checkers/PublishedApiUsageChecker.kt | 51 +++++++++++++ .../diagnostics/tests/publishedApi.kt | 71 +++++++++++++++++++ .../checkers/DiagnosticsTestGenerated.java | 6 ++ 6 files changed, 132 insertions(+) create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/PublishedApiUsageChecker.kt create mode 100644 compiler/testData/diagnostics/tests/publishedApi.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 84be6b2747d..992e67b35ec 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -865,6 +865,7 @@ public interface Errors { DiagnosticFactory1 INLINE_CALL_CYCLE = DiagnosticFactory1.create(ERROR, DEFAULT); DiagnosticFactory0 NON_LOCAL_RETURN_IN_DISABLED_INLINE = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 INLINE_PROPERTY_WITH_BACKING_FIELD = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE); + DiagnosticFactory0 NON_INTERNAL_PUBLISHED_API = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 NON_LOCAL_SUSPENSION_POINT = DiagnosticFactory0.create(ERROR); 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 08b67ac6aea..1b1287788b2 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -831,6 +831,7 @@ public class DefaultErrorMessages { MAP.put(NULLABLE_INLINE_PARAMETER, "Inline-parameter ''{0}'' of ''{1}'' must not be nullable. Add ''noinline'' modifier to the parameter declaration or make its type not nullable", ELEMENT_TEXT, SHORT_NAMES_IN_TYPES); MAP.put(RECURSION_IN_INLINE, "Inline function ''{1}'' cannot be recursive", ELEMENT_TEXT, SHORT_NAMES_IN_TYPES); MAP.put(INLINE_PROPERTY_WITH_BACKING_FIELD, "Inline property cannot have backing field"); + MAP.put(NON_INTERNAL_PUBLISHED_API, "@PublishedApi annotation is only applicable for internal declaration"); //Inline non locals MAP.put(NON_LOCAL_RETURN_NOT_ALLOWED, "Can''t inline ''{0}'' here: it may contain non-local returns. Add ''crossinline'' modifier to parameter declaration ''{0}''", ELEMENT_TEXT); MAP.put(INLINE_CALL_CYCLE, "The ''{0}'' invocation is a part of inline cycle", NAME); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java index ee7f3d34909..05f21ea6d8f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java @@ -29,6 +29,7 @@ import org.jetbrains.kotlin.lexer.KtModifierKeywordToken; import org.jetbrains.kotlin.lexer.KtTokens; import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker; +import org.jetbrains.kotlin.resolve.checkers.PublishedApiUsageChecker; import org.jetbrains.kotlin.resolve.checkers.UnderscoreChecker; import java.util.*; @@ -244,6 +245,7 @@ public class ModifiersChecker { checker.check(declaration, descriptor, trace, trace.getBindingContext(), languageVersionSettings); } OperatorModifierChecker.INSTANCE.check(declaration, descriptor, trace, languageVersionSettings); + PublishedApiUsageChecker.INSTANCE.check(declaration, descriptor, trace); } public void checkTypeParametersModifiers(@NotNull KtModifierListOwner modifierListOwner) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/PublishedApiUsageChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/PublishedApiUsageChecker.kt new file mode 100644 index 00000000000..7e05efb44f3 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/PublishedApiUsageChecker.kt @@ -0,0 +1,51 @@ +/* + * 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.checkers + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility +import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor +import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.BindingTrace +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe +import org.jetbrains.kotlin.types.TypeUtils + +object PublishedApiUsageChecker { + + fun check( + declaration: KtDeclaration, + descriptor: DeclarationDescriptor, + trace: BindingTrace + ) { + if (descriptor !is DeclarationDescriptorWithVisibility || descriptor.visibility == Visibilities.INTERNAL) return + /*Don't report diagnostic twise*/ + if (descriptor is PropertyAccessorDescriptor) return + + for (entry in declaration.annotationEntries) { + val annotationDescriptor = trace.get(BindingContext.ANNOTATION, entry) ?: continue + val classDescriptor = TypeUtils.getClassDescriptor(annotationDescriptor.type) ?: continue + if (classDescriptor.fqNameSafe == KotlinBuiltIns.FQ_NAMES.publishedApi) { + trace.report(Errors.NON_INTERNAL_PUBLISHED_API.on(entry)) + } + } + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/publishedApi.kt b/compiler/testData/diagnostics/tests/publishedApi.kt new file mode 100644 index 00000000000..b51dff2487e --- /dev/null +++ b/compiler/testData/diagnostics/tests/publishedApi.kt @@ -0,0 +1,71 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER +@kotlin.PublishedApi +class A + +@kotlin.PublishedApi +internal class B + +@kotlin.PublishedApi +private class C + + +@kotlin.PublishedApi +fun a() {} + +@kotlin.PublishedApi +internal fun b() {} + +@kotlin.PublishedApi +internal fun c() {} + + +@kotlin.PublishedApi +var ap = 1 + +@kotlin.PublishedApi +internal var bp = 1 + +@kotlin.PublishedApi +internal var c = 1 + + + +class E { + @kotlin.PublishedApi + fun a() {} + + @kotlin.PublishedApi + internal fun b() {} + + @kotlin.PublishedApi + private fun c() {} + + @kotlin.PublishedApi + protected fun d() {} + + + @kotlin.PublishedApi + val ap = 1 + + @kotlin.PublishedApi + internal val bp = 1 + + @kotlin.PublishedApi + protected val c = 1 + + @kotlin.PublishedApi + private val d = 1 +} + + +class D @kotlin.PublishedApi constructor() { + + @kotlin.PublishedApi + constructor(a: String) : this() + + @kotlin.PublishedApi + private constructor(a: String, b: String): this() + + @kotlin.PublishedApi + internal constructor(a: String, b: String, c: String): this() +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 6c47d02f717..52ea0e9b4e2 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -566,6 +566,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("publishedApi.kt") + public void testPublishedApi() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/publishedApi.kt"); + doTest(fileName); + } + @TestMetadata("QualifiedExpressions.kt") public void testQualifiedExpressions() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/QualifiedExpressions.kt");