Redundant modality modifier inspection #KT-11450 Fixed

This commit is contained in:
Mikhail Glukhikh
2016-04-06 17:24:05 +03:00
parent 8a0641a639
commit d79e3f322f
15 changed files with 267 additions and 25 deletions
@@ -758,8 +758,7 @@ public class DescriptorResolver {
return propertyDescriptor;
}
/*package*/
static boolean hasBody(KtProperty property) {
public static boolean hasBody(KtProperty property) {
boolean hasBody = property.hasDelegateExpressionOrInitializer();
if (!hasBody) {
KtPropertyAccessor getter = property.getGetter();
@@ -26,9 +26,11 @@ import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.getLambdaArgumentName
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierType
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorResolver
import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.resolve.calls.callUtil.getValueArgumentsInParentheses
@@ -188,6 +190,27 @@ fun KtDeclaration.implicitVisibility(): KtModifierKeywordToken? {
return defaultVisibilityKeyword
}
fun KtDeclaration.implicitModality(): KtModifierKeywordToken {
if (this is KtClassOrObject) return KtTokens.FINAL_KEYWORD
val klass = containingClassOrObject ?: return KtTokens.FINAL_KEYWORD
if (hasModifier(KtTokens.OVERRIDE_KEYWORD)) {
if (klass.hasModifier(KtTokens.ABSTRACT_KEYWORD) ||
klass.hasModifier(KtTokens.OPEN_KEYWORD) ||
klass.hasModifier(KtTokens.SEALED_KEYWORD)) {
return KtTokens.OPEN_KEYWORD
}
}
if (klass is KtClass && klass.isInterface() && !hasModifier(KtTokens.PRIVATE_KEYWORD)) {
val hasBody = when (this) {
is KtProperty -> DescriptorResolver.hasBody(this)
is KtFunction -> hasBody()
else -> false
}
return if (hasBody) KtTokens.OPEN_KEYWORD else KtTokens.ABSTRACT_KEYWORD
}
return KtTokens.FINAL_KEYWORD
}
fun KtSecondaryConstructor.getOrCreateBody(): KtBlockExpression {
bodyExpression?.let { return it }
@@ -0,0 +1,6 @@
<html>
<body>
This inspection reports modality modifiers which match the default modality of an element
(<code>final</code> for most elements, <code>open</code> for members with override).
</body>
</html>
+8
View File
@@ -1443,6 +1443,14 @@
level="WARNING"
/>
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.RedundantModalityModifierInspection"
displayName="Redundant modality modifier"
groupName="Kotlin"
enabledByDefault="true"
cleanupTool="true"
level="WARNING"
/>
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
@@ -0,0 +1,40 @@
/*
* 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.idea.inspections
import com.intellij.codeInspection.*
import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.idea.core.implicitModality
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.psi.psiUtil.modalityModifier
class RedundantModalityModifierInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitDeclaration(declaration: KtDeclaration) {
val modalityModifier = declaration.modalityModifier() ?: return
if (modalityModifier.node.elementType == declaration.implicitModality()) {
holder.registerProblem(modalityModifier,
"Redundant modality modifier",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
RemoveModifierFix("Remove redundant modality modifier"))
}
}
}
}
}
@@ -17,43 +17,24 @@
package org.jetbrains.kotlin.idea.inspections
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.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtModifierList
import org.jetbrains.kotlin.psi.KtModifierListOwner
import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.psi.addRemoveModifier.removeModifier
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifier
class RedundantVisibilityModifierInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitDeclaration(dcl: KtDeclaration) {
val visibilityModifier = dcl.visibilityModifier() ?: return
if (visibilityModifier.node.elementType == dcl.implicitVisibility()) {
override fun visitDeclaration(declaration: KtDeclaration) {
val visibilityModifier = declaration.visibilityModifier() ?: return
if (visibilityModifier.node.elementType == declaration.implicitVisibility()) {
holder.registerProblem(visibilityModifier,
"Redundant visibility modifier",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
RemoveVisibilityModifierFix())
RemoveModifierFix("Remove redundant visibility modifier"))
}
}
}
}
class RemoveVisibilityModifierFix : LocalQuickFix {
override fun getName(): String = "Remove redundant visibility modifier"
override fun getFamilyName(): String = name
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val modifierKeyword = descriptor.psiElement.node.elementType as KtModifierKeywordToken
val modifierListOwner = descriptor.psiElement.getParentOfType<KtModifierListOwner>(true)
?: throw IllegalStateException("Can't find modifier list owner for modifier")
removeModifier(modifierListOwner, modifierKeyword)
}
}
}
@@ -0,0 +1,38 @@
/*
* 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.idea.inspections
import com.intellij.codeInspection.LocalQuickFix
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.psi.KtModifierListOwner
import org.jetbrains.kotlin.psi.addRemoveModifier.removeModifier
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
class RemoveModifierFix(val text: String) : LocalQuickFix {
override fun getName(): String = text
override fun getFamilyName(): String = text
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val modifierKeyword = descriptor.psiElement.node.elementType as KtModifierKeywordToken
val modifierListOwner = descriptor.psiElement.getParentOfType<KtModifierListOwner>(true)
?: throw IllegalStateException("Can't find modifier list owner for modifier")
removeModifier(modifierListOwner, modifierKeyword)
}
}
@@ -0,0 +1,74 @@
<problems>
<problem>
<file>redundantModalityModifier.kt</file>
<line>4</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/redundantModalityModifier.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Redundant modality modifier</problem_class>
<description>Redundant modality modifier</description>
</problem>
<problem>
<file>redundantModalityModifier.kt</file>
<line>13</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/redundantModalityModifier.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Redundant modality modifier</problem_class>
<description>Redundant modality modifier</description>
</problem>
<problem>
<file>redundantModalityModifier.kt</file>
<line>22</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/redundantModalityModifier.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Redundant modality modifier</problem_class>
<description>Redundant modality modifier</description>
</problem>
<problem>
<file>redundantModalityModifier.kt</file>
<line>25</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/redundantModalityModifier.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Redundant modality modifier</problem_class>
<description>Redundant modality modifier</description>
</problem>
<problem>
<file>redundantModalityModifier.kt</file>
<line>29</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/redundantModalityModifier.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Redundant modality modifier</problem_class>
<description>Redundant modality modifier</description>
</problem>
<problem>
<file>redundantModalityModifier.kt</file>
<line>31</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/redundantModalityModifier.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Redundant modality modifier</problem_class>
<description>Redundant modality modifier</description>
</problem>
<problem>
<file>redundantModalityModifier.kt</file>
<line>33</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/redundantModalityModifier.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Redundant modality modifier</problem_class>
<description>Redundant modality modifier</description>
</problem>
<problem>
<file>redundantModalityModifier.kt</file>
<line>39</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/redundantModalityModifier.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Redundant modality modifier</problem_class>
<description>Redundant modality modifier</description>
</problem>
<problem>
<file>redundantModalityModifier.kt</file>
<line>41</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/redundantModalityModifier.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Redundant modality modifier</problem_class>
<description>Redundant modality modifier</description>
</problem>
</problems>
@@ -0,0 +1 @@
// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.RedundantModalityModifierInspection
@@ -0,0 +1,42 @@
// Abstract
abstract class Base {
// Redundant final
final fun foo() {}
// Abstract
abstract fun bar()
// Open
open val gav = 42
}
class FinalDerived : Base() {
// Redundant final
override final fun bar() {}
// Non-final member in final class
override open val gav = 13
}
// Open
open class OpenDerived : Base() {
// Final
override final fun bar() {}
// Redundant open
override open val gav = 13
}
// Redundant final
final class Final
// Interface
interface Interface {
// Redundant
abstract fun foo()
// Redundant
private final fun bar() {}
// Redundant
open val gav: Int
get() = 42
}
// Derived interface
interface Derived : Interface {
// Redundant
override open fun foo() {}
// Redundant
final class Nested
}
@@ -0,0 +1 @@
org.jetbrains.kotlin.idea.inspections.RedundantModalityModifierInspection
@@ -0,0 +1,4 @@
// "Remove redundant modality modifier" "true"
open class C {
<caret>final fun foo(){}
}
@@ -0,0 +1,4 @@
// "Remove redundant modality modifier" "true"
open class C {
fun foo(){}
}
@@ -160,6 +160,12 @@ public class InspectionTestGenerated extends AbstractInspectionTest {
doTest(fileName);
}
@TestMetadata("redundantModalityModifier/inspectionData/inspections.test")
public void testRedundantModalityModifier_inspectionData_Inspections_test() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/redundantModalityModifier/inspectionData/inspections.test");
doTest(fileName);
}
@TestMetadata("redundantSamConstructor/inspectionData/inspections.test")
public void testRedundantSamConstructor_inspectionData_Inspections_test() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/redundantSamConstructor/inspectionData/inspections.test");
@@ -6128,6 +6128,21 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
}
}
@TestMetadata("idea/testData/quickfix/redundantModalityModifier")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class RedundantModalityModifier extends AbstractQuickFixTest {
public void testAllFilesPresentInRedundantModalityModifier() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/redundantModalityModifier"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/redundantModalityModifier/simple.kt");
doTest(fileName);
}
}
@TestMetadata("idea/testData/quickfix/redundantVisibilityModifier")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)