diff --git a/idea/resources/inspectionDescriptions/WrapUnaryOperator.html b/idea/resources/inspectionDescriptions/WrapUnaryOperator.html
new file mode 100644
index 00000000000..e7ee5a0a05b
--- /dev/null
+++ b/idea/resources/inspectionDescriptions/WrapUnaryOperator.html
@@ -0,0 +1,5 @@
+
+
+This inspection reports an unary operator followed by a dot qualifier (such as -1.inc()) that is potentially wrong.
+
+
diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml
index ee7032fc6de..1d32b6bf800 100644
--- a/idea/src/META-INF/plugin.xml
+++ b/idea/src/META-INF/plugin.xml
@@ -2214,6 +2214,14 @@
language="kotlin"
/>
+
+
diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/WrapUnaryOperatorInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/WrapUnaryOperatorInspection.kt
new file mode 100644
index 00000000000..bfb31679a06
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/inspections/WrapUnaryOperatorInspection.kt
@@ -0,0 +1,71 @@
+/*
+ * 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.LocalQuickFix
+import com.intellij.codeInspection.ProblemDescriptor
+import com.intellij.codeInspection.ProblemHighlightType
+import com.intellij.codeInspection.ProblemsHolder
+import com.intellij.openapi.project.Project
+import com.intellij.psi.PsiElementVisitor
+import com.intellij.psi.tree.IElementType
+import org.jetbrains.kotlin.KtNodeTypes
+import org.jetbrains.kotlin.idea.intentions.getLeftMostReceiverExpression
+import org.jetbrains.kotlin.idea.intentions.replaceFirstReceiver
+import org.jetbrains.kotlin.lexer.KtTokens
+import org.jetbrains.kotlin.psi.*
+
+class WrapUnaryOperatorInspection : AbstractKotlinInspection() {
+
+ val numberTypes = listOf(KtNodeTypes.INTEGER_CONSTANT, KtNodeTypes.FLOAT_CONSTANT)
+
+ override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
+ return object : KtVisitorVoid() {
+ override fun visitPrefixExpression(expression: KtPrefixExpression) {
+ super.visitPrefixExpression(expression)
+ if (expression.operationToken.isUnaryMinusOrPlus()) {
+ val baseExpression = expression.baseExpression
+ if (baseExpression is KtDotQualifiedExpression) {
+ val receiverExpression = baseExpression.receiverExpression
+ if (receiverExpression is KtConstantExpression &&
+ receiverExpression.node.elementType in numberTypes) {
+ holder.registerProblem(expression,
+ "Wrap unary operator and value with ()",
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
+ WrapUnaryOperatorQuickfix())
+ }
+ }
+ }
+ }
+
+ private fun IElementType.isUnaryMinusOrPlus() = this == KtTokens.MINUS || this == KtTokens.PLUS
+ }
+ }
+
+ private class WrapUnaryOperatorQuickfix : LocalQuickFix {
+ override fun getFamilyName() = "Wrap unary operator and value with ()"
+
+ override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
+ val expression = descriptor.psiElement as? KtPrefixExpression ?: return
+ val dotQualifiedExpression = expression.baseExpression as? KtDotQualifiedExpression ?: return
+ val factory = KtPsiFactory(project)
+ val newReceiver = factory.createExpressionByPattern("($0$1)", expression.operationReference.text, dotQualifiedExpression.getLeftMostReceiverExpression())
+ val newExpression = dotQualifiedExpression.replaceFirstReceiver(factory, newReceiver)
+ expression.replace(newExpression)
+ }
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/inspections/wrapUnaryOperator/inspectionData/expected.xml b/idea/testData/inspections/wrapUnaryOperator/inspectionData/expected.xml
new file mode 100644
index 00000000000..a98c36d672e
--- /dev/null
+++ b/idea/testData/inspections/wrapUnaryOperator/inspectionData/expected.xml
@@ -0,0 +1,58 @@
+
+
+ simple.kt
+ 6
+ light_idea_test_case
+
+ Ambiguous unary operator use with number constant
+ Wrap unary operator and value with ()
+
+
+ simple.kt
+ 9
+ light_idea_test_case
+
+ Ambiguous unary operator use with number constant
+ Wrap unary operator and value with ()
+
+
+ simple.kt
+ 10
+ light_idea_test_case
+
+ Ambiguous unary operator use with number constant
+ Wrap unary operator and value with ()
+
+
+ simple.kt
+ 12
+ light_idea_test_case
+
+ Ambiguous unary operator use with number constant
+ Wrap unary operator and value with ()
+
+
+ simple.kt
+ 14
+ light_idea_test_case
+
+ Ambiguous unary operator use with number constant
+ Wrap unary operator and value with ()
+
+
+ simple.kt
+ 22
+ light_idea_test_case
+
+ Ambiguous unary operator use with number constant
+ Wrap unary operator and value with ()
+
+
+ simple.kt
+ 23
+ light_idea_test_case
+
+ Ambiguous unary operator use with number constant
+ Wrap unary operator and value with ()
+
+
\ No newline at end of file
diff --git a/idea/testData/inspections/wrapUnaryOperator/inspectionData/inspections.test b/idea/testData/inspections/wrapUnaryOperator/inspectionData/inspections.test
new file mode 100644
index 00000000000..6deaa9e0d9c
--- /dev/null
+++ b/idea/testData/inspections/wrapUnaryOperator/inspectionData/inspections.test
@@ -0,0 +1 @@
+// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.WrapUnaryOperatorInspection
diff --git a/idea/testData/inspections/wrapUnaryOperator/simple.kt b/idea/testData/inspections/wrapUnaryOperator/simple.kt
new file mode 100644
index 00000000000..53739be6fab
--- /dev/null
+++ b/idea/testData/inspections/wrapUnaryOperator/simple.kt
@@ -0,0 +1,27 @@
+object Constants {
+ val ONE = 1
+}
+
+fun foo() {
+ println(-1.inc())
+ val i = 100
+ println(-i.inc())
+ println(-1.1.dec())
+ println(-1.1f.dec())
+ println(1 - -1.dec().dec())
+ println(1 - -1.javaClass)
+ println(1 - - 1.dec().dec())
+ println(1 - - 1.javaClass)
+ println(1-1.inc()) // NG
+ println(1 - 1.dec().dec()) // NG
+ println(1 - 1.javaClass) // NG
+ println(-1) //NG
+ println(!true) // NG
+ println(!true.not()) // NG
+ println(+1) // NG
+ println(+1.inc())
+ println(+3.14.inc())
+
+ println(-Constants.ONE) // NG
+ println(-"2".toInt()) // NG
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/wrapUnaryOperator/.inspection b/idea/testData/inspectionsLocal/wrapUnaryOperator/.inspection
new file mode 100644
index 00000000000..c92bb50f7e7
--- /dev/null
+++ b/idea/testData/inspectionsLocal/wrapUnaryOperator/.inspection
@@ -0,0 +1 @@
+org.jetbrains.kotlin.idea.inspections.WrapUnaryOperatorInspection
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/wrapUnaryOperator/simple.kt b/idea/testData/inspectionsLocal/wrapUnaryOperator/simple.kt
new file mode 100644
index 00000000000..cb52d0e2527
--- /dev/null
+++ b/idea/testData/inspectionsLocal/wrapUnaryOperator/simple.kt
@@ -0,0 +1,3 @@
+fun Int.inc() = this + 1
+
+val x = -1.inc()
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/wrapUnaryOperator/simple.kt.after b/idea/testData/inspectionsLocal/wrapUnaryOperator/simple.kt.after
new file mode 100644
index 00000000000..ccc62ce789c
--- /dev/null
+++ b/idea/testData/inspectionsLocal/wrapUnaryOperator/simple.kt.after
@@ -0,0 +1,3 @@
+fun Int.inc() = this + 1
+
+val x = (-1).inc()
\ No newline at end of file
diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java
index 1b071259d52..10d31b153c6 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java
@@ -400,5 +400,11 @@ public class InspectionTestGenerated extends AbstractInspectionTest {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/unusedSymbol/typeParameter/inspectionData/inspections.test");
doTest(fileName);
}
+
+ @TestMetadata("wrapUnaryOperator/inspectionData/inspections.test")
+ public void testWrapUnaryOperator_inspectionData_Inspections_test() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/wrapUnaryOperator/inspectionData/inspections.test");
+ doTest(fileName);
+ }
}
}
diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java
index 067700e7d90..d07c8802a28 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java
@@ -320,4 +320,19 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
doTest(fileName);
}
}
+
+ @TestMetadata("idea/testData/inspectionsLocal/wrapUnaryOperator")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class WrapUnaryOperator extends AbstractLocalInspectionTest {
+ public void testAllFilesPresentInWrapUnaryOperator() throws Exception {
+ KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/wrapUnaryOperator"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
+ }
+
+ @TestMetadata("simple.kt")
+ public void testSimple() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/wrapUnaryOperator/simple.kt");
+ doTest(fileName);
+ }
+ }
}