mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-06 03:21:12 +07:00
[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:
committed by
intellij-monorepo-bot
parent
b4352ad4f8
commit
d4110a129f
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user