From 9a0c3c47c87abcb5bf25cc4d82923705a8d5acaf Mon Sep 17 00:00:00 2001 From: Dmitry Gridin Date: Wed, 7 Oct 2020 15:15:34 +0700 Subject: [PATCH] [inspections] fix false positive "Redundant Unit" inspection in lambda with dynamic return type ^KT-40700 Fixed --- .../RedundantUnitExpressionInspection.kt | 26 +++++++++++----- .../redundantUnitExpression/dynamic.kt | 10 +++++++ .../redundantUnitExpression/dynamic2.kt | 10 +++++++ .../redundantUnitExpression/dynamic3.kt | 14 +++++++++ .../redundantUnitExpression/dynamic4.kt | 12 ++++++++ .../redundantUnitExpression/dynamic4.kt.after | 11 +++++++ .../redundantUnitExpression/dynamic5.kt | 12 ++++++++ .../redundantUnitExpression/dynamic5.kt.after | 11 +++++++ .../redundantUnitExpression/dynamic6.kt | 10 +++++++ .../LocalInspectionTestGenerated.java | 30 +++++++++++++++++++ 10 files changed, 138 insertions(+), 8 deletions(-) create mode 100644 idea/testData/inspectionsLocal/redundantUnitExpression/dynamic.kt create mode 100644 idea/testData/inspectionsLocal/redundantUnitExpression/dynamic2.kt create mode 100644 idea/testData/inspectionsLocal/redundantUnitExpression/dynamic3.kt create mode 100644 idea/testData/inspectionsLocal/redundantUnitExpression/dynamic4.kt create mode 100644 idea/testData/inspectionsLocal/redundantUnitExpression/dynamic4.kt.after create mode 100644 idea/testData/inspectionsLocal/redundantUnitExpression/dynamic5.kt create mode 100644 idea/testData/inspectionsLocal/redundantUnitExpression/dynamic5.kt.after create mode 100644 idea/testData/inspectionsLocal/redundantUnitExpression/dynamic6.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantUnitExpressionInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantUnitExpressionInspection.kt index 6b3f38360dc..f94fe10d393 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantUnitExpressionInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantUnitExpressionInspection.kt @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.resolve.bindingContextUtil.getTargetFunctionDescript import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.isDynamic import org.jetbrains.kotlin.types.typeUtil.isUnit class RedundantUnitExpressionInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool { @@ -50,6 +51,7 @@ class RedundantUnitExpressionInspection : AbstractKotlinInspection(), CleanupLoc if (referenceExpression == parent.lastBlockStatementOrThis()) { val prev = referenceExpression.previousStatement() ?: return true if (prev.isUnitLiteral()) return true + if (prev is KtDeclaration && isDynamicCall(parent)) return false val prevType = prev.analyze(BodyResolveMode.PARTIAL).getType(prev) if (prevType != null) { return prevType.isUnit() @@ -72,20 +74,28 @@ class RedundantUnitExpressionInspection : AbstractKotlinInspection(), CleanupLoc } } +private fun isDynamicCall(parent: KtBlockExpression): Boolean = parent.getStrictParentOfType() + ?.findLambdaReturnType() + ?.isDynamic() == true + private fun KtExpression.isUnitLiteral(): Boolean = StandardNames.FqNames.unit.shortName() == (this as? KtNameReferenceExpression)?.getReferencedNameAsName() private fun KtReturnExpression.expectedReturnType(): KotlinType? { val functionDescriptor = getTargetFunctionDescriptor(analyze()) ?: return null val functionLiteral = DescriptorToSourceUtils.descriptorToDeclaration(functionDescriptor) as? KtFunctionLiteral - if (functionLiteral != null) { - val callExpression = functionLiteral.getStrictParentOfType() ?: return null - val resolvedCall = callExpression.resolveToCall() ?: return null - val valueArgument = functionLiteral.getStrictParentOfType() ?: return null - val mapping = resolvedCall.getArgumentMapping(valueArgument) as? ArgumentMatch ?: return null - return mapping.valueParameter.returnType?.arguments?.lastOrNull()?.type - } - return functionDescriptor.returnType + return if (functionLiteral != null) + functionLiteral.findLambdaReturnType() + else + functionDescriptor.returnType +} + +private fun KtFunctionLiteral.findLambdaReturnType(): KotlinType? { + val callExpression = getStrictParentOfType() ?: return null + val resolvedCall = callExpression.resolveToCall() ?: return null + val valueArgument = getStrictParentOfType() ?: return null + val mapping = resolvedCall.getArgumentMapping(valueArgument) as? ArgumentMatch ?: return null + return mapping.valueParameter.returnType?.arguments?.lastOrNull()?.type } private class RemoveRedundantUnitFix : LocalQuickFix { diff --git a/idea/testData/inspectionsLocal/redundantUnitExpression/dynamic.kt b/idea/testData/inspectionsLocal/redundantUnitExpression/dynamic.kt new file mode 100644 index 00000000000..50d927aad26 --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantUnitExpression/dynamic.kt @@ -0,0 +1,10 @@ +// PROBLEM: none +// ERROR: Unsupported [Dynamic types are not supported in this context] + +fun foo() { + fun bar(f: () -> dynamic): Unit {} + bar { + val a = 1 + Unit + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantUnitExpression/dynamic2.kt b/idea/testData/inspectionsLocal/redundantUnitExpression/dynamic2.kt new file mode 100644 index 00000000000..465d8af68f3 --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantUnitExpression/dynamic2.kt @@ -0,0 +1,10 @@ +// PROBLEM: none +// ERROR: Unsupported [Dynamic types are not supported in this context] + +fun foo() { + fun bar(f: () -> dynamic): Unit {} + bar({ + val a = 1 + Unit + }) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantUnitExpression/dynamic3.kt b/idea/testData/inspectionsLocal/redundantUnitExpression/dynamic3.kt new file mode 100644 index 00000000000..cd19d84d870 --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantUnitExpression/dynamic3.kt @@ -0,0 +1,14 @@ +// PROBLEM: none +// ERROR: Unsupported [Dynamic types are not supported in this context] +// ERROR: Unsupported [Dynamic types are not supported in this context] + +fun foo() { + fun bar(c: () -> dynamic, f: () -> dynamic): Unit {} + bar({ + val a = 1 + Unit + }) { + val a = 1 + Unit + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantUnitExpression/dynamic4.kt b/idea/testData/inspectionsLocal/redundantUnitExpression/dynamic4.kt new file mode 100644 index 00000000000..54ce7dcafdb --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantUnitExpression/dynamic4.kt @@ -0,0 +1,12 @@ +// ERROR: Unsupported [Dynamic types are not supported in this context] + +fun foo() { + fun bar(c: () -> dynamic, f: () -> T): Unit {} + bar({ + val a = 1 + Unit + }) { + val a = 1 + Unit + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantUnitExpression/dynamic4.kt.after b/idea/testData/inspectionsLocal/redundantUnitExpression/dynamic4.kt.after new file mode 100644 index 00000000000..3c3187faee3 --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantUnitExpression/dynamic4.kt.after @@ -0,0 +1,11 @@ +// ERROR: Unsupported [Dynamic types are not supported in this context] + +fun foo() { + fun bar(c: () -> dynamic, f: () -> T): Unit {} + bar({ + val a = 1 + Unit + }) { + val a = 1 + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantUnitExpression/dynamic5.kt b/idea/testData/inspectionsLocal/redundantUnitExpression/dynamic5.kt new file mode 100644 index 00000000000..d171f627471 --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantUnitExpression/dynamic5.kt @@ -0,0 +1,12 @@ +// ERROR: Unsupported [Dynamic types are not supported in this context] + +fun foo() { + fun bar(c: () -> T, f: () -> dynamic): Unit {} + bar({ + val a = 1 + Unit + }) { + val a = 1 + Unit + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantUnitExpression/dynamic5.kt.after b/idea/testData/inspectionsLocal/redundantUnitExpression/dynamic5.kt.after new file mode 100644 index 00000000000..2511b8fd915 --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantUnitExpression/dynamic5.kt.after @@ -0,0 +1,11 @@ +// ERROR: Unsupported [Dynamic types are not supported in this context] + +fun foo() { + fun bar(c: () -> T, f: () -> dynamic): Unit {} + bar({ + val a = 1 + }) { + val a = 1 + Unit + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantUnitExpression/dynamic6.kt b/idea/testData/inspectionsLocal/redundantUnitExpression/dynamic6.kt new file mode 100644 index 00000000000..a93d276a79a --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantUnitExpression/dynamic6.kt @@ -0,0 +1,10 @@ +// PROBLEM: none +// ERROR: Unsupported [Dynamic types are not supported in this context] + +fun foo() { + fun bar(f: (Int) -> dynamic): Unit {} + bar { + val a = 1 + Unit + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index 5fe9e53264c..7752c4b1cbd 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -9095,6 +9095,36 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { runTest("idea/testData/inspectionsLocal/redundantUnitExpression/atLastAfterVal.kt"); } + @TestMetadata("dynamic.kt") + public void testDynamic() throws Exception { + runTest("idea/testData/inspectionsLocal/redundantUnitExpression/dynamic.kt"); + } + + @TestMetadata("dynamic2.kt") + public void testDynamic2() throws Exception { + runTest("idea/testData/inspectionsLocal/redundantUnitExpression/dynamic2.kt"); + } + + @TestMetadata("dynamic3.kt") + public void testDynamic3() throws Exception { + runTest("idea/testData/inspectionsLocal/redundantUnitExpression/dynamic3.kt"); + } + + @TestMetadata("dynamic4.kt") + public void testDynamic4() throws Exception { + runTest("idea/testData/inspectionsLocal/redundantUnitExpression/dynamic4.kt"); + } + + @TestMetadata("dynamic5.kt") + public void testDynamic5() throws Exception { + runTest("idea/testData/inspectionsLocal/redundantUnitExpression/dynamic5.kt"); + } + + @TestMetadata("dynamic6.kt") + public void testDynamic6() throws Exception { + runTest("idea/testData/inspectionsLocal/redundantUnitExpression/dynamic6.kt"); + } + @TestMetadata("labeledReturnAny.kt") public void testLabeledReturnAny() throws Exception { runTest("idea/testData/inspectionsLocal/redundantUnitExpression/labeledReturnAny.kt");