[uast] Migrate inspections to findSourceAnnotation

Introduces utilities to work with source annotations in UAST and use them in UAST inspections. #KTIJ-33663

GitOrigin-RevId: 4a876d6dcc1c551781e52fde478e47809f6e3669
This commit is contained in:
Bart van Helvert
2025-05-28 14:14:24 +02:00
committed by intellij-monorepo-bot
parent b4352ad4f8
commit d4110a129f
7 changed files with 55 additions and 55 deletions

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2025 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.analysis.JvmAnalysisBundle
@@ -67,16 +67,13 @@ class MissingDeprecatedAnnotationOnScheduledForRemovalApiInspection : LocalInspe
}
private fun hasDeprecatedAnnotation(node: UAnnotated) =
node.findAnnotation(DEPRECATED_ANNOTATION_NAME) != null ||
node.findAnnotation(KOTLIN_DEPRECATED_ANNOTATION_NAME) != null ||
node.findAnnotation(SCALA_DEPRECATED_ANNOTATION_NAME) != null ||
(
node is UField &&
node.sourceAnnotations?.find { it.qualifiedName == KOTLIN_DEPRECATED_ANNOTATION_NAME } != null
)
node.findSourceAnnotation(DEPRECATED_ANNOTATION_NAME) != null ||
node.findSourceAnnotation(KOTLIN_DEPRECATED_ANNOTATION_NAME) != null ||
node.findSourceAnnotation(SCALA_DEPRECATED_ANNOTATION_NAME) != null ||
node.findSourceAnnotation(KOTLIN_DEPRECATED_ANNOTATION_NAME) != null
private fun isScheduledForRemoval(annotated: UAnnotated): Boolean =
annotated.findAnnotation(SCHEDULED_FOR_REMOVAL_ANNOTATION_NAME) != null
annotated.findSourceAnnotation(SCHEDULED_FOR_REMOVAL_ANNOTATION_NAME) != null
}
}

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2025 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.analysis.JvmAnalysisBundle
@@ -13,6 +13,7 @@ import org.jetbrains.annotations.VisibleForTesting
import org.jetbrains.uast.UAnnotated
import org.jetbrains.uast.UDeclaration
import org.jetbrains.uast.evaluateString
import org.jetbrains.uast.findSourceAnnotation
import org.jetbrains.uast.visitor.AbstractUastNonRecursiveVisitor
private inline val SCHEDULED_FOR_REMOVAL_ANNOTATION_NAME get() = ApiStatus.ScheduledForRemoval::class.java.canonicalName
@@ -66,7 +67,7 @@ class MustAlreadyBeRemovedApiInspection : AbstractBaseUastLocalInspectionTool()
}
private fun getVersionOfScheduledRemoval(annotated: UAnnotated): String? {
val annotation = annotated.findAnnotation(SCHEDULED_FOR_REMOVAL_ANNOTATION_NAME) ?: return null
val annotation = annotated.findSourceAnnotation(SCHEDULED_FOR_REMOVAL_ANNOTATION_NAME) ?: return null
return annotation.findDeclaredAttributeValue("inVersion")?.evaluateString()
}
}

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2025 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.analysis.JvmAnalysisBundle
@@ -44,7 +44,7 @@ class ObsoleteApiUsageInspection : LocalInspectionTool() {
val declaration = target.toUElement(UDeclaration::class.java)
if (declaration != null && !arePsiElementsFromTheSameFile(sourceNode.sourcePsi, target)) {
if (declaration !is UClass && declaration !is UMethod && declaration !is UField) return
if (declaration.findAnnotation(OBSOLETE_ANNOTATION_NAME) != null) {
if (declaration.findSourceAnnotation(OBSOLETE_ANNOTATION_NAME) != null) {
val elementToHighlight = (sourceNode as? UDeclaration)?.uastAnchor.sourcePsiElement ?: sourceNode.sourcePsi ?: return
// Do not highlight method references that map to obsolete functional interface.
// this problem will be highlighted elsewhere (e.g., at declaration of the method accepting functional interface)

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInspection.sourceToSink
import com.intellij.codeInsight.AnnotationTargetUtil
@@ -165,13 +165,7 @@ class TaintValueFactory(private val myConfiguration: UntaintedConfiguration) {
}
val toUElement = owner.toUElement()
if (info == TaintValue.UNKNOWN && toUElement is UVariable) {
val uAnnotations =
if (toUElement is UField) {
toUElement.sourceAnnotations ?: toUElement.uAnnotations
} else {
toUElement.uAnnotations
}
return uAnnotations
return toUElement.sourceAnnotations()
.mapNotNull { fromUAnnotation(it) }
.firstOrNull { it != TaintValue.UNKNOWN } ?: info
}

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInspection.test
import com.intellij.analysis.JvmAnalysisBundle
@@ -65,7 +65,7 @@ class TestOnlyInspection : AbstractBaseUastLocalInspectionTool() {
}
}
private fun isDirectlyTestOnly(member: UDeclaration) = member.findAnnotation(AnnotationUtil.TEST_ONLY) != null
private fun isDirectlyTestOnly(member: UDeclaration) = member.findSourceAnnotation(AnnotationUtil.TEST_ONLY) != null
inner class TestOnlyApiUsageProcessor(private val holder: ProblemsHolder) : ApiUsageProcessor {

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.uast.kotlin
import com.intellij.psi.*
@@ -7,12 +7,7 @@ import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.uast.UAnnotation
import org.jetbrains.uast.UElement
import org.jetbrains.uast.UField
import org.jetbrains.uast.UFieldEx
import org.jetbrains.uast.UastLazyPart
import org.jetbrains.uast.getOrBuild
import org.jetbrains.uast.*
import org.jetbrains.uast.internal.acceptList
import org.jetbrains.uast.visitor.UastVisitor
@@ -72,25 +67,19 @@ open class KotlinUField(
private val sourceAnnotationsPart = UastLazyPart<List<UAnnotation>?>()
override val sourceAnnotations: List<UAnnotation>?
override val sourceAnnotations: List<UAnnotation>
get() = sourceAnnotationsPart.getOrBuild {
val entries =
when (val origin = sourcePsi) {
val entries = when (val origin = sourcePsi) {
is KtParameter -> origin.annotationEntries
is KtProperty -> origin.annotationEntries
else -> return@getOrBuild null
}
}
sourceAnnotations(entries)
}
} ?: emptyList()
private fun sourceAnnotations(entries: List<KtAnnotationEntry>): List<UAnnotation> = buildList {
for (ktAnnotation in entries) {
add(
baseResolveProviderService.baseKotlinConverter.convertAnnotation(
ktAnnotation,
this@KotlinUField
)
)
add(baseResolveProviderService.baseKotlinConverter.convertAnnotation(ktAnnotation, this@KotlinUField))
}
}
}

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.uast
import com.intellij.psi.*
@@ -96,20 +96,21 @@ interface UField : UVariable, PsiField {
override val psi: PsiField
/**
* Annotations on source PSI.
*
* If annotations are annotated with no specific use-site, it follows default rules
* according to https://kotlinlang.org/docs/annotations.html#annotation-use-site-targets
* where `param`, `property`, and `field` are considered in order. Therefore, it is likely
* that the annotation is applied to property (before field) and belongs to $annotations
* in JVM bytecode. This might not be a user's intention, and some static analyses and/or
* inspections still depend on annotation without a use-site on property.
* To make [UVariable]'s annotation modeling accurate (i.e., conform to those in JVM bytecode)
* while mitigating existing analyses' behavior, this returns annotations on source PSI.
* Returns all annotations as defined on the [sourcePsi], in some cases this can differ with [uAnnotations] where only annotations that
* are strictly applied to the field are returned. Consider the following example:
* ```Java
* public @interface Foo { }
* ```
* ```Kotlin
* @Foo
* val foo = 0
* ```
* According to the [Kotlin docs](https://kotlinlang.org/docs/annotations.html#annotation-use-site-targets), if no `@Target` is specified,
* the first applicable target will be taken from the following order: param, property, field. Therefore `Foo` will only be applied to the
* property and won't be returned in [uAnnotations], to get annotations that are applied to the property, use [sourceAnnotations].
*/
@get:ApiStatus.Experimental
val sourceAnnotations: List<UAnnotation>?
get() = null
val sourceAnnotations: List<UAnnotation>
@ApiStatus.Experimental get() = uAnnotations
override fun asLogString(): String = log("name = $name")
@@ -122,6 +123,24 @@ interface UField : UVariable, PsiField {
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D): R = visitor.visitField(this, data)
}
/**
* Returns all annotations as defined on the [UElement.sourcePsi].
* @see UField.sourceAnnotations
*/
@ApiStatus.Experimental
fun UAnnotated.sourceAnnotations(): List<UAnnotation> {
return if (this is UField) sourceAnnotations else uAnnotations
}
/**
* Returns the first source annotation matching [fqName].
* @see sourceAnnotations
*/
@ApiStatus.Experimental
fun UAnnotated.findSourceAnnotation(fqName: String): UAnnotation? {
return sourceAnnotations().firstOrNull { it.qualifiedName == fqName }
}
interface UFieldEx : UField, UDeclarationEx {
override val javaPsi: PsiField
}