Add inspection for redundant overrides that only calls the super method
So #KT-19428 Fixed
This commit is contained in:
committed by
Mikhail Glukhikh
parent
8af7a25f8e
commit
806aa7d4c1
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
This inspection reports redundant 'override' function which can be omitted.
|
||||
</body>
|
||||
</html>
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
+13
@@ -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()
|
||||
}
|
||||
+7
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user