[jvm] Remove unnecessary extension point for annotation suppressions

The extension point was used to run logic for various suppression annotations. Instead of running different logic based on the language, we can run the same logic for all languages. #IDEA-337709

GitOrigin-RevId: 7c51e3774d8d0c5232d37b817d1086ba4ebdda57
This commit is contained in:
Bart van Helvert
2023-12-12 22:06:39 +01:00
committed by intellij-monorepo-bot
parent e9802d2cb8
commit 316a5db6c8
6 changed files with 24 additions and 128 deletions

View File

@@ -1,36 +0,0 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInspection
import com.intellij.lang.LanguageExtension
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.util.IntellijInternalApi
import org.jetbrains.annotations.ApiStatus.Internal
import org.jetbrains.uast.UAnnotation
import org.jetbrains.uast.UExpression
private val EP_NAME: ExtensionPointName<SuppressionAnnotationUtil> =
ExtensionPointName.create("com.intellij.codeInspection.suppressionAnnotationUtil")
/**
* Helper extension providing utility methods used by [com.intellij.codeInspection.SuppressionAnnotationInspection].
*/
@Internal
@IntellijInternalApi
interface SuppressionAnnotationUtil {
companion object {
@JvmField
val extension = LanguageExtension<SuppressionAnnotationUtil>(EP_NAME.name)
}
/**
* @return true if a given annotation is a suppression annotation in its language,
* e.g. [SuppressWarnings] in Java or [Suppress] in Kotlin.
*/
fun isSuppressionAnnotation(annotation: UAnnotation): Boolean
/**
* @return values of a suppression annotation attribute defining suppressed problem names.
* Returned values should be [org.jetbrains.uast.UNamedExpression.expression].
*/
fun getSuppressionAnnotationAttributeExpressions(annotation: UAnnotation): List<UExpression>
}

View File

@@ -9,11 +9,6 @@
beanClass="com.intellij.lang.LanguageExtensionPoint"> beanClass="com.intellij.lang.LanguageExtensionPoint">
<with attribute="implementationClass" implements="com.intellij.codeInspection.sourceToSink.SourceToSinkProvider"/> <with attribute="implementationClass" implements="com.intellij.codeInspection.sourceToSink.SourceToSinkProvider"/>
</extensionPoint> </extensionPoint>
<extensionPoint qualifiedName="com.intellij.codeInspection.suppressionAnnotationUtil"
dynamic="true"
beanClass="com.intellij.lang.LanguageExtensionPoint">
<with attribute="implementationClass" implements="com.intellij.codeInspection.SuppressionAnnotationUtil"/>
</extensionPoint>
</extensionPoints> </extensionPoints>
<extensions defaultExtensionNs="com.intellij"> <extensions defaultExtensionNs="com.intellij">
<!--Test frameworks--> <!--Test frameworks-->
@@ -163,7 +158,6 @@
</extensions> </extensions>
<extensions defaultExtensionNs="com.intellij.codeInspection"> <extensions defaultExtensionNs="com.intellij.codeInspection">
<sourceToSinkProvider language="JAVA" implementationClass="com.intellij.codeInspection.sourceToSink.JavaSourceToSinkProvider"/> <sourceToSinkProvider language="JAVA" implementationClass="com.intellij.codeInspection.sourceToSink.JavaSourceToSinkProvider"/>
<suppressionAnnotationUtil language="JAVA" implementationClass="com.intellij.codeInspection.JavaSuppressionAnnotationUtil"/>
</extensions> </extensions>
<actions> <actions>
<group id="UastInternal" text="UAST" internal="true" popup="true"> <group id="UastInternal" text="UAST" internal="true" popup="true">

View File

