Inspection "can be primary constructor property" with relevant quick-fix #KT-8477 Fixed
(cherry picked from commit 2db7562)
This commit is contained in:
@@ -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>
|
||||
@@ -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"/>
|
||||
|
||||
+88
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -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>
|
||||
+1
@@ -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
|
||||
}
|
||||
+3
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user