Inspection "can be primary constructor property" with relevant quick-fix #KT-8477 Fixed

(cherry picked from commit 2db7562)
This commit is contained in:
Mikhail Glukhikh
2016-06-16 13:09:32 +03:00
parent 9240c82934
commit ac03d98cb2
13 changed files with 196 additions and 0 deletions
@@ -0,0 +1,5 @@
<html>
<body>
This inspection reports properties which are explicitly assigned by constructor arguments and can be declared directly in constructor instead.
</body>
</html>
+8
View File
@@ -1569,6 +1569,14 @@
language="kotlin"
/>
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.CanBePrimaryConstructorPropertyInspection"
displayName="Property is explicitly assigned to constructor parameter"
groupName="Kotlin"
enabledByDefault="true"
level="WARNING"
language="kotlin"
/>
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
@@ -0,0 +1,88 @@
/*
* Copyright 2010-2016 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.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getAnnotationEntries
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
class CanBePrimaryConstructorPropertyInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitProperty(property: KtProperty) {
if (property.isLocal) return
if (property.getter != null || property.setter != null || property.delegate != null) return
val assigned = property.initializer as? KtReferenceExpression ?: return
val context = property.analyzeFully()
val assignedDescriptor = context.get(BindingContext.REFERENCE_TARGET, assigned) as? ValueParameterDescriptor ?: return
// to prevent some exotic situations
if (!assignedDescriptor.annotations.isEmpty() || !assigned.getAnnotationEntries().isEmpty()) return
val containingConstructor = assignedDescriptor.containingDeclaration as? ConstructorDescriptor ?: return
if (containingConstructor.containingDeclaration.isData) return
val propertyTypeReference = property.typeReference
val propertyType = context.get(BindingContext.TYPE, propertyTypeReference)
if (propertyType != null && propertyType != assignedDescriptor.type) return
val nameIdentifier = property.nameIdentifier ?: return
if (nameIdentifier.text != assignedDescriptor.name.asString()) return
val assignedParameter = DescriptorToSourceUtils.descriptorToDeclaration(assignedDescriptor) as? KtParameter ?: return
holder.registerProblem(holder.manager.createProblemDescriptor(
nameIdentifier,
nameIdentifier,
"Property is explicitly assigned by parameter ${assignedDescriptor.name}, can be declared directly in constructor",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
isOnTheFly,
MakeConstructorPropertyFix(property, assignedParameter)
))
}
}
}
class MakeConstructorPropertyFix(val original: KtProperty, val parameter: KtParameter) : LocalQuickFix {
override fun getName() = "Move to constructor"
override fun getFamilyName() = "Move to constructor"
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val isVar = original.isVar
val modifiers = original.modifierList?.text
val factory = KtPsiFactory(project)
val valOrVar = if (isVar) factory.createVarKeyword() else factory.createValKeyword()
parameter.addBefore(valOrVar, parameter.nameIdentifier)
if (modifiers != null) {
val newModifiers = factory.createModifierList(modifiers)
parameter.addBefore(newModifiers, parameter.valOrVarKeyword)
}
original.delete()
}
}
}
@@ -0,0 +1,18 @@
<problems>
<problem>
<file>properties.kt</file>
<line>3</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/properties.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Property is explicitly assigned to constructor parameter</problem_class>
<description>Property is explicitly assigned by parameter simple, can be declared directly in constructor</description>
</problem>
<problem>
<file>properties.kt</file>
<line>5</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/properties.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Property is explicitly assigned to constructor parameter</problem_class>
<description>Property is explicitly assigned by parameter withType, can be declared directly in constructor</description>
</problem>
</problems>
@@ -0,0 +1 @@
// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.CanBePrimaryConstructorPropertyInspection
@@ -0,0 +1,34 @@
class Correct(simple: String, withType: Int, otherName: Double) {
val simple = simple
val withType: Int = withType
// Questionable case (due to possible named parameters), not allowed now
val anotherName = otherName
}
// Inspection should not work here anywhere
class Incorrect(val property: String, withGetter: Double, withSetter: Int, differentType: String) {
val another = property
val fromProperty = another
val withGetter = withGetter
get() = 2 * field
val withSetter = withSetter
private set
val differentType: String? = differentType
constructor(param: Int): this("", 0.0, param, "") {
val local = param
}
}
// For data class inspection also should not work
data class Data(name: String) {
val name = name
}
@@ -0,0 +1 @@
org.jetbrains.kotlin.idea.inspections.CanBePrimaryConstructorPropertyInspection
@@ -0,0 +1,4 @@
// "Move to constructor" "true"
class Container(index: Int) {
protected open var <caret>index = index
}
@@ -0,0 +1,3 @@
// "Move to constructor" "true"
class Container(protected open var index: Int) {
}
@@ -0,0 +1,4 @@
// "Move to constructor" "true"
class Correct(name: String) {
val <caret>name: String = name
}
@@ -0,0 +1,3 @@
// "Move to constructor" "true"
class Correct(val name: String) {
}
@@ -130,6 +130,12 @@ public class InspectionTestGenerated extends AbstractInspectionTest {
doTest(fileName);
}
@TestMetadata("canBePrimaryConstructorProperty/inspectionData/inspections.test")
public void testCanBePrimaryConstructorProperty_inspectionData_Inspections_test() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/canBePrimaryConstructorProperty/inspectionData/inspections.test");
doTest(fileName);
}
@TestMetadata("canBeVal/inspectionData/inspections.test")
public void testCanBeVal_inspectionData_Inspections_test() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/canBeVal/inspectionData/inspections.test");
@@ -716,6 +716,27 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
}
}
@TestMetadata("idea/testData/quickfix/canBePrimaryConstructorProperty")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class CanBePrimaryConstructorProperty extends AbstractQuickFixTest {
public void testAllFilesPresentInCanBePrimaryConstructorProperty() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/canBePrimaryConstructorProperty"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
}
@TestMetadata("protectedOpenVar.kt")
public void testProtectedOpenVar() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/canBePrimaryConstructorProperty/protectedOpenVar.kt");
doTest(fileName);
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/canBePrimaryConstructorProperty/simple.kt");
doTest(fileName);
}
}
@TestMetadata("idea/testData/quickfix/changeSignature")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)