Introduce inspection to detect vals might be marked as const
So #KT-20644 Fixed
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
This inspection reports object and top-level <code>val</code> that might be declared as <code>const</code>
|
||||||
|
for better performance and Java interoperability.
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -2712,6 +2712,15 @@
|
|||||||
cleanupTool="true"
|
cleanupTool="true"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.MayBeConstantInspection"
|
||||||
|
displayName="Might be 'const'"
|
||||||
|
groupPath="Kotlin"
|
||||||
|
groupName="Style issues"
|
||||||
|
enabledByDefault="true"
|
||||||
|
level="WEAK WARNING"
|
||||||
|
language="kotlin"
|
||||||
|
/>
|
||||||
|
|
||||||
<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,69 @@
|
|||||||
|
/*
|
||||||
|
* 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.IntentionWrapper
|
||||||
|
import com.intellij.codeInspection.ProblemHighlightType
|
||||||
|
import com.intellij.codeInspection.ProblemsHolder
|
||||||
|
import com.intellij.psi.PsiElementVisitor
|
||||||
|
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.AddConstModifierFix
|
||||||
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
|
import org.jetbrains.kotlin.psi.KtClass
|
||||||
|
import org.jetbrains.kotlin.psi.KtProperty
|
||||||
|
import org.jetbrains.kotlin.psi.KtVisitorVoid
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
|
||||||
|
import org.jetbrains.kotlin.resolve.constants.evaluate.isStandaloneOnlyConstant
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.annotations.hasJvmFieldAnnotation
|
||||||
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
|
|
||||||
|
class MayBeConstantInspection : AbstractKotlinInspection() {
|
||||||
|
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
|
||||||
|
return object : KtVisitorVoid() {
|
||||||
|
override fun visitProperty(property: KtProperty) {
|
||||||
|
super.visitProperty(property)
|
||||||
|
|
||||||
|
if (property.isLocal || property.isVar) return
|
||||||
|
val initializer = property.initializer ?: return
|
||||||
|
if (property.hasModifier(KtTokens.CONST_KEYWORD)) return
|
||||||
|
// Top-level or object only
|
||||||
|
if (property.containingClassOrObject is KtClass) return
|
||||||
|
|
||||||
|
// For some reason constant evaluation does not work for property.analyze()
|
||||||
|
val context = initializer.analyze(BodyResolveMode.PARTIAL)
|
||||||
|
val propertyDescriptor = context[BindingContext.DECLARATION_TO_DESCRIPTOR, property] as? VariableDescriptor ?: return
|
||||||
|
val withJvmField = propertyDescriptor.hasJvmFieldAnnotation()
|
||||||
|
if (property.annotationEntries.isNotEmpty() && !withJvmField) return
|
||||||
|
|
||||||
|
val initializerValue = ConstantExpressionEvaluator.getConstant(
|
||||||
|
initializer, context
|
||||||
|
)?.toConstantValue(propertyDescriptor.type) ?: return
|
||||||
|
if (initializerValue.isStandaloneOnlyConstant()) return
|
||||||
|
|
||||||
|
holder.registerProblem(
|
||||||
|
property.nameIdentifier ?: property,
|
||||||
|
if (withJvmField) "'const' might be used instead of '@JvmField'" else "Might be 'const'",
|
||||||
|
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||||
|
IntentionWrapper(AddConstModifierFix(property), property.containingFile)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
org.jetbrains.kotlin.idea.inspections.MayBeConstantInspection
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
const val y = 0
|
||||||
|
|
||||||
|
class Your {
|
||||||
|
companion object {
|
||||||
|
@JvmField val <caret>z = y + 3
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
const val y = 0
|
||||||
|
|
||||||
|
class Your {
|
||||||
|
companion object {
|
||||||
|
const val z = y + 3
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
class Your {
|
||||||
|
companion object {
|
||||||
|
val <caret>IMPORTANT = 2 * 2
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
class Your {
|
||||||
|
companion object {
|
||||||
|
const val IMPORTANT = 2 * 2
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// PROBLEM: none
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
const val <caret>x = 13
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
// PROBLEM: none
|
||||||
|
|
||||||
|
val <caret>withGetter get() = 42
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// PROBLEM: none
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
class My {
|
||||||
|
@JvmField val <caret>x = 13
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// PROBLEM: none
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
annotation class A
|
||||||
|
|
||||||
|
@A val <caret>x = 55
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
object O {
|
||||||
|
@JvmField val <caret>s = "He" + "llo"
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
object O {
|
||||||
|
const val s = "He" + "llo"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
@JvmField val <caret>x = 1
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
const val x = 1
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
val <caret>x = 1
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
const val x = 1
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
// PROBLEM: none
|
||||||
|
|
||||||
|
var <caret>n = 1
|
||||||
@@ -1281,6 +1281,75 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("idea/testData/inspectionsLocal/mayBeConstant")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class MayBeConstant extends AbstractLocalInspectionTest {
|
||||||
|
public void testAllFilesPresentInMayBeConstant() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/mayBeConstant"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("cascadeConst.kt")
|
||||||
|
public void testCascadeConst() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/mayBeConstant/cascadeConst.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("companion.kt")
|
||||||
|
public void testCompanion() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/mayBeConstant/companion.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("const.kt")
|
||||||
|
public void testConst() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/mayBeConstant/const.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("getter.kt")
|
||||||
|
public void testGetter() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/mayBeConstant/getter.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inClass.kt")
|
||||||
|
public void testInClass() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/mayBeConstant/inClass.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nonJvmFieldAnnotated.kt")
|
||||||
|
public void testNonJvmFieldAnnotated() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/mayBeConstant/nonJvmFieldAnnotated.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("object.kt")
|
||||||
|
public void testObject() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/mayBeConstant/object.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("simple.kt")
|
||||||
|
public void testSimple() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/mayBeConstant/simple.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("simplest.kt")
|
||||||
|
public void testSimplest() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/mayBeConstant/simplest.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("var.kt")
|
||||||
|
public void testVar() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/mayBeConstant/var.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("idea/testData/inspectionsLocal/memberVisibilityCanBePrivate")
|
@TestMetadata("idea/testData/inspectionsLocal/memberVisibilityCanBePrivate")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user