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"
|
displayName="Local variable naming convention"
|
||||||
level="WEAK WARNING"/>
|
level="WEAK WARNING"/>
|
||||||
|
|
||||||
|
|
||||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.PackageNameInspection"
|
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.PackageNameInspection"
|
||||||
language="kotlin"
|
language="kotlin"
|
||||||
groupPath="Kotlin"
|
groupPath="Kotlin"
|
||||||
@@ -2529,6 +2528,15 @@
|
|||||||
displayName="Package naming convention"
|
displayName="Package naming convention"
|
||||||
level="WEAK WARNING"/>
|
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"/>
|
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
|
||||||
|
|
||||||
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
|
<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")
|
@TestMetadata("idea/testData/inspectionsLocal/removeRedundantSpreadOperator")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user