diff --git a/idea/resources/inspectionDescriptions/UnnecessaryVariable.html b/idea/resources/inspectionDescriptions/UnnecessaryVariable.html
new file mode 100644
index 00000000000..94431e516e5
--- /dev/null
+++ b/idea/resources/inspectionDescriptions/UnnecessaryVariable.html
@@ -0,0 +1,6 @@
+
+
+This inspection reports local variables either used only in the very next return or exact copies of other variables.
+In both cases it's better to inline such a variable.
+
+
diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml
index eb9497c2a40..d91d91dc084 100644
--- a/idea/src/META-INF/plugin.xml
+++ b/idea/src/META-INF/plugin.xml
@@ -2400,6 +2400,15 @@
language="kotlin"
/>
+
+
diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/UnnecessaryVariableInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/UnnecessaryVariableInspection.kt
new file mode 100644
index 00000000000..03390562c28
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/inspections/UnnecessaryVariableInspection.kt
@@ -0,0 +1,95 @@
+/*
+ * 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 org.jetbrains.kotlin.descriptors.FunctionDescriptor
+import org.jetbrains.kotlin.descriptors.VariableDescriptor
+import org.jetbrains.kotlin.idea.caches.resolve.analyze
+import org.jetbrains.kotlin.idea.refactoring.inline.KotlinInlineValHandler
+import org.jetbrains.kotlin.psi.KtNameReferenceExpression
+import org.jetbrains.kotlin.psi.KtProperty
+import org.jetbrains.kotlin.psi.KtReturnExpression
+import org.jetbrains.kotlin.psi.KtVisitorVoid
+import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespaceAndComments
+import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
+import org.jetbrains.kotlin.resolve.BindingContext.DECLARATION_TO_DESCRIPTOR
+import org.jetbrains.kotlin.resolve.BindingContext.REFERENCE_TARGET
+
+class UnnecessaryVariableInspection : AbstractKotlinInspection() {
+
+ override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor =
+ object : KtVisitorVoid() {
+ override fun visitProperty(property: KtProperty) {
+ super.visitProperty(property)
+
+ if (!property.isLocal) return
+ val initializer = property.initializer ?: return
+ val nameIdentifier = property.nameIdentifier ?: return
+
+ if (!property.isVar && initializer is KtNameReferenceExpression && property.typeReference == null) {
+ val context = property.analyze()
+ val initializerDescriptor = context[REFERENCE_TARGET, initializer]
+ if (initializerDescriptor is VariableDescriptor) {
+ if (!initializerDescriptor.isVar &&
+ initializerDescriptor.containingDeclaration is FunctionDescriptor) {
+ holder.registerProblem(
+ nameIdentifier,
+ "Variable is an exact copy of another variable and can be inlined",
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
+ InlineVariableFix()
+ )
+ return
+ }
+ }
+ }
+
+ val nextStatement = property.getNextSiblingIgnoringWhitespaceAndComments()
+ if (nextStatement is KtReturnExpression) {
+ val returned = nextStatement.returnedExpression
+ if (returned is KtNameReferenceExpression) {
+ val context = nextStatement.analyze()
+ if (context[REFERENCE_TARGET, returned] == context[DECLARATION_TO_DESCRIPTOR, property]) {
+ holder.registerProblem(
+ nameIdentifier,
+ "Variable used only in following return and can be inlined",
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
+ InlineVariableFix()
+ )
+ }
+ }
+ }
+ }
+ }
+
+ class InlineVariableFix : LocalQuickFix {
+
+ override fun getName() = "Inline variable"
+
+ override fun getFamilyName() = name
+
+ override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
+ val property = descriptor.psiElement.getParentOfType(strict = true) ?: return
+ KotlinInlineValHandler().inlineElement(project, property.findExistingEditor(), property)
+ }
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/unnecessaryVariable/.inspection b/idea/testData/inspectionsLocal/unnecessaryVariable/.inspection
new file mode 100644
index 00000000000..d93cf0244e1
--- /dev/null
+++ b/idea/testData/inspectionsLocal/unnecessaryVariable/.inspection
@@ -0,0 +1 @@
+org.jetbrains.kotlin.idea.inspections.UnnecessaryVariableInspection
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/unnecessaryVariable/copyOfVal.kt b/idea/testData/inspectionsLocal/unnecessaryVariable/copyOfVal.kt
new file mode 100644
index 00000000000..cc5c1c6d391
--- /dev/null
+++ b/idea/testData/inspectionsLocal/unnecessaryVariable/copyOfVal.kt
@@ -0,0 +1,5 @@
+fun test(): Int {
+ val x = 1
+ val y = x
+ return x + y
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/unnecessaryVariable/copyOfVal.kt.after b/idea/testData/inspectionsLocal/unnecessaryVariable/copyOfVal.kt.after
new file mode 100644
index 00000000000..40073d8fffd
--- /dev/null
+++ b/idea/testData/inspectionsLocal/unnecessaryVariable/copyOfVal.kt.after
@@ -0,0 +1,4 @@
+fun test(): Int {
+ val x = 1
+ return x + x
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/unnecessaryVariable/copyOfValWithExplicitType.kt b/idea/testData/inspectionsLocal/unnecessaryVariable/copyOfValWithExplicitType.kt
new file mode 100644
index 00000000000..e6309caa174
--- /dev/null
+++ b/idea/testData/inspectionsLocal/unnecessaryVariable/copyOfValWithExplicitType.kt
@@ -0,0 +1,8 @@
+// PROBLEM: none
+
+fun test(): Int {
+ val x = 1
+ // With explicitly given type looks dangerous
+ val y: Int = x
+ return x + y
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/unnecessaryVariable/copyOfVar.kt b/idea/testData/inspectionsLocal/unnecessaryVariable/copyOfVar.kt
new file mode 100644
index 00000000000..a18eb4ee8f7
--- /dev/null
+++ b/idea/testData/inspectionsLocal/unnecessaryVariable/copyOfVar.kt
@@ -0,0 +1,8 @@
+// PROBLEM: none
+
+fun test(): Int {
+ var x = 1
+ val y = x
+ x++
+ return x + y
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/unnecessaryVariable/paramCopy.kt b/idea/testData/inspectionsLocal/unnecessaryVariable/paramCopy.kt
new file mode 100644
index 00000000000..8caec8e0903
--- /dev/null
+++ b/idea/testData/inspectionsLocal/unnecessaryVariable/paramCopy.kt
@@ -0,0 +1,4 @@
+fun sqr(arg: Int): Int {
+ val other = arg
+ return other * other
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/unnecessaryVariable/paramCopy.kt.after b/idea/testData/inspectionsLocal/unnecessaryVariable/paramCopy.kt.after
new file mode 100644
index 00000000000..f28309dca00
--- /dev/null
+++ b/idea/testData/inspectionsLocal/unnecessaryVariable/paramCopy.kt.after
@@ -0,0 +1,3 @@
+fun sqr(arg: Int): Int {
+ return arg * arg
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/unnecessaryVariable/propertyCopy.kt b/idea/testData/inspectionsLocal/unnecessaryVariable/propertyCopy.kt
new file mode 100644
index 00000000000..744a3dcfbcf
--- /dev/null
+++ b/idea/testData/inspectionsLocal/unnecessaryVariable/propertyCopy.kt
@@ -0,0 +1,9 @@
+// PROBLEM: none
+
+class My(val x: Number)
+
+fun My.foo(): Int {
+ val y = x
+ if (y is Int) return y
+ return 0
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/unnecessaryVariable/simpleReturn.kt b/idea/testData/inspectionsLocal/unnecessaryVariable/simpleReturn.kt
new file mode 100644
index 00000000000..f8f2b8e8580
--- /dev/null
+++ b/idea/testData/inspectionsLocal/unnecessaryVariable/simpleReturn.kt
@@ -0,0 +1,4 @@
+fun sum(a: Int, b: Int): Int {
+ val c = a + b
+ return c
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/unnecessaryVariable/simpleReturn.kt.after b/idea/testData/inspectionsLocal/unnecessaryVariable/simpleReturn.kt.after
new file mode 100644
index 00000000000..1516f76e075
--- /dev/null
+++ b/idea/testData/inspectionsLocal/unnecessaryVariable/simpleReturn.kt.after
@@ -0,0 +1,3 @@
+fun sum(a: Int, b: Int): Int {
+ return a + b
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/unnecessaryVariable/topLevelCopy.kt b/idea/testData/inspectionsLocal/unnecessaryVariable/topLevelCopy.kt
new file mode 100644
index 00000000000..fff5b40d8d0
--- /dev/null
+++ b/idea/testData/inspectionsLocal/unnecessaryVariable/topLevelCopy.kt
@@ -0,0 +1,9 @@
+// PROBLEM: none
+
+val x: Number? = null
+
+fun foo(): Int {
+ val y = x
+ if (y is Int) return y
+ return 0
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/unnecessaryVariable/varCopy.kt b/idea/testData/inspectionsLocal/unnecessaryVariable/varCopy.kt
new file mode 100644
index 00000000000..fe62349cb3d
--- /dev/null
+++ b/idea/testData/inspectionsLocal/unnecessaryVariable/varCopy.kt
@@ -0,0 +1,7 @@
+// PROBLEM: none
+
+fun sqrPlusOne(arg: Int): Int {
+ var other = arg
+ other++
+ return other * other
+}
\ 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 7c877f98e3c..f10157ac78f 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java
@@ -1458,6 +1458,63 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
}
}
+ @TestMetadata("idea/testData/inspectionsLocal/unnecessaryVariable")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class UnnecessaryVariable extends AbstractLocalInspectionTest {
+ public void testAllFilesPresentInUnnecessaryVariable() throws Exception {
+ KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/unnecessaryVariable"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
+ }
+
+ @TestMetadata("copyOfVal.kt")
+ public void testCopyOfVal() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/unnecessaryVariable/copyOfVal.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("copyOfValWithExplicitType.kt")
+ public void testCopyOfValWithExplicitType() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/unnecessaryVariable/copyOfValWithExplicitType.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("copyOfVar.kt")
+ public void testCopyOfVar() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/unnecessaryVariable/copyOfVar.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("paramCopy.kt")
+ public void testParamCopy() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/unnecessaryVariable/paramCopy.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("propertyCopy.kt")
+ public void testPropertyCopy() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/unnecessaryVariable/propertyCopy.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("simpleReturn.kt")
+ public void testSimpleReturn() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/unnecessaryVariable/simpleReturn.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("topLevelCopy.kt")
+ public void testTopLevelCopy() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/unnecessaryVariable/topLevelCopy.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("varCopy.kt")
+ public void testVarCopy() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/unnecessaryVariable/varCopy.kt");
+ doTest(fileName);
+ }
+ }
+
@TestMetadata("idea/testData/inspectionsLocal/useExpressionBody")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)