From 0d7116aa5db8a2437afdc112a6261b6297d4327f Mon Sep 17 00:00:00 2001 From: Toshiaki Kameyama Date: Wed, 24 Oct 2018 11:58:59 +0300 Subject: [PATCH] Introduce "Boolean literal arguments" inspection #KT-2029 Fixed --- idea/resources/META-INF/plugin-common.xml | 9 +++ .../BooleanLiteralArgument.html | 5 ++ .../BooleanLiteralArgumentInspection.kt | 57 +++++++++++++++++ .../AddNamesToCallArgumentsIntention.kt | 35 ++++++----- .../booleanLiteralArgument/.inspection | 1 + .../booleanLiteralArgument/boolean.kt | 6 ++ .../booleanLiteralArgument/booleanLiteral.kt | 7 +++ .../booleanLiteral.kt.after | 7 +++ .../booleanLiteralArgument/booleanLiteral2.kt | 7 +++ .../booleanLiteral2.kt.after | 7 +++ .../booleanLiteralArgument/booleanLiteral3.kt | 7 +++ .../booleanLiteral3.kt.after | 7 +++ .../booleanLiteralArgument/booleanLiteral4.kt | 7 +++ .../booleanLiteral4.kt.after | 7 +++ .../booleanLiteralArgument/hasError.kt | 7 +++ .../booleanLiteralArgument/hasName.kt | 6 ++ .../booleanLiteralArgument/hasVararg.kt | 7 +++ .../booleanLiteralArgument/intLiteral.kt | 6 ++ .../booleanLiteralArgument/javaMethod.1.java | 3 + .../booleanLiteralArgument/javaMethod.kt | 4 ++ .../LocalInspectionTestGenerated.java | 63 +++++++++++++++++++ 21 files changed, 248 insertions(+), 17 deletions(-) create mode 100644 idea/resources/inspectionDescriptions/BooleanLiteralArgument.html create mode 100644 idea/src/org/jetbrains/kotlin/idea/inspections/BooleanLiteralArgumentInspection.kt create mode 100644 idea/testData/inspectionsLocal/booleanLiteralArgument/.inspection create mode 100644 idea/testData/inspectionsLocal/booleanLiteralArgument/boolean.kt create mode 100644 idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral.kt create mode 100644 idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral.kt.after create mode 100644 idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral2.kt create mode 100644 idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral2.kt.after create mode 100644 idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral3.kt create mode 100644 idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral3.kt.after create mode 100644 idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral4.kt create mode 100644 idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral4.kt.after create mode 100644 idea/testData/inspectionsLocal/booleanLiteralArgument/hasError.kt create mode 100644 idea/testData/inspectionsLocal/booleanLiteralArgument/hasName.kt create mode 100644 idea/testData/inspectionsLocal/booleanLiteralArgument/hasVararg.kt create mode 100644 idea/testData/inspectionsLocal/booleanLiteralArgument/intLiteral.kt create mode 100644 idea/testData/inspectionsLocal/booleanLiteralArgument/javaMethod.1.java create mode 100644 idea/testData/inspectionsLocal/booleanLiteralArgument/javaMethod.kt diff --git a/idea/resources/META-INF/plugin-common.xml b/idea/resources/META-INF/plugin-common.xml index 503b5dbda6c..6a05a337bab 100644 --- a/idea/resources/META-INF/plugin-common.xml +++ b/idea/resources/META-INF/plugin-common.xml @@ -3164,6 +3164,15 @@ language="kotlin" /> + + diff --git a/idea/resources/inspectionDescriptions/BooleanLiteralArgument.html b/idea/resources/inspectionDescriptions/BooleanLiteralArgument.html new file mode 100644 index 00000000000..67fe55548ce --- /dev/null +++ b/idea/resources/inspectionDescriptions/BooleanLiteralArgument.html @@ -0,0 +1,5 @@ + + +This inspection reports boolean literal arguments should be named. + + diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/BooleanLiteralArgumentInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/BooleanLiteralArgumentInspection.kt new file mode 100644 index 00000000000..b526d1b97f7 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/BooleanLiteralArgumentInspection.kt @@ -0,0 +1,57 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.inspections + +import com.intellij.codeInspection.IntentionWrapper +import com.intellij.codeInspection.ProblemHighlightType.GENERIC_ERROR_OR_WARNING +import com.intellij.codeInspection.ProblemsHolder +import com.intellij.openapi.util.TextRange +import org.jetbrains.kotlin.KtNodeTypes +import org.jetbrains.kotlin.diagnostics.Severity +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall +import org.jetbrains.kotlin.idea.intentions.AddNameToArgumentIntention +import org.jetbrains.kotlin.idea.intentions.AddNamesToCallArgumentsIntention +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType +import org.jetbrains.kotlin.psi.psiUtil.startOffset + +class BooleanLiteralArgumentInspection : AbstractKotlinInspection() { + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = + valueArgumentVisitor(fun(argument: KtValueArgument) { + if (argument.getArgumentName() != null) return + val argumentExpression = argument.getArgumentExpression() as? KtConstantExpression ?: return + if (argumentExpression.node.elementType != KtNodeTypes.BOOLEAN_CONSTANT) return + if (argumentExpression.analyze().diagnostics.forElement(argumentExpression).any { it.severity == Severity.ERROR }) return + val call = argument.getStrictParentOfType() ?: return + if (call.resolveToCall()?.resultingDescriptor?.hasStableParameterNames() != true) return + + when { + call.valueArguments.takeLastWhile { it != argument }.none { !it.isNamed() } -> + holder.registerProblem( + argument, + "Boolean literal argument without parameter name", + GENERIC_ERROR_OR_WARNING, + IntentionWrapper(AddNameToArgumentIntention(), argument.containingKtFile) + ) + AddNamesToCallArgumentsIntention.canAddNamesToCallArguments(call) -> + holder.registerProblem( + holder.manager.createProblemDescriptor( + call, + argument.textRange.shiftRight(-call.startOffset), + description, + highlightType, + isOnTheFly, + IntentionWrapper(AddNamesIntention(), file) + ) + ) + } + }) + + private class AddNamesIntention : AddNamesToCallArgumentsIntention() { + override fun applicabilityRange(element: KtCallElement): TextRange? = element.textRange + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/AddNamesToCallArgumentsIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/AddNamesToCallArgumentsIntention.kt index 828a516ae89..cb700a06e19 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/AddNamesToCallArgumentsIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/AddNamesToCallArgumentsIntention.kt @@ -23,26 +23,12 @@ import org.jetbrains.kotlin.idea.project.languageVersionSettings import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch -class AddNamesToCallArgumentsIntention : SelfTargetingRangeIntention( +open class AddNamesToCallArgumentsIntention : SelfTargetingRangeIntention( KtCallElement::class.java, "Add names to call arguments" ) { - override fun applicabilityRange(element: KtCallElement): TextRange? { - val arguments = element.valueArguments - if (arguments.all { it.isNamed() || it is LambdaArgument }) return null - - val resolvedCall = element.resolveToCall() ?: return null - if (!resolvedCall.resultingDescriptor.hasStableParameterNames()) return null - - if (arguments.all { - AddNameToArgumentIntention.argumentMatchedAndCouldBeNamedInCall(it, resolvedCall, element.languageVersionSettings) - } - ) { - return element.calleeExpression?.textRange - } - - return null - } + override fun applicabilityRange(element: KtCallElement): TextRange? = + element.calleeExpression?.textRange?.takeIf { canAddNamesToCallArguments(element) } override fun applyTo(element: KtCallElement, editor: Editor?) { val arguments = element.valueArguments @@ -59,4 +45,19 @@ class AddNamesToCallArgumentsIntention : SelfTargetingRangeIntention) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral.kt b/idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral.kt new file mode 100644 index 00000000000..d1e3dfc7cdb --- /dev/null +++ b/idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral.kt @@ -0,0 +1,7 @@ +// HIGHLIGHT: GENERIC_ERROR_OR_WARNING +// FIX: Add 'c =' to argument +fun foo(a: Boolean, b: Boolean, c: Boolean) {} + +fun test() { + foo(true, true, true) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral.kt.after b/idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral.kt.after new file mode 100644 index 00000000000..6485f9ac346 --- /dev/null +++ b/idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral.kt.after @@ -0,0 +1,7 @@ +// HIGHLIGHT: GENERIC_ERROR_OR_WARNING +// FIX: Add 'c =' to argument +fun foo(a: Boolean, b: Boolean, c: Boolean) {} + +fun test() { + foo(true, true, c = true) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral2.kt b/idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral2.kt new file mode 100644 index 00000000000..ce6191b4ddf --- /dev/null +++ b/idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral2.kt @@ -0,0 +1,7 @@ +// HIGHLIGHT: GENERIC_ERROR_OR_WARNING +// FIX: Add names to call arguments +fun foo(a: Boolean, b: Boolean, c: Boolean) {} + +fun test() { + foo(true, true, true) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral2.kt.after b/idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral2.kt.after new file mode 100644 index 00000000000..99251ab678b --- /dev/null +++ b/idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral2.kt.after @@ -0,0 +1,7 @@ +// HIGHLIGHT: GENERIC_ERROR_OR_WARNING +// FIX: Add names to call arguments +fun foo(a: Boolean, b: Boolean, c: Boolean) {} + +fun test() { + foo(a = true, b = true, c = true) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral3.kt b/idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral3.kt new file mode 100644 index 00000000000..e22e79a68c4 --- /dev/null +++ b/idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral3.kt @@ -0,0 +1,7 @@ +// HIGHLIGHT: GENERIC_ERROR_OR_WARNING +// FIX: Add 'b =' to argument +fun foo(a: Boolean, b: Boolean, c: Boolean) {} + +fun test() { + foo(true, true, c = true) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral3.kt.after b/idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral3.kt.after new file mode 100644 index 00000000000..551e42c5a90 --- /dev/null +++ b/idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral3.kt.after @@ -0,0 +1,7 @@ +// HIGHLIGHT: GENERIC_ERROR_OR_WARNING +// FIX: Add 'b =' to argument +fun foo(a: Boolean, b: Boolean, c: Boolean) {} + +fun test() { + foo(true, b = true, c = true) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral4.kt b/idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral4.kt new file mode 100644 index 00000000000..bc9033ae4a2 --- /dev/null +++ b/idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral4.kt @@ -0,0 +1,7 @@ +// HIGHLIGHT: GENERIC_ERROR_OR_WARNING +// FIX: Add names to call arguments +fun foo(a: Boolean, b: Boolean, c: Boolean) {} + +fun test() { + foo(true, true, c = true) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral4.kt.after b/idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral4.kt.after new file mode 100644 index 00000000000..99251ab678b --- /dev/null +++ b/idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral4.kt.after @@ -0,0 +1,7 @@ +// HIGHLIGHT: GENERIC_ERROR_OR_WARNING +// FIX: Add names to call arguments +fun foo(a: Boolean, b: Boolean, c: Boolean) {} + +fun test() { + foo(a = true, b = true, c = true) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/booleanLiteralArgument/hasError.kt b/idea/testData/inspectionsLocal/booleanLiteralArgument/hasError.kt new file mode 100644 index 00000000000..bc298b5b5f0 --- /dev/null +++ b/idea/testData/inspectionsLocal/booleanLiteralArgument/hasError.kt @@ -0,0 +1,7 @@ +// PROBLEM: none +// DISABLE-ERRORS +fun foo(vararg a: Int, b: Boolean) {} + +fun test() { + foo(1, 2, 3, true) +} diff --git a/idea/testData/inspectionsLocal/booleanLiteralArgument/hasName.kt b/idea/testData/inspectionsLocal/booleanLiteralArgument/hasName.kt new file mode 100644 index 00000000000..16c030d92f5 --- /dev/null +++ b/idea/testData/inspectionsLocal/booleanLiteralArgument/hasName.kt @@ -0,0 +1,6 @@ +// PROBLEM: none +fun foo(a: Boolean, b: Boolean, c: Boolean) {} + +fun test() { + foo(true, true, c = true) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/booleanLiteralArgument/hasVararg.kt b/idea/testData/inspectionsLocal/booleanLiteralArgument/hasVararg.kt new file mode 100644 index 00000000000..08eb6009ae1 --- /dev/null +++ b/idea/testData/inspectionsLocal/booleanLiteralArgument/hasVararg.kt @@ -0,0 +1,7 @@ +// PROBLEM: none +fun foo(a: Boolean, vararg b: Int) {} + +fun test() { + foo(true, 1, 2, 3) +} + diff --git a/idea/testData/inspectionsLocal/booleanLiteralArgument/intLiteral.kt b/idea/testData/inspectionsLocal/booleanLiteralArgument/intLiteral.kt new file mode 100644 index 00000000000..2da8505ade0 --- /dev/null +++ b/idea/testData/inspectionsLocal/booleanLiteralArgument/intLiteral.kt @@ -0,0 +1,6 @@ +// PROBLEM: none +fun foo(a: Boolean, b: Boolean, c: Int) {} + +fun test(b: Boolean) { + foo(true, true, 1) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/booleanLiteralArgument/javaMethod.1.java b/idea/testData/inspectionsLocal/booleanLiteralArgument/javaMethod.1.java new file mode 100644 index 00000000000..639262590c8 --- /dev/null +++ b/idea/testData/inspectionsLocal/booleanLiteralArgument/javaMethod.1.java @@ -0,0 +1,3 @@ +public class JavaClass { + void foo(boolean b) {} +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/booleanLiteralArgument/javaMethod.kt b/idea/testData/inspectionsLocal/booleanLiteralArgument/javaMethod.kt new file mode 100644 index 00000000000..355f5529c21 --- /dev/null +++ b/idea/testData/inspectionsLocal/booleanLiteralArgument/javaMethod.kt @@ -0,0 +1,4 @@ +// PROBLEM: none +fun test() { + JavaClass().foo(false) +} \ 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 c4c6ed0b55c..159d21963a7 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -47,6 +47,69 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { } } + @TestMetadata("idea/testData/inspectionsLocal/booleanLiteralArgument") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class BooleanLiteralArgument extends AbstractLocalInspectionTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInBooleanLiteralArgument() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/booleanLiteralArgument"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true); + } + + @TestMetadata("boolean.kt") + public void testBoolean() throws Exception { + runTest("idea/testData/inspectionsLocal/booleanLiteralArgument/boolean.kt"); + } + + @TestMetadata("booleanLiteral.kt") + public void testBooleanLiteral() throws Exception { + runTest("idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral.kt"); + } + + @TestMetadata("booleanLiteral2.kt") + public void testBooleanLiteral2() throws Exception { + runTest("idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral2.kt"); + } + + @TestMetadata("booleanLiteral3.kt") + public void testBooleanLiteral3() throws Exception { + runTest("idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral3.kt"); + } + + @TestMetadata("booleanLiteral4.kt") + public void testBooleanLiteral4() throws Exception { + runTest("idea/testData/inspectionsLocal/booleanLiteralArgument/booleanLiteral4.kt"); + } + + @TestMetadata("hasError.kt") + public void testHasError() throws Exception { + runTest("idea/testData/inspectionsLocal/booleanLiteralArgument/hasError.kt"); + } + + @TestMetadata("hasName.kt") + public void testHasName() throws Exception { + runTest("idea/testData/inspectionsLocal/booleanLiteralArgument/hasName.kt"); + } + + @TestMetadata("hasVararg.kt") + public void testHasVararg() throws Exception { + runTest("idea/testData/inspectionsLocal/booleanLiteralArgument/hasVararg.kt"); + } + + @TestMetadata("intLiteral.kt") + public void testIntLiteral() throws Exception { + runTest("idea/testData/inspectionsLocal/booleanLiteralArgument/intLiteral.kt"); + } + + @TestMetadata("javaMethod.kt") + public void testJavaMethod() throws Exception { + runTest("idea/testData/inspectionsLocal/booleanLiteralArgument/javaMethod.kt"); + } + } + @TestMetadata("idea/testData/inspectionsLocal/branched") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)