Add inspection and quick-fix to remove empty parentheses in annotation entries
This commit is contained in:
committed by
Mikhail Glukhikh
parent
e903b2f92a
commit
793cac02e8
@@ -335,6 +335,13 @@ fun referenceExpressionRecursiveVisitor(block: (KtReferenceExpression) -> Unit)
|
||||
}
|
||||
}
|
||||
|
||||
fun valueArgumentListVisitor(block: (KtValueArgumentList) -> Unit) =
|
||||
object : KtVisitorVoid() {
|
||||
override fun visitValueArgumentList(list: KtValueArgumentList) {
|
||||
block(list)
|
||||
}
|
||||
}
|
||||
|
||||
fun valueArgumentVisitor(block: (KtValueArgument) -> Unit) =
|
||||
object : KtVisitorVoid() {
|
||||
override fun visitArgument(valueArgument: KtValueArgument) {
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<html>
|
||||
<body>
|
||||
This inspection reports unnecessary parentheses in annotation entries. For example:
|
||||
<br /><br />
|
||||
<pre>
|
||||
annotation class MyAnnotation
|
||||
|
||||
@MyAnnotation<b>()</b> // There parentheses can be omitted
|
||||
fun test() {
|
||||
|
||||
}
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
@@ -3030,6 +3030,15 @@
|
||||
language="kotlin"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.RemoveEmptyParenthesesFromAnnotationEntryInspection"
|
||||
displayName="Remove unnecessary parentheses"
|
||||
groupPath="Kotlin"
|
||||
groupName="Style issues"
|
||||
enabledByDefault="true"
|
||||
level="WEAK WARNING"
|
||||
language="kotlin"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.OptionalExpectationInspection"
|
||||
displayName="Optionally expected annotation has no actual annotation"
|
||||
groupPath="Kotlin"
|
||||
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
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.ClassConstructorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.psi.KtAnnotationEntry
|
||||
import org.jetbrains.kotlin.psi.KtValueArgumentList
|
||||
import org.jetbrains.kotlin.psi.valueArgumentListVisitor
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
|
||||
class RemoveEmptyParenthesesFromAnnotationEntryInspection : AbstractKotlinInspection() {
|
||||
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor =
|
||||
valueArgumentListVisitor(fun(list) {
|
||||
if (list.arguments.isNotEmpty()) return
|
||||
val annotationEntry = list.parent as? KtAnnotationEntry ?: return
|
||||
if (annotationEntry.typeArguments.isNotEmpty()) return
|
||||
|
||||
val annotationClassDescriptor = annotationEntry.getAnnotationClassDescriptor() ?: return
|
||||
|
||||
// if all annotation constructors must receive at least one argument
|
||||
// then parentheses *are* necessary and inspection should not trigger
|
||||
if (annotationClassDescriptor.constructors.all { it.hasParametersWithoutDefault() }) return
|
||||
|
||||
holder.registerProblem(
|
||||
list,
|
||||
"Parentheses should be removed",
|
||||
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
|
||||
RemoveEmptyParenthesesFromAnnotationEntryFix()
|
||||
)
|
||||
})
|
||||
|
||||
private fun KtAnnotationEntry.getAnnotationClassDescriptor(): ClassDescriptor? {
|
||||
val context = analyze(BodyResolveMode.PARTIAL)
|
||||
return context[BindingContext.ANNOTATION, this]?.annotationClass
|
||||
}
|
||||
|
||||
private fun ClassConstructorDescriptor.hasParametersWithoutDefault() = valueParameters.any { !it.declaresDefaultValue() }
|
||||
|
||||
}
|
||||
|
||||
private class RemoveEmptyParenthesesFromAnnotationEntryFix : LocalQuickFix {
|
||||
|
||||
override fun getName() = "Remove unnecessary parentheses"
|
||||
|
||||
override fun getFamilyName() = name
|
||||
|
||||
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
|
||||
(descriptor.psiElement as? KtValueArgumentList)?.delete()
|
||||
}
|
||||
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.idea.inspections.RemoveEmptyParenthesesFromAnnotationEntryInspection
|
||||
idea/testData/inspectionsLocal/removeEmptyParenthesesFromAnnotationEntry/allParameterHaveDefaults.kt
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
annotation class MyAnnotation(val x: Int = 10)
|
||||
|
||||
@MyAnnotation(<caret>)
|
||||
fun test() {
|
||||
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
annotation class MyAnnotation(val x: Int = 10)
|
||||
|
||||
@MyAnnotation
|
||||
fun test() {
|
||||
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
@Target(AnnotationTarget.EXPRESSION)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class MyAnnotation
|
||||
|
||||
fun test() {
|
||||
val x = @MyAnnotation(<caret>) 5
|
||||
}
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
@Target(AnnotationTarget.EXPRESSION)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class MyAnnotation
|
||||
|
||||
fun test() {
|
||||
val x = @MyAnnotation 5
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
annotation class MyAnnotation
|
||||
|
||||
@MyAnnotation(
|
||||
<caret>
|
||||
)
|
||||
fun test() {
|
||||
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
annotation class MyAnnotation
|
||||
|
||||
@MyAnnotation
|
||||
fun test() {
|
||||
|
||||
}
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
// PROBLEM: none
|
||||
// ERROR: No value passed for parameter 'x'
|
||||
annotation class MyAnnotation(val x: Int)
|
||||
|
||||
// there is an error but the inspection is not triggered because parentheses are needed in the end
|
||||
@MyAnnotation(<caret>)
|
||||
fun test() {
|
||||
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
annotation class MyAnnotation
|
||||
|
||||
@MyAnnotation(<caret>)
|
||||
fun test() {
|
||||
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
annotation class MyAnnotation
|
||||
|
||||
@MyAnnotation
|
||||
fun test() {
|
||||
|
||||
}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
public @interface MyJavaAnnotation {}
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
@MyJavaAnnotation(<caret>)
|
||||
fun test() {
|
||||
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
@MyJavaAnnotation
|
||||
fun test() {
|
||||
|
||||
}
|
||||
+43
@@ -4944,6 +4944,49 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/inspectionsLocal/removeEmptyParenthesesFromAnnotationEntry")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class RemoveEmptyParenthesesFromAnnotationEntry extends AbstractLocalInspectionTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInRemoveEmptyParenthesesFromAnnotationEntry() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/removeEmptyParenthesesFromAnnotationEntry"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("allParameterHaveDefaults.kt")
|
||||
public void testAllParameterHaveDefaults() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/removeEmptyParenthesesFromAnnotationEntry/allParameterHaveDefaults.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("annotatedExpr.kt")
|
||||
public void testAnnotatedExpr() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/removeEmptyParenthesesFromAnnotationEntry/annotatedExpr.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("parenthesesWithWhitespace.kt")
|
||||
public void testParenthesesWithWhitespace() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/removeEmptyParenthesesFromAnnotationEntry/parenthesesWithWhitespace.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("requiresArguments.kt")
|
||||
public void testRequiresArguments() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/removeEmptyParenthesesFromAnnotationEntry/requiresArguments.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/removeEmptyParenthesesFromAnnotationEntry/simple.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("usingJavaAnnotation.kt")
|
||||
public void testUsingJavaAnnotation() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/removeEmptyParenthesesFromAnnotationEntry/usingJavaAnnotation.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/inspectionsLocal/removeRedundantBackticks")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user