From 88fa7c2952b43a91eaef4eaa6eae3e962b3a7e66 Mon Sep 17 00:00:00 2001 From: Dimach Date: Tue, 25 Jul 2017 13:08:27 +0300 Subject: [PATCH] Introduce "double negation" inspection #KT-4748 Fixed --- .../KotlinDoubleNegation.html | 5 ++ idea/src/META-INF/plugin.xml | 9 +++ .../KotlinDoubleNegationInspection.kt | 69 +++++++++++++++++++ .../doubleNegation/.inspection | 1 + .../doubleNegation/function.kt | 3 + .../doubleNegation/function.kt.after | 3 + .../doubleNegation/invalid.kt | 5 ++ .../doubleNegation/parenthesized.kt | 3 + .../doubleNegation/parenthesized.kt.after | 3 + .../inspectionsLocal/doubleNegation/simple.kt | 3 + .../doubleNegation/simple.kt.after | 3 + .../LocalInspectionTestGenerated.java | 33 +++++++++ 12 files changed, 140 insertions(+) create mode 100644 idea/resources/inspectionDescriptions/KotlinDoubleNegation.html create mode 100644 idea/src/org/jetbrains/kotlin/idea/inspections/KotlinDoubleNegationInspection.kt create mode 100644 idea/testData/inspectionsLocal/doubleNegation/.inspection create mode 100644 idea/testData/inspectionsLocal/doubleNegation/function.kt create mode 100644 idea/testData/inspectionsLocal/doubleNegation/function.kt.after create mode 100644 idea/testData/inspectionsLocal/doubleNegation/invalid.kt create mode 100644 idea/testData/inspectionsLocal/doubleNegation/parenthesized.kt create mode 100644 idea/testData/inspectionsLocal/doubleNegation/parenthesized.kt.after create mode 100644 idea/testData/inspectionsLocal/doubleNegation/simple.kt create mode 100644 idea/testData/inspectionsLocal/doubleNegation/simple.kt.after diff --git a/idea/resources/inspectionDescriptions/KotlinDoubleNegation.html b/idea/resources/inspectionDescriptions/KotlinDoubleNegation.html new file mode 100644 index 00000000000..a55c51a630a --- /dev/null +++ b/idea/resources/inspectionDescriptions/KotlinDoubleNegation.html @@ -0,0 +1,5 @@ + + +This inspection reports redundant double negation usages, like val truth = !!true + + diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 54279a74a93..9654a838a88 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -2428,6 +2428,15 @@ language="kotlin" /> + + diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinDoubleNegationInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinDoubleNegationInspection.kt new file mode 100644 index 00000000000..31044cd2ab9 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinDoubleNegationInspection.kt @@ -0,0 +1,69 @@ +/* + * Copyright 2010-2017 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 org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.core.replaced +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.KtParenthesizedExpression +import org.jetbrains.kotlin.psi.KtPrefixExpression +import org.jetbrains.kotlin.psi.KtVisitorVoid +import org.jetbrains.kotlin.resolve.calls.callUtil.getType +import org.jetbrains.kotlin.types.typeUtil.isBoolean + +class KotlinDoubleNegationInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool { + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) = + object : KtVisitorVoid() { + override fun visitPrefixExpression(expression: KtPrefixExpression) { + if (expression.operationToken != KtTokens.EXCL || + expression.baseExpression?.getType(expression.analyze())?.isBoolean() != true) { + return + } + var parent = expression.parent + while (parent is KtParenthesizedExpression) { + parent = parent.parent + } + if (parent is KtPrefixExpression && parent.operationToken == KtTokens.EXCL) { + holder.registerProblem(expression, + "Redundant double negation", + ProblemHighlightType.GENERIC_ERROR_OR_WARNING, + DoubleNegationFix()) + } + } + } + + private class DoubleNegationFix : LocalQuickFix { + override fun getName() = "Remove redundant negations" + override fun getFamilyName() = name + + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + applyFix(descriptor.psiElement as? KtPrefixExpression ?: return) + } + + private fun applyFix(expression: KtPrefixExpression) { + var parent = expression.parent + while (parent is KtParenthesizedExpression) { + parent = parent.parent + } + if (parent is KtPrefixExpression && parent.operationToken == KtTokens.EXCL) { + expression.baseExpression?.let { parent.replaced(it) } + } + } + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/doubleNegation/.inspection b/idea/testData/inspectionsLocal/doubleNegation/.inspection new file mode 100644 index 00000000000..74167207ab7 --- /dev/null +++ b/idea/testData/inspectionsLocal/doubleNegation/.inspection @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.inspections.KotlinDoubleNegationInspection \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/doubleNegation/function.kt b/idea/testData/inspectionsLocal/doubleNegation/function.kt new file mode 100644 index 00000000000..60c2dbfedb7 --- /dev/null +++ b/idea/testData/inspectionsLocal/doubleNegation/function.kt @@ -0,0 +1,3 @@ +fun foo() { + val b = !!"".equals("") +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/doubleNegation/function.kt.after b/idea/testData/inspectionsLocal/doubleNegation/function.kt.after new file mode 100644 index 00000000000..30e5aae6f0a --- /dev/null +++ b/idea/testData/inspectionsLocal/doubleNegation/function.kt.after @@ -0,0 +1,3 @@ +fun foo() { + val b = "".equals("") +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/doubleNegation/invalid.kt b/idea/testData/inspectionsLocal/doubleNegation/invalid.kt new file mode 100644 index 00000000000..4288a338686 --- /dev/null +++ b/idea/testData/inspectionsLocal/doubleNegation/invalid.kt @@ -0,0 +1,5 @@ +// PROBLEM: none +operator fun Int.not() = this * -1 +fun foo() { + val c = !!1 +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/doubleNegation/parenthesized.kt b/idea/testData/inspectionsLocal/doubleNegation/parenthesized.kt new file mode 100644 index 00000000000..9ec4228cadc --- /dev/null +++ b/idea/testData/inspectionsLocal/doubleNegation/parenthesized.kt @@ -0,0 +1,3 @@ +fun foo() { + val b = !(!true) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/doubleNegation/parenthesized.kt.after b/idea/testData/inspectionsLocal/doubleNegation/parenthesized.kt.after new file mode 100644 index 00000000000..d07007d3673 --- /dev/null +++ b/idea/testData/inspectionsLocal/doubleNegation/parenthesized.kt.after @@ -0,0 +1,3 @@ +fun foo() { + val b = true +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/doubleNegation/simple.kt b/idea/testData/inspectionsLocal/doubleNegation/simple.kt new file mode 100644 index 00000000000..f926363f3f9 --- /dev/null +++ b/idea/testData/inspectionsLocal/doubleNegation/simple.kt @@ -0,0 +1,3 @@ +fun foo() { + val b = !!true +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/doubleNegation/simple.kt.after b/idea/testData/inspectionsLocal/doubleNegation/simple.kt.after new file mode 100644 index 00000000000..d07007d3673 --- /dev/null +++ b/idea/testData/inspectionsLocal/doubleNegation/simple.kt.after @@ -0,0 +1,3 @@ +fun foo() { + val b = true +} \ 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 8f3f4c0626d..ffe41c59c79 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -492,6 +492,39 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { } } + @TestMetadata("idea/testData/inspectionsLocal/doubleNegation") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class DoubleNegation extends AbstractLocalInspectionTest { + public void testAllFilesPresentInDoubleNegation() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/doubleNegation"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("function.kt") + public void testFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/doubleNegation/function.kt"); + doTest(fileName); + } + + @TestMetadata("invalid.kt") + public void testInvalid() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/doubleNegation/invalid.kt"); + doTest(fileName); + } + + @TestMetadata("parenthesized.kt") + public void testParenthesized() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/doubleNegation/parenthesized.kt"); + doTest(fileName); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/doubleNegation/simple.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/inspectionsLocal/emptyRange") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)