[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">
<with attribute="implementationClass" implements="com.intellij.codeInspection.sourceToSink.SourceToSinkProvider"/>
</extensionPoint>
<extensionPoint qualifiedName="com.intellij.codeInspection.suppressionAnnotationUtil"
dynamic="true"
beanClass="com.intellij.lang.LanguageExtensionPoint">
<with attribute="implementationClass" implements="com.intellij.codeInspection.SuppressionAnnotationUtil"/>
</extensionPoint>
</extensionPoints>
<extensions defaultExtensionNs="com.intellij">
<!--Test frameworks-->
@@ -163,7 +158,6 @@
</extensions>
<extensions defaultExtensionNs="com.intellij.codeInspection">
<sourceToSinkProvider language="JAVA" implementationClass="com.intellij.codeInspection.sourceToSink.JavaSourceToSinkProvider"/>
<suppressionAnnotationUtil language="JAVA" implementationClass="com.intellij.codeInspection.JavaSuppressionAnnotationUtil"/>
</extensions>
<actions>
<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.openapi.project.Project
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiAnnotation
import com.intellij.psi.PsiElement
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 org.jetbrains.annotations.VisibleForTesting
import org.jetbrains.uast.*
import org.jetbrains.uast.expressions.UInjectionHost
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
class SuppressionAnnotationInspection : AbstractBaseUastLocalInspectionTool() {
var myAllowedSuppressions: MutableList<String> = ArrayList()
@@ -51,7 +54,6 @@ class SuppressionAnnotationInspection : AbstractBaseUastLocalInspectionTool() {
}
private inner class SuppressionAnnotationVisitor(private val holder: ProblemsHolder) : AbstractUastNonRecursiveVisitor() {
override fun visitElement(node: UElement): Boolean {
if (node is UComment) {
return visitComment(node)
@@ -96,13 +98,12 @@ class SuppressionAnnotationInspection : AbstractBaseUastLocalInspectionTool() {
}
override fun visitAnnotation(node: UAnnotation): Boolean {
val suppressionAnnotationUtil = SuppressionAnnotationUtil.extension.forLanguage(node.lang) ?: return false
if (suppressionAnnotationUtil.isSuppressionAnnotation(node)) {
val ids = getInspectionIdsSuppressedInAnnotation(node, suppressionAnnotationUtil)
when {
ids.isNotEmpty() && !myAllowedSuppressions.containsAll(ids) -> registerProblem(node, true)
ids.isEmpty() -> registerProblem(node, false)
}
val suppressDescriptor = node.suppressDescriptor()
if (suppressDescriptor == null) return true
val suppressIds = node.suppressIds(suppressDescriptor.attributeValue)
when {
suppressIds.isNotEmpty() && !myAllowedSuppressions.containsAll(suppressIds) -> registerProblem(node, true)
suppressIds.isEmpty() -> registerProblem(node, false)
}
return true
}
@@ -151,8 +152,8 @@ class SuppressionAnnotationInspection : AbstractBaseUastLocalInspectionTool() {
private fun getIds(psiElement: PsiElement): Collection<String>? {
val annotation = psiElement.toUElement()?.getParentOfType<UAnnotation>(strict = false)
if (annotation != null) {
val suppressionAnnotationUtil = SuppressionAnnotationUtil.extension.forLanguage(annotation.lang) ?: return null
return getInspectionIdsSuppressedInAnnotation(annotation, suppressionAnnotationUtil)
val attributeValue = annotation.suppressDescriptor()?.attributeValue ?: return null
return annotation.suppressIds(attributeValue)
}
else {
val comment = psiElement.toUElement(UComment::class.java) ?: return null
@@ -170,29 +171,16 @@ class SuppressionAnnotationInspection : AbstractBaseUastLocalInspectionTool() {
}
}
private fun getInspectionIdsSuppressedInAnnotation(annotation: UAnnotation,
suppressionAnnotationUtil: SuppressionAnnotationUtil): List<String> {
val sourcePsi = annotation.sourcePsi ?: return emptyList()
return CachedValuesManager.getCachedValue(sourcePsi) {
CachedValueProvider.Result.create(
doGetInspectionIdsSuppressedInAnnotation(annotation, suppressionAnnotationUtil),
PsiModificationTracker.MODIFICATION_COUNT
)
}
private fun UAnnotation.suppressDescriptor(): SuppressAnnotationDescriptor? {
val shortName = uastAnchor?.name ?: return null
if (suppressAnnotations.none { descr -> descr.shortName == shortName }) return null
return suppressAnnotations.firstOrNull { descr -> "${descr.pkg}.${descr.shortName}" == qualifiedName }
}
// do not move it into visitor class, as it will cause CachedValue-related exceptions
private fun doGetInspectionIdsSuppressedInAnnotation(annotation: UAnnotation,
suppressionAnnotationUtil: SuppressionAnnotationUtil): List<String> {
val expressions = suppressionAnnotationUtil.getSuppressionAnnotationAttributeExpressions(annotation)
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()
private fun UAnnotation.suppressIds(attributeName: String): List<String> = flattenedAttributeValues(attributeName).mapNotNull {
when (it) {
is UInjectionHost, is UReferenceExpression -> it.evaluateString()
else -> null
}
}
@@ -205,18 +193,4 @@ private fun getIdsFromComment(commentText: String): List<String>? {
else {
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")
}
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`() {
testAllowSuppressionQuickFix(JvmLanguage.JAVA, """
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"/>
<definitionsScopedSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinDefinitionsSearcher"/>
<codeInspection.suppressionAnnotationUtil
language="kotlin"
implementationClass="org.jetbrains.kotlin.idea.inspections.KotlinSuppressionAnnotationUtil"/>
</extensions>