mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
quick fix for deprecation inspection: replace method call with suggested in javadoc if no red code produced IDEA-130816
This commit is contained in:
+94
-4
@@ -19,9 +19,7 @@ import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.codeInsight.daemon.JavaErrorMessages;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightMessageUtil;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.JavaHighlightUtil;
|
||||
import com.intellij.codeInspection.BaseJavaBatchLocalInspectionTool;
|
||||
import com.intellij.codeInspection.ProblemHighlightType;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.codeInspection.*;
|
||||
import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel;
|
||||
import com.intellij.lang.annotation.HighlightSeverity;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
@@ -31,7 +29,14 @@ import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.JavaConstantExpressionEvaluator;
|
||||
import com.intellij.psi.impl.PsiImplUtil;
|
||||
import com.intellij.psi.infos.MethodCandidateInfo;
|
||||
import com.intellij.psi.javadoc.PsiDocComment;
|
||||
import com.intellij.psi.javadoc.PsiDocTag;
|
||||
import com.intellij.psi.javadoc.PsiInlineDocTag;
|
||||
import com.intellij.psi.util.*;
|
||||
import com.intellij.refactoring.util.RefactoringChangeUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import one.util.streamex.MoreCollectors;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -275,7 +280,16 @@ abstract class DeprecationInspectionBase extends BaseJavaBatchLocalInspectionToo
|
||||
String description = JavaErrorMessages.message(forRemoval ? "marked.for.removal.symbol" : "deprecated.symbol",
|
||||
HighlightMessageUtil.getSymbolName(refElement, PsiSubstitutor.EMPTY));
|
||||
|
||||
holder.registerProblem(elementToHighlight, getDescription(description, forRemoval, highlightType), highlightType, rangeInElement);
|
||||
LocalQuickFix quickFix = null;
|
||||
PsiMethodCallExpression methodCall = getMethodCall(elementToHighlight);
|
||||
if (refElement instanceof PsiMethod && methodCall != null) {
|
||||
PsiMethod replacement = findReplacementInJavaDoc((PsiMethod)refElement, methodCall);
|
||||
if (replacement != null) {
|
||||
quickFix = new ReplaceMethodCallFix((PsiMethodCallExpression)elementToHighlight.getParent().getParent(), replacement);
|
||||
}
|
||||
}
|
||||
|
||||
holder.registerProblem(elementToHighlight, getDescription(description, forRemoval, highlightType), highlightType, rangeInElement, quickFix);
|
||||
}
|
||||
|
||||
private static boolean isMarkedForRemoval(PsiModifierListOwner element, boolean forRemoval) {
|
||||
@@ -326,4 +340,80 @@ abstract class DeprecationInspectionBase extends BaseJavaBatchLocalInspectionToo
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
private static PsiMethod findReplacementInJavaDoc(@NotNull PsiMethod method, @NotNull PsiMethodCallExpression call) {
|
||||
if (method instanceof PsiConstructorCall) return null;
|
||||
PsiDocComment doc = method.getDocComment();
|
||||
if (doc == null) return null;
|
||||
|
||||
PsiDocTag[] docTags = PsiTreeUtil.getChildrenOfType(doc, PsiInlineDocTag.class);
|
||||
PsiDocTag[] tags = doc.getTags();
|
||||
PsiDocTag[] allTags = docTags == null ? tags : ArrayUtil.mergeArrays(docTags, tags);
|
||||
if (allTags.length == 0) return null;
|
||||
PsiMethod tagMethod = (PsiMethod)Arrays
|
||||
.stream(allTags)
|
||||
.filter(t -> {
|
||||
String name = t.getName();
|
||||
return "link".equals(name) || "see".equals(name);
|
||||
})
|
||||
.collect(MoreCollectors.onlyOne())
|
||||
.map(tag -> tag.getValueElement())
|
||||
.map(value -> value.getReference())
|
||||
.map(reference -> reference.resolve())
|
||||
.filter(resolved -> resolved instanceof PsiMethod)
|
||||
.orElse(null);
|
||||
return tagMethod == null || tagMethod.isDeprecated() || tagMethod.isEquivalentTo(method) || !areReplaceable(method, tagMethod, call)
|
||||
? null
|
||||
: tagMethod;
|
||||
}
|
||||
|
||||
private static boolean areReplaceable(@NotNull PsiMethod initial,
|
||||
@NotNull PsiMethod suggestedReplacement,
|
||||
@NotNull PsiMethodCallExpression call) {
|
||||
if (!PsiResolveHelper.SERVICE.getInstance(call.getProject()).isAccessible(suggestedReplacement, call, null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isInitialStatic = initial.hasModifierProperty(PsiModifier.STATIC);
|
||||
boolean isSuggestedStatic = suggestedReplacement.hasModifierProperty(PsiModifier.STATIC);
|
||||
if (isInitialStatic && !isSuggestedStatic) {
|
||||
return false;
|
||||
}
|
||||
if (!isInitialStatic && !isSuggestedStatic && !InheritanceUtil.isInheritorOrSelf(getQualifierClass(call), suggestedReplacement.getContainingClass(), true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String qualifierText;
|
||||
if (isInitialStatic) {
|
||||
qualifierText = ObjectUtils.notNull(suggestedReplacement.getContainingClass()).getQualifiedName() + ".";
|
||||
} else {
|
||||
PsiExpression qualifierExpression = call.getMethodExpression().getQualifierExpression();
|
||||
qualifierText = qualifierExpression == null ? "" : (qualifierExpression.getText() + ".");
|
||||
}
|
||||
|
||||
PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(initial.getProject());
|
||||
PsiExpressionList arguments = call.getArgumentList();
|
||||
PsiMethodCallExpression suggestedCall = (PsiMethodCallExpression)elementFactory
|
||||
.createExpressionFromText(qualifierText + suggestedReplacement.getName() + arguments.getText(), call);
|
||||
|
||||
MethodCandidateInfo result = ObjectUtils.tryCast(suggestedCall.resolveMethodGenerics(), MethodCandidateInfo.class);
|
||||
return result != null && result.isApplicable();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiClass getQualifierClass(@NotNull PsiMethodCallExpression call) {
|
||||
PsiExpression expression = call.getMethodExpression().getQualifierExpression();
|
||||
if (expression == null) {
|
||||
return RefactoringChangeUtil.getThisClass(call);
|
||||
}
|
||||
return PsiUtil.resolveClassInType(expression.getType());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiMethodCallExpression getMethodCall(@NotNull PsiElement element) {
|
||||
if (!(element instanceof PsiIdentifier)) return null;
|
||||
PsiElement parent = element.getParent();
|
||||
if (!(parent instanceof PsiReferenceExpression)) return null;
|
||||
return ObjectUtils.tryCast(parent.getParent(), PsiMethodCallExpression.class);
|
||||
}
|
||||
}
|
||||
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.codeInspection.deprecation
|
||||
|
||||
import com.intellij.codeInspection.LocalQuickFixOnPsiElement
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager
|
||||
import com.intellij.psi.util.PsiFormatUtil
|
||||
import com.intellij.psi.util.PsiFormatUtilBase
|
||||
import com.intellij.util.ObjectUtils
|
||||
import org.jetbrains.annotations.Nls
|
||||
|
||||
internal class ReplaceMethodCallFix(expr: PsiMethodCallExpression, replacementMethod: PsiMethod) : LocalQuickFixOnPsiElement(expr) {
|
||||
private val myReplacementMethodPointer =
|
||||
SmartPointerManager.getInstance(replacementMethod.project).createSmartPsiElementPointer(replacementMethod)
|
||||
private val myReplacementText =
|
||||
PsiFormatUtil.formatMethod(replacementMethod, PsiSubstitutor.EMPTY, PsiFormatUtilBase.SHOW_CONTAINING_CLASS or PsiFormatUtilBase.SHOW_NAME, 0)
|
||||
|
||||
override fun getText(): String {
|
||||
return "Replace method call with " + myReplacementText
|
||||
}
|
||||
|
||||
@Nls
|
||||
override fun getFamilyName(): String {
|
||||
return "Replace Method Call"
|
||||
}
|
||||
|
||||
override fun invoke(project: Project, file: PsiFile, startElement: PsiElement, endElement: PsiElement) {
|
||||
val expr = ObjectUtils.tryCast(startElement, PsiMethodCallExpression::class.java) ?: return
|
||||
val replacementMethod = myReplacementMethodPointer.element ?: return
|
||||
|
||||
val qualifierExpression = expr.methodExpression.qualifierExpression
|
||||
val isReplacementStatic = replacementMethod.hasModifierProperty(PsiModifier.STATIC)
|
||||
val qualifierText = if (qualifierExpression != null && !isReplacementStatic) {
|
||||
qualifierExpression.text + "."
|
||||
}
|
||||
else if (isReplacementStatic) {
|
||||
replacementMethod.containingClass!!.qualifiedName!! + "."
|
||||
}
|
||||
else {
|
||||
""
|
||||
}
|
||||
|
||||
val elementFactory = JavaPsiFacade.getElementFactory(project)
|
||||
val newMethodCall = elementFactory.createExpressionFromText(qualifierText + replacementMethod.name + expr.argumentList.text, expr)
|
||||
val replaced = expr.replace(newMethodCall) as PsiMethodCallExpression
|
||||
JavaCodeStyleManager.getInstance(project).shortenClassReferences(replaced.methodExpression)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// "Replace method call with Test.nnn" "true"
|
||||
class Test<T> {
|
||||
static void example() {
|
||||
Test<String> t = new Test<>();
|
||||
t.nnn("");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Test#nnn(Object)}
|
||||
*
|
||||
*/
|
||||
@Deprecated
|
||||
void mmm(T t) {
|
||||
}
|
||||
|
||||
void nnn(T t) {
|
||||
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// "Replace method call with Test2.nnn" "true"
|
||||
class Test extends Test2 {
|
||||
static void example() {
|
||||
Test t = new Test();
|
||||
t.nnn("");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Test2#nnn(String)}
|
||||
*
|
||||
*/
|
||||
@Deprecated
|
||||
void mmm(String t) {
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class Test2 {
|
||||
void nnn(String t) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// "Replace method call with Test.nnn" "true"
|
||||
public class Test {
|
||||
|
||||
void foo() {
|
||||
nnn("");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Test#nnn(CharSequence)}
|
||||
*
|
||||
*/
|
||||
@Deprecated
|
||||
void mmm(String s) {
|
||||
}
|
||||
|
||||
void nnn(CharSequence ss) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// "Replace method call with Test.mmm1" "true"
|
||||
class Test {
|
||||
static void example() {
|
||||
Test t = new Test();
|
||||
Test.mmm1("");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Test#mmm1(String)}
|
||||
*
|
||||
*/
|
||||
@Deprecated
|
||||
void mmm(String t) {
|
||||
}
|
||||
|
||||
static void mmm1(String string) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// "Replace method call with Test.nnn" "true"
|
||||
class Test<T> {
|
||||
static void example() {
|
||||
Test<String> t = new Test<>();
|
||||
t.m<caret>mm("");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Test#nnn(Object)}
|
||||
*
|
||||
*/
|
||||
@Deprecated
|
||||
void mmm(T t) {
|
||||
}
|
||||
|
||||
void nnn(T t) {
|
||||
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// "Replace method call with Test2.nnn" "true"
|
||||
class Test extends Test2 {
|
||||
static void example() {
|
||||
Test t = new Test();
|
||||
t.m<caret>mm("");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Test2#nnn(String)}
|
||||
*
|
||||
*/
|
||||
@Deprecated
|
||||
void mmm(String t) {
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class Test2 {
|
||||
void nnn(String t) {
|
||||
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// "Replace method call with Test2.nnn" "false"
|
||||
class Test {
|
||||
static void example() {
|
||||
Test t = new Test();
|
||||
t.m<caret>mm("");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Test2#nnn(String)}
|
||||
*
|
||||
*/
|
||||
@Deprecated
|
||||
void mmm(String t) {
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class Test2 {
|
||||
void nnn(String t) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// "Replace method call with Test.nnn" "true"
|
||||
public class Test {
|
||||
|
||||
void foo() {
|
||||
m<caret>mm("");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Test#nnn(CharSequence)}
|
||||
*
|
||||
*/
|
||||
@Deprecated
|
||||
void mmm(String s) {
|
||||
}
|
||||
|
||||
void nnn(CharSequence ss) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// "Replace method call with Test.mmm1" "true"
|
||||
class Test {
|
||||
static void example() {
|
||||
Test t = new Test();
|
||||
t.m<caret>mm("");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Test#mmm1(String)}
|
||||
*
|
||||
*/
|
||||
@Deprecated
|
||||
void mmm(String t) {
|
||||
}
|
||||
|
||||
static void mmm1(String string) {
|
||||
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.java.codeInspection;
|
||||
|
||||
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
|
||||
import com.intellij.codeInspection.LocalInspectionTool;
|
||||
import com.intellij.codeInspection.deprecation.DeprecationInspection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class DeprecationInspectionFixTest extends LightQuickFixParameterizedTestCase {
|
||||
@NotNull
|
||||
@Override
|
||||
protected LocalInspectionTool[] configureLocalInspectionTools() {
|
||||
return new LocalInspectionTool[]{new DeprecationInspection()};
|
||||
}
|
||||
|
||||
public void test() { doAllTests(); }
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return "/inspection/deprecation/quickFix";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user