mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-06 11:50:54 +07:00
[kotlin] K2: implement inspection suppression
A large part of functionality is made common from K1 implementation or ported to Analysis API. ^KTIJ-23786 Fixed GitOrigin-RevId: 2c5a250073bb571cbde6b9ebc61d84bed35ccaad
This commit is contained in:
committed by
intellij-monorepo-bot
parent
f875ce8e4b
commit
f7f82026d0
@@ -1 +1,19 @@
|
||||
highlighter.tool.tip.text.run.test=Run Test
|
||||
highlighter.tool.tip.text.run.test=Run Test
|
||||
intention.suppress.family=Suppress warnings
|
||||
intention.suppress.text=Suppress ''{0}'' for {1} {2}
|
||||
declaration.kind.statement=statement
|
||||
declaration.kind.initializer=initializer
|
||||
declaration.kind.object=object
|
||||
declaration.kind.companion.object=companion object
|
||||
declaration.kind.file=file
|
||||
declaration.kind.secondary.constructor.of=secondary constructor of
|
||||
declaration.kind.enum.entry=enum entry
|
||||
declaration.kind.type.parameter=type parameter
|
||||
declaration.kind.class=class
|
||||
declaration.kind.interface=interface
|
||||
declaration.kind.fun=fun
|
||||
declaration.kind.val=val
|
||||
declaration.kind.var=var
|
||||
declaration.kind.parameter=parameter
|
||||
declaration.name.0.of.1={0} of {1}
|
||||
declaration.name.anonymous=<anonymous>
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// 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.suppress
|
||||
|
||||
import com.intellij.codeInsight.intention.FileModifier.SafeTypeForPreview
|
||||
import com.intellij.openapi.util.NlsSafe
|
||||
import org.jetbrains.annotations.Nls
|
||||
|
||||
@SafeTypeForPreview
|
||||
class AnnotationHostKind(
|
||||
/** Human-readable `KtElement` kind on which the annotation is placed. E.g., 'file', 'class' or 'statement'. */
|
||||
@Nls val kind: String,
|
||||
|
||||
/** Name of the annotation owner. Might be null if the owner is not a named declaration (for instance, if it is a statement). */
|
||||
@NlsSafe val name: String?,
|
||||
|
||||
/** True if the annotation needs to be added to a separate line. */
|
||||
val newLineNeeded: Boolean
|
||||
)
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// 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
|
||||
package org.jetbrains.kotlin.idea.inspections.suppress
|
||||
|
||||
import com.intellij.codeInsight.daemon.QuickFixBundle
|
||||
import com.intellij.codeInspection.*
|
||||
@@ -10,13 +10,10 @@ import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.isAncestor
|
||||
import com.intellij.psi.util.parentOfType
|
||||
import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
|
||||
import org.jetbrains.kotlin.diagnostics.Severity
|
||||
import org.jetbrains.kotlin.idea.base.psi.KotlinPsiHeuristics
|
||||
import org.jetbrains.kotlin.idea.base.psi.findSingleLiteralStringTemplateText
|
||||
import org.jetbrains.kotlin.idea.base.psi.textRangeIn
|
||||
import org.jetbrains.kotlin.idea.highlighter.createSuppressWarningActions
|
||||
import org.jetbrains.kotlin.idea.util.findSingleLiteralStringTemplateText
|
||||
import org.jetbrains.kotlin.psi.KtAnnotated
|
||||
import org.jetbrains.kotlin.psi.KtAnnotatedExpression
|
||||
import org.jetbrains.kotlin.utils.KotlinExceptionWithAttachments
|
||||
@@ -41,9 +38,8 @@ class KotlinInspectionSuppressor : InspectionSuppressor, RedundantSuppressionDet
|
||||
}.toTypedArray()
|
||||
}
|
||||
|
||||
override fun isSuppressedFor(element: PsiElement, toolId: String): Boolean = KotlinCacheService.getInstance(element.project)
|
||||
.getSuppressionCache()
|
||||
.isSuppressed(element, element.containingFile, toolId, Severity.WARNING)
|
||||
override fun isSuppressedFor(element: PsiElement, toolId: String): Boolean =
|
||||
KotlinSuppressionChecker.getInstance().isSuppressedFor(element, toolId)
|
||||
|
||||
override fun getSuppressionIds(element: PsiElement): String? = suppressionIds(element).ifNotEmpty { joinToString(separator = ",") }
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.kotlin.idea.quickfix
|
||||
// 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.suppress
|
||||
|
||||
import com.intellij.codeInsight.intention.FileModifier.SafeFieldForPreview
|
||||
import com.intellij.codeInsight.intention.FileModifier
|
||||
import com.intellij.codeInspection.SuppressIntentionAction
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
@@ -9,78 +9,77 @@ import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.idea.base.fe10.codeInsight.KotlinBaseFe10CodeInsightBundle
|
||||
import org.jetbrains.kotlin.idea.base.fe10.highlighting.KotlinBaseFe10HighlightingBundle
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.highlighter.AnnotationHostKind
|
||||
import org.jetbrains.kotlin.idea.base.codeInsight.KotlinBaseCodeInsightBundle
|
||||
import org.jetbrains.kotlin.idea.util.addAnnotation
|
||||
import org.jetbrains.kotlin.idea.util.findAnnotation
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer
|
||||
import org.jetbrains.kotlin.psi.psiUtil.replaceFileAnnotationList
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
class KotlinSuppressIntentionAction(
|
||||
suppressAt: KtElement,
|
||||
private val suppressionKey: String,
|
||||
@SafeFieldForPreview private val kind: AnnotationHostKind
|
||||
suppressAt: KtElement,
|
||||
private val suppressionKey: String,
|
||||
@FileModifier.SafeFieldForPreview private val kind: AnnotationHostKind
|
||||
) : SuppressIntentionAction() {
|
||||
private val pointer = suppressAt.createSmartPointer()
|
||||
|
||||
@SafeFieldForPreview
|
||||
@FileModifier.SafeFieldForPreview
|
||||
private val project = suppressAt.project
|
||||
|
||||
override fun getFamilyName() = KotlinBaseFe10CodeInsightBundle.message("intention.suppress.family")
|
||||
override fun getText() = KotlinBaseFe10CodeInsightBundle.message("intention.suppress.text", suppressionKey, kind.kind, kind.name ?: "")
|
||||
private val suppressionKeyString = "\"$suppressionKey\""
|
||||
|
||||
override fun getFamilyName() = KotlinBaseCodeInsightBundle.message("intention.suppress.family")
|
||||
|
||||
override fun getText() = KotlinBaseCodeInsightBundle.message("intention.suppress.text", suppressionKey, kind.kind, kind.name ?: "")
|
||||
|
||||
override fun isAvailable(project: Project, editor: Editor?, element: PsiElement): Boolean {
|
||||
if (isLambdaParameter(element)) {
|
||||
// Lambda parameters can't be annotated: KT-13900
|
||||
return false
|
||||
}
|
||||
|
||||
return element.isValid
|
||||
}
|
||||
|
||||
private fun isLambdaParameter(element: PsiElement): Boolean {
|
||||
if (kind.kind != KotlinBaseFe10HighlightingBundle.message("declaration.kind.parameter")) return false
|
||||
val parentParameter = element.parent.safeAs<KtParameter>()
|
||||
?: element.parent.safeAs<KtDestructuringDeclarationEntry>()?.parent?.parent.safeAs<KtParameter>()
|
||||
if (kind.kind != KotlinBaseCodeInsightBundle.message("declaration.kind.parameter")) return false
|
||||
val parentParameter = element.parent as? KtParameter
|
||||
?: (element.parent as? KtDestructuringDeclarationEntry)?.parent?.parent as? KtParameter
|
||||
return parentParameter?.isLambdaParameter == true
|
||||
}
|
||||
|
||||
override fun invoke(project: Project, editor: Editor?, element: PsiElement) {
|
||||
if (!element.isValid) return
|
||||
val suppressAt = pointer.element ?: return
|
||||
|
||||
val id = "\"$suppressionKey\""
|
||||
when (suppressAt) {
|
||||
is KtModifierListOwner -> suppressAt.addAnnotation(
|
||||
StandardNames.FqNames.suppress,
|
||||
id,
|
||||
StandardClassIds.Annotations.Suppress,
|
||||
suppressionKeyString,
|
||||
whiteSpaceText = if (kind.newLineNeeded) "\n" else " ",
|
||||
addToExistingAnnotation = { entry ->
|
||||
addArgumentToSuppressAnnotation(
|
||||
entry,
|
||||
id
|
||||
suppressionKeyString
|
||||
); true
|
||||
})
|
||||
|
||||
is KtAnnotatedExpression ->
|
||||
suppressAtAnnotatedExpression(CaretBox(suppressAt, editor), id)
|
||||
suppressAtAnnotatedExpression(CaretBox(suppressAt, editor), suppressionKeyString)
|
||||
|
||||
is KtExpression ->
|
||||
suppressAtExpression(CaretBox(suppressAt, editor), id)
|
||||
suppressAtExpression(CaretBox(suppressAt, editor), suppressionKeyString)
|
||||
|
||||
is KtFile ->
|
||||
suppressAtFile(suppressAt, id)
|
||||
suppressAtFile(suppressAt, suppressionKeyString)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getFileModifierForPreview(target: PsiFile): KotlinSuppressIntentionAction {
|
||||
return KotlinSuppressIntentionAction(
|
||||
PsiTreeUtil.findSameElementInCopy(pointer.element, target),
|
||||
suppressionKey,
|
||||
kind
|
||||
PsiTreeUtil.findSameElementInCopy(pointer.element, target),
|
||||
suppressionKey,
|
||||
kind
|
||||
)
|
||||
}
|
||||
|
||||
@@ -91,12 +90,13 @@ class KotlinSuppressIntentionAction(
|
||||
if (fileAnnotationList == null) {
|
||||
val newAnnotationList = psiFactory.createFileAnnotationListWithAnnotation(suppressAnnotationText(id, false))
|
||||
val packageDirective = ktFile.packageDirective
|
||||
val createAnnotationList = if (packageDirective != null && PsiTreeUtil.skipWhitespacesForward(packageDirective) == ktFile.importList) {
|
||||
// packageDirective could be empty but suppression still should be added before it to generate consistent PSI
|
||||
ktFile.addBefore(newAnnotationList, packageDirective) as KtFileAnnotationList
|
||||
} else {
|
||||
replaceFileAnnotationList(ktFile, newAnnotationList)
|
||||
}
|
||||
val createAnnotationList =
|
||||
if (packageDirective != null && PsiTreeUtil.skipWhitespacesForward(packageDirective) == ktFile.importList) {
|
||||
// packageDirective could be empty but suppression still should be added before it to generate consistent PSI
|
||||
ktFile.addBefore(newAnnotationList, packageDirective) as KtFileAnnotationList
|
||||
} else {
|
||||
replaceFileAnnotationList(ktFile, newAnnotationList)
|
||||
}
|
||||
ktFile.addAfter(psiFactory.createWhiteSpace(kind), createAnnotationList)
|
||||
|
||||
return
|
||||
@@ -107,7 +107,6 @@ class KotlinSuppressIntentionAction(
|
||||
val newSuppressAnnotation = psiFactory.createFileAnnotation(suppressAnnotationText(id, false))
|
||||
fileAnnotationList.add(psiFactory.createWhiteSpace(kind))
|
||||
fileAnnotationList.add(newSuppressAnnotation) as KtAnnotationEntry
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -149,8 +148,10 @@ class KotlinSuppressIntentionAction(
|
||||
when {
|
||||
args == null -> // new argument list
|
||||
entry.addAfter(newArgList, entry.lastChild)
|
||||
|
||||
args.arguments.isEmpty() -> // replace '()' with a new argument list
|
||||
args.replace(newArgList)
|
||||
|
||||
else -> args.addArgument(newArgList.arguments[0])
|
||||
}
|
||||
}
|
||||
@@ -160,19 +161,11 @@ class KotlinSuppressIntentionAction(
|
||||
}
|
||||
|
||||
private fun findSuppressAnnotation(annotated: KtAnnotated): KtAnnotationEntry? {
|
||||
val context = annotated.analyze()
|
||||
return findSuppressAnnotation(context, annotated.annotationEntries)
|
||||
return annotated.findAnnotation(StandardClassIds.Annotations.Suppress)
|
||||
}
|
||||
|
||||
private fun findSuppressAnnotation(annotationList: KtFileAnnotationList): KtAnnotationEntry? {
|
||||
val context = annotationList.analyze()
|
||||
return findSuppressAnnotation(context, annotationList.annotationEntries)
|
||||
}
|
||||
|
||||
private fun findSuppressAnnotation(context: BindingContext, annotationEntries: List<KtAnnotationEntry>): KtAnnotationEntry? {
|
||||
return annotationEntries.firstOrNull { entry ->
|
||||
context.get(BindingContext.ANNOTATION, entry)?.fqName == StandardNames.FqNames.suppress
|
||||
}
|
||||
return annotationList.findAnnotation(StandardClassIds.Annotations.Suppress)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,28 @@
|
||||
// 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-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.highlighter
|
||||
package org.jetbrains.kotlin.idea.inspections.suppress
|
||||
|
||||
import com.intellij.codeInspection.SuppressIntentionAction
|
||||
import com.intellij.codeInspection.SuppressableProblemGroup
|
||||
import com.intellij.openapi.util.NlsSafe
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
|
||||
import org.jetbrains.kotlin.diagnostics.Severity
|
||||
import org.jetbrains.kotlin.idea.base.fe10.highlighting.KotlinBaseFe10HighlightingBundle
|
||||
import org.jetbrains.kotlin.idea.base.codeInsight.KotlinBaseCodeInsightBundle
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
|
||||
class KotlinSuppressableWarningProblemGroup(private val diagnosticFactory: DiagnosticFactory<*>) : SuppressableProblemGroup {
|
||||
init {
|
||||
assert(diagnosticFactory.severity == Severity.WARNING)
|
||||
}
|
||||
|
||||
override fun getProblemName() = diagnosticFactory.name
|
||||
class KotlinSuppressableWarningProblemGroup(private val factoryName: String) : SuppressableProblemGroup {
|
||||
override fun getProblemName() = factoryName
|
||||
|
||||
override fun getSuppressActions(element: PsiElement?): Array<SuppressIntentionAction> {
|
||||
return if (element != null) {
|
||||
createSuppressWarningActions(element, diagnosticFactory).toTypedArray()
|
||||
createSuppressWarningActions(element, Severity.WARNING, factoryName).toTypedArray()
|
||||
} else {
|
||||
SuppressIntentionAction.EMPTY_ARRAY
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun createSuppressWarningActions(element: PsiElement, diagnosticFactory: DiagnosticFactory<*>): List<SuppressIntentionAction> =
|
||||
createSuppressWarningActions(element, diagnosticFactory.severity, diagnosticFactory.name)
|
||||
|
||||
fun createSuppressWarningActions(element: PsiElement, severity: Severity, suppressionKey: String): List<SuppressIntentionAction> {
|
||||
if (severity != Severity.WARNING) {
|
||||
return emptyList()
|
||||
@@ -39,14 +31,13 @@ fun createSuppressWarningActions(element: PsiElement, severity: Severity, suppre
|
||||
val actions = arrayListOf<SuppressIntentionAction>()
|
||||
var current: PsiElement? = element
|
||||
var suppressAtStatementAllowed = true
|
||||
|
||||
while (current != null) {
|
||||
when {
|
||||
current is KtDeclaration && current !is KtDestructuringDeclaration -> {
|
||||
val declaration = current
|
||||
val kind = DeclarationKindDetector.detect(declaration)
|
||||
if (kind != null) {
|
||||
actions.add(Fe10QuickFixProvider.getInstance(declaration.project).createSuppressFix(declaration, suppressionKey, kind))
|
||||
if (kind != null ) {
|
||||
actions.add(KotlinSuppressIntentionAction(declaration, suppressionKey, kind))
|
||||
}
|
||||
suppressAtStatementAllowed = false
|
||||
}
|
||||
@@ -55,19 +46,19 @@ fun createSuppressWarningActions(element: PsiElement, severity: Severity, suppre
|
||||
// Add suppress action at first statement
|
||||
if (current.parent is KtBlockExpression || current.parent is KtDestructuringDeclaration) {
|
||||
val kind = if (current.parent is KtBlockExpression)
|
||||
KotlinBaseFe10HighlightingBundle.message("declaration.kind.statement")
|
||||
KotlinBaseCodeInsightBundle.message("declaration.kind.statement")
|
||||
else
|
||||
KotlinBaseFe10HighlightingBundle.message("declaration.kind.initializer")
|
||||
KotlinBaseCodeInsightBundle.message("declaration.kind.initializer")
|
||||
|
||||
val hostKind = AnnotationHostKind(kind, null, true)
|
||||
actions.add(Fe10QuickFixProvider.getInstance(current.project).createSuppressFix(current, suppressionKey, hostKind))
|
||||
actions.add(KotlinSuppressIntentionAction(current, suppressionKey, hostKind))
|
||||
suppressAtStatementAllowed = false
|
||||
}
|
||||
}
|
||||
|
||||
current is KtFile -> {
|
||||
val hostKind = AnnotationHostKind(KotlinBaseFe10HighlightingBundle.message("declaration.kind.file"), current.name, true)
|
||||
actions.add(Fe10QuickFixProvider.getInstance(current.project).createSuppressFix(current, suppressionKey, hostKind))
|
||||
val hostKind = AnnotationHostKind(KotlinBaseCodeInsightBundle.message("declaration.kind.file"), current.name, true)
|
||||
actions.add(KotlinSuppressIntentionAction(current, suppressionKey, hostKind))
|
||||
suppressAtStatementAllowed = false
|
||||
}
|
||||
}
|
||||
@@ -84,64 +75,55 @@ private object DeclarationKindDetector : KtVisitor<AnnotationHostKind?, Unit?>()
|
||||
override fun visitDeclaration(declaration: KtDeclaration, data: Unit?) = null
|
||||
|
||||
private fun getDeclarationName(declaration: KtDeclaration): @NlsSafe String {
|
||||
return declaration.name ?: KotlinBaseFe10HighlightingBundle.message("declaration.name.anonymous")
|
||||
return declaration.name ?: KotlinBaseCodeInsightBundle.message("declaration.name.anonymous")
|
||||
}
|
||||
|
||||
override fun visitClass(declaration: KtClass, data: Unit?): AnnotationHostKind {
|
||||
val kind = when {
|
||||
declaration.isInterface() -> KotlinBaseFe10HighlightingBundle.message("declaration.kind.interface")
|
||||
else -> KotlinBaseFe10HighlightingBundle.message("declaration.kind.class")
|
||||
declaration.isInterface() -> KotlinBaseCodeInsightBundle.message("declaration.kind.interface")
|
||||
else -> KotlinBaseCodeInsightBundle.message("declaration.kind.class")
|
||||
}
|
||||
return AnnotationHostKind(kind, getDeclarationName(declaration), newLineNeeded = true)
|
||||
}
|
||||
|
||||
override fun visitNamedFunction(declaration: KtNamedFunction, data: Unit?): AnnotationHostKind {
|
||||
val kind = KotlinBaseFe10HighlightingBundle.message("declaration.kind.fun")
|
||||
val kind = KotlinBaseCodeInsightBundle.message("declaration.kind.fun")
|
||||
return AnnotationHostKind(kind, getDeclarationName(declaration), newLineNeeded = true)
|
||||
}
|
||||
|
||||
override fun visitProperty(declaration: KtProperty, data: Unit?): AnnotationHostKind {
|
||||
val kind = when {
|
||||
declaration.isVar -> KotlinBaseFe10HighlightingBundle.message("declaration.kind.var")
|
||||
else -> KotlinBaseFe10HighlightingBundle.message("declaration.kind.val")
|
||||
declaration.isVar -> KotlinBaseCodeInsightBundle.message("declaration.kind.var")
|
||||
else -> KotlinBaseCodeInsightBundle.message("declaration.kind.val")
|
||||
}
|
||||
return AnnotationHostKind(kind, getDeclarationName(declaration), newLineNeeded = true)
|
||||
}
|
||||
|
||||
override fun visitDestructuringDeclaration(declaration: KtDestructuringDeclaration, data: Unit?): AnnotationHostKind {
|
||||
val kind = when {
|
||||
declaration.isVar -> KotlinBaseFe10HighlightingBundle.message("declaration.kind.var")
|
||||
else -> KotlinBaseFe10HighlightingBundle.message("declaration.kind.val")
|
||||
}
|
||||
val name = declaration.entries.joinToString(", ", "(", ")") { it.name!! }
|
||||
return AnnotationHostKind(kind, name, newLineNeeded = true)
|
||||
}
|
||||
|
||||
override fun visitTypeParameter(declaration: KtTypeParameter, data: Unit?): AnnotationHostKind {
|
||||
val kind = KotlinBaseFe10HighlightingBundle.message("declaration.kind.type.parameter")
|
||||
val kind = KotlinBaseCodeInsightBundle.message("declaration.kind.type.parameter")
|
||||
return AnnotationHostKind(kind, getDeclarationName(declaration), newLineNeeded = false)
|
||||
}
|
||||
|
||||
override fun visitEnumEntry(declaration: KtEnumEntry, data: Unit?): AnnotationHostKind {
|
||||
val kind = KotlinBaseFe10HighlightingBundle.message("declaration.kind.enum.entry")
|
||||
val kind = KotlinBaseCodeInsightBundle.message("declaration.kind.enum.entry")
|
||||
return AnnotationHostKind(kind, getDeclarationName(declaration), newLineNeeded = true)
|
||||
}
|
||||
|
||||
override fun visitParameter(declaration: KtParameter, data: Unit?): AnnotationHostKind {
|
||||
val kind = KotlinBaseFe10HighlightingBundle.message("declaration.kind.parameter")
|
||||
val kind = KotlinBaseCodeInsightBundle.message("declaration.kind.parameter")
|
||||
return AnnotationHostKind(kind, getDeclarationName(declaration), newLineNeeded = false)
|
||||
}
|
||||
|
||||
override fun visitSecondaryConstructor(declaration: KtSecondaryConstructor, data: Unit?): AnnotationHostKind {
|
||||
val kind = KotlinBaseFe10HighlightingBundle.message("declaration.kind.secondary.constructor.of")
|
||||
val kind = KotlinBaseCodeInsightBundle.message("declaration.kind.secondary.constructor.of")
|
||||
return AnnotationHostKind(kind, getDeclarationName(declaration), newLineNeeded = true)
|
||||
}
|
||||
|
||||
override fun visitObjectDeclaration(d: KtObjectDeclaration, data: Unit?): AnnotationHostKind? {
|
||||
return when {
|
||||
d.isCompanion() -> {
|
||||
val kind = KotlinBaseFe10HighlightingBundle.message("declaration.kind.companion.object")
|
||||
val name = KotlinBaseFe10HighlightingBundle.message(
|
||||
val kind = KotlinBaseCodeInsightBundle.message("declaration.kind.companion.object")
|
||||
val name = KotlinBaseCodeInsightBundle.message(
|
||||
"declaration.name.0.of.1",
|
||||
d.name.toString(),
|
||||
d.getStrictParentOfType<KtClass>()?.name.toString()
|
||||
@@ -150,7 +132,7 @@ private object DeclarationKindDetector : KtVisitor<AnnotationHostKind?, Unit?>()
|
||||
}
|
||||
d.parent is KtObjectLiteralExpression -> null
|
||||
else -> {
|
||||
val kind = KotlinBaseFe10HighlightingBundle.message("declaration.kind.object")
|
||||
val kind = KotlinBaseCodeInsightBundle.message("declaration.kind.object")
|
||||
AnnotationHostKind(kind, getDeclarationName(d), newLineNeeded = true)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
// 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.util
|
||||
|
||||
import org.jetbrains.kotlin.analysis.api.analyze
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
|
||||
import org.jetbrains.kotlin.idea.base.codeInsight.ShortenReferencesFacility
|
||||
import org.jetbrains.kotlin.idea.base.psi.KotlinPsiHeuristics
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.renderer.render
|
||||
|
||||
/**
|
||||
* Add a new annotation to the declaration or expression, or modify an existing annotation. Uses [analyze].
|
||||
*
|
||||
* Note: if the search for an existing annotation is enabled (this is default for compatibility), the function will
|
||||
* resolve annotation names. This should be avoided if the function is called from an event dispatcher thread (EDT),
|
||||
* e.g., if it is a part of a write action such as the `invoke()` function of a quick fix.
|
||||
*
|
||||
* @receiver the annotation owner to modify
|
||||
* @param annotationClassId fully qualified name of the annotation to add
|
||||
* @param annotationInnerText the inner text (rendered arguments) of the annotation, or null if there is no inner text (default)
|
||||
* @param useSiteTarget the use site target of the annotation, or null if no explicit use site target is provided
|
||||
* @param searchForExistingEntry `true` if the function should search for an existing annotation and update it instead of creating a new entry
|
||||
* @param whiteSpaceText the whitespace separator that should be inserted between annotation entries (newline by default)
|
||||
* @param addToExistingAnnotation a lambda expression to run on an existing annotation if it has been found
|
||||
* @return `true` if an annotation has been added or modified, `false` otherwise
|
||||
*/
|
||||
fun KtModifierListOwner.addAnnotation(
|
||||
annotationClassId: ClassId,
|
||||
annotationInnerText: String? = null,
|
||||
useSiteTarget: AnnotationUseSiteTarget? = null,
|
||||
searchForExistingEntry: Boolean = true,
|
||||
whiteSpaceText: String = "\n",
|
||||
addToExistingAnnotation: ((KtAnnotationEntry) -> Boolean)? = null
|
||||
): Boolean {
|
||||
val annotationText = buildString {
|
||||
append('@')
|
||||
if (useSiteTarget != null) append("${useSiteTarget.renderName}:")
|
||||
append(annotationClassId.asSingleFqName().render())
|
||||
if (annotationInnerText != null) append("($annotationInnerText)")
|
||||
}
|
||||
|
||||
val psiFactory = KtPsiFactory(project)
|
||||
val modifierList = modifierList
|
||||
|
||||
if (modifierList == null) {
|
||||
val addedAnnotation = addAnnotationEntry(psiFactory.createAnnotationEntry(annotationText))
|
||||
ShortenReferencesFacility.getInstance().shorten(addedAnnotation)
|
||||
return true
|
||||
}
|
||||
|
||||
val entry = if (searchForExistingEntry) findAnnotation(annotationClassId, useSiteTarget) else null
|
||||
if (entry == null) {
|
||||
// no annotation
|
||||
val newAnnotation = psiFactory.createAnnotationEntry(annotationText)
|
||||
val addedAnnotation = modifierList.addBefore(newAnnotation, modifierList.firstChild) as KtElement
|
||||
val whiteSpace = psiFactory.createWhiteSpace(whiteSpaceText)
|
||||
modifierList.addAfter(whiteSpace, addedAnnotation)
|
||||
|
||||
ShortenReferencesFacility.getInstance().shorten(addedAnnotation)
|
||||
return true
|
||||
}
|
||||
|
||||
if (addToExistingAnnotation != null) {
|
||||
return addToExistingAnnotation(entry)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
fun KtAnnotated.findAnnotation(
|
||||
annotationClassId: ClassId,
|
||||
useSiteTarget: AnnotationUseSiteTarget? = null,
|
||||
withResolve: Boolean = false
|
||||
): KtAnnotationEntry? {
|
||||
val annotationEntry = KotlinPsiHeuristics.findAnnotation(this, annotationClassId.asSingleFqName(), useSiteTarget)
|
||||
return annotationEntry?.takeIf { isAnnotationWithClassId(it, annotationClassId, withResolve) }
|
||||
}
|
||||
|
||||
fun KtFileAnnotationList.findAnnotation(
|
||||
annotationClassId: ClassId,
|
||||
useSiteTarget: AnnotationUseSiteTarget? = null,
|
||||
withResolve: Boolean = false
|
||||
): KtAnnotationEntry? {
|
||||
val annotationEntry = KotlinPsiHeuristics.findAnnotation(this, annotationClassId.asSingleFqName(), useSiteTarget)
|
||||
return annotationEntry?.takeIf { isAnnotationWithClassId(it, annotationClassId, withResolve) }
|
||||
}
|
||||
|
||||
private fun isAnnotationWithClassId(entry: KtAnnotationEntry, classId: ClassId, withResolve: Boolean): Boolean {
|
||||
if (entry.shortName != classId.shortClassName) return false
|
||||
if (!withResolve) return true
|
||||
return analyze(entry) {
|
||||
entry.typeReference?.getKtType()?.isClassTypeWithClassId(classId) == true
|
||||
}
|
||||
}
|
||||
@@ -10,11 +10,11 @@ import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.classic.quickfixes.UnresolvedReferenceQuickFixFactory
|
||||
import org.jetbrains.kotlin.idea.highlighter.AnnotationHostKind
|
||||
import org.jetbrains.kotlin.idea.highlighter.Fe10QuickFixProvider
|
||||
import org.jetbrains.kotlin.idea.highlighter.RegisterQuickFixesLaterIntentionAction
|
||||
import org.jetbrains.kotlin.idea.inspections.suppress.AnnotationHostKind
|
||||
import org.jetbrains.kotlin.idea.inspections.suppress.KotlinSuppressIntentionAction
|
||||
import org.jetbrains.kotlin.idea.quickfix.KotlinIntentionActionsFactory
|
||||
import org.jetbrains.kotlin.idea.quickfix.KotlinSuppressIntentionAction
|
||||
import org.jetbrains.kotlin.idea.quickfix.QuickFixes
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
|
||||
@@ -22,5 +22,6 @@
|
||||
<orderEntry type="module" module-name="kotlin.base.fe10.analysis" />
|
||||
<orderEntry type="module" module-name="intellij.platform.lang.impl" />
|
||||
<orderEntry type="module" module-name="intellij.java.psi" />
|
||||
<orderEntry type="module" module-name="kotlin.base.code-insight" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -21,24 +21,6 @@ root.package=root package
|
||||
function.receiver.0=receiver: {0}
|
||||
function.arguments=arguments:\u0020
|
||||
|
||||
declaration.name.0.of.1={0} of {1}
|
||||
declaration.name.anonymous=<anonymous>
|
||||
|
||||
declaration.kind.statement=statement
|
||||
declaration.kind.initializer=initializer
|
||||
declaration.kind.object=object
|
||||
declaration.kind.companion.object=companion object
|
||||
declaration.kind.file=file
|
||||
declaration.kind.secondary.constructor.of=secondary constructor of
|
||||
declaration.kind.enum.entry=enum entry
|
||||
declaration.kind.type.parameter=type parameter
|
||||
declaration.kind.class=class
|
||||
declaration.kind.interface=interface
|
||||
declaration.kind.fun=fun
|
||||
declaration.kind.val=val
|
||||
declaration.kind.var=var
|
||||
declaration.kind.parameter=parameter
|
||||
|
||||
html.0.has.no.corresponding.expected.declaration.1.html={0} has no corresponding expected declaration{1}
|
||||
html.0.is.not.abstract.and.does.not.implement.abstract.base.class.member.br.1.html={0} is not abstract and does not implement abstract base class member<br/>{1}
|
||||
html.0.is.not.abstract.and.does.not.implement.abstract.member.br.1.html={0} is not abstract and does not implement abstract member<br/>{1}
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.Severity
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
|
||||
import org.jetbrains.kotlin.idea.base.fe10.highlighting.KotlinBaseFe10HighlightingBundle
|
||||
import org.jetbrains.kotlin.idea.inspections.suppress.KotlinSuppressableWarningProblemGroup
|
||||
import org.jetbrains.kotlin.idea.util.application.isApplicationInternalMode
|
||||
import org.jetbrains.kotlin.idea.util.application.isUnitTestMode
|
||||
|
||||
@@ -45,7 +46,7 @@ class AnnotationPresentationInfo(
|
||||
for (range in ranges) {
|
||||
for (diagnostic in diagnostics) {
|
||||
val group = if (diagnostic.severity == Severity.WARNING) {
|
||||
KotlinSuppressableWarningProblemGroup(diagnostic.factory)
|
||||
KotlinSuppressableWarningProblemGroup(diagnostic.factory.name)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
@@ -6,11 +6,10 @@ import com.intellij.codeInspection.SuppressIntentionAction
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.NlsSafe
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.util.containers.MultiMap
|
||||
import org.jetbrains.annotations.Nls
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.idea.inspections.suppress.AnnotationHostKind
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
|
||||
interface Fe10QuickFixProvider {
|
||||
@@ -25,17 +24,6 @@ interface Fe10QuickFixProvider {
|
||||
fun createSuppressFix(element: KtElement, suppressionKey: String, hostKind: AnnotationHostKind): SuppressIntentionAction
|
||||
}
|
||||
|
||||
class AnnotationHostKind(
|
||||
/** Human-readable `KtElement` kind on which the annotation is placed. E.g., 'file', 'class' or 'statement'. */
|
||||
@Nls val kind: String,
|
||||
|
||||
/** Name of the annotation owner. Might be null if the owner is not a named declaration (for instance, if it is a statement). */
|
||||
@NlsSafe val name: String?,
|
||||
|
||||
/** True if the annotation needs to be added to a separate line. */
|
||||
val newLineNeeded: Boolean
|
||||
)
|
||||
|
||||
object RegisterQuickFixesLaterIntentionAction : IntentionAction {
|
||||
override fun getText(): String = ""
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.resolve.konan.diagnostics.ErrorsNative
|
||||
import java.lang.reflect.Modifier
|
||||
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.classic.inspections.AbstractKotlinInspection
|
||||
import org.jetbrains.kotlin.idea.inspections.suppress.KotlinInspectionSuppressor
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
|
||||
class KotlinRedundantDiagnosticSuppressInspection : AbstractKotlinInspection() {
|
||||
|
||||
Reference in New Issue
Block a user