@@ -13,18 +13,21 @@ import com.intellij.modcommand.ModPsiUpdater
import com.intellij.modcommand.PsiUpdateModCommandQuickFix import com.intellij.modcommand.PsiUpdateModCommandQuickFix
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiAnnotation
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementVisitor import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
import com.intellij.psi.util.PsiModificationTracker
import com.intellij.uast.UastHintedVisitorAdapter import com.intellij.uast.UastHintedVisitorAdapter
import org.jetbrains.annotations.VisibleForTesting import org.jetbrains.annotations.VisibleForTesting
import org.jetbrains.uast.* import org.jetbrains.uast.*
import org.jetbrains.uast.expressions.UInjectionHost import org.jetbrains.uast.expressions.UInjectionHost
import org.jetbrains.uast.visitor.AbstractUastNonRecursiveVisitor import org.jetbrains.uast.visitor.AbstractUastNonRecursiveVisitor
private class SuppressAnnotationDescriptor(val pkg: String, val shortName: String, val attributeValue: String)
private val suppressAnnotations = listOf(
SuppressAnnotationDescriptor("kotlin", "Suppress", "names"),
SuppressAnnotationDescriptor("java.lang", "SuppressWarnings", "value")
)
@VisibleForTesting @VisibleForTesting
class SuppressionAnnotationInspection : AbstractBaseUastLocalInspectionTool() { class SuppressionAnnotationInspection : AbstractBaseUastLocalInspectionTool() {
var myAllowedSuppressions: MutableList<String> = ArrayList() var myAllowedSuppressions: MutableList<String> = ArrayList()
@@ -51,7 +54,6 @@ class SuppressionAnnotationInspection : AbstractBaseUastLocalInspectionTool() {
} }
private inner class SuppressionAnnotationVisitor(private val holder: ProblemsHolder) : AbstractUastNonRecursiveVisitor() { private inner class SuppressionAnnotationVisitor(private val holder: ProblemsHolder) : AbstractUastNonRecursiveVisitor() {
override fun visitElement(node: UElement): Boolean { override fun visitElement(node: UElement): Boolean {
if (node is UComment) { if (node is UComment) {
return visitComment(node) return visitComment(node)
@@ -96,13 +98,12 @@ class SuppressionAnnotationInspection : AbstractBaseUastLocalInspectionTool() {
} }
override fun visitAnnotation(node: UAnnotation): Boolean { override fun visitAnnotation(node: UAnnotation): Boolean {
val suppressionAnnotationUtil = SuppressionAnnotationUtil.extension.forLanguage(node.lang) ?: return false val suppressDescriptor = node.suppressDescriptor()
if (suppressionAnnotationUtil.isSuppressionAnnotation(node)) { if (suppressDescriptor == null) return true
val ids = getInspectionIdsSuppressedInAnnotation(node, suppressionAnnotationUtil) val suppressIds = node.suppressIds(suppressDescriptor.attributeValue)
when { when {
ids.isNotEmpty() && !myAllowedSuppressions.containsAll(ids) -> registerProblem(node, true) suppressIds.isNotEmpty() && !myAllowedSuppressions.containsAll(suppressIds) -> registerProblem(node, true)
ids.isEmpty() -> registerProblem(node, false) suppressIds.isEmpty() -> registerProblem(node, false)
}
} }
return true return true
} }
@@ -151,8 +152,8 @@ class SuppressionAnnotationInspection : AbstractBaseUastLocalInspectionTool() {
private fun getIds(psiElement: PsiElement): Collection<String>? { private fun getIds(psiElement: PsiElement): Collection<String>? {
val annotation = psiElement.toUElement()?.getParentOfType<UAnnotation>(strict = false) val annotation = psiElement.toUElement()?.getParentOfType<UAnnotation>(strict = false)
if (annotation != null) { if (annotation != null) {
val suppressionAnnotationUtil = SuppressionAnnotationUtil.extension.forLanguage(annotation.lang) ?: return null val attributeValue = annotation.suppressDescriptor()?.attributeValue ?: return null
return getInspectionIdsSuppressedInAnnotation(annotation, suppressionAnnotationUtil) return annotation.suppressIds(attributeValue)
} }
else { else {
val comment = psiElement.toUElement(UComment::class.java) ?: return null val comment = psiElement.toUElement(UComment::class.java) ?: return null
@@ -170,29 +171,16 @@ class SuppressionAnnotationInspection : AbstractBaseUastLocalInspectionTool() {
} }
} }
private fun getInspectionIdsSuppressedInAnnotation(annotation: UAnnotation, private fun UAnnotation.suppressDescriptor(): SuppressAnnotationDescriptor? {
suppressionAnnotationUtil: SuppressionAnnotationUtil): List<String> { val shortName = uastAnchor?.name ?: return null
val sourcePsi = annotation.sourcePsi ?: return emptyList() if (suppressAnnotations.none { descr -> descr.shortName == shortName }) return null
return CachedValuesManager.getCachedValue(sourcePsi) { return suppressAnnotations.firstOrNull { descr -> "${descr.pkg}.${descr.shortName}" == qualifiedName }
CachedValueProvider.Result.create(
doGetInspectionIdsSuppressedInAnnotation(annotation, suppressionAnnotationUtil),
PsiModificationTracker.MODIFICATION_COUNT
)
}
} }
// do not move it into visitor class, as it will cause CachedValue-related exceptions private fun UAnnotation.suppressIds(attributeName: String): List<String> = flattenedAttributeValues(attributeName).mapNotNull {
private fun doGetInspectionIdsSuppressedInAnnotation(annotation: UAnnotation, when (it) {
suppressionAnnotationUtil: SuppressionAnnotationUtil): List<String> { is UInjectionHost, is UReferenceExpression -> it.evaluateString()
val expressions = suppressionAnnotationUtil.getSuppressionAnnotationAttributeExpressions(annotation) else -> null
return expressions.flatMap { getInspectionIdsSuppressedInAnnotation(it) }
}
private fun getInspectionIdsSuppressedInAnnotation(expression: UExpression): List<String> {
return when (expression) {
is UInjectionHost, is UReferenceExpression -> listOfNotNull(expression.evaluateString())
is UCallExpression -> expression.valueArguments.flatMap { getInspectionIdsSuppressedInAnnotation(it) }
else -> emptyList()
} }
} }
@@ -205,18 +193,4 @@ private fun getIdsFromComment(commentText: String): List<String>? {
else { else {
return null return null
} }
} }
internal class JavaSuppressionAnnotationUtil : SuppressionAnnotationUtil {
override fun isSuppressionAnnotation(annotation: UAnnotation): Boolean {
val psiAnnotation = annotation.sourcePsi as? PsiAnnotation ?: return false
val referenceText = psiAnnotation.nameReferenceElement?.text ?: return false
return "SuppressWarnings" == referenceText || BatchSuppressManager.SUPPRESS_INSPECTIONS_ANNOTATION_NAME == referenceText
}
override fun getSuppressionAnnotationAttributeExpressions(annotation: UAnnotation): List<UExpression> {
return annotation.attributeValues
.filter { it.name == null || it.name == "value" }
.map { it.expression }
}
}

View File

@@ -88,15 +88,6 @@ class JavaSuppressionAnnotationInspectionTest : SuppressionAnnotationInspectionT
""".trimIndent(), "PublicField") """.trimIndent(), "PublicField")
} }
fun `test quickfix - allow multiple suppressions from annotation`() {
testAllowSuppressionQuickFix(JvmLanguage.JAVA, """
public class A {
@SuppressWarnings("Public<caret>Field", "HardCodedStringLiteral")
public String s = "test";
}
""".trimIndent(), "PublicField", "HardCodedStringLiteral")
}
fun `test quickfix - allow multiple suppressions from annotation when array form used`() { fun `test quickfix - allow multiple suppressions from annotation when array form used`() {
testAllowSuppressionQuickFix(JvmLanguage.JAVA, """ testAllowSuppressionQuickFix(JvmLanguage.JAVA, """
public class A { public class A {

View File

@@ -1,22 +0,0 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.kotlin.idea.inspections
import com.intellij.codeInspection.SuppressionAnnotationUtil
import org.jetbrains.uast.UAnnotation
import org.jetbrains.uast.UExpression
internal class KotlinSuppressionAnnotationUtil : SuppressionAnnotationUtil {
private val suppressionAnnotationClassQualifiedName = Suppress::class.java.name
override fun isSuppressionAnnotation(annotation: UAnnotation): Boolean {
return annotation.sourcePsi?.text?.contains("Suppress") == true && // avoid resolving
annotation.qualifiedName == suppressionAnnotationClassQualifiedName
}
override fun getSuppressionAnnotationAttributeExpressions(annotation: UAnnotation): List<UExpression> {
return annotation.attributeValues
.filter { it.name == null || it.name == "names" }
.map { it.expression }
}
}

View File

@@ -117,11 +117,6 @@
<documentationProvider implementation="org.jetbrains.kotlin.idea.KotlinDocumentationProvider"/> <documentationProvider implementation="org.jetbrains.kotlin.idea.KotlinDocumentationProvider"/>
<definitionsScopedSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinDefinitionsSearcher"/> <definitionsScopedSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinDefinitionsSearcher"/>
<codeInspection.suppressionAnnotationUtil
language="kotlin"
implementationClass="org.jetbrains.kotlin.idea.inspections.KotlinSuppressionAnnotationUtil"/>
</extensions> </extensions>