Redundant Sam Constructor inspection
This commit is contained in:
+1
-1
@@ -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();
|
||||
|
||||
@@ -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) {}
|
||||
}
|
||||
+15
@@ -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> { })
|
||||
}
|
||||
+138
@@ -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>
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package redundantSamConstructor
|
||||
|
||||
import a.*
|
||||
import a.A.*
|
||||
|
||||
fun testExpectedType() {
|
||||
val s1: JFunction1<String> = A.expectedType({ })
|
||||
val s2: JFunction1<String> = expectedType({ })
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.RedundantSamConstructorInspection
|
||||
Vendored
+12
@@ -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({})
|
||||
}
|
||||
Vendored
+40
@@ -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)
|
||||
}
|
||||
+26
@@ -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)
|
||||
}
|
||||
+26
@@ -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)
|
||||
}
|
||||
+26
@@ -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)
|
||||
}
|
||||
idea/testData/inspections/redundantSamConstructor/redundantSamConstructorWithGenericsInReturnType.kt
Vendored
+26
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user