Use descriptor modality in IDE inspections/intentions

This fixes KT-18160, KT-18194, KT-18195, KT-18197 ~
This commit is contained in:
Yan Zhulanow
2017-05-30 18:13:22 +03:00
parent 942fa7719a
commit e037cafe18
20 changed files with 123 additions and 67 deletions
@@ -36,7 +36,8 @@ interface DeclarationAttributeAltererExtension {
declaration: DeclarationDescriptor?,
containingDeclaration: DeclarationDescriptor?,
currentModality: Modality,
bindingContext: BindingContext
bindingContext: BindingContext,
isImplicitModality: Boolean
): Modality? = null
fun shouldConvertFirstSAMParameterToReceiver(function: FunctionDescriptor) : Boolean = false
@@ -178,27 +178,6 @@ fun KtBlockExpression.contentRange(): PsiChildRange {
// ----------- Inheritance -----------------------------------------------------------------------------------------------------------------
fun KtClass.isInheritable(): Boolean {
return isInterface() || hasModifier(KtTokens.OPEN_KEYWORD) ||
hasModifier(KtTokens.ABSTRACT_KEYWORD) || hasModifier(KtTokens.SEALED_KEYWORD)
}
fun KtDeclaration.isOverridable(): Boolean {
val parent = parent
if (!(parent is KtClassBody || parent is KtParameterList)) return false
val klass = if (parent.parent is KtPrimaryConstructor)
parent.parent.parent as? KtClass
else
parent.parent as? KtClass
if (klass == null || (!klass.isInheritable() && !klass.isEnum())) return false
if (hasModifier(KtTokens.FINAL_KEYWORD) || hasModifier(KtTokens.PRIVATE_KEYWORD)) return false
return klass.isInterface() ||
hasModifier(KtTokens.ABSTRACT_KEYWORD) || hasModifier(KtTokens.OPEN_KEYWORD) || hasModifier(KtTokens.OVERRIDE_KEYWORD)
}
fun KtClass.isAbstract(): Boolean = isInterface() || hasModifier(KtTokens.ABSTRACT_KEYWORD)
/**
@@ -131,7 +131,7 @@ public class ModifiersChecker {
DeclarationDescriptor descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, modifierListOwner);
for (DeclarationAttributeAltererExtension extension : extensions) {
Modality newModality = extension.refineDeclarationModality(
modifierListOwner, descriptor, containingDescriptor, modality, bindingContext);
modifierListOwner, descriptor, containingDescriptor, modality, bindingContext, false);
if (newModality != null) {
modality = newModality;
@@ -26,10 +26,10 @@ import com.intellij.util.MergeQuery
import com.intellij.util.Processor
import com.intellij.util.Query
import org.jetbrains.kotlin.asJava.toLightMethods
import org.jetbrains.kotlin.idea.core.isOverridable
import org.jetbrains.kotlin.idea.search.allScope
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.psiUtil.isOverridable
import java.util.*
fun PsiElement.isOverridableElement(): Boolean = when (this) {
@@ -19,11 +19,10 @@ package org.jetbrains.kotlin.idea.core
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.impl.source.codeStyle.CodeEditUtil
import com.intellij.psi.tree.IElementType
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.extensions.DeclarationAttributeAltererExtension
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
@@ -232,7 +231,84 @@ fun KtModifierListOwner.canBeProtected(): Boolean {
}
}
fun KtClass.isInheritable(): Boolean {
return when (getModalityFromDescriptor()) {
KtTokens.ABSTRACT_KEYWORD, KtTokens.OPEN_KEYWORD, KtTokens.SEALED_KEYWORD -> true
else -> false
}
}
fun KtDeclaration.isOverridable(): Boolean {
val parent = parent
if (!(parent is KtClassBody || parent is KtParameterList)) return false
val klass = if (parent.parent is KtPrimaryConstructor)
parent.parent.parent as? KtClass
else
parent.parent as? KtClass
if (klass == null || (!klass.isInheritable() && !klass.isEnum())) return false
if (this.hasModifier(KtTokens.PRIVATE_KEYWORD)) {
// 'private' is incompatible with 'open'
return false
}
return when (getModalityFromDescriptor()) {
KtTokens.ABSTRACT_KEYWORD, KtTokens.OPEN_KEYWORD -> true
else -> false
}
}
fun KtDeclaration.getModalityFromDescriptor(): KtModifierKeywordToken? {
val descriptor = this.resolveToDescriptor(BodyResolveMode.PARTIAL)
if (descriptor is MemberDescriptor) {
return mapModality(descriptor.modality)
}
return null
}
fun KtDeclaration.implicitModality(): KtModifierKeywordToken {
var predictedModality = predictImplicitModality()
val bindingContext = analyze(BodyResolveMode.PARTIAL)
val descriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, this] ?: return predictedModality
val containingDescriptor = descriptor.containingDeclaration ?: return predictedModality
val extensions = DeclarationAttributeAltererExtension.getInstances(this.project)
for (extension in extensions) {
val newModality = extension.refineDeclarationModality(
this,
null,
containingDescriptor,
mapModalityToken(predictedModality),
bindingContext,
isImplicitModality = true)
if (newModality != null) {
predictedModality = mapModality(newModality)
}
}
return predictedModality
}
fun mapModality(accurateModality: Modality): KtModifierKeywordToken = when (accurateModality) {
Modality.FINAL -> KtTokens.FINAL_KEYWORD
Modality.SEALED -> KtTokens.SEALED_KEYWORD
Modality.OPEN -> KtTokens.OPEN_KEYWORD
Modality.ABSTRACT -> KtTokens.ABSTRACT_KEYWORD
}
private fun mapModalityToken(modalityToken: IElementType): Modality = when (modalityToken) {
KtTokens.FINAL_KEYWORD -> Modality.FINAL
KtTokens.SEALED_KEYWORD -> Modality.SEALED
KtTokens.OPEN_KEYWORD -> Modality.OPEN
KtTokens.ABSTRACT_KEYWORD -> Modality.ABSTRACT
else -> error("Unexpected modality keyword $modalityToken")
}
private fun KtDeclaration.predictImplicitModality(): KtModifierKeywordToken {
if (this is KtClassOrObject) {
if (this is KtClass && this.isInterface()) return KtTokens.ABSTRACT_KEYWORD
return KtTokens.FINAL_KEYWORD
@@ -25,11 +25,11 @@ import com.intellij.openapi.ui.VerticalFlowLayout
import com.intellij.refactoring.classMembers.AbstractMemberInfoModel
import com.intellij.ui.NonFocusableCheckBox
import com.intellij.util.containers.HashMap
import org.jetbrains.kotlin.idea.core.isInheritable
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberSelectionPanel
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.psi.psiUtil.isInheritable
import org.jetbrains.kotlin.utils.keysToMap
import javax.swing.JLabel
import javax.swing.JPanel
@@ -29,6 +29,7 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.search.searches.OverridingMethodsSearch
import org.jetbrains.kotlin.asJava.toLightMethods
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.idea.core.isOverridable
import org.jetbrains.kotlin.idea.findUsages.handlers.DelegatingFindMemberUsagesHandler
import org.jetbrains.kotlin.idea.findUsages.handlers.KotlinFindClassUsagesHandler
import org.jetbrains.kotlin.idea.findUsages.handlers.KotlinFindMemberUsagesHandler
@@ -36,7 +37,6 @@ import org.jetbrains.kotlin.idea.findUsages.handlers.KotlinTypeParameterFindUsag
import org.jetbrains.kotlin.idea.refactoring.checkSuperMethods
import org.jetbrains.kotlin.plugin.findUsages.handlers.KotlinFindUsagesHandlerDecorator
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.isOverridable
import org.jetbrains.kotlin.psi.psiUtil.parameterIndex
import java.lang.IllegalArgumentException
@@ -32,12 +32,11 @@ import com.intellij.ui.StateRestoringCheckBox;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.asJava.LightClassUtilsKt;
import org.jetbrains.kotlin.idea.KotlinBundle;
import org.jetbrains.kotlin.idea.core.PsiModificationUtilsKt;
import org.jetbrains.kotlin.idea.findUsages.KotlinClassFindUsagesOptions;
import org.jetbrains.kotlin.idea.refactoring.RenderingUtilsKt;
import org.jetbrains.kotlin.psi.KtClass;
import org.jetbrains.kotlin.psi.KtClassOrObject;
import org.jetbrains.kotlin.psi.psiUtil.KtPsiUtilKt;
import javax.swing.*;
import static org.jetbrains.kotlin.asJava.LightClassUtilsKt.toLightClass;
@@ -91,7 +90,7 @@ public class KotlinFindClassUsagesDialog extends FindClassUsagesDialog {
//noinspection ConstantConditions
javaClass.getModifierList().setModifierProperty(
PsiModifier.FINAL,
!(classOrObject instanceof KtClass && KtPsiUtilKt.isInheritable((KtClass) classOrObject))
!(classOrObject instanceof KtClass && PsiModificationUtilsKt.isInheritable((KtClass) classOrObject))
);
javaClass.putUserData(ORIGINAL_CLASS, classOrObject);
@@ -41,13 +41,13 @@ import org.jetbrains.kotlin.descriptors.MemberDescriptor
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.idea.KotlinIcons
import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor
import org.jetbrains.kotlin.idea.core.isInheritable
import org.jetbrains.kotlin.idea.core.isOverridable
import org.jetbrains.kotlin.idea.core.toDescriptor
import org.jetbrains.kotlin.idea.highlighter.allImplementingCompatibleModules
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.isInheritable
import org.jetbrains.kotlin.psi.psiUtil.isOverridable
import java.awt.event.MouseEvent
import java.util.*
import javax.swing.Icon
@@ -27,12 +27,16 @@ import com.intellij.psi.search.PsiSearchHelper
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.util.Processor
import org.jetbrains.kotlin.idea.core.toDescriptor
import org.jetbrains.kotlin.idea.core.isInheritable
import org.jetbrains.kotlin.idea.core.isOverridable
import org.jetbrains.kotlin.idea.quickfix.AddModifierFix
import org.jetbrains.kotlin.idea.refactoring.isConstructorDeclaredProperty
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.jvm.annotations.hasJvmFieldAnnotation
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
class MemberVisibilityCanPrivateInspection : AbstractKotlinInspection() {
@@ -21,6 +21,7 @@ import com.intellij.codeInspection.*
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.idea.core.implicitVisibility
import org.jetbrains.kotlin.idea.core.isInheritable
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtDeclaration
@@ -28,7 +29,6 @@ import org.jetbrains.kotlin.psi.KtModifierListOwner
import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.psi.addRemoveModifier.addModifier
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.psi.psiUtil.isInheritable
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifier
class ProtectedInFinalInspection : AbstractKotlinInspection() {
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.extensions.DeclarationAttributeAltererExtension
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.core.implicitModality
import org.jetbrains.kotlin.idea.core.mapModality
import org.jetbrains.kotlin.idea.quickfix.RemoveModifierFix
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtDeclaration
@@ -41,10 +42,6 @@ class RedundantModalityModifierInspection : AbstractKotlinInspection(), CleanupL
if (modalityModifierType != implicitModality) return
// Descriptor may have the different modality (in case of all-open plugin enabled, for example)
val modalityFromPsi = getModality(implicitModality)
if (modalityFromPsi != null && hasRefinedModalityInDescriptor(declaration, modalityFromPsi)) return
holder.registerProblem(modalityModifier,
"Redundant modality modifier",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
@@ -53,24 +50,4 @@ class RedundantModalityModifierInspection : AbstractKotlinInspection(), CleanupL
}
}
}
private fun hasRefinedModalityInDescriptor(
declaration: KtDeclaration,
implicitModality: Modality
): Boolean {
val bindingContext = declaration.analyze(BodyResolveMode.PARTIAL)
val descriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, declaration] ?: return false
return DeclarationAttributeAltererExtension.getInstances(declaration.project).any {
it.refineDeclarationModality(declaration, descriptor, descriptor.containingDeclaration,
implicitModality, bindingContext) != null
}
}
private fun getModality(modalityToken: IElementType): Modality? = when (modalityToken) {
KtTokens.FINAL_KEYWORD -> Modality.FINAL
KtTokens.SEALED_KEYWORD -> Modality.SEALED
KtTokens.OPEN_KEYWORD -> Modality.OPEN
KtTokens.ABSTRACT_KEYWORD -> Modality.ABSTRACT
else -> null
}
}
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.core.isOverridable
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinChangeSignatureConfiguration
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinMethodDescriptor
import org.jetbrains.kotlin.idea.refactoring.changeSignature.modify
@@ -33,7 +34,6 @@ import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
import org.jetbrains.kotlin.idea.util.getThisReceiverOwner
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.isOverridable
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
@@ -52,6 +52,7 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotated
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.core.isInheritable
import org.jetbrains.kotlin.idea.core.toDescriptor
import org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory
import org.jetbrains.kotlin.idea.findUsages.handlers.KotlinFindClassUsagesHandler
@@ -29,7 +29,6 @@ import com.intellij.util.PlatformIcons
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
@@ -25,6 +25,7 @@ import com.intellij.refactoring.HelpID
import com.intellij.refactoring.RefactoringBundle
import com.intellij.refactoring.util.CommonRefactoringUtil
import com.intellij.refactoring.util.RefactoringUIUtil
import org.jetbrains.kotlin.idea.core.isInheritable
import org.jetbrains.kotlin.idea.refactoring.AbstractPullPushMembersHandler
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfoStorage
@@ -33,7 +34,6 @@ import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.psiUtil.isInheritable
val PUSH_MEMBERS_DOWN = "Push Members Down"
@@ -42,6 +42,7 @@ import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.core.isOverridable
import org.jetbrains.kotlin.idea.findUsages.KotlinFunctionFindUsagesOptions
import org.jetbrains.kotlin.idea.findUsages.KotlinPropertyFindUsagesOptions
import org.jetbrains.kotlin.idea.findUsages.processAllExactUsages
@@ -1,4 +1,22 @@
<problems>
<problem>
<file>test.kt</file>
<line>8</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/test.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Redundant modality modifier</problem_class>
<description>Redundant modality modifier</description>
</problem>
<problem>
<file>test.kt</file>
<line>9</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/test.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Redundant modality modifier</problem_class>
<description>Redundant modality modifier</description>
</problem>
<problem>
<file>test.kt</file>
<line>22</line>
@@ -42,7 +42,8 @@ abstract class AbstractAllOpenDeclarationAttributeAltererExtension : Declaration
declaration: DeclarationDescriptor?,
containingDeclaration: DeclarationDescriptor?,
currentModality: Modality,
bindingContext: BindingContext
bindingContext: BindingContext,
isImplicitModality: Boolean
): Modality? {
if (currentModality != Modality.FINAL) {
return null
@@ -54,7 +55,7 @@ abstract class AbstractAllOpenDeclarationAttributeAltererExtension : Declaration
val descriptor = declaration as? ClassDescriptor ?: containingDeclaration ?: return null
if (descriptor.hasSpecialAnnotation(modifierListOwner)) {
return if (modifierListOwner.hasModifier(KtTokens.FINAL_KEYWORD))
return if (!isImplicitModality && modifierListOwner.hasModifier(KtTokens.FINAL_KEYWORD))
Modality.FINAL // Explicit final
else
Modality.OPEN
@@ -33,13 +33,13 @@ import org.jetbrains.kotlin.asJava.toLightMethods
import org.jetbrains.kotlin.descriptors.MemberDescriptor
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.core.isInheritable
import org.jetbrains.kotlin.idea.core.isOverridable
import org.jetbrains.kotlin.idea.inspections.AbstractKotlinInspection
import org.jetbrains.kotlin.idea.spring.isAnnotatedWith
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.isInheritable
import org.jetbrains.kotlin.psi.psiUtil.isOverridable
class KotlinFinalClassOrFunSpringInspection : AbstractKotlinInspection() {
class QuickFix<T: KtModifierListOwner>(private val element: T) : LocalQuickFix {