Inspection & fix for redundant supertype qualification

This commit is contained in:
Valentin Kipyatkov
2015-08-26 15:05:10 +03:00
parent d62eeb1c25
commit 4717b17418
30 changed files with 531 additions and 2 deletions
@@ -23,8 +23,10 @@ import org.jetbrains.kotlin.name.Name
open public class JetExpressionWithLabel(node: ASTNode) : JetExpressionImpl(node) {
public fun getTargetLabel(): JetSimpleNameExpression? =
findChildByType<JetContainerNode>(JetNodeTypes.LABEL_QUALIFIER)?.
findChildByType(JetNodeTypes.LABEL) as? JetSimpleNameExpression
labelQualifier?.findChildByType(JetNodeTypes.LABEL) as? JetSimpleNameExpression
public val labelQualifier: JetContainerNode?
get() = findChildByType(JetNodeTypes.LABEL_QUALIFIER)
public fun getLabelName(): String? = getTargetLabel()?.getReferencedName()
public fun getLabelNameAsName(): Name? = getTargetLabel()?.getReferencedNameAsName()
@@ -494,6 +494,10 @@ public class JetPsiUtil {
return JetExpressionParsing.Precedence.ASSIGNMENT.ordinal();
}
if (expression instanceof JetSuperExpression) {
return maxPriority;
}
if (expression instanceof JetDeclaration || expression instanceof JetStatementExpression) {
return 0;
}
@@ -0,0 +1,5 @@
<html>
<body>
This inspection reports any super member call with redundant supertype qualification
</body>
</html>
@@ -0,0 +1,9 @@
open class B {
open fun foo(){}
}
class A : B() {
override fun foo(p: String) {
<spot>super</spot>.foo("")
}
}
@@ -0,0 +1,9 @@
open class B {
open fun foo(){}
}
class A : B() {
override fun foo() {
<spot>super<B></spot>.foo("")
}
}
@@ -0,0 +1,5 @@
<html>
<body>
This intention removes a redundant supertype qualification from a "super" expression
</body>
</html>
+12
View File
@@ -727,6 +727,11 @@
<category>Kotlin</category>
</intentionAction>
<intentionAction>
<className>org.jetbrains.kotlin.idea.intentions.RemoveExplicitSuperQualifierIntention</className>
<category>Kotlin</category>
</intentionAction>
<intentionAction>
<className>org.jetbrains.kotlin.idea.intentions.RemoveCurlyBracesFromTemplateIntention</className>
<category>Kotlin</category>
@@ -1021,6 +1026,13 @@
level="WEAK WARNING"
/>
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.RemoveExplicitSuperQualifierInspection"
displayName="Supertype qualification is unnecessary"
groupName="Kotlin"
enabledByDefault="true"
level="WEAK WARNING"
/>
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.SimplifyNegatedBinaryExpressionInspection"
displayName="Simplify negated binary expression"
groupName="Kotlin"
@@ -0,0 +1,77 @@
/*
* Copyright 2010-2015 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.intentions
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.util.TextRange
import org.jetbrains.kotlin.idea.analysis.analyzeInContext
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection
import org.jetbrains.kotlin.psi.JetPsiFactory
import org.jetbrains.kotlin.psi.JetQualifiedExpression
import org.jetbrains.kotlin.psi.JetSuperExpression
import org.jetbrains.kotlin.psi.createExpressionByPattern
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForReceiver
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.TypeUtils
public class RemoveExplicitSuperQualifierInspection : IntentionBasedInspection<JetSuperExpression>(RemoveExplicitSuperQualifierIntention()) {
override val problemHighlightType: ProblemHighlightType
get() = ProblemHighlightType.LIKE_UNUSED_SYMBOL
}
public class RemoveExplicitSuperQualifierIntention : JetSelfTargetingRangeIntention<JetSuperExpression>(javaClass(), "Remove explicit supertype qualification") {
override fun applicabilityRange(element: JetSuperExpression): TextRange? {
if (element.superTypeQualifier == null) return null
val qualifiedExpression = element.getQualifiedExpressionForReceiver() ?: return null
val selector = qualifiedExpression.selectorExpression ?: return null
val bindingContext = selector.analyze(BodyResolveMode.PARTIAL)
if (selector.getResolvedCall(bindingContext) == null) return null
val resolutionScope = bindingContext[BindingContext.RESOLUTION_SCOPE, qualifiedExpression] ?: return null
val dataFlowInfo = bindingContext.getDataFlowInfo(element)
val expectedType = bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, qualifiedExpression] ?: TypeUtils.NO_EXPECTED_TYPE
val newQualifiedExpression = JetPsiFactory(element).createExpressionByPattern("$0.$1", toNonQualified(element), selector) as JetQualifiedExpression
val newBindingContext = newQualifiedExpression.analyzeInContext(resolutionScope, qualifiedExpression, dataFlowInfo = dataFlowInfo, expectedType = expectedType, isStatement = true)
val newResolvedCall = newQualifiedExpression.selectorExpression.getResolvedCall(newBindingContext) ?: return null
if (ErrorUtils.isError(newResolvedCall.resultingDescriptor)) return null
return TextRange(element.instanceReference.endOffset, element.labelQualifier?.startOffset ?: element.endOffset)
}
override fun applyTo(element: JetSuperExpression, editor: Editor) {
element.replace(toNonQualified(element))
}
private fun toNonQualified(superExpression: JetSuperExpression): JetSuperExpression {
val factory = JetPsiFactory(superExpression)
val labelName = superExpression.getLabelNameAsName()
return (if (labelName != null)
factory.createExpressionByPattern("super@$0", labelName)
else
factory.createExpression("super")) as JetSuperExpression
}
}
@@ -0,0 +1 @@
org.jetbrains.kotlin.idea.intentions.RemoveExplicitSuperQualifierIntention
@@ -0,0 +1,15 @@
// IS_APPLICABLE: false
open class B {
open fun foo(p: String){}
}
interface I {
fun foo(p: String) {}
}
class A : B(), I {
override fun foo(p: String) {
super<B><caret>.foo("")
}
}
@@ -0,0 +1,15 @@
// IS_APPLICABLE: false
open class B {
open val v: Int = 0
}
interface I {
val v: Int
get() = 0
}
class A : B(), I {
override val v: Int
get() = super<B><caret>.v
}
@@ -0,0 +1,17 @@
open class B {
open fun foo(p: String){}
fun foo(p: Int){}
}
interface I {
fun foo(p: String)
}
class A : B(), I {
fun foo(p: Any) {
if (p is Int) {
super<B><caret>.foo(p)
}
}
}
@@ -0,0 +1,17 @@
open class B {
open fun foo(p: String){}
fun foo(p: Int){}
}
interface I {
fun foo(p: String)
}
class A : B(), I {
fun foo(p: Any) {
if (p is Int) {
super<caret>.foo(p)
}
}
}
@@ -0,0 +1,15 @@
// ERROR: No value passed for parameter p
open class B {
open fun foo(p: String){}
}
interface I {
fun foo(p: String)
}
class A : B(), I {
override fun foo(p: String) {
super<B><caret>.foo()
}
}
@@ -0,0 +1,15 @@
// ERROR: No value passed for parameter p
open class B {
open fun foo(p: String){}
}
interface I {
fun foo(p: String)
}
class A : B(), I {
override fun foo(p: String) {
super<caret>.foo()
}
}
@@ -0,0 +1,17 @@
// IS_APPLICABLE: false
// ERROR: <html>None of the following functions can be called with the arguments supplied. <ul><li>foo(<font color=red><b>Int</b></font>) <i>defined in</i> B</li><li>foo(<font color=red><b>String</b></font>) <i>defined in</i> B</li></ul></html>
open class B {
open fun foo(p: String){}
fun foo(p: Int){}
}
interface I {
fun foo(p: String)
}
class A : B(), I {
override fun foo(p: String) {
super<B><caret>.foo()
}
}
@@ -0,0 +1,8 @@
open class B {
open fun foo(){}
}
class A : B() {
override fun foo() {
super<B><caret>.foo()
}
}
@@ -0,0 +1,8 @@
open class B {
open fun foo(){}
}
class A : B() {
override fun foo() {
super<caret>.foo()
}
}
@@ -0,0 +1,16 @@
open class Base {
open fun foo() {
}
}
class A : Base() {
override fun foo() {
super.foo()
}
inner class C {
fun test() {
super<Base><caret>@A.foo()
}
}
}
@@ -0,0 +1,16 @@
open class Base {
open fun foo() {
}
}
class A : Base() {
override fun foo() {
super.foo()
}
inner class C {
fun test() {
super@A.foo()
}
}
}
@@ -0,0 +1,15 @@
open class B {
open fun foo(p: String){}
fun foo(p: Int){}
}
interface I {
fun foo(p: String)
}
class A : B(), I {
override fun foo(p: String) {
super<B><caret>.foo("")
}
}
@@ -0,0 +1,15 @@
open class B {
open fun foo(p: String){}
fun foo(p: Int){}
}
interface I {
fun foo(p: String)
}
class A : B(), I {
override fun foo(p: String) {
super<caret>.foo("")
}
}
@@ -0,0 +1,12 @@
open class B {
open val v: Int = 0
}
interface I {
val v: Int
}
class A : B(), I {
override val v: Int
get() = super<caret><B>.v
}
@@ -0,0 +1,12 @@
open class B {
open val v: Int = 0
}
interface I {
val v: Int
}
class A : B(), I {
override val v: Int
get() = super<caret>.v
}
@@ -0,0 +1,18 @@
// IS_APPLICABLE: false
open class Base {
open fun foo() {
}
}
class A : Base() {
override fun foo() {
super.foo()
}
inner class C {
fun test() {
super<Base>@<caret>A.foo()
}
}
}
@@ -0,0 +1,18 @@
// IS_APPLICABLE: false
open class Base {
open fun foo() {
}
}
class A : Base() {
override fun foo() {
super.foo()
}
inner class C {
fun test() {
supe<caret>r<Base>@A.foo()
}
}
}
@@ -0,0 +1,73 @@
<problems>
<problem>
<file>UnambiguousSuperProperty.kt</file>
<line>11</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="UnambiguousSuperProperty.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Supertype qualification is unnecessary</problem_class>
<description>Remove explicit supertype qualification</description>
</problem>
<problem>
<file>UnambiguousSuperMethod.kt</file>
<line>13</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="UnambiguousSuperMethod.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Supertype qualification is unnecessary</problem_class>
<description>Remove explicit supertype qualification</description>
</problem>
<problem>
<file>SingleSuper.kt</file>
<line>6</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="SingleSuper.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Supertype qualification is unnecessary</problem_class>
<description>Remove explicit supertype qualification</description>
</problem>
<problem>
<file>IncompleteCall.kt</file>
<line>13</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="IncompleteCall.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Supertype qualification is unnecessary</problem_class>
<description>Remove explicit supertype qualification</description>
</problem>
<problem>
<file>DataFlowInfoUsed.kt</file>
<line>14</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="DataFlowInfoUsed.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Supertype qualification is unnecessary</problem_class>
<description>Remove explicit supertype qualification</description>
</problem>
<problem>
<file>SuperWithLabel.kt</file>
<line>13</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="SuperWithLabel.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Supertype qualification is unnecessary</problem_class>
<description>Remove explicit supertype qualification</description>
</problem>
<problem>
<file>WrongOffset1.kt</file>
<line>15</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="WrongOffset1.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Supertype qualification is unnecessary</problem_class>
<description>Remove explicit supertype qualification</description>
</problem>
<problem>
<file>WrongOffset2.kt</file>
<line>15</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="WrongOffset2.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Supertype qualification is unnecessary</problem_class>
<description>Remove explicit supertype qualification</description>
</problem>
</problems>
@@ -0,0 +1 @@
// INSPECTION_CLASS: org.jetbrains.kotlin.idea.intentions.RemoveExplicitSuperQualifierInspection
@@ -67,6 +67,12 @@ public class JetInspectionTestGenerated extends AbstractJetInspectionTest {
doTest(fileName);
}
@TestMetadata("removeExplicitSuperQualifier/inspectionData/inspections.test")
public void testRemoveExplicitSuperQualifier_inspectionData_Inspections_test() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitSuperQualifier/inspectionData/inspections.test");
doTest(fileName);
}
@TestMetadata("removeExplicitTypeArguments/inspectionData/inspections.test")
public void testRemoveExplicitTypeArguments_inspectionData_Inspections_test() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitTypeArguments/inspectionData/inspections.test");
@@ -6019,6 +6019,82 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
}
}
@TestMetadata("idea/testData/intentions/removeExplicitSuperQualifier")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class RemoveExplicitSuperQualifier extends AbstractIntentionTest {
public void testAllFilesPresentInRemoveExplicitSuperQualifier() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/removeExplicitSuperQualifier"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
}
@TestMetadata("AmbiguousSuperMethod.kt")
public void testAmbiguousSuperMethod() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitSuperQualifier/AmbiguousSuperMethod.kt");
doTest(fileName);
}
@TestMetadata("AmbiguousSuperProperty.kt")
public void testAmbiguousSuperProperty() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitSuperQualifier/AmbiguousSuperProperty.kt");
doTest(fileName);
}
@TestMetadata("DataFlowInfoUsed.kt")
public void testDataFlowInfoUsed() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitSuperQualifier/DataFlowInfoUsed.kt");
doTest(fileName);
}
@TestMetadata("IncompleteCall.kt")
public void testIncompleteCall() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitSuperQualifier/IncompleteCall.kt");
doTest(fileName);
}
@TestMetadata("IncompleteCallAmbiguous.kt")
public void testIncompleteCallAmbiguous() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitSuperQualifier/IncompleteCallAmbiguous.kt");
doTest(fileName);
}
@TestMetadata("SingleSuper.kt")
public void testSingleSuper() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitSuperQualifier/SingleSuper.kt");
doTest(fileName);
}
@TestMetadata("SuperWithLabel.kt")
public void testSuperWithLabel() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitSuperQualifier/SuperWithLabel.kt");
doTest(fileName);
}
@TestMetadata("UnambiguousSuperMethod.kt")
public void testUnambiguousSuperMethod() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitSuperQualifier/UnambiguousSuperMethod.kt");
doTest(fileName);
}
@TestMetadata("UnambiguousSuperProperty.kt")
public void testUnambiguousSuperProperty() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitSuperQualifier/UnambiguousSuperProperty.kt");
doTest(fileName);
}
@TestMetadata("WrongOffset1.kt")
public void testWrongOffset1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitSuperQualifier/WrongOffset1.kt");
doTest(fileName);
}
@TestMetadata("WrongOffset2.kt")
public void testWrongOffset2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitSuperQualifier/WrongOffset2.kt");
doTest(fileName);
}
}
@TestMetadata("idea/testData/intentions/removeExplicitType")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)