Array in data class inspection: warning on array properties in data classes #KT-10299 Fixed
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
This inspection reports array properties in a data class without overridden <code>equals()</code> or <code>hashCode()</code> inside.
|
||||||
|
Due to default <code>equals()</code> behaviour for arrays in JVM, it's strongly recommended to override <code>equals()</code> and <code>hashCode()</code> in such cases.
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1418,6 +1418,13 @@
|
|||||||
level="WARNING"
|
level="WARNING"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.ArrayInDataClassInspection"
|
||||||
|
displayName="Array property in data class"
|
||||||
|
groupName="Kotlin"
|
||||||
|
enabledByDefault="true"
|
||||||
|
level="WEAK WARNING"
|
||||||
|
/>
|
||||||
|
|
||||||
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
|
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
|
||||||
|
|
||||||
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
|
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* 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.LocalInspectionToolSession
|
||||||
|
import com.intellij.codeInspection.ProblemHighlightType
|
||||||
|
import com.intellij.codeInspection.ProblemsHolder
|
||||||
|
import com.intellij.psi.PsiElementVisitor
|
||||||
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
|
import org.jetbrains.kotlin.psi.KtClass
|
||||||
|
import org.jetbrains.kotlin.psi.KtFunction
|
||||||
|
import org.jetbrains.kotlin.psi.KtVisitorVoid
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
|
|
||||||
|
class ArrayInDataClassInspection : AbstractKotlinInspection() {
|
||||||
|
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
|
||||||
|
return object : KtVisitorVoid() {
|
||||||
|
override fun visitClass(klass: KtClass) {
|
||||||
|
if (!klass.isData()) return
|
||||||
|
val constructor = klass.getPrimaryConstructor() ?: return
|
||||||
|
if (hasOverriddenEqualsAndHashCode(klass)) return
|
||||||
|
val context = constructor.analyze(BodyResolveMode.PARTIAL)
|
||||||
|
for (parameter in constructor.valueParameters) {
|
||||||
|
if (!parameter.hasValOrVar()) continue
|
||||||
|
val type = context.get(BindingContext.TYPE, parameter.typeReference) ?: continue
|
||||||
|
if (KotlinBuiltIns.isArray(type) || KotlinBuiltIns.isPrimitiveArray(type)) {
|
||||||
|
holder.registerProblem(parameter,
|
||||||
|
"Array property in data class: it's recommended to override equals() / hashCode()",
|
||||||
|
ProblemHighlightType.WEAK_WARNING)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hasOverriddenEqualsAndHashCode(klass: KtClass): Boolean {
|
||||||
|
var overriddenEquals = false
|
||||||
|
var overriddenHashCode = false
|
||||||
|
for (declaration in klass.declarations) {
|
||||||
|
if (declaration !is KtFunction) continue
|
||||||
|
if (!declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) continue
|
||||||
|
if (declaration.nameAsName == OperatorNameConventions.EQUALS && declaration.valueParameters.size == 1) {
|
||||||
|
val parameter = declaration.valueParameters.single()
|
||||||
|
val context = declaration.analyze(BodyResolveMode.PARTIAL)
|
||||||
|
val type = context.get(BindingContext.TYPE, parameter.typeReference)
|
||||||
|
if (type != null && KotlinBuiltIns.isNullableAny(type)) {
|
||||||
|
overriddenEquals = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (declaration.name == "hashCode" && declaration.valueParameters.size == 0) {
|
||||||
|
overriddenHashCode = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return overriddenEquals && overriddenHashCode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<problems>
|
||||||
|
<problem>
|
||||||
|
<file>test.kt</file>
|
||||||
|
<line>1</line>
|
||||||
|
<module>light_idea_test_case</module>
|
||||||
|
<entry_point TYPE="file" FQNAME="temp:///src/test.kt" />
|
||||||
|
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Array property in data class</problem_class>
|
||||||
|
<description>Array property in data class: it's recommended to override equals() / hashCode()</description>
|
||||||
|
</problem>
|
||||||
|
<problem>
|
||||||
|
<file>test.kt</file>
|
||||||
|
<line>5</line>
|
||||||
|
<module>light_idea_test_case</module>
|
||||||
|
<entry_point TYPE="file" FQNAME="temp:///src/test.kt" />
|
||||||
|
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Array property in data class</problem_class>
|
||||||
|
<description>Array property in data class: it's recommended to override equals() / hashCode()</description>
|
||||||
|
</problem>
|
||||||
|
</problems>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.ArrayInDataClassInspection
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
data class First(val x: Array<String>, val y: Int)
|
||||||
|
|
||||||
|
class Second(val x: Array<String>)
|
||||||
|
|
||||||
|
data class Third(val x: String, val y: IntArray)
|
||||||
|
|
||||||
|
data class Correct(val x: IntArray) {
|
||||||
|
override fun equals(other: Any?) = other is Correct
|
||||||
|
|
||||||
|
override fun hashCode() = 0
|
||||||
|
}
|
||||||
@@ -112,6 +112,12 @@ public class InspectionTestGenerated extends AbstractInspectionTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("arrayInDataClass/inspectionData/inspections.test")
|
||||||
|
public void testArrayInDataClass_inspectionData_Inspections_test() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/arrayInDataClass/inspectionData/inspections.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("canBeVal/inspectionData/inspections.test")
|
@TestMetadata("canBeVal/inspectionData/inspections.test")
|
||||||
public void testCanBeVal_inspectionData_Inspections_test() throws Exception {
|
public void testCanBeVal_inspectionData_Inspections_test() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/canBeVal/inspectionData/inspections.test");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/canBeVal/inspectionData/inspections.test");
|
||||||
|
|||||||
Reference in New Issue
Block a user