ScheduledForRemovalInspection and UnstableApiUsageInspection: convert to Kotlin and support method overriding case.

GitOrigin-RevId: dd24dbf613673ae788e1f2ae0229c0ef27e66fe3
This commit is contained in:
Sergey Patrikeev
2019-06-21 01:44:29 +03:00
committed by intellij-monorepo-bot
parent 856a375563
commit ed1acb9b0d
11 changed files with 217 additions and 131 deletions

View File

@@ -4,9 +4,13 @@ jvm.inspections.unstable.api.usage.display.name=Unstable API Usage
jvm.inspections.unstable.api.usage.annotations.list=Unstable API annotations
jvm.inspections.api.usage.ignore.inside.imports=Ignore inside imports
jvm.inspections.unstable.api.usage.description=''{0}'' is marked unstable
jvm.inspections.unstable.method.overridden.description=Overridden method ''{0}'' is marked unstable
jvm.inspections.scheduled.for.removal.display.name=Usage of API scheduled for removal
jvm.inspections.scheduled.for.removal.description.no.version=''{0}'' is scheduled for removal
jvm.inspections.scheduled.for.removal.description.with.version=''{0}'' is scheduled for removal in {1}
jvm.inspections.scheduled.for.removal.method.overridden.no.version.description=Overridden method ''{0}'' is scheduled for removal
jvm.inspections.scheduled.for.removal.method.overridden.with.version.description=Overridden method ''{0}'' is scheduled for removal in {1}
jvm.inspections.missing.deprecated.annotation.on.scheduled.for.removal.api.display.name=Missing '@Deprecated' annotation on scheduled for removal API
jvm.inspections.missing.deprecated.annotation.on.scheduled.for.removal.api.description=Scheduled for removal API must also be marked with '@Deprecated' annotation
jvm.inspections.missing.deprecated.annotation.on.scheduled.for.removal.api.quick.fix=Add '@Deprecated' annotation

View File

@@ -2,9 +2,11 @@
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;
@@ -17,4 +19,10 @@ public interface AnnotatedApiUsageProcessor {
@NotNull PsiModifierListOwner annotatedTarget,
@NotNull List<? extends PsiAnnotation> annotations
);
void processAnnotatedMethodOverriding(
@NotNull UMethod method,
@NotNull PsiMethod overriddenMethod,
@NotNull List<? extends PsiAnnotation> annotations
);
}

View File

@@ -19,6 +19,7 @@ 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;
@@ -120,6 +121,14 @@ public abstract class AnnotatedElementInspectionBase extends LocalInspectionTool
}
}
@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()) {

View File

@@ -1,59 +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.psi.PsiAnnotation;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiModifierListOwner;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.uast.UElement;
import java.util.Collections;
import java.util.List;
//TODO quickfix like in deprecation inspection?
public class ScheduledForRemovalInspection extends AnnotatedElementInspectionBase {
private static final String ANNOTATION_NAME = ApiStatus.ScheduledForRemoval.class.getCanonicalName();
@NotNull
@Override
protected List<String> getAnnotations() {
return Collections.singletonList(ANNOTATION_NAME);
}
@NotNull
@Override
protected AnnotatedApiUsageProcessor buildAnnotatedApiUsageProcessor(@NotNull ProblemsHolder holder) {
return new AnnotatedApiUsageProcessor() {
@Override
public void processAnnotatedTarget(@NotNull UElement sourceNode,
@NotNull PsiModifierListOwner annotatedTarget,
@NotNull List<? extends PsiAnnotation> annotations) {
if (!AnnotatedElementInspectionBase.isLibraryElement(annotatedTarget)) {
return;
}
//TODO determine highlight severity like in MarkedForRemovalInspection?
PsiElement elementToHighlight = sourceNode.getSourcePsi();
PsiAnnotation scheduledForRemoval = ContainerUtil.find(
annotations, psiAnnotation -> psiAnnotation.hasQualifiedName(ANNOTATION_NAME)
);
if (elementToHighlight != null && scheduledForRemoval != null) {
String inVersion = AnnotationUtil.getDeclaredStringAttributeValue(scheduledForRemoval, "inVersion");
String targetText = getPresentableText(annotatedTarget);
String message;
if (inVersion == null || inVersion.isEmpty()) {
message = JvmAnalysisBundle.message("jvm.inspections.scheduled.for.removal.description.no.version", targetText);
}
else {
message = JvmAnalysisBundle.message("jvm.inspections.scheduled.for.removal.description.with.version", targetText, inVersion);
}
holder.registerProblem(elementToHighlight, message, ProblemHighlightType.LIKE_MARKED_FOR_REMOVAL);
}
}
};
}
}

View File

@@ -0,0 +1,76 @@
// 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.psi.PsiAnnotation
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiModifierListOwner
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
//TODO quickfix like in deprecation inspection?
class ScheduledForRemovalInspection : AnnotatedElementInspectionBase() {
private companion object {
private val ANNOTATION_NAME = ApiStatus.ScheduledForRemoval::class.java.canonicalName
}
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)
}
}
}
}

