Explicit Api mode: prototype with check for missing explicit visibility
Add cli flag with 3-state switch
This commit is contained in:
+12
@@ -316,6 +316,14 @@ abstract class CommonCompilerArguments : CommonToolArguments() {
|
||||
@Argument(value = "-Xdisable-default-scripting-plugin", description = "Do not enable scripting plugin by default")
|
||||
var disableDefaultScriptingPlugin: Boolean by FreezableVar(false)
|
||||
|
||||
@Argument(
|
||||
value = "-Xapi-mode",
|
||||
valueDescription = "{enable|disable|migration}",
|
||||
description = "Enable api mode, force compiler to report errors an all public API declarations without explicit visibility.\n" +
|
||||
"Use 'migration' level to issue warnings instead of errors."
|
||||
)
|
||||
var apiMode: String by FreezableVar(ApiMode.DISABLED.state)
|
||||
|
||||
open fun configureAnalysisFlags(collector: MessageCollector): MutableMap<AnalysisFlag<*>, Any> {
|
||||
return HashMap<AnalysisFlag<*>, Any>().apply {
|
||||
put(AnalysisFlags.skipMetadataVersionCheck, skipMetadataVersionCheck)
|
||||
@@ -324,6 +332,10 @@ abstract class CommonCompilerArguments : CommonToolArguments() {
|
||||
put(AnalysisFlags.useExperimental, useExperimental?.toList().orEmpty())
|
||||
put(AnalysisFlags.explicitApiVersion, apiVersion != null)
|
||||
put(AnalysisFlags.allowResultReturnType, allowResultReturnType)
|
||||
ApiMode.fromString(apiMode)?.also { put(AnalysisFlags.apiMode, it) } ?: collector.report(
|
||||
CompilerMessageSeverity.ERROR,
|
||||
"Unknown value for parameter -Xapi-mode: '$apiMode'. Value should be one of ${ApiMode.availableValues()}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,9 @@ object AnalysisFlags {
|
||||
@JvmStatic
|
||||
val allowResultReturnType by AnalysisFlag.Delegates.Boolean
|
||||
|
||||
@JvmStatic
|
||||
val apiMode by AnalysisFlag.Delegates.ApiModeDisabledByDefault
|
||||
|
||||
@JvmStatic
|
||||
val constraintSystemForOverloadResolution by AnalysisFlag.Delegates.ConstraintSystemForOverloadResolution
|
||||
|
||||
|
||||
@@ -207,6 +207,8 @@ public interface Errors {
|
||||
DiagnosticFactory2<PsiElement, KtModifierKeywordToken, String> DEPRECATED_MODIFIER_FOR_TARGET = DiagnosticFactory2.create(WARNING);
|
||||
DiagnosticFactory2<PsiElement, KtModifierKeywordToken, KtModifierKeywordToken> DEPRECATED_MODIFIER = DiagnosticFactory2.create(WARNING);
|
||||
DiagnosticFactory2<PsiElement, KtModifierKeywordToken, String> REDUNDANT_MODIFIER_FOR_TARGET = DiagnosticFactory2.create(WARNING);
|
||||
DiagnosticFactory0<PsiElement> NO_EXPLICIT_VISIBILITY_IN_API_MODE = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> NO_EXPLICIT_VISIBILITY_IN_API_MODE_MIGRATION = DiagnosticFactory0.create(WARNING);
|
||||
DiagnosticFactory2<PsiElement, KtModifierKeywordToken, String> WRONG_MODIFIER_CONTAINING_DECLARATION = DiagnosticFactory2.create(ERROR);
|
||||
DiagnosticFactory2<PsiElement, KtModifierKeywordToken, String> DEPRECATED_MODIFIER_CONTAINING_DECLARATION = DiagnosticFactory2.create(WARNING);
|
||||
DiagnosticFactory1<PsiElement, KtModifierKeywordToken> ILLEGAL_INLINE_PARAMETER_MODIFIER = DiagnosticFactory1.create(ERROR);
|
||||
|
||||
+2
@@ -119,6 +119,8 @@ public class DefaultErrorMessages {
|
||||
MAP.put(DEPRECATED_MODIFIER_FOR_TARGET, "Modifier ''{0}'' is deprecated for ''{1}''", TO_STRING, TO_STRING);
|
||||
MAP.put(DEPRECATED_MODIFIER, "Modifier ''{0}'' is deprecated, use ''{1}'' instead", TO_STRING, TO_STRING);
|
||||
MAP.put(REDUNDANT_MODIFIER_FOR_TARGET, "Modifier ''{0}'' is redundant for ''{1}''", TO_STRING, TO_STRING);
|
||||
MAP.put(NO_EXPLICIT_VISIBILITY_IN_API_MODE, "This declaration is effectively public API and API mode is on, but no visibility is specified");
|
||||
MAP.put(NO_EXPLICIT_VISIBILITY_IN_API_MODE_MIGRATION, "This declaration is effectively public API and API mode is on, but no visibility is specified");
|
||||
MAP.put(WRONG_MODIFIER_CONTAINING_DECLARATION, "Modifier ''{0}'' is not applicable inside ''{1}''", TO_STRING, TO_STRING);
|
||||
MAP.put(DEPRECATED_MODIFIER_CONTAINING_DECLARATION, "Modifier ''{0}'' is deprecated inside ''{1}''", TO_STRING, TO_STRING);
|
||||
MAP.put(ILLEGAL_INLINE_PARAMETER_MODIFIER, "Modifier ''{0}'' is allowed only for function parameters of an inline function", TO_STRING);
|
||||
|
||||
@@ -32,6 +32,7 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf(
|
||||
ReservedMembersAndConstructsForInlineClass(),
|
||||
ResultClassInReturnTypeChecker(),
|
||||
LocalVariableTypeParametersChecker(),
|
||||
ApiModeDeclarationChecker(),
|
||||
TailrecFunctionChecker,
|
||||
TrailingCommaDeclarationChecker
|
||||
)
|
||||
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.checkers
|
||||
|
||||
import org.jetbrains.kotlin.config.AnalysisFlags
|
||||
import org.jetbrains.kotlin.config.ApiMode
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.diagnostics.reportDiagnosticOnce
|
||||
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifier
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyPublicApi
|
||||
|
||||
class ApiModeDeclarationChecker : DeclarationChecker {
|
||||
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
|
||||
val state = isEnabled(context.languageVersionSettings)
|
||||
if (state == ApiMode.DISABLED) return
|
||||
|
||||
val isApi = (descriptor as? DeclarationDescriptorWithVisibility)?.isEffectivelyPublicApi ?: return
|
||||
if (!isApi) return
|
||||
val modifier = declaration.visibilityModifier()?.node?.elementType as? KtModifierKeywordToken
|
||||
if (modifier != null) return
|
||||
|
||||
if (excludeForDiagnostic(descriptor)) return
|
||||
val diagnostic =
|
||||
if (state == ApiMode.ENABLED)
|
||||
Errors.NO_EXPLICIT_VISIBILITY_IN_API_MODE.on(declaration)
|
||||
else
|
||||
Errors.NO_EXPLICIT_VISIBILITY_IN_API_MODE_MIGRATION.on(declaration)
|
||||
context.trace.reportDiagnosticOnce(diagnostic)
|
||||
}
|
||||
|
||||
/**
|
||||
* Exclusion list:
|
||||
* 1. Primary constructors of public API classes
|
||||
* 3. Members of public API interfaces
|
||||
* 4. do not report overrides of public API? effectively, this means 'no report on overrides at all'
|
||||
*
|
||||
* Do we need something like @PublicApiFile to disable (or invert) this inspection per-file?
|
||||
*/
|
||||
private fun excludeForDiagnostic(descriptor: DeclarationDescriptor): Boolean {
|
||||
/* 1. */ if ((descriptor as? ClassConstructorDescriptor)?.isPrimary == true) return true
|
||||
val isMemberOfPublicInterface =
|
||||
(descriptor.containingDeclaration as? ClassDescriptor)?.let { DescriptorUtils.isInterface(it) && it.effectiveVisibility().publicApi }
|
||||
/* 3. */ if (descriptor is CallableDescriptor && isMemberOfPublicInterface == true) return true
|
||||
/* 4. */ if ((descriptor as? CallableDescriptor)?.overriddenDescriptors?.isNotEmpty() == true) return true
|
||||
return false
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun isEnabled(settings: LanguageVersionSettings): ApiMode {
|
||||
return settings.getFlag(AnalysisFlags.apiMode)
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -11,6 +11,9 @@ where advanced options include:
|
||||
-Xtyped-arrays Translate primitive arrays to JS typed arrays
|
||||
-Xallow-kotlin-package Allow compiling code in package 'kotlin' and allow not requiring kotlin.stdlib in module-info
|
||||
-Xallow-result-return-type Allow compiling code when `kotlin.Result` is used as a return type
|
||||
-Xapi-mode={enable|disable|migration}
|
||||
Enable api mode, force compiler to report errors an all public API declarations without explicit visibility.
|
||||
Use 'migration' level to issue warnings instead of errors.
|
||||
-Xcheck-phase-conditions Check pre- and postconditions on phases
|
||||
-Xcheck-sticky-phase-conditions
|
||||
Run sticky condition checks on subsequent phases as well. Implies -Xcheck-phase-conditions
|
||||
|
||||
+3
@@ -70,6 +70,9 @@ where advanced options include:
|
||||
-Xuse-type-table Use type table in metadata serialization
|
||||
-Xallow-kotlin-package Allow compiling code in package 'kotlin' and allow not requiring kotlin.stdlib in module-info
|
||||
-Xallow-result-return-type Allow compiling code when `kotlin.Result` is used as a return type
|
||||
-Xapi-mode={enable|disable|migration}
|
||||
Enable api mode, force compiler to report errors an all public API declarations without explicit visibility.
|
||||
Use 'migration' level to issue warnings instead of errors.
|
||||
-Xcheck-phase-conditions Check pre- and postconditions on phases
|
||||
-Xcheck-sticky-phase-conditions
|
||||
Run sticky condition checks on subsequent phases as well. Implies -Xcheck-phase-conditions
|
||||
|
||||
@@ -38,6 +38,10 @@ class AnalysisFlag<out T> internal constructor(
|
||||
operator fun provideDelegate(instance: Any?, property: KProperty<*>) = Delegate(property.name, JvmDefaultMode.DISABLE)
|
||||
}
|
||||
|
||||
object ApiModeDisabledByDefault {
|
||||
operator fun provideDelegate(instance: Any?, property: KProperty<*>) = Delegate(property.name, ApiMode.DISABLED)
|
||||
}
|
||||
|
||||
object ListOfStrings {
|
||||
operator fun provideDelegate(instance: Any?, property: KProperty<*>) = Delegate(property.name, emptyList<String>())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.config
|
||||
|
||||
enum class ApiMode(val state: String) {
|
||||
DISABLED("disable"),
|
||||
ENABLED("enable"),
|
||||
MIGRATION("migration");
|
||||
|
||||
companion object {
|
||||
fun fromString(string: String): ApiMode? = values().find { it.state == string }
|
||||
|
||||
fun availableValues() = values().joinToString(prefix = "{", postfix = "}") { it.state }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user