Redundant Sam Constructor inspection

This commit is contained in:
Natalia Ukhorskaya
2015-08-13 14:16:37 +03:00
parent ccb4abee95
commit bf7f56d458
25 changed files with 706 additions and 1 deletions
@@ -86,7 +86,7 @@ public class SingleAbstractMethodUtils {
}
@Nullable
private static JetType getFunctionTypeForSamType(@NotNull JetType samType) {
public static JetType getFunctionTypeForSamType(@NotNull JetType samType) {
// e.g. samType == Comparator<String>?
ClassifierDescriptor classifier = samType.getConstructor().getDeclarationDescriptor();
+7
View File
@@ -1078,6 +1078,13 @@
level="WARNING"
/>
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.RedundantSamConstructorInspection"
displayName="Redundant SAM constructor"
groupName="Kotlin"
enabledByDefault="true"
level="WARNING"
/>
<localInspection implementationClass="org.jetbrains.kotlin.idea.kdoc.KDocUnresolvedReferenceInspection"
displayName="Unresolved reference in KDoc"
groupName="Kotlin"
@@ -0,0 +1,223 @@
/*
* 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.inspections
import com.intellij.codeInspection.*
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.codegen.SamCodegenUtil
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.core.getResolutionScope
import org.jetbrains.kotlin.idea.resolve.frontendService
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.load.java.descriptors.SamAdapterDescriptor
import org.jetbrains.kotlin.load.java.descriptors.SamConstructorDescriptor
import org.jetbrains.kotlin.load.java.sam.SingleAbstractMethodUtils
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTraceContext
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
import org.jetbrains.kotlin.resolve.calls.CallResolver
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.util.DelegatingCall
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.synthetic.SamAdapterExtensionFunctionDescriptor
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.utils.addToStdlib.check
import org.jetbrains.kotlin.utils.addToStdlib.singletonList
public class RedundantSamConstructorInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : JetVisitorVoid() {
private fun createQuickFix(expression: JetCallExpression): LocalQuickFix {
return object : LocalQuickFix {
override fun getName() = "Remove redundant SAM-constructor"
override fun getFamilyName() = name
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
replaceSamConstructorCall(expression)
}
}
}
private fun createQuickFix(expressions: List<JetCallExpression>): LocalQuickFix {
return object : LocalQuickFix {
override fun getName() = "Remove redundant SAM-constructors"
override fun getFamilyName() = name
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
for (callExpression in expressions) {
replaceSamConstructorCall(callExpression)
}
}
}
}
override fun visitCallExpression(expression: JetCallExpression) {
if (expression.valueArguments.isEmpty()) return
val samConstructorCalls = samConstructorCallsToBeConverted(expression)
if (samConstructorCalls.isEmpty()) return
if (samConstructorCalls.size() == 1) {
val single = samConstructorCalls.single()
val problemDescriptor = holder.manager.
createProblemDescriptor(single.calleeExpression!!,
single.typeArgumentList ?: single.calleeExpression!!,
"Redundant SAM-constructor",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
isOnTheFly,
createQuickFix(single))
holder.registerProblem(problemDescriptor)
}
else {
val problemDescriptor = holder.manager.
createProblemDescriptor(expression.valueArgumentList!!,
"Redundant SAM-constructors",
createQuickFix(samConstructorCalls),
ProblemHighlightType.WEAK_WARNING,
isOnTheFly)
holder.registerProblem(problemDescriptor)
}
}
}
}
companion object {
public fun replaceSamConstructorCall(callExpression: JetCallExpression): JetExpression {
val functionalArgument = callExpression.samConstructorValueArgument()?.getArgumentExpression()
?: throw AssertionError("SAM-constructor should have a FunctionLiteralExpression as single argument: ${callExpression.getElementTextWithContext()}")
return callExpression.replace(functionalArgument) as JetExpression
}
private fun canBeReplaced(parentCall: JetCallExpression, callExpressions: List<JetCallExpression>): Boolean {
val context = parentCall.analyze(BodyResolveMode.PARTIAL)
val calleeExpression = parentCall.calleeExpression ?: return false
val scope = context[BindingContext.RESOLUTION_SCOPE, calleeExpression] ?: return false
val originalCall = parentCall.getResolvedCall(context) ?: return false
val dataFlow = context.getDataFlowInfo(parentCall)
val callResolver = parentCall.getResolutionFacade().frontendService<CallResolver>()
val newCall = CallWithNewArguments(originalCall.call, callExpressions)
val qualifiedExpression = parentCall.getQualifiedExpressionForSelectorOrThis()
val expectedType = context[BindingContext.EXPECTED_EXPRESSION_TYPE, qualifiedExpression] ?: TypeUtils.NO_EXPECTED_TYPE
val resolutionResults = callResolver.resolveFunctionCall(BindingTraceContext(), scope, newCall, expectedType, dataFlow, false)
if (!resolutionResults.isSuccess) return false
val samAdapterOriginalDescriptor = SamCodegenUtil.getOriginalIfSamAdapter(resolutionResults.resultingDescriptor) ?: return false
return samAdapterOriginalDescriptor.original == originalCall.resultingDescriptor.original
}
private class CallWithNewArguments(original: Call, val callExpressions: List<JetCallExpression>): DelegatingCall(original) {
private val newArguments: List<ValueArgument>
init {
val factory = JetPsiFactory(callElement)
newArguments = original.valueArguments.map { argument ->
argument.getArgumentExpression()
?.check { callExpressions.contains(it) }
?.let { factory.createArgument(replaceSamConstructorCall(it.copy() as JetCallExpression), argument.getArgumentName()?.asName) }
?: argument
}
}
override fun getValueArguments() = newArguments
}
public fun samConstructorCallsToBeConverted(functionCall: JetCallExpression): List<JetCallExpression> {
if (functionCall.valueArguments.all { !canBeSamConstructorCall(it) }) {
return emptyList()
}
val bindingContext = functionCall.analyze(BodyResolveMode.PARTIAL)
val functionResolvedCall = functionCall.getResolvedCall(bindingContext) ?: return emptyList()
if (!functionResolvedCall.status.isSuccess) return emptyList()
val samConstructorCalls = functionCall.valueArguments.map {
(it.getArgumentExpression() as? JetCallExpression)
?.check { it.getResolvedCall(bindingContext)?.resultingDescriptor?.original is SamConstructorDescriptor }
}.filterNotNull()
if (samConstructorCalls.isEmpty()) return emptyList()
val originalFunctionDescriptor = functionResolvedCall.resultingDescriptor.original as? FunctionDescriptor ?: return emptyList()
val containingClass = originalFunctionDescriptor.containingDeclaration as? ClassDescriptor ?: return emptyList()
// SAM adapters for static functions
for (staticFunWithSameName in containingClass.staticScope.getFunctions(functionResolvedCall.resultingDescriptor.name, NoLookupLocation.FROM_IDE)) {
if (staticFunWithSameName is SamAdapterDescriptor<*>) {
if (isSamAdapterSuitableForCall(staticFunWithSameName, originalFunctionDescriptor, samConstructorCalls.size())) {
return samConstructorCalls.check { canBeReplaced(functionCall, it) } ?: emptyList()
}
}
}
// SAM adapters for member functions
val resolutionScope = functionCall.getResolutionScope(bindingContext, functionCall.getResolutionFacade())
val syntheticExtensions = resolutionScope.getSyntheticExtensionFunctions(
containingClass.defaultType.singletonList(),
functionResolvedCall.resultingDescriptor.name,
NoLookupLocation.FROM_IDE
)
for (syntheticExtension in syntheticExtensions) {
val samAdapter = syntheticExtension as? SamAdapterExtensionFunctionDescriptor ?: continue
if (isSamAdapterSuitableForCall(samAdapter, originalFunctionDescriptor, samConstructorCalls.size())) {
return samConstructorCalls.check { canBeReplaced(functionCall, it) } ?: emptyList()
}
}
return emptyList()
}
private fun canBeSamConstructorCall(argument: JetValueArgument): Boolean {
val argumentExpression = argument.getArgumentExpression()
if (argumentExpression !is JetCallExpression) return false
return argumentExpression.samConstructorValueArgument() != null
}
private fun JetCallExpression.samConstructorValueArgument(): JetValueArgument? {
return valueArguments.singleOrNull()?.check { it.getArgumentExpression() is JetFunctionLiteralExpression }
}
private fun isSamAdapterSuitableForCall(
samAdapter: FunctionDescriptor,
originalFunction: FunctionDescriptor,
samConstructorsCount: Int
): Boolean {
val samAdapterOriginalFunction = SamCodegenUtil.getOriginalIfSamAdapter(samAdapter)?.original
if (samAdapterOriginalFunction != originalFunction) return false
val parametersWithSamTypeCount = originalFunction.valueParameters.count {
SingleAbstractMethodUtils.getFunctionTypeForSamType(it.type) != null
}
return parametersWithSamTypeCount == samConstructorsCount
}
}
}
@@ -0,0 +1,9 @@
package a;
public class A<T> {
public void foo(JFunction1<T> r) {}
public static <T> JFunction1<T> expectedType(JFunction1<T> r) {
return r;
}
}
@@ -0,0 +1,11 @@
package a;
public class GenericClass1 {
public static <T> void staticFun1(JFunction1<T> r) {}
public static <T> void staticFun2(JFunction1<T> r1, JFunction1<T> r2) {}
public static <T> void staticFunWithOtherParam(int r1, JFunction1<T> r2) {}
public <T> void memberFun1(JFunction1<T> r) {}
public <T> void memberFun2(JFunction1<T> r1, JFunction1<T> r2) {}
public <T> void memberFunWithOtherParam(int r1, JFunction1<T> r2) {}
}
@@ -0,0 +1,11 @@
package a;
public class GenericClass2 {
public static <T> void staticFun1(JFunction0<T> r) {}
public static <T> void staticFun2(JFunction0<T> r1, JFunction0<T> r2) {}
public static <T> void staticFunWithOtherParam(int r1, JFunction0<T> r2) {}
public <T> void memberFun1(JFunction0<T> r) {}
public <T> void memberFun2(JFunction0<T> r1, JFunction0<T> r2) {}
public <T> void memberFunWithOtherParam(int r1, JFunction0<T> r2) {}
}
@@ -0,0 +1,15 @@
package a;
public class GenericClassCantBeReplaced {
public static <T> void staticFun1(JFunction0<T> r) {}
public static <T> void staticFun1(JFunction1<T> r) {}
public static <T> void staticFun2(JFunction0<T> r1, JFunction0<T> r2) {}
public static <T> void staticFun2(JFunction1<T> r1, JFunction1<T> r2) {}
public <T> void memberFun1(JFunction0<T> r) {}
public <T> void memberFun1(JFunction1<T> r) {}
public <T> void memberFun2(JFunction0<T> r1, JFunction0<T> r2) {}
public <T> void memberFun2(JFunction1<T> r1, JFunction1<T> r2) {}
}
@@ -0,0 +1,5 @@
package a;
public interface JFunction0<T> {
T foo();
}
@@ -0,0 +1,5 @@
package a;
public interface JFunction1<T> {
void foo(T t);
}
@@ -0,0 +1,14 @@
package a;
public class MyJavaClass {
public static void staticFun1(Runnable r) {}
public static void staticFun2(Runnable r1, Runnable r2) {}
public static void staticFunWithOtherParam(int r1, Runnable r2) {}
public void memberFun1(Runnable r) {}
public void memberFun2(Runnable r1, Runnable r2) {}
public void memberFunWithOtherParam(int r1, Runnable r2) {}
public static <T> void foo1(Runnable r, T t) {}
public static <T> void foo2(T t, Runnable r) {}
}
@@ -0,0 +1,9 @@
package redundantSamConstructor
import a.*
import a.A.*
fun testExpectedType() {
val s1: JFunction1<String> = A.expectedType(JFunction1<String> { })
val s2: JFunction1<String> = expectedType(JFunction1<String> { })
}
@@ -0,0 +1,138 @@
<problems>
<problem>
<file>redundantSamConstructor.kt</file>
<line>9</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/redundantSamConstructor.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Redundant SAM-constructor</problem_class>
<description>Redundant SAM-constructor</description>
</problem>
<problem>
<file>redundantSamConstructor.kt</file>
<line>11</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/redundantSamConstructor.kt" />
<problem_class severity="WARNING" attribute_key="WEAK_WARNING">Redundant SAM-constructors</problem_class>
<description>Redundant SAM-constructor</description>
</problem>
<problem>
<file>redundantSamConstructor.kt</file>
<line>22</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/redundantSamConstructor.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Redundant SAM-constructor</problem_class>
<description>Redundant SAM-constructor</description>
</problem>
<problem>
<file>redundantSamConstructor.kt</file>
<line>25</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/redundantSamConstructor.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Redundant SAM-constructor</problem_class>
<description>Redundant SAM-constructor</description>
</problem>
<problem>
<file>redundantSamConstructor.kt</file>
<line>27</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/redundantSamConstructor.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Redundant SAM-constructor</problem_class>
<description>Redundant SAM-constructor</description>
</problem>
<problem>
<file>redundantSamConstructor.kt</file>
<line>38</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/redundantSamConstructor.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Redundant SAM-constructor</problem_class>
<description>Redundant SAM-constructor</description>
</problem>
<problem>
<file>redundantSamConstructorWithGenericsInReturnType.kt</file>
<line>9</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/redundantSamConstructorWithGenericsInReturnType.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Redundant SAM-constructor</problem_class>
<description>Redundant SAM-constructor</description>
</problem>
<problem>
<file>redundantSamConstructorWithGenericsInReturnType.kt</file>
<line>11</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/redundantSamConstructorWithGenericsInReturnType.kt" />
<problem_class severity="WARNING" attribute_key="WEAK_WARNING">Redundant SAM-constructors</problem_class>
<description>Redundant SAM-constructor</description>
</problem>
<problem>
<file>redundantSamConstructorWithGenericsInReturnType.kt</file>
<line>15</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/redundantSamConstructorWithGenericsInReturnType.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Redundant SAM-constructor</problem_class>
<description>Redundant SAM-constructor</description>
</problem>
<problem>
<file>redundantSamConstructorWithGenericsInReturnType.kt</file>
<line>18</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/redundantSamConstructorWithGenericsInReturnType.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Redundant SAM-constructor</problem_class>
<description>Redundant SAM-constructor</description>
</problem>
<problem>
<file>redundantSamConstructorWithGenericsInReturnType.kt</file>
<line>20</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/redundantSamConstructorWithGenericsInReturnType.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Redundant SAM-constructor</problem_class>
<description>Redundant SAM-constructor</description>
</problem>
<problem>
<file>redundantSamConstructorWithGenericsInReturnType.kt</file>
<line>24</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/redundantSamConstructorWithGenericsInReturnType.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Redundant SAM-constructor</problem_class>
<description>Redundant SAM-constructor</description>
</problem>
<problem>
<file>otherGenericsInParams.kt</file>
<line>6</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/otherGenericsInParams.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Redundant SAM-constructor</problem_class>
<description>Redundant SAM-constructor</description>
</problem>
<problem>
<file>otherGenericsInParams.kt</file>
<line>8</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/otherGenericsInParams.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Redundant SAM-constructor</problem_class>
<description>Redundant SAM-constructor</description>
</problem>
<problem>
<file>otherGenericsInParams.kt</file>
<line>11</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/otherGenericsInParams.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Redundant SAM-constructor</problem_class>
<description>Redundant SAM-constructor</description>
</problem>
<problem>
<file>expectedType.kt</file>
<line>7</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/expectedType.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Redundant SAM constructor</problem_class>
<description>Redundant SAM-constructor</description>
</problem>
<problem>
<file>expectedType.kt</file>
<line>8</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/expectedType.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Redundant SAM constructor</problem_class>
<description>Redundant SAM-constructor</description>
</problem>
</problems>
@@ -0,0 +1,9 @@
package redundantSamConstructor
import a.*
import a.A.*
fun testExpectedType() {
val s1: JFunction1<String> = A.expectedType({ })
val s2: JFunction1<String> = expectedType({ })
}
@@ -0,0 +1 @@
// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.RedundantSamConstructorInspection
@@ -0,0 +1,12 @@
package redundantSamConstructor
import a.*
fun test() {
MyJavaClass.foo1({ }, 1)
MyJavaClass.foo1(Runnable { }, Runnable { })
MyJavaClass.foo2(1, { })
MyJavaClass.foo2(Runnable { }, Runnable { })
A<String>().foo({})
}
@@ -0,0 +1,40 @@
package redundantSamConstructor
import a.*
fun test() {
val runnable = Runnable { }
val klass = MyJavaClass()
MyJavaClass.staticFun1({ })
MyJavaClass.staticFun1(runnable)
MyJavaClass.staticFun2({ }, { })
MyJavaClass.staticFun2(runnable, Runnable { })
MyJavaClass.staticFun2({ }, { })
MyJavaClass.staticFun2(
object: Runnable {
override fun run() { }
},
object: Runnable {
override fun run() { }
})
MyJavaClass.staticFunWithOtherParam(1, { })
MyJavaClass.staticFunWithOtherParam(1, runnable)
klass.memberFun1({ })
klass.memberFun1(runnable)
klass.memberFun2({ }, { })
klass.memberFun2(runnable, Runnable { })
klass.memberFun2({ }, { })
klass.memberFun2(
object: Runnable {
override fun run() { }
},
object: Runnable {
override fun run() { }
})
klass.memberFunWithOtherParam(1, { })
klass.memberFunWithOtherParam(1, runnable)
}
@@ -0,0 +1,26 @@
package redundantSamConstructor
import a.*
fun testGenerics() {
val runnable = JFunction1<String> { }
val klass = GenericClass1()
GenericClass1.staticFun1(JFunction1<String> { })
GenericClass1.staticFun1(runnable)
GenericClass1.staticFun2(JFunction1<String> { }, JFunction1<String> { })
GenericClass1.staticFun2(runnable, JFunction1<String> { })
GenericClass1.staticFun2({ s: String -> }, { s: String -> })
GenericClass1.staticFunWithOtherParam(1, JFunction1<String> { })
GenericClass1.staticFunWithOtherParam(1, runnable)
klass.memberFun1(JFunction1<String> { })
klass.memberFun1(runnable)
klass.memberFun2(JFunction1<String> { }, JFunction1<String> { })
klass.memberFun2(runnable, JFunction1<String> { })
klass.memberFun2({ s: String -> }, { s: String -> })
klass.memberFunWithOtherParam(1, JFunction1<String> { })
klass.memberFunWithOtherParam(1, runnable)
}
@@ -0,0 +1,26 @@
package redundantSamConstructor
import a.*
fun testGenericsReturnType() {
val runnable = JFunction0 { "" }
val klass = GenericClass2()
GenericClass2.staticFun1({ "" })
GenericClass2.staticFun1(runnable)
GenericClass2.staticFun2({ "" }, { "" })
GenericClass2.staticFun2(runnable, JFunction0 { "" })
GenericClass2.staticFun2({ -> "" }, { -> "" })
GenericClass2.staticFunWithOtherParam(1, { "" })
GenericClass2.staticFunWithOtherParam(1, runnable)
klass.memberFun1({ "" })
klass.memberFun1(runnable)
klass.memberFun2({ "" }, { "" })
klass.memberFun2(runnable, JFunction0 { "" })
klass.memberFun2({ -> "" }, { -> "" })
klass.memberFunWithOtherParam(1, { "" })
klass.memberFunWithOtherParam(1, runnable)
}
@@ -0,0 +1,12 @@
package redundantSamConstructor
import a.*
fun testNonApplicableAmbiguity() {
GenericClassCantBeReplaced.staticFun1(JFunction0 { "" })
GenericClassCantBeReplaced.staticFun2(JFunction0 { "" }, JFunction0 { "" })
val klass = GenericClassCantBeReplaced()
klass.memberFun1(JFunction0 { "" })
klass.memberFun2(JFunction0 { "" }, JFunction0 { "" })
}
@@ -0,0 +1,12 @@
package redundantSamConstructor
import a.*
fun test() {
MyJavaClass.foo1(Runnable { }, 1)
MyJavaClass.foo1(Runnable { }, Runnable { })
MyJavaClass.foo2(1, Runnable { })
MyJavaClass.foo2(Runnable { }, Runnable { })
A<String>().foo(JFunction1<String> {})
}
@@ -0,0 +1,40 @@
package redundantSamConstructor
import a.*
fun test() {
val runnable = Runnable { }
val klass = MyJavaClass()
MyJavaClass.staticFun1(Runnable { })
MyJavaClass.staticFun1(runnable)
MyJavaClass.staticFun2(Runnable { }, Runnable { })
MyJavaClass.staticFun2(runnable, Runnable { })
MyJavaClass.staticFun2({ }, { })
MyJavaClass.staticFun2(
object: Runnable {
override fun run() { }
},
object: Runnable {
override fun run() { }
})
MyJavaClass.staticFunWithOtherParam(1, Runnable { })
MyJavaClass.staticFunWithOtherParam(1, runnable)
klass.memberFun1(Runnable { })
klass.memberFun1(runnable)
klass.memberFun2(Runnable { }, Runnable { })
klass.memberFun2(runnable, Runnable { })
klass.memberFun2({ }, { })
klass.memberFun2(
object: Runnable {
override fun run() { }
},
object: Runnable {
override fun run() { }
})
klass.memberFunWithOtherParam(1, Runnable { })
klass.memberFunWithOtherParam(1, runnable)
}
@@ -0,0 +1,26 @@
package redundantSamConstructor
import a.*
fun testGenerics() {
val runnable = JFunction1<String> { }
val klass = GenericClass1()
GenericClass1.staticFun1(JFunction1<String> { })
GenericClass1.staticFun1(runnable)
GenericClass1.staticFun2(JFunction1<String> { }, JFunction1<String> { })
GenericClass1.staticFun2(runnable, JFunction1<String> { })
GenericClass1.staticFun2({ s: String -> }, { s: String -> })
GenericClass1.staticFunWithOtherParam(1, JFunction1<String> { })
GenericClass1.staticFunWithOtherParam(1, runnable)
klass.memberFun1(JFunction1<String> { })
klass.memberFun1(runnable)
klass.memberFun2(JFunction1<String> { }, JFunction1<String> { })
klass.memberFun2(runnable, JFunction1<String> { })
klass.memberFun2({ s: String -> }, { s: String -> })
klass.memberFunWithOtherParam(1, JFunction1<String> { })
klass.memberFunWithOtherParam(1, runnable)
}
@@ -0,0 +1,26 @@
package redundantSamConstructor
import a.*
fun testGenericsReturnType() {
val runnable = JFunction0 { "" }
val klass = GenericClass2()
GenericClass2.staticFun1(JFunction0 { "" })
GenericClass2.staticFun1(runnable)
GenericClass2.staticFun2(JFunction0 { "" }, JFunction0 { "" })
GenericClass2.staticFun2(runnable, JFunction0 { "" })
GenericClass2.staticFun2({ -> "" }, { -> "" })
GenericClass2.staticFunWithOtherParam(1, JFunction0 { "" })
GenericClass2.staticFunWithOtherParam(1, runnable)
klass.memberFun1(JFunction0 { "" })
klass.memberFun1(runnable)
klass.memberFun2(JFunction0 { "" }, JFunction0 { "" })
klass.memberFun2(runnable, JFunction0 { "" })
klass.memberFun2({ -> "" }, { -> "" })
klass.memberFunWithOtherParam(1, JFunction0 { "" })
klass.memberFunWithOtherParam(1, runnable)
}
@@ -22,7 +22,9 @@ import com.intellij.codeInspection.LocalInspectionTool
import com.intellij.codeInspection.ex.EntryPointsManagerBase
import com.intellij.codeInspection.ex.InspectionManagerEx
import com.intellij.codeInspection.ex.LocalInspectionToolWrapper
import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.util.io.FileUtil
import com.intellij.psi.PsiFile
import com.intellij.testFramework.IdeaTestUtil
import com.intellij.testFramework.InspectionTestUtil
import com.intellij.testFramework.LightProjectDescriptor
@@ -31,10 +33,12 @@ import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
import org.jetbrains.kotlin.idea.test.JetLightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.idea.test.JetLightProjectDescriptor
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import org.jetbrains.kotlin.test.JetTestUtils
import java.io.File
import kotlin.test.assertFalse
import java.util.*
public abstract class AbstractJetInspectionTest: JetLightCodeInsightFixtureTestCase() {
companion object {
@@ -66,6 +70,7 @@ public abstract class AbstractJetInspectionTest: JetLightCodeInsightFixtureTestC
with(myFixture) {
setTestDataPath("${JetTestUtils.getHomeDirectory()}/$srcDir")
val afterFiles = srcDir.listFiles { it.name == "inspectionData" }?.single()?.listFiles { it.extension == "after" } ?: emptyArray()
val psiFiles = srcDir.walkTopDown().filter { it.name != "inspectionData" }.map {
file ->
if (file.isDirectory) {
@@ -115,6 +120,23 @@ public abstract class AbstractJetInspectionTest: JetLightCodeInsightFixtureTestC
InspectionTestUtil.runTool(toolWrapper, scope, globalContext)
InspectionTestUtil.compareToolResults(globalContext, toolWrapper, false, inspectionsTestDir.getPath())
if (afterFiles.isNotEmpty()) {
globalContext.getPresentation(toolWrapper).problemDescriptors.forEach {
problem ->
problem.fixes?.forEach {
CommandProcessor.getInstance().executeCommand(project, {
runWriteAction { it.applyFix(project, problem) }
}, it.name, it.familyName)
}
}
for (filePath in afterFiles) {
val kotlinFile = psiFiles.first { filePath.name == it.name + ".after" }
JetTestUtils.assertEqualsToFile(filePath, kotlinFile.text)
}
}
}
finally {
if (isWithRuntime) {
@@ -88,6 +88,12 @@ public class JetInspectionTestGenerated extends AbstractJetInspectionTest {
JetTestUtils.assertAllTestsPresentInSingleGeneratedClass(this.getClass(), new File("idea/testData/inspections"), Pattern.compile("^(inspections\\.test)$"));
}
@TestMetadata("redundantSamConstructor/inspectionData/inspections.test")
public void testRedundantSamConstructor_inspectionData_Inspections_test() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/inspections/redundantSamConstructor/inspectionData/inspections.test");
doTest(fileName);
}
@TestMetadata("spelling/inspectionData/inspections.test")
public void testSpelling_inspectionData_Inspections_test() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/inspections/spelling/inspectionData/inspections.test");