Add quickfix for setting explicit public visibility for diagnostic reported in API mode

This commit is contained in:
Leonid Startsev
2019-09-16 14:51:12 +03:00
parent 2b708093c0
commit 7fada51c42
6 changed files with 48 additions and 39 deletions
@@ -207,9 +207,9 @@ 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);
DiagnosticFactory1<PsiElement, DeclarationDescriptor> NO_EXPLICIT_VISIBILITY_IN_API_MODE = DiagnosticFactory1.create(ERROR);
DiagnosticFactory0<PsiElement> NO_EXPLICIT_RETURN_TYPE_IN_API_MODE = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> NO_EXPLICIT_VISIBILITY_IN_API_MODE_MIGRATION = DiagnosticFactory0.create(WARNING);
DiagnosticFactory1<PsiElement, DeclarationDescriptor> NO_EXPLICIT_VISIBILITY_IN_API_MODE_MIGRATION = DiagnosticFactory1.create(WARNING);
DiagnosticFactory0<PsiElement> NO_EXPLICIT_RETURN_TYPE_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);
@@ -119,9 +119,9 @@ 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, "Declaration ''{0}'' is effectively public API and API mode is on, but no visibility is specified", NAME);
MAP.put(NO_EXPLICIT_RETURN_TYPE_IN_API_MODE, "This declaration is effectively public API and API mode is on, but no explicit return type 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(NO_EXPLICIT_VISIBILITY_IN_API_MODE_MIGRATION, "Declaration ''{0}'' is effectively public API and API mode is on, but no visibility is specified", NAME);
MAP.put(NO_EXPLICIT_RETURN_TYPE_IN_API_MODE_MIGRATION, "This declaration is effectively public API and API mode is on, but no explicit return type 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);
@@ -5,12 +5,10 @@
package org.jetbrains.kotlin.resolve.checkers
import com.intellij.psi.PsiElement
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.DiagnosticFactory0
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.diagnostics.reportDiagnosticOnce
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
@@ -35,19 +33,19 @@ class ApiModeDeclarationChecker : DeclarationChecker {
private fun checkVisibilityModifier(
state: ApiMode,
declaration: KtDeclaration,
descriptor: DeclarationDescriptor,
descriptor: DeclarationDescriptorWithVisibility,
context: DeclarationCheckerContext
) {
val modifier = declaration.visibilityModifier()?.node?.elementType as? KtModifierKeywordToken
if (modifier != null) return
if (excludeForDiagnostic(descriptor)) return
context.selectDiagnosticAndReport(
declaration,
state,
Errors.NO_EXPLICIT_VISIBILITY_IN_API_MODE,
Errors.NO_EXPLICIT_VISIBILITY_IN_API_MODE_MIGRATION
)
val diagnostic =
if (state == ApiMode.ENABLED)
Errors.NO_EXPLICIT_VISIBILITY_IN_API_MODE.on(declaration, descriptor)
else
Errors.NO_EXPLICIT_VISIBILITY_IN_API_MODE_MIGRATION.on(declaration, descriptor)
context.trace.reportDiagnosticOnce(diagnostic)
}
private fun checkExplicitReturnType(
@@ -65,26 +63,15 @@ class ApiModeDeclarationChecker : DeclarationChecker {
checkForInternal = false,
checkForPrivate = false
)
if (shouldReport) context.selectDiagnosticAndReport(
declaration,
state,
Errors.NO_EXPLICIT_RETURN_TYPE_IN_API_MODE,
Errors.NO_EXPLICIT_RETURN_TYPE_IN_API_MODE_MIGRATION
)
}
private fun DeclarationCheckerContext.selectDiagnosticAndReport(
on: KtDeclaration,
state: ApiMode,
ifError: DiagnosticFactory0<PsiElement>,
ifWarning: DiagnosticFactory0<PsiElement>
) {
val diagnostic =
if (state == ApiMode.ENABLED)
ifError.on(on)
else
ifWarning.on(on)
trace.reportDiagnosticOnce(diagnostic)
if (shouldReport) {
val diagnostic =
if (state == ApiMode.ENABLED)
Errors.NO_EXPLICIT_RETURN_TYPE_IN_API_MODE.on(declaration)
else
Errors.NO_EXPLICIT_RETURN_TYPE_IN_API_MODE_MIGRATION
.on(declaration)
context.trace.reportDiagnosticOnce(diagnostic)
}
}
/**
@@ -288,9 +288,8 @@ fun KtDeclaration.toDescriptor(): DeclarationDescriptor? {
return descriptor
}
//TODO: code style option whether to insert redundant 'public' keyword or not
fun KtModifierListOwner.setVisibility(visibilityModifier: KtModifierKeywordToken) {
if (this is KtDeclaration) {
fun KtModifierListOwner.setVisibility(visibilityModifier: KtModifierKeywordToken, addImplicitVisibilityModifier: Boolean = false) {
if (this is KtDeclaration && !addImplicitVisibilityModifier) {
val defaultVisibilityKeyword = implicitVisibility()
if (visibilityModifier == defaultVisibilityKeyword) {
@@ -20,9 +20,12 @@ import com.intellij.codeInsight.intention.HighPriorityAction
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1
import org.jetbrains.kotlin.idea.core.canBeInternal
import org.jetbrains.kotlin.idea.core.canBePrivate
import org.jetbrains.kotlin.idea.core.canBeProtected
@@ -42,7 +45,8 @@ import org.jetbrains.kotlin.resolve.ExposedVisibilityChecker
open class ChangeVisibilityFix(
element: KtModifierListOwner,
private val elementName: String,
private val visibilityModifier: KtModifierKeywordToken
private val visibilityModifier: KtModifierKeywordToken,
private val addImplicitVisibilityModifier: Boolean = false
) : KotlinQuickFixAction<KtModifierListOwner>(element) {
override fun getText() = "Make '$elementName' $visibilityModifier"
@@ -52,9 +56,9 @@ open class ChangeVisibilityFix(
val pointer = element?.createSmartPointer()
val originalElement = pointer?.element
if (originalElement is KtDeclaration) {
originalElement.runOnExpectAndAllActuals(useOnSelf = true) { it.setVisibility(visibilityModifier) }
originalElement.runOnExpectAndAllActuals(useOnSelf = true) { it.setVisibility(visibilityModifier, addImplicitVisibilityModifier) }
} else {
originalElement?.setVisibility(visibilityModifier)
originalElement?.setVisibility(visibilityModifier, addImplicitVisibilityModifier)
}
val propertyAccessor = pointer?.element as? KtPropertyAccessor
@@ -112,4 +116,21 @@ open class ChangeVisibilityFix(
}
}
}
object SetExplicitVisibilityFactory : KotlinIntentionActionsFactory() {
@Suppress("UNCHECKED_CAST")
override fun doCreateActions(diagnostic: Diagnostic): List<IntentionAction> {
val factory = diagnostic.factory as DiagnosticFactory1<*, DeclarationDescriptor>
val descriptor = factory.cast(diagnostic).a as? DeclarationDescriptorWithVisibility ?: return emptyList()
val element = diagnostic.psiElement as? KtModifierListOwner ?: return emptyList()
return listOf(
ChangeVisibilityFix(
element,
descriptor.name.asString(),
KtTokens.PUBLIC_KEYWORD,
addImplicitVisibilityModifier = true
)
)
}
}
}
@@ -151,6 +151,8 @@ class QuickFixRegistrar : QuickFixContributor {
FORBIDDEN_BINARY_MOD.registerFactory(removeModifierFactory)
FORBIDDEN_BINARY_MOD.registerFactory(RenameModToRemFix.Factory)
NO_EXPLICIT_VISIBILITY_IN_API_MODE.registerFactory(ChangeVisibilityFix.SetExplicitVisibilityFactory)
NO_EXPLICIT_VISIBILITY_IN_API_MODE_MIGRATION.registerFactory(ChangeVisibilityFix.SetExplicitVisibilityFactory)
NO_EXPLICIT_RETURN_TYPE_IN_API_MODE.registerActions(SpecifyTypeExplicitlyFix())
NO_EXPLICIT_RETURN_TYPE_IN_API_MODE_MIGRATION.registerActions(SpecifyTypeExplicitlyFix())