Add inspection for redundant overrides that only calls the super method

So #KT-19428 Fixed
This commit is contained in:
takahirom
2017-08-15 12:45:38 +03:00
committed by Mikhail Glukhikh
parent 8af7a25f8e
commit 806aa7d4c1
18 changed files with 317 additions and 1 deletions
@@ -0,0 +1,5 @@
<html>
<body>
This inspection reports redundant 'override' function which can be omitted.
</body>
</html>
+9 -1
View File
@@ -2520,7 +2520,6 @@
displayName="Local variable naming convention"
level="WEAK WARNING"/>
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.PackageNameInspection"
language="kotlin"
groupPath="Kotlin"
@@ -2529,6 +2528,15 @@
displayName="Package naming convention"
level="WEAK WARNING"/>
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.KotlinRedundantOverrideInspection"
displayName="Redundant override"
groupPath="Kotlin"
groupName="Redundant constructs"
enabledByDefault="true"
level="WEAK WARNING"
language="kotlin"
/>
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
@@ -0,0 +1,91 @@
/*
* Copyright 2010-2017 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.openapi.project.Project
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getCallNameExpression
class KotlinRedundantOverrideInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) =
object : KtVisitorVoid() {
override fun visitNamedFunction(function: KtNamedFunction) {
super.visitNamedFunction(function)
if (!function.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return
if (MODIFIER_EXCLUDE_OVERRIDE.any { function.hasModifier(it) }) return
if (function.annotationEntries.isNotEmpty()) return
val bodyExpression = function.bodyExpression ?: return
val qualifiedExpression = when (bodyExpression) {
is KtDotQualifiedExpression -> bodyExpression
is KtBlockExpression -> {
val body = bodyExpression.statements.singleOrNull()
when (body) {
is KtReturnExpression -> body.returnedExpression
is KtDotQualifiedExpression -> body.takeIf {
function.typeReference.let { it == null || it.text == "Unit" }
}
else -> null
}
}
else -> null
} as? KtDotQualifiedExpression ?: return
val superExpression = qualifiedExpression.receiverExpression as? KtSuperExpression ?: return
if (superExpression.superTypeQualifier != null) return
val superCallElement = qualifiedExpression.selectorExpression as? KtCallElement ?: return
if (!isSameFunctionName(superCallElement, function)) return
if (!isSameArguments(superCallElement, function)) return
holder.registerProblem(function,
"Redundant override",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
RedundantOverrideFix())
}
}
private fun isSameArguments(superCallElement: KtCallElement, function: KtNamedFunction): Boolean {
val arguments = superCallElement.valueArguments
val parameters = function.valueParameters
if (arguments.size != parameters.size) return false
return arguments.zip(parameters).all { (argument, parameter) ->
argument.getArgumentExpression()?.text == parameter.name
}
}
private fun isSameFunctionName(superSelectorExpression: KtCallElement, function: KtNamedFunction): Boolean {
val superCallMethodName = superSelectorExpression.getCallNameExpression()?.text ?: return false
return function.name == superCallMethodName
}
private class RedundantOverrideFix : LocalQuickFix {
override fun getName() = "Remove redundant overrides"
override fun getFamilyName() = name
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
descriptor.psiElement.delete()
}
}
companion object {
private val MODIFIER_EXCLUDE_OVERRIDE = KtTokens.MODIFIER_KEYWORDS_ARRAY.asList() - KtTokens.OVERRIDE_KEYWORD
}
}
@@ -0,0 +1 @@
org.jetbrains.kotlin.idea.inspections.KotlinRedundantOverrideInspection
@@ -0,0 +1,15 @@
// PROBLEM: none
@Target(AnnotationTarget.FUNCTION)
annotation class Annotation
open class Foo {
open fun simple() {
}
}
class Bar : Foo() {
@Annotation override <caret>fun simple() {
super.simple()
}
}
@@ -0,0 +1,10 @@
open class Foo {
open fun arguments(arg1: Int, arg2: Long) {
}
}
class Bar : Foo() {
override <caret>fun arguments(arg1: Int, arg2: Long) {
super.arguments(arg1, arg2)
}
}
@@ -0,0 +1,7 @@
open class Foo {
open fun arguments(arg1: Int, arg2: Long) {
}
}
class Bar : Foo() {
}
@@ -0,0 +1,11 @@
// PROBLEM: none
open class Foo {
open fun arguments(arg1: Int, arg2: Int) {
}
}
class Bar : Foo() {
override <caret>fun arguments(arg1: Int, arg2: Int) {
super.arguments(arg2, arg1)
}
}
@@ -0,0 +1,10 @@
open class Foo {
open fun simple() {
}
}
class Bar : Foo() {
override <caret>fun simple() {
super.simple()
}
}
@@ -0,0 +1,7 @@
open class Foo {
open fun simple() {
}
}
class Bar : Foo() {
}
@@ -0,0 +1,15 @@
// PROBLEM: none
open class Foo {
open fun simple() {
}
open fun callDifferentSuperMethod() {
}
}
class Bar : Foo() {
override <caret>fun callDifferentSuperMethod() {
super.simple()
}
}
@@ -0,0 +1,12 @@
// PROBLEM: none
open class Foo {
open fun simple() {
}
}
class Bar : Foo() {
override <caret>fun simple() {
1 + 1;
}
}
@@ -0,0 +1,13 @@
// PROBLEM: none
open class Foo {
open fun simple() {
}
}
class Bar : Foo() {
final override <caret>fun simple() {
super.simple()
}
}
@@ -0,0 +1,13 @@
// PROBLEM: none
open class Foo {
protected open fun simple() {
}
}
class Bar : Foo() {
public override <caret>fun simple() {
super.simple()
}
}
@@ -0,0 +1,8 @@
open class Foo {
open fun singleExpression() {
}
}
class Bar : Foo() {
override <caret>fun singleExpression() = super.singleExpression()
}
@@ -0,0 +1,7 @@
open class Foo {
open fun singleExpression() {
}
}
class Bar : Foo() {
}
@@ -0,0 +1,14 @@
// PROBLEM: none
interface First {
fun foo() = 2
}
interface Second {
fun foo() = 3
}
class Diamond : First, Second {
override <caret>fun foo(): Int {
return super<First>.foo()
}
}
@@ -1326,6 +1326,75 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
}
}
@TestMetadata("idea/testData/inspectionsLocal/redundantOverride")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class RedundantOverride extends AbstractLocalInspectionTest {
public void testAllFilesPresentInRedundantOverride() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/redundantOverride"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("annotated.kt")
public void testAnnotated() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/redundantOverride/annotated.kt");
doTest(fileName);
}
@TestMetadata("arguments.kt")
public void testArguments() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/redundantOverride/arguments.kt");
doTest(fileName);
}
@TestMetadata("argumentsReplaced.kt")
public void testArgumentsReplaced() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/redundantOverride/argumentsReplaced.kt");
doTest(fileName);
}
@TestMetadata("basic.kt")
public void testBasic() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/redundantOverride/basic.kt");
doTest(fileName);
}
@TestMetadata("callDifferentSuperMethod.kt")
public void testCallDifferentSuperMethod() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/redundantOverride/callDifferentSuperMethod.kt");
doTest(fileName);
}
@TestMetadata("notCallSuper.kt")
public void testNotCallSuper() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/redundantOverride/notCallSuper.kt");
doTest(fileName);
}
@TestMetadata("overrideModifireFinal.kt")
public void testOverrideModifireFinal() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/redundantOverride/overrideModifireFinal.kt");
doTest(fileName);
}
@TestMetadata("overrideModifireVisibility.kt")
public void testOverrideModifireVisibility() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/redundantOverride/overrideModifireVisibility.kt");
doTest(fileName);
}
@TestMetadata("singleExpressionFunction.kt")
public void testSingleExpressionFunction() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/redundantOverride/singleExpressionFunction.kt");
doTest(fileName);
}
@TestMetadata("useGenericsSuper.kt")
public void testUseGenericsSuper() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/redundantOverride/useGenericsSuper.kt");
doTest(fileName);
}
}
@TestMetadata("idea/testData/inspectionsLocal/removeRedundantSpreadOperator")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)