Introduce "Boolean literal arguments" inspection #KT-2029 Fixed
This commit is contained in:
committed by
Mikhail Glukhikh
parent
aa9e48b9b6
commit
0d7116aa5d
@@ -3164,6 +3164,15 @@
|
||||
language="kotlin"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.BooleanLiteralArgumentInspection"
|
||||
displayName="Boolean literal argument without parameter name"
|
||||
groupPath="Kotlin"
|
||||
groupName="Style issues"
|
||||
enabledByDefault="true"
|
||||
level="INFO"
|
||||
language="kotlin"
|
||||
/>
|
||||
|
||||
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
|
||||
|
||||
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
This inspection reports boolean literal arguments should be named.
|
||||
</body>
|
||||
</html>
|
||||
@@ -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<KtCallExpression>() ?: 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
|
||||
}
|
||||
}
|
||||
@@ -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<KtCallElement>(
|
||||
open class AddNamesToCallArgumentsIntention : SelfTargetingRangeIntention<KtCallElement>(
|
||||
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<KtCallEleme
|
||||
argument.replace(newArgument)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun canAddNamesToCallArguments(element: KtCallElement): Boolean {
|
||||
val arguments = element.valueArguments
|
||||
if (arguments.all { it.isNamed() || it is LambdaArgument }) return false
|
||||
|
||||
val resolvedCall = element.resolveToCall() ?: return false
|
||||
if (!resolvedCall.resultingDescriptor.hasStableParameterNames()) return false
|
||||
|
||||
return arguments.all {
|
||||
AddNameToArgumentIntention.argumentMatchedAndCouldBeNamedInCall(it, resolvedCall, element.languageVersionSettings)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.idea.inspections.BooleanLiteralArgumentInspection
|
||||
@@ -0,0 +1,6 @@
|
||||
// PROBLEM: none
|
||||
fun foo(a: Boolean, b: Boolean, c: Boolean) {}
|
||||
|
||||
fun test(b: Boolean) {
|
||||
foo(true, true, b<caret>)
|
||||
}
|
||||
@@ -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<caret>)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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<caret>, true)
|
||||
}
|
||||
+7
@@ -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)
|
||||
}
|
||||
@@ -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, <caret>true, c = true)
|
||||
}
|
||||
+7
@@ -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)
|
||||
}
|
||||
@@ -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(<caret>true, true, c = true)
|
||||
}
|
||||
+7
@@ -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)
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// PROBLEM: none
|
||||
// DISABLE-ERRORS
|
||||
fun foo(vararg a: Int, b: Boolean) {}
|
||||
|
||||
fun test() {
|
||||
foo(1, 2, 3, true<caret>)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// PROBLEM: none
|
||||
fun foo(a: Boolean, b: Boolean, c: Boolean) {}
|
||||
|
||||
fun test() {
|
||||
foo(true, true, c = true<caret>)
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// PROBLEM: none
|
||||
fun foo(a: Boolean, vararg b: Int) {}
|
||||
|
||||
fun test() {
|
||||
foo(true<caret>, 1, 2, 3)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// PROBLEM: none
|
||||
fun foo(a: Boolean, b: Boolean, c: Int) {}
|
||||
|
||||
fun test(b: Boolean) {
|
||||
foo(true, true, 1<caret>)
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
public class JavaClass {
|
||||
void foo(boolean b) {}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// PROBLEM: none
|
||||
fun test() {
|
||||
JavaClass().foo(false<caret>)
|
||||
}
|
||||
+63
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user