mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Refactoring of ScheduledForRemovalInspection\UnstableApiUsageInspection: avoid unnecessary inheritance from base class.
GitOrigin-RevId: fee3aa43418998f249a8d094bd108f6075748a93
This commit is contained in:
committed by
intellij-monorepo-bot
parent
ea1f0bc5dd
commit
fcac91fe5f
@@ -1,28 +0,0 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.codeInspection;
|
||||
|
||||
import com.intellij.psi.PsiAnnotation;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.PsiModifierListOwner;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.uast.UElement;
|
||||
import org.jetbrains.uast.UMethod;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Processor of usages of APIs marked with specified annotations, which are detected by {@link AnnotatedElementInspectionBase}.
|
||||
*/
|
||||
public interface AnnotatedApiUsageProcessor {
|
||||
void processAnnotatedTarget(
|
||||
@NotNull UElement sourceNode,
|
||||
@NotNull PsiModifierListOwner annotatedTarget,
|
||||
@NotNull List<? extends PsiAnnotation> annotations
|
||||
);
|
||||
|
||||
void processAnnotatedMethodOverriding(
|
||||
@NotNull UMethod method,
|
||||
@NotNull PsiMethod overriddenMethod,
|
||||
@NotNull List<? extends PsiAnnotation> annotations
|
||||
);
|
||||
}
|
||||
-140
@@ -1,140 +0,0 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.codeInspection;
|
||||
|
||||
import com.intellij.analysis.JvmAnalysisBundle;
|
||||
import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.codeInspection.apiUsage.ApiUsageProcessor;
|
||||
import com.intellij.codeInspection.apiUsage.ApiUsageUastVisitor;
|
||||
import com.intellij.codeInspection.deprecation.DeprecationInspectionBase;
|
||||
import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ProjectFileIndex;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.util.PsiUtilCore;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.uast.UClass;
|
||||
import org.jetbrains.uast.UElement;
|
||||
import org.jetbrains.uast.UExpression;
|
||||
import org.jetbrains.uast.UMethod;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This class can be extended by inspections that should report usage of elements annotated with some particular annotation(s).
|
||||
*/
|
||||
public abstract class AnnotatedElementInspectionBase extends LocalInspectionTool {
|
||||
public boolean myIgnoreInsideImports = true;
|
||||
|
||||
|
||||
@NotNull
|
||||
protected abstract List<String> getAnnotations();
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public JPanel createOptionsPanel() {
|
||||
return new SingleCheckboxOptionsPanel(
|
||||
JvmAnalysisBundle.message("jvm.inspections.api.usage.ignore.inside.imports"), this, "myIgnoreInsideImports");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected abstract AnnotatedApiUsageProcessor buildAnnotatedApiUsageProcessor(@NotNull ProblemsHolder holder);
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
|
||||
if (!isApplicable(holder.getFile(), holder.getProject())) {
|
||||
return PsiElementVisitor.EMPTY_VISITOR;
|
||||
}
|
||||
|
||||
AnnotatedApiUsageProcessor annotatedApiProcessor = buildAnnotatedApiUsageProcessor(holder);
|
||||
AnnotatedApiUsageProcessorBridge processorBridge = new AnnotatedApiUsageProcessorBridge(
|
||||
myIgnoreInsideImports, getAnnotations(), annotatedApiProcessor
|
||||
);
|
||||
return ApiUsageUastVisitor.createPsiElementVisitor(processorBridge);
|
||||
}
|
||||
|
||||
private boolean isApplicable(@Nullable PsiFile file, @Nullable Project project) {
|
||||
if (file == null || project == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(project);
|
||||
GlobalSearchScope scope = file.getResolveScope();
|
||||
for (String annotation : getAnnotations()) {
|
||||
if (javaPsiFacade.findClass(annotation, scope) != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static String getPresentableText(@NotNull PsiElement psiElement) {
|
||||
return DeprecationInspectionBase.getPresentableName(psiElement);
|
||||
}
|
||||
|
||||
protected static boolean isLibraryElement(@NotNull PsiElement element) {
|
||||
if (ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
return true;
|
||||
}
|
||||
VirtualFile containingVirtualFile = PsiUtilCore.getVirtualFile(element);
|
||||
return containingVirtualFile != null && ProjectFileIndex.getInstance(element.getProject()).isInLibraryClasses(containingVirtualFile);
|
||||
}
|
||||
|
||||
private static final class AnnotatedApiUsageProcessorBridge implements ApiUsageProcessor {
|
||||
private final boolean myIgnoreInsideImports;
|
||||
private final List<String> myAnnotations;
|
||||
private final AnnotatedApiUsageProcessor myAnnotatedApiProcessor;
|
||||
|
||||
private AnnotatedApiUsageProcessorBridge(boolean ignoreInsideImports,
|
||||
@NotNull List<String> annotations,
|
||||
@NotNull AnnotatedApiUsageProcessor annotatedApiProcessor) {
|
||||
myIgnoreInsideImports = ignoreInsideImports;
|
||||
myAnnotations = annotations;
|
||||
myAnnotatedApiProcessor = annotatedApiProcessor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processImportReference(@NotNull UElement sourceNode, @NotNull PsiModifierListOwner target) {
|
||||
if (!myIgnoreInsideImports) {
|
||||
maybeProcessAnnotatedTarget(sourceNode, target);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processReference(@NotNull UElement sourceNode, @NotNull PsiModifierListOwner target, @Nullable UExpression qualifier) {
|
||||
maybeProcessAnnotatedTarget(sourceNode, target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processConstructorInvocation(@NotNull UElement sourceNode,
|
||||
@NotNull PsiClass instantiatedClass,
|
||||
@Nullable PsiMethod constructor,
|
||||
@Nullable UClass subclassDeclaration) {
|
||||
if (constructor != null) {
|
||||
maybeProcessAnnotatedTarget(sourceNode, constructor);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processMethodOverriding(@NotNull UMethod method, @NotNull PsiMethod overriddenMethod) {
|
||||
List<PsiAnnotation> annotations = AnnotationUtil.findAllAnnotations(overriddenMethod, myAnnotations, false);
|
||||
if (!annotations.isEmpty()) {
|
||||
myAnnotatedApiProcessor.processAnnotatedMethodOverriding(method, overriddenMethod, annotations);
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeProcessAnnotatedTarget(@NotNull UElement sourceNode, @NotNull PsiModifierListOwner target) {
|
||||
List<PsiAnnotation> annotations = AnnotationUtil.findAllAnnotations(target, myAnnotations, false);
|
||||
if (annotations.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
myAnnotatedApiProcessor.processAnnotatedTarget(sourceNode, target, annotations);
|
||||
}
|
||||
}
|
||||
}
|
||||
+103
-63
@@ -3,74 +3,114 @@ package com.intellij.codeInspection
|
||||
|
||||
import com.intellij.analysis.JvmAnalysisBundle
|
||||
import com.intellij.codeInsight.AnnotationUtil
|
||||
import com.intellij.psi.PsiAnnotation
|
||||
import com.intellij.psi.PsiMethod
|
||||
import com.intellij.psi.PsiModifierListOwner
|
||||
import com.intellij.codeInspection.apiUsage.ApiUsageProcessor
|
||||
import com.intellij.codeInspection.apiUsage.ApiUsageUastVisitor
|
||||
import com.intellij.codeInspection.deprecation.DeprecationInspectionBase
|
||||
import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.roots.ProjectFileIndex
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.util.PsiUtilCore
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.uast.UDeclaration
|
||||
import org.jetbrains.uast.UElement
|
||||
import org.jetbrains.uast.UMethod
|
||||
import org.jetbrains.uast.sourcePsiElement
|
||||
import org.jetbrains.uast.*
|
||||
|
||||
//TODO quickfix like in deprecation inspection?
|
||||
class ScheduledForRemovalInspection : AnnotatedElementInspectionBase() {
|
||||
class ScheduledForRemovalInspection : LocalInspectionTool() {
|
||||
|
||||
@JvmField
|
||||
var myIgnoreInsideImports: Boolean = true
|
||||
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) =
|
||||
ApiUsageUastVisitor.createPsiElementVisitor(
|
||||
ScheduledForRemovalApiUsageProcessor(holder, myIgnoreInsideImports)
|
||||
)
|
||||
|
||||
override fun createOptionsPanel() = SingleCheckboxOptionsPanel(
|
||||
JvmAnalysisBundle.message("jvm.inspections.api.usage.ignore.inside.imports"), this, "myIgnoreInsideImports"
|
||||
)
|
||||
}
|
||||
|
||||
private class ScheduledForRemovalApiUsageProcessor(
|
||||
private val problemsHolder: ProblemsHolder,
|
||||
private val ignoreInsideImports: Boolean
|
||||
) : ApiUsageProcessor {
|
||||
|
||||
private companion object {
|
||||
private val ANNOTATION_NAME = ApiStatus.ScheduledForRemoval::class.java.canonicalName
|
||||
|
||||
fun findScheduledForRemovalAnnotation(psiModifierListOwner: PsiModifierListOwner): PsiAnnotation? {
|
||||
val ownAnnotation = AnnotationUtil.findAnnotation(psiModifierListOwner, listOf(ANNOTATION_NAME), false)
|
||||
if (ownAnnotation != null) {
|
||||
return ownAnnotation
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun isLibraryElement(element: PsiElement): Boolean {
|
||||
if (ApplicationManager.getApplication().isUnitTestMode) {
|
||||
return true
|
||||
}
|
||||
val containingVirtualFile = PsiUtilCore.getVirtualFile(element)
|
||||
return containingVirtualFile != null && ProjectFileIndex.getInstance(element.project).isInLibraryClasses(containingVirtualFile)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAnnotations() = listOf(ANNOTATION_NAME)
|
||||
|
||||
override fun buildAnnotatedApiUsageProcessor(holder: ProblemsHolder): AnnotatedApiUsageProcessor =
|
||||
object : AnnotatedApiUsageProcessor {
|
||||
override fun processAnnotatedTarget(
|
||||
sourceNode: UElement,
|
||||
annotatedTarget: PsiModifierListOwner,
|
||||
annotations: List<PsiAnnotation>
|
||||
) {
|
||||
checkScheduledForRemovalApiUsage(annotatedTarget, sourceNode, annotations, false)
|
||||
}
|
||||
|
||||
override fun processAnnotatedMethodOverriding(
|
||||
method: UMethod,
|
||||
overriddenMethod: PsiMethod,
|
||||
annotations: List<PsiAnnotation>
|
||||
) {
|
||||
checkScheduledForRemovalApiUsage(overriddenMethod, method, annotations, true)
|
||||
}
|
||||
|
||||
private fun checkScheduledForRemovalApiUsage(
|
||||
annotatedTarget: PsiModifierListOwner,
|
||||
sourceNode: UElement,
|
||||
annotations: List<PsiAnnotation>,
|
||||
isMethodOverriding: Boolean
|
||||
) {
|
||||
if (!isLibraryElement(annotatedTarget)) {
|
||||
return
|
||||
}
|
||||
val elementToHighlight = (sourceNode as? UDeclaration)?.uastAnchor.sourcePsiElement ?: sourceNode.sourcePsi
|
||||
val scheduledForRemoval = annotations.find { psiAnnotation -> psiAnnotation.hasQualifiedName(ANNOTATION_NAME) }
|
||||
if (elementToHighlight != null && scheduledForRemoval != null) {
|
||||
val inVersion = AnnotationUtil.getDeclaredStringAttributeValue(scheduledForRemoval, "inVersion")
|
||||
val targetName = getPresentableText(annotatedTarget)
|
||||
val isEmptyVersion = inVersion == null || inVersion.isEmpty()
|
||||
val message: String = when {
|
||||
isEmptyVersion && isMethodOverriding -> JvmAnalysisBundle.message(
|
||||
"jvm.inspections.scheduled.for.removal.method.overridden.no.version.description", targetName
|
||||
)
|
||||
|
||||
!isEmptyVersion && isMethodOverriding -> JvmAnalysisBundle.message(
|
||||
"jvm.inspections.scheduled.for.removal.method.overridden.with.version.description", targetName, inVersion
|
||||
)
|
||||
|
||||
!isEmptyVersion && !isMethodOverriding -> JvmAnalysisBundle.message(
|
||||
"jvm.inspections.scheduled.for.removal.description.with.version", targetName, inVersion
|
||||
)
|
||||
|
||||
else -> JvmAnalysisBundle.message("jvm.inspections.scheduled.for.removal.description.no.version", targetName)
|
||||
}
|
||||
holder.registerProblem(elementToHighlight, message, ProblemHighlightType.LIKE_MARKED_FOR_REMOVAL)
|
||||
}
|
||||
}
|
||||
override fun processImportReference(sourceNode: UElement, target: PsiModifierListOwner) {
|
||||
if (!ignoreInsideImports) {
|
||||
checkScheduledForRemovalApiUsage(target, sourceNode, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun processReference(sourceNode: UElement, target: PsiModifierListOwner, qualifier: UExpression?) {
|
||||
checkScheduledForRemovalApiUsage(target, sourceNode, false)
|
||||
}
|
||||
|
||||
override fun processConstructorInvocation(sourceNode: UElement,
|
||||
instantiatedClass: PsiClass,
|
||||
constructor: PsiMethod?,
|
||||
subclassDeclaration: UClass?) {
|
||||
if (constructor != null) {
|
||||
checkScheduledForRemovalApiUsage(constructor, sourceNode, false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun processMethodOverriding(method: UMethod, overriddenMethod: PsiMethod) {
|
||||
checkScheduledForRemovalApiUsage(overriddenMethod, method, true)
|
||||
}
|
||||
|
||||
fun checkScheduledForRemovalApiUsage(target: PsiModifierListOwner, sourceNode: UElement, isMethodOverriding: Boolean) {
|
||||
if (!isLibraryElement(target)) {
|
||||
return
|
||||
}
|
||||
val scheduledForRemovalAnnotation = findScheduledForRemovalAnnotation(target) ?: return
|
||||
val elementToHighlight = (sourceNode as? UDeclaration)?.uastAnchor.sourcePsiElement ?: sourceNode.sourcePsi
|
||||
if (elementToHighlight != null) {
|
||||
val message = buildMessage(scheduledForRemovalAnnotation, target, isMethodOverriding)
|
||||
problemsHolder.registerProblem(elementToHighlight, message, ProblemHighlightType.LIKE_MARKED_FOR_REMOVAL)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildMessage(
|
||||
scheduledForRemovalAnnotation: PsiAnnotation,
|
||||
target: PsiModifierListOwner,
|
||||
isMethodOverriding: Boolean
|
||||
): String {
|
||||
val inVersion = AnnotationUtil.getDeclaredStringAttributeValue(scheduledForRemovalAnnotation, "inVersion")
|
||||
val targetName = DeprecationInspectionBase.getPresentableName(target)
|
||||
val isEmptyVersion = inVersion == null || inVersion.isEmpty()
|
||||
return when {
|
||||
isEmptyVersion && isMethodOverriding -> JvmAnalysisBundle.message(
|
||||
"jvm.inspections.scheduled.for.removal.method.overridden.no.version.description", targetName
|
||||
)
|
||||
|
||||
!isEmptyVersion && isMethodOverriding -> JvmAnalysisBundle.message(
|
||||
"jvm.inspections.scheduled.for.removal.method.overridden.with.version.description", targetName, inVersion
|
||||
)
|
||||
|
||||
!isEmptyVersion && !isMethodOverriding -> JvmAnalysisBundle.message(
|
||||
"jvm.inspections.scheduled.for.removal.description.with.version", targetName, inVersion
|
||||
)
|
||||
|
||||
else -> JvmAnalysisBundle.message("jvm.inspections.scheduled.for.removal.description.no.version", targetName)
|
||||
}
|
||||
}
|
||||
}
|
||||
+86
-44
@@ -2,21 +2,26 @@
|
||||
package com.intellij.codeInspection
|
||||
|
||||
import com.intellij.analysis.JvmAnalysisBundle
|
||||
import com.intellij.codeInsight.AnnotationUtil
|
||||
import com.intellij.codeInspection.apiUsage.ApiUsageProcessor
|
||||
import com.intellij.codeInspection.apiUsage.ApiUsageUastVisitor
|
||||
import com.intellij.codeInspection.deprecation.DeprecationInspectionBase.getPresentableName
|
||||
import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel
|
||||
import com.intellij.codeInspection.util.SpecialAnnotationsUtil
|
||||
import com.intellij.psi.PsiAnnotation
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.roots.ProjectFileIndex
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiMethod
|
||||
import com.intellij.psi.PsiModifierListOwner
|
||||
import com.intellij.psi.util.PsiUtilCore
|
||||
import com.intellij.util.ArrayUtilRt
|
||||
import com.siyeh.ig.ui.ExternalizableStringSet
|
||||
import org.jetbrains.uast.UDeclaration
|
||||
import org.jetbrains.uast.UElement
|
||||
import org.jetbrains.uast.UMethod
|
||||
import org.jetbrains.uast.sourcePsiElement
|
||||
import org.jetbrains.uast.*
|
||||
import java.awt.BorderLayout
|
||||
import javax.swing.JPanel
|
||||
|
||||
class UnstableApiUsageInspection : AnnotatedElementInspectionBase() {
|
||||
class UnstableApiUsageInspection : LocalInspectionTool() {
|
||||
|
||||
companion object {
|
||||
val DEFAULT_UNSTABLE_API_ANNOTATIONS: List<String> = listOf(
|
||||
@@ -37,50 +42,23 @@ class UnstableApiUsageInspection : AnnotatedElementInspectionBase() {
|
||||
*ArrayUtilRt.toStringArray(DEFAULT_UNSTABLE_API_ANNOTATIONS)
|
||||
)
|
||||
|
||||
override fun getAnnotations() = unstableApiAnnotations
|
||||
@JvmField
|
||||
var myIgnoreInsideImports: Boolean = true
|
||||
|
||||
override fun buildAnnotatedApiUsageProcessor(holder: ProblemsHolder) =
|
||||
object : AnnotatedApiUsageProcessor {
|
||||
override fun processAnnotatedTarget(
|
||||
sourceNode: UElement,
|
||||
annotatedTarget: PsiModifierListOwner,
|
||||
annotations: List<PsiAnnotation>
|
||||
) {
|
||||
checkUnstableApiUsage(annotatedTarget, sourceNode, false)
|
||||
}
|
||||
|
||||
override fun processAnnotatedMethodOverriding(
|
||||
method: UMethod,
|
||||
overriddenMethod: PsiMethod,
|
||||
annotations: List<PsiAnnotation>
|
||||
) {
|
||||
checkUnstableApiUsage(overriddenMethod, method, true)
|
||||
}
|
||||
|
||||
private fun checkUnstableApiUsage(annotatedTarget: PsiModifierListOwner, sourceNode: UElement, isMethodOverriding: Boolean) {
|
||||
if (!isLibraryElement(annotatedTarget)) {
|
||||
return
|
||||
}
|
||||
val targetName = getPresentableName(annotatedTarget)
|
||||
val message = if (isMethodOverriding) {
|
||||
JvmAnalysisBundle.message("jvm.inspections.unstable.method.overridden.description", targetName)
|
||||
}
|
||||
else {
|
||||
JvmAnalysisBundle.message("jvm.inspections.unstable.api.usage.description", targetName)
|
||||
}
|
||||
val elementToHighlight = (sourceNode as? UDeclaration)?.uastAnchor.sourcePsiElement ?: sourceNode.sourcePsi
|
||||
if (elementToHighlight != null) {
|
||||
holder.registerProblem(elementToHighlight, message, ProblemHighlightType.GENERIC_ERROR_OR_WARNING)
|
||||
}
|
||||
}
|
||||
}
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) =
|
||||
ApiUsageUastVisitor.createPsiElementVisitor(
|
||||
UnstableApiUsageProcessor(holder, myIgnoreInsideImports, unstableApiAnnotations.toList())
|
||||
)
|
||||
|
||||
override fun createOptionsPanel(): JPanel {
|
||||
val checkboxPanel = super.createOptionsPanel()
|
||||
val checkboxPanel = SingleCheckboxOptionsPanel(
|
||||
JvmAnalysisBundle.message("jvm.inspections.api.usage.ignore.inside.imports"), this, "myIgnoreInsideImports"
|
||||
)
|
||||
|
||||
//TODO in add annotation window "Include non-project items" should be enabled by default
|
||||
val annotationsListControl = SpecialAnnotationsUtil.createSpecialAnnotationsListControl(
|
||||
unstableApiAnnotations, JvmAnalysisBundle.message("jvm.inspections.unstable.api.usage.annotations.list"))
|
||||
unstableApiAnnotations, JvmAnalysisBundle.message("jvm.inspections.unstable.api.usage.annotations.list")
|
||||
)
|
||||
|
||||
val panel = JPanel(BorderLayout(2, 2))
|
||||
panel.add(checkboxPanel, BorderLayout.NORTH)
|
||||
@@ -88,3 +66,67 @@ class UnstableApiUsageInspection : AnnotatedElementInspectionBase() {
|
||||
return panel
|
||||
}
|
||||
}
|
||||
|
||||
private class UnstableApiUsageProcessor(
|
||||
private val problemsHolder: ProblemsHolder,
|
||||
private val ignoreInsideImports: Boolean,
|
||||
private val annotations: List<String>
|
||||
) : ApiUsageProcessor {
|
||||
|
||||
private companion object {
|
||||
fun isLibraryElement(element: PsiElement): Boolean {
|
||||
if (ApplicationManager.getApplication().isUnitTestMode) {
|
||||
return true
|
||||
}
|
||||
val containingVirtualFile = PsiUtilCore.getVirtualFile(element)
|
||||
return containingVirtualFile != null && ProjectFileIndex.getInstance(element.project).isInLibraryClasses(containingVirtualFile)
|
||||
}
|
||||
}
|
||||
|
||||
override fun processImportReference(sourceNode: UElement, target: PsiModifierListOwner) {
|
||||
if (!ignoreInsideImports) {
|
||||
checkUnstableApiUsage(target, sourceNode, false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun processReference(sourceNode: UElement, target: PsiModifierListOwner, qualifier: UExpression?) {
|
||||
checkUnstableApiUsage(target, sourceNode, false)
|
||||
}
|
||||
|
||||
override fun processConstructorInvocation(
|
||||
sourceNode: UElement,
|
||||
instantiatedClass: PsiClass,
|
||||
constructor: PsiMethod?,
|
||||
subclassDeclaration: UClass?
|
||||
) {
|
||||
if (constructor != null) {
|
||||
checkUnstableApiUsage(constructor, sourceNode, false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun processMethodOverriding(method: UMethod, overriddenMethod: PsiMethod) {
|
||||
checkUnstableApiUsage(overriddenMethod, method, true)
|
||||
}
|
||||
|
||||
private fun checkUnstableApiUsage(target: PsiModifierListOwner, sourceNode: UElement, isMethodOverriding: Boolean) {
|
||||
if (!isLibraryElement(target)) {
|
||||
return
|
||||
}
|
||||
val annotations = AnnotationUtil.findAllAnnotations(target, annotations, false)
|
||||
if (annotations.isEmpty()) {
|
||||
return
|
||||
}
|
||||
val targetName = getPresentableName(target)
|
||||
val message = if (isMethodOverriding) {
|
||||
JvmAnalysisBundle.message("jvm.inspections.unstable.method.overridden.description", targetName)
|
||||
}
|
||||
else {
|
||||
JvmAnalysisBundle.message("jvm.inspections.unstable.api.usage.description", targetName)
|
||||
}
|
||||
val elementToHighlight = (sourceNode as? UDeclaration)?.uastAnchor.sourcePsiElement ?: sourceNode.sourcePsi
|
||||
if (elementToHighlight != null) {
|
||||
problemsHolder.registerProblem(elementToHighlight, message, ProblemHighlightType.GENERIC_ERROR_OR_WARNING)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user