View File

@@ -1,72 +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.codeInspection.util.SpecialAnnotationsUtil;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiModifierListOwner;
import com.siyeh.ig.ui.ExternalizableStringSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.uast.UElement;
import javax.swing.*;
import java.awt.*;
import java.util.List;
import static com.intellij.codeInspection.deprecation.DeprecationInspectionBase.getPresentableName;
public class UnstableApiUsageInspection extends AnnotatedElementInspectionBase {
public final List<String> unstableApiAnnotations = new ExternalizableStringSet(
"org.jetbrains.annotations.ApiStatus.Experimental",
"org.jetbrains.annotations.ApiStatus.Internal",
"com.google.common.annotations.Beta",
"io.reactivex.annotations.Beta",
"io.reactivex.annotations.Experimental",
"rx.annotations.Experimental",
"rx.annotations.Beta",
"org.apache.http.annotation.Beta",
"org.gradle.api.Incubating"
);
@NotNull
@Override
protected List<String> getAnnotations() {
return unstableApiAnnotations;
}
@NotNull
@Override
protected AnnotatedApiUsageProcessor buildAnnotatedApiUsageProcessor(@NotNull ProblemsHolder holder) {
return new AnnotatedApiUsageProcessor() {
@Override
public void processAnnotatedTarget(@NotNull UElement sourceNode,
@NotNull PsiModifierListOwner annotatedTarget,
@NotNull List<? extends PsiAnnotation> annotations) {
if (!AnnotatedElementInspectionBase.isLibraryElement(annotatedTarget)) {
return;
}
String message = JvmAnalysisBundle.message("jvm.inspections.unstable.api.usage.description", getPresentableName(annotatedTarget));
PsiElement elementToHighlight = sourceNode.getSourcePsi();
if (elementToHighlight != null) {
holder.registerProblem(elementToHighlight, message, ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
}
}
};
}
@NotNull
@Override
public JPanel createOptionsPanel() {
JPanel checkboxPanel = super.createOptionsPanel();
//TODO in add annotation window "Include non-project items" should be enabled by default
JPanel annotationsListControl = SpecialAnnotationsUtil.createSpecialAnnotationsListControl(
unstableApiAnnotations, JvmAnalysisBundle.message("jvm.inspections.unstable.api.usage.annotations.list"));
JPanel panel = new JPanel(new BorderLayout(2, 2));
panel.add(checkboxPanel, BorderLayout.NORTH);
panel.add(annotationsListControl, BorderLayout.CENTER);
return panel;
}
}

View File

@@ -0,0 +1,81 @@
// 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.codeInspection.deprecation.DeprecationInspectionBase.getPresentableName
import com.intellij.codeInspection.util.SpecialAnnotationsUtil
import com.intellij.psi.PsiAnnotation
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiModifierListOwner
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 java.awt.BorderLayout
import javax.swing.JPanel
class UnstableApiUsageInspection : AnnotatedElementInspectionBase() {
val unstableApiAnnotations: List<String> = ExternalizableStringSet(
"org.jetbrains.annotations.ApiStatus.Experimental",
"org.jetbrains.annotations.ApiStatus.Internal",
"com.google.common.annotations.Beta",
"io.reactivex.annotations.Beta",
"io.reactivex.annotations.Experimental",
"rx.annotations.Experimental",
"rx.annotations.Beta",
"org.apache.http.annotation.Beta",
"org.gradle.api.Incubating"
)
override fun getAnnotations() = unstableApiAnnotations
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 createOptionsPanel(): JPanel {
val checkboxPanel = super.createOptionsPanel()
//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"))
val panel = JPanel(BorderLayout(2, 2))
panel.add(checkboxPanel, BorderLayout.NORTH)
panel.add(annotationsListControl, BorderLayout.CENTER)
return panel
}
}