Java: Implemented enhanced deprecation inspection (JEP 277) for Java 9 (IDEA-159124)

This commit is contained in:
Pavel Dolgov
2017-08-15 13:33:08 +03:00
parent 7635a25881
commit 504a7ef9e0
20 changed files with 735 additions and 224 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
* Copyright 2000-2016 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.
@@ -15,30 +15,23 @@
*/
package com.intellij.codeInspection.deprecation;
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.DeprecationUtil;
import com.intellij.codeInspection.ProblemHighlightType;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.util.MethodSignatureBackedByPsiMethod;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import org.intellij.lang.annotations.Pattern;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.List;
/**
* @author max
*/
public class DeprecationInspection extends BaseJavaBatchLocalInspectionTool {
public class DeprecationInspection extends DeprecationInspectionBase {
public static final String SHORT_NAME = DeprecationUtil.DEPRECATION_SHORT_NAME;
public static final String ID = DeprecationUtil.DEPRECATION_ID;
public static final String DISPLAY_NAME = DeprecationUtil.DEPRECATION_DISPLAY_NAME;
@@ -51,9 +44,10 @@ public class DeprecationInspection extends BaseJavaBatchLocalInspectionTool {
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new DeprecationElementVisitor(holder, IGNORE_INSIDE_DEPRECATED, IGNORE_ABSTRACT_DEPRECATED_OVERRIDES,
IGNORE_IMPORT_STATEMENTS, IGNORE_METHODS_OF_DEPRECATED);
IGNORE_IMPORT_STATEMENTS, IGNORE_METHODS_OF_DEPRECATED,
false);
}
@Override
@@ -81,11 +75,6 @@ public class DeprecationInspection extends BaseJavaBatchLocalInspectionTool {
return ID;
}
@Override
public boolean isEnabledByDefault() {
return true;
}
@Override
public JComponent createOptionsPanel() {
final MultipleCheckboxOptionsPanel panel = new MultipleCheckboxOptionsPanel(this);
@@ -96,210 +85,10 @@ public class DeprecationInspection extends BaseJavaBatchLocalInspectionTool {
return panel;
}
private static class DeprecationElementVisitor extends JavaElementVisitor {
private final ProblemsHolder myHolder;
private final boolean myIgnoreInsideDeprecated;
private final boolean myIgnoreAbstractDeprecatedOverrides;
private final boolean myIgnoreImportStatements;
private final boolean myIgnoreMethodsOfDeprecated;
private DeprecationElementVisitor(ProblemsHolder holder,
boolean ignoreInsideDeprecated,
boolean ignoreAbstractDeprecatedOverrides,
boolean ignoreImportStatements,
boolean ignoreMethodsOfDeprecated) {
myHolder = holder;
myIgnoreInsideDeprecated = ignoreInsideDeprecated;
myIgnoreAbstractDeprecatedOverrides = ignoreAbstractDeprecatedOverrides;
myIgnoreImportStatements = ignoreImportStatements;
myIgnoreMethodsOfDeprecated = ignoreMethodsOfDeprecated;
}
@Override
public void visitReferenceElement(PsiJavaCodeReferenceElement reference) {
PsiElement resolved = reference.advancedResolve(true).getElement();
PsiElement refName = reference.getReferenceNameElement();
checkDeprecated(resolved, refName, null, myIgnoreInsideDeprecated, myIgnoreImportStatements, myIgnoreMethodsOfDeprecated, myHolder);
}
@Override
public void visitImportStaticStatement(PsiImportStaticStatement statement) {
PsiJavaCodeReferenceElement importReference = statement.getImportReference();
if (importReference != null) {
PsiElement refName = importReference.getReferenceNameElement();
checkDeprecated(importReference.resolve(), refName, null, myIgnoreInsideDeprecated, false, true, myHolder);
}
}
@Override
public void visitReferenceExpression(PsiReferenceExpression expression) {
visitReferenceElement(expression);
}
@Override
public void visitNewExpression(PsiNewExpression expression) {
PsiClass aClass = null;
PsiType type = expression.getType();
if (type instanceof PsiClassType) {
aClass = ((PsiClassType)type).resolveGenerics().getElement();
if (aClass instanceof PsiAnonymousClass) {
type = ((PsiAnonymousClass)aClass).getBaseClassType();
aClass = ((PsiClassType)type).resolveGenerics().getElement();
}
}
if (aClass == null) return;
PsiExpressionList list = expression.getArgumentList();
PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(expression.getProject()).getResolveHelper();
if (list != null && aClass.getConstructors().length > 0) {
JavaResolveResult[] results = resolveHelper.multiResolveConstructor((PsiClassType)type, list, list);
MethodCandidateInfo result = null;
if (results.length == 1) result = (MethodCandidateInfo)results[0];
PsiMethod constructor = result == null ? null : result.getElement();
PsiJavaCodeReferenceElement ref;
if (constructor != null && (ref = expression.getClassOrAnonymousClassReference()) != null) {
if (expression.getClassReference() == null && constructor.getParameterList().getParametersCount() == 0) return;
checkDeprecated(constructor, ref, null, myIgnoreInsideDeprecated, myIgnoreImportStatements, true, myHolder);
}
}
}
@Override
public void visitMethod(PsiMethod method) {
MethodSignatureBackedByPsiMethod methodSignature = MethodSignatureBackedByPsiMethod.create(method, PsiSubstitutor.EMPTY);
if (!method.isConstructor()) {
List<MethodSignatureBackedByPsiMethod> superMethodSignatures = method.findSuperMethodSignaturesIncludingStatic(true);
checkMethodOverridesDeprecated(methodSignature, superMethodSignatures, myIgnoreAbstractDeprecatedOverrides, myHolder);
}
else {
checkImplicitCallToSuper(method);
}
}
private void checkImplicitCallToSuper(PsiMethod method) {
final PsiClass containingClass = method.getContainingClass();
assert containingClass != null;
final PsiClass superClass = containingClass.getSuperClass();
if (hasDefaultDeprecatedConstructor(superClass)) {
if (superClass instanceof PsiAnonymousClass) {
final PsiExpressionList argumentList = ((PsiAnonymousClass)superClass).getArgumentList();
if (argumentList != null && argumentList.getExpressions().length > 0) return;
}
final PsiCodeBlock body = method.getBody();
if (body != null) {
final PsiStatement[] statements = body.getStatements();
if (statements.length == 0 || !JavaHighlightUtil.isSuperOrThisCall(statements[0], true, true)) {
registerDefaultConstructorProblem(superClass, method.getNameIdentifier(), false);
}
}
}
}
private void registerDefaultConstructorProblem(PsiClass superClass, PsiElement nameIdentifier, boolean asDeprecated) {
ProblemHighlightType type = asDeprecated ? ProblemHighlightType.LIKE_DEPRECATED : ProblemHighlightType.GENERIC_ERROR_OR_WARNING;
myHolder.registerProblem(nameIdentifier, "Default constructor in " + superClass.getQualifiedName() + " is deprecated", type);
}
@Override
public void visitClass(PsiClass aClass) {
if (aClass instanceof PsiTypeParameter) return;
final PsiMethod[] currentConstructors = aClass.getConstructors();
if (currentConstructors.length == 0) {
final PsiClass superClass = aClass.getSuperClass();
if (hasDefaultDeprecatedConstructor(superClass)) {
final boolean isAnonymous = aClass instanceof PsiAnonymousClass;
if (isAnonymous) {
final PsiExpressionList argumentList = ((PsiAnonymousClass)aClass).getArgumentList();
if (argumentList != null && argumentList.getExpressions().length > 0) return;
}
PsiElement identifier = isAnonymous ? ((PsiAnonymousClass)aClass).getBaseClassReference() : aClass.getNameIdentifier();
registerDefaultConstructorProblem(superClass, identifier, isAnonymous);
}
}
}
@Override
public void visitRequiresStatement(PsiRequiresStatement statement) {
PsiJavaModuleReferenceElement refElement = statement.getReferenceElement();
if (refElement != null) {
PsiPolyVariantReference ref = refElement.getReference();
PsiElement target = ref != null ? ref.resolve() : null;
if (target instanceof PsiJavaModule && PsiImplUtil.isDeprecatedByAnnotation((PsiJavaModule)target)) {
String message = JavaErrorMessages.message("deprecated.symbol", HighlightMessageUtil.getSymbolName(target));
myHolder.registerProblem(refElement, message, ProblemHighlightType.LIKE_DEPRECATED);
}
}
}
}
private static boolean hasDefaultDeprecatedConstructor(PsiClass superClass) {
if (superClass != null) {
final PsiMethod[] constructors = superClass.getConstructors();
for (PsiMethod constructor : constructors) {
if (constructor.getParameterList().getParametersCount() == 0 && constructor.isDeprecated()) {
return true;
}
}
}
return false;
}
private static void checkMethodOverridesDeprecated(MethodSignatureBackedByPsiMethod methodSignature,
List<MethodSignatureBackedByPsiMethod> superMethodSignatures,
boolean ignoreAbstractDeprecatedOverrides,
ProblemsHolder holder) {
PsiMethod method = methodSignature.getMethod();
PsiElement methodName = method.getNameIdentifier();
if (methodName == null) return;
for (MethodSignatureBackedByPsiMethod superMethodSignature : superMethodSignatures) {
PsiMethod superMethod = superMethodSignature.getMethod();
PsiClass aClass = superMethod.getContainingClass();
if (aClass == null) continue;
// do not show deprecated warning for class implementing deprecated methods
if (ignoreAbstractDeprecatedOverrides && !aClass.isDeprecated() && superMethod.hasModifierProperty(PsiModifier.ABSTRACT)) continue;
if (superMethod.isDeprecated()) {
String description = JavaErrorMessages.message("overrides.deprecated.method", HighlightMessageUtil.getSymbolName(aClass));
holder.registerProblem(methodName, description, ProblemHighlightType.LIKE_DEPRECATED);
}
}
}
public static void checkDeprecated(PsiElement refElement,
PsiElement elementToHighlight,
@Nullable TextRange rangeInElement,
ProblemsHolder holder) {
checkDeprecated(refElement, elementToHighlight, rangeInElement, false, false, true, holder);
checkDeprecated(refElement, elementToHighlight, rangeInElement, false, false, true, holder, false);
}
private static void checkDeprecated(PsiElement refElement,
PsiElement elementToHighlight,
@Nullable TextRange rangeInElement,
boolean ignoreInsideDeprecated,
boolean ignoreImportStatements,
boolean ignoreMethodsOfDeprecated,
ProblemsHolder holder) {
if (!(refElement instanceof PsiDocCommentOwner)) return;
if (!((PsiDocCommentOwner)refElement).isDeprecated()) {
if (!ignoreMethodsOfDeprecated) {
checkDeprecated(((PsiDocCommentOwner)refElement).getContainingClass(), elementToHighlight, rangeInElement,
ignoreInsideDeprecated, ignoreImportStatements, false, holder);
}
return;
}
if (ignoreInsideDeprecated) {
PsiElement parent = elementToHighlight;
while ((parent = PsiTreeUtil.getParentOfType(parent, PsiDocCommentOwner.class, true)) != null) {
if (((PsiDocCommentOwner)parent).isDeprecated()) return;
}
}
if (ignoreImportStatements && PsiTreeUtil.getParentOfType(elementToHighlight, PsiImportStatementBase.class) != null) {
return;
}
String description = JavaErrorMessages.message("deprecated.symbol", HighlightMessageUtil.getSymbolName(refElement));
holder.registerProblem(elementToHighlight, description, ProblemHighlightType.LIKE_DEPRECATED, rangeInElement);
}
}
}
@@ -0,0 +1,286 @@
/*
* 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.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.openapi.util.TextRange;
import com.intellij.pom.java.LanguageLevel;
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.util.MethodSignatureBackedByPsiMethod;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.List;
abstract class DeprecationInspectionBase extends BaseJavaBatchLocalInspectionTool {
@Override
public boolean isEnabledByDefault() {
return true;
}
protected static class DeprecationElementVisitor extends JavaElementVisitor {
private final ProblemsHolder myHolder;
private final boolean myIgnoreInsideDeprecated;
private final boolean myIgnoreAbstractDeprecatedOverrides;
private final boolean myIgnoreImportStatements;
private final boolean myIgnoreMethodsOfDeprecated;
private final boolean myForRemoval;
DeprecationElementVisitor(@NotNull ProblemsHolder holder,
boolean ignoreInsideDeprecated,
boolean ignoreAbstractDeprecatedOverrides,
boolean ignoreImportStatements,
boolean ignoreMethodsOfDeprecated,
boolean forRemoval) {
myHolder = holder;
myIgnoreInsideDeprecated = ignoreInsideDeprecated;
myIgnoreAbstractDeprecatedOverrides = ignoreAbstractDeprecatedOverrides;
myIgnoreImportStatements = ignoreImportStatements;
myIgnoreMethodsOfDeprecated = ignoreMethodsOfDeprecated;
myForRemoval = forRemoval;
}
@Override
public void visitReferenceElement(PsiJavaCodeReferenceElement reference) {
JavaResolveResult result = reference.advancedResolve(true);
PsiElement resolved = result.getElement();
checkDeprecated(resolved, reference.getReferenceNameElement(), null, myIgnoreInsideDeprecated, myIgnoreImportStatements,
myIgnoreMethodsOfDeprecated, myHolder, myForRemoval);
}
@Override
public void visitImportStaticStatement(PsiImportStaticStatement statement) {
PsiFile file = statement.getContainingFile();
if (file instanceof PsiJavaFile && ((PsiJavaFile)file).getLanguageLevel().isAtLeast(LanguageLevel.JDK_1_9)) return;
final PsiJavaCodeReferenceElement importReference = statement.getImportReference();
if (importReference != null) {
PsiElement resolved = importReference.resolve();
checkDeprecated(resolved, importReference.getReferenceNameElement(), null, myIgnoreInsideDeprecated,
false, true, myHolder, myForRemoval);
}
}
@Override
public void visitReferenceExpression(PsiReferenceExpression expression) {
visitReferenceElement(expression);
}
@Override
public void visitNewExpression(PsiNewExpression expression) {
PsiType type = expression.getType();
PsiExpressionList list = expression.getArgumentList();
if (!(type instanceof PsiClassType)) return;
PsiClassType.ClassResolveResult typeResult = ((PsiClassType)type).resolveGenerics();
PsiClass aClass = typeResult.getElement();
if (aClass == null) return;
if (aClass instanceof PsiAnonymousClass) {
type = ((PsiAnonymousClass)aClass).getBaseClassType();
typeResult = ((PsiClassType)type).resolveGenerics();
aClass = typeResult.getElement();
if (aClass == null) return;
}
final PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(expression.getProject()).getResolveHelper();
final PsiMethod[] constructors = aClass.getConstructors();
if (constructors.length > 0 && list != null) {
JavaResolveResult[] results = resolveHelper.multiResolveConstructor((PsiClassType)type, list, list);
MethodCandidateInfo result = null;
if (results.length == 1) result = (MethodCandidateInfo)results[0];
PsiMethod constructor = result == null ? null : result.getElement();
if (constructor != null && expression.getClassOrAnonymousClassReference() != null) {
if (expression.getClassReference() == null && constructor.getParameterList().getParametersCount() == 0) return;
checkDeprecated(constructor, expression.getClassOrAnonymousClassReference(), null, myIgnoreInsideDeprecated,
myIgnoreImportStatements, true, myHolder, myForRemoval);
}
}
}
@Override
public void visitMethod(PsiMethod method) {
MethodSignatureBackedByPsiMethod methodSignature = MethodSignatureBackedByPsiMethod.create(method, PsiSubstitutor.EMPTY);
if (!method.isConstructor()) {
List<MethodSignatureBackedByPsiMethod> superMethodSignatures = method.findSuperMethodSignaturesIncludingStatic(true);
checkMethodOverridesDeprecated(methodSignature, superMethodSignatures, myIgnoreAbstractDeprecatedOverrides, myHolder, myForRemoval);
}
else {
checkImplicitCallToSuper(method);
}
}
private void checkImplicitCallToSuper(PsiMethod method) {
final PsiClass containingClass = method.getContainingClass();
assert containingClass != null;
final PsiClass superClass = containingClass.getSuperClass();
if (hasDefaultDeprecatedConstructor(superClass, myForRemoval)) {
if (superClass instanceof PsiAnonymousClass) {
final PsiExpressionList argumentList = ((PsiAnonymousClass)superClass).getArgumentList();
if (argumentList != null && argumentList.getExpressions().length > 0) return;
}
final PsiCodeBlock body = method.getBody();
if (body != null) {
final PsiStatement[] statements = body.getStatements();
if (statements.length == 0 || !JavaHighlightUtil.isSuperOrThisCall(statements[0], true, true)) {
registerDefaultConstructorProblem(superClass, method.getNameIdentifier(), false);
}
}
}
}
private void registerDefaultConstructorProblem(PsiClass superClass, PsiElement nameIdentifier, boolean asDeprecated) {
String description =
JavaErrorMessages.message(myForRemoval ? "marked.for.removal.default.constructor" : "deprecated.default.constructor",
superClass.getQualifiedName());
ProblemHighlightType type =
asDeprecated && !myForRemoval ? ProblemHighlightType.LIKE_DEPRECATED : ProblemHighlightType.GENERIC_ERROR_OR_WARNING;
myHolder.registerProblem(nameIdentifier, description, type);
}
@Override
public void visitClass(PsiClass aClass) {
if (aClass instanceof PsiTypeParameter) return;
final PsiMethod[] currentConstructors = aClass.getConstructors();
if (currentConstructors.length == 0) {
final PsiClass superClass = aClass.getSuperClass();
if (hasDefaultDeprecatedConstructor(superClass, myForRemoval)) {
final boolean isAnonymous = aClass instanceof PsiAnonymousClass;
if (isAnonymous) {
final PsiExpressionList argumentList = ((PsiAnonymousClass)aClass).getArgumentList();
if (argumentList != null && argumentList.getExpressions().length > 0) return;
}
registerDefaultConstructorProblem(superClass,
isAnonymous ? ((PsiAnonymousClass)aClass).getBaseClassReference() : aClass.getNameIdentifier(),
isAnonymous);
}
}
}
@Override
public void visitRequiresStatement(PsiRequiresStatement statement) {
PsiJavaModuleReferenceElement refElement = statement.getReferenceElement();
if (refElement != null) {
PsiPolyVariantReference ref = refElement.getReference();
PsiElement target = ref != null ? ref.resolve() : null;
if (target instanceof PsiJavaModule &&
isMarkedForRemoval((PsiJavaModule)target, myForRemoval) &&
PsiImplUtil.isDeprecatedByAnnotation((PsiJavaModule)target)) {
String description = JavaErrorMessages.message(myForRemoval ? "marked.for.removal.symbol" : "deprecated.symbol",
HighlightMessageUtil.getSymbolName(target));
ProblemHighlightType type = myForRemoval ? ProblemHighlightType.GENERIC_ERROR_OR_WARNING : ProblemHighlightType.LIKE_DEPRECATED;
myHolder.registerProblem(refElement, description, type);
}
}
}
}
private static boolean hasDefaultDeprecatedConstructor(PsiClass superClass, boolean forRemoval) {
return superClass != null && Arrays.stream(superClass.getConstructors())
.anyMatch(constructor -> constructor.getParameterList().getParametersCount() == 0 &&
constructor.isDeprecated() &&
isMarkedForRemoval(constructor, forRemoval));
}
//@top
static void checkMethodOverridesDeprecated(MethodSignatureBackedByPsiMethod methodSignature,
List<MethodSignatureBackedByPsiMethod> superMethodSignatures,
boolean ignoreAbstractDeprecatedOverrides, ProblemsHolder holder, boolean forRemoval) {
PsiMethod method = methodSignature.getMethod();
PsiElement methodName = method.getNameIdentifier();
if (methodName == null) return;
for (MethodSignatureBackedByPsiMethod superMethodSignature : superMethodSignatures) {
PsiMethod superMethod = superMethodSignature.getMethod();
PsiClass aClass = superMethod.getContainingClass();
if (aClass == null) continue;
// do not show deprecated warning for class implementing deprecated methods
if (ignoreAbstractDeprecatedOverrides && !aClass.isDeprecated() && superMethod.hasModifierProperty(PsiModifier.ABSTRACT)) continue;
if (superMethod.isDeprecated() && isMarkedForRemoval(superMethod, forRemoval)) {
String description = JavaErrorMessages.message(forRemoval ? "overrides.marked.for.removal.method" : "overrides.deprecated.method",
HighlightMessageUtil.getSymbolName(aClass, PsiSubstitutor.EMPTY));
ProblemHighlightType type = forRemoval ? ProblemHighlightType.GENERIC_ERROR_OR_WARNING : ProblemHighlightType.LIKE_DEPRECATED;
holder.registerProblem(methodName, description, type);
}
}
}
static void checkDeprecated(PsiElement refElement,
PsiElement elementToHighlight,
@Nullable TextRange rangeInElement,
boolean ignoreInsideDeprecated,
boolean ignoreImportStatements,
boolean ignoreMethodsOfDeprecated,
ProblemsHolder holder,
boolean forRemoval) {
if (!(refElement instanceof PsiDocCommentOwner) || !isMarkedForRemoval((PsiDocCommentOwner)refElement, forRemoval)) {
return;
}
if (!((PsiDocCommentOwner)refElement).isDeprecated()) {
if (!ignoreMethodsOfDeprecated) {
checkDeprecated(((PsiDocCommentOwner)refElement).getContainingClass(), elementToHighlight, rangeInElement,
ignoreInsideDeprecated, ignoreImportStatements, false, holder, forRemoval);
}
return;
}
if (ignoreInsideDeprecated) {
PsiElement parent = elementToHighlight;
while ((parent = PsiTreeUtil.getParentOfType(parent, PsiDocCommentOwner.class, true)) != null) {
if (((PsiDocCommentOwner)parent).isDeprecated()) return;
}
}
if (ignoreImportStatements && PsiTreeUtil.getParentOfType(elementToHighlight, PsiImportStatementBase.class) != null) {
return;
}
String description = JavaErrorMessages.message(forRemoval ? "marked.for.removal.symbol" : "deprecated.symbol",
HighlightMessageUtil.getSymbolName(refElement, PsiSubstitutor.EMPTY));
ProblemHighlightType type = forRemoval ? ProblemHighlightType.GENERIC_ERROR_OR_WARNING : ProblemHighlightType.LIKE_DEPRECATED;
holder.registerProblem(elementToHighlight, description, type, rangeInElement);
}
private static boolean isMarkedForRemoval(PsiModifierListOwner superMethod, boolean forRemoval) {
return isMarkedForRemoval(superMethod) == forRemoval;
}
private static boolean isMarkedForRemoval(@Nullable PsiModifierListOwner element) {
PsiAnnotation annotation = AnnotationUtil.findAnnotation(element, CommonClassNames.JAVA_LANG_DEPRECATED);
if (annotation == null) {
return false;
}
PsiAnnotationMemberValue value = annotation.findAttributeValue("forRemoval");
Object result = null;
if (value instanceof PsiLiteral) {
result = ((PsiLiteral)value).getValue();
}
else if (value instanceof PsiExpression) {
result = JavaConstantExpressionEvaluator.computeConstantExpression((PsiExpression)value, false);
}
return result instanceof Boolean && ((Boolean)result);
}
}
@@ -0,0 +1,68 @@
/*
* 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.DeprecationUtil;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiJavaFile;
import org.intellij.lang.annotations.Pattern;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
public class MarkedForRemovalInspection extends DeprecationInspectionBase {
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
PsiFile file = holder.getFile();
if (file instanceof PsiJavaFile && ((PsiJavaFile)file).getLanguageLevel().isAtLeast(LanguageLevel.JDK_1_9)) {
return new DeprecationElementVisitor(holder, false, false,
false, false,
true);
}
return PsiElementVisitor.EMPTY_VISITOR;
}
@Override
@NotNull
public String getDisplayName() {
return DeprecationUtil.FOR_REMOVAL_DISPLAY_NAME;
}
@Override
@NotNull
public String getGroupDisplayName() {
return "";
}
@Override
@NotNull
public String getShortName() {
return DeprecationUtil.FOR_REMOVAL_SHORT_NAME;
}
@Pattern(VALID_ID_PATTERN)
@Override
@NotNull
@NonNls
public String getID() {
return DeprecationUtil.FOR_REMOVAL_ID;
}
}
@@ -167,6 +167,9 @@ constructor.call.must.be.first.statement=Call to ''{0}'' must be first statement
direct.abstract.method.access=Abstract method ''{0}'' cannot be accessed directly
unrelated.overriding.methods.return.types=methods have unrelated return types
overrides.deprecated.method=Overrides deprecated method in ''{0}''
overrides.marked.for.removal.method=Overrides method marked for removal in ''{0}''
deprecated.default.constructor=Default constructor in ''{0}'' is deprecated
marked.for.removal.default.constructor=Default constructor in ''{0}'' is deprecated and marked for removal
recursive.constructor.invocation=Recursive constructor invocation
wrong.constructor.arguments=''{0}'' cannot be applied to ''{1}''
cannot.resolve.constructor=Cannot resolve constructor ''{0}''
@@ -275,6 +278,7 @@ numeric.overflow.in.expression=Numeric overflow in expression
static.member.accessed.via.instance.reference=Static member ''{0}.{1}'' accessed via instance reference
unresolved.label=Undefined label: ''{0}''
deprecated.symbol=''{0}'' is deprecated
marked.for.removal.symbol=''{0}'' is deprecated and marked for removal
qualifier.must.be.expression=Qualifier must be an expression
cannot.resolve.symbol=Cannot resolve symbol ''{0}''
class.is.already.defined.in.single.static.import=Class ''{0}'' is already defined in a single static import
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<problems>
<problem>
<file>Test.java</file>
<line>9</line>
<description>'Test' is deprecated and marked for removal</description>
</problem>
<problem>
<file>Test.java</file>
<line>10</line>
<description>'Test' is deprecated and marked for removal</description>
</problem>
<problem>
<file>Test.java</file>
<line>15</line>
<description>'Test' is deprecated and marked for removal</description>
</problem>
<problem>
<file>Test.java</file>
<line>16</line>
<description>'Test' is deprecated and marked for removal</description>
</problem>
<problem>
<file>Test.java</file>
<line>21</line>
<description>'Test' is deprecated and marked for removal</description>
</problem>
<problem>
<file>Test.java</file>
<line>22</line>
<description>'Test' is deprecated and marked for removal</description>
</problem>
<problem>
<file>Test.java</file>
<line>27</line>
<description>'Test' is deprecated and marked for removal</description>
</problem>
<problem>
<file>Test.java</file>
<line>28</line>
<description>'Test' is deprecated and marked for removal</description>
</problem>
</problems>
@@ -0,0 +1,36 @@
@Deprecated(forRemoval = true)
class Test {
public Test() {
}
}
class Usages {
void normal() {
Test t =
new Test();
}
@Deprecated()
void normallyDeprecated() {
Test t =
new Test();
}
@Deprecated(forRemoval = 1 + 1 == 2)
void deprecatedForRemoval() {
Test t =
new Test();
}
@SuppressWarnings("deprecation")
void suppressDeprecation() {
Test t =
new Test();
}
@SuppressWarnings("removal")
void suppressRemoval() {
Test t =
new Test();
}
}
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<problems>
<problem>
<file>Test.java</file>
<line>5</line>
<description>Default constructor in C is deprecated and marked for removal</description>
</problem>
<problem>
<file>Test.java</file>
<line>10</line>
<description>Default constructor in C is deprecated and marked for removal</description>
</problem>
<problem>
<file>Test.java</file>
<line>15</line>
<description>Default constructor in C is deprecated and marked for removal</description>
</problem>
</problems>
@@ -0,0 +1,22 @@
class C {
@Deprecated(forRemoval=true) C() { }
}
class D extends C {
}
class P {
void normal() {
new C(){};
}
@SuppressWarnings("deprecation")
void suppressDeprecation() {
new C(){};
}
@SuppressWarnings("removal")
void suppressRemoval() {
new C(){};
}
}
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<problems>
<problem>
<file>Test.java</file>
<line>6</line>
<description>'foo' is deprecated and marked for removal</description>
</problem>
<problem>
<file>Test.java</file>
<line>11</line>
<description>'foo' is deprecated and marked for removal</description>
</problem>
<problem>
<file>Test.java</file>
<line>16</line>
<description>'foo' is deprecated and marked for removal</description>
</problem>
<problem>
<file>Test.java</file>
<line>21</line>
<description>'foo' is deprecated and marked for removal</description>
</problem>
</problems>
@@ -0,0 +1,28 @@
public class Test {
@Deprecated(forRemoval = true)
int foo;
int normal() {
return foo;
}
@Deprecated()
int normallyDeprecated() {
return foo;
}
@Deprecated(forRemoval = 1 + 1 == 2)
int deprecatedForRemoval() {
return foo;
}
@SuppressWarnings("deprecation")
int suppressDeprecation() {
return foo;
}
@SuppressWarnings("removal")
int suppressRemoval() {
return foo;
}
}
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<problems>
<problem>
<file>Test.java</file>
<line>6</line>
<description>'foo()' is deprecated and marked for removal</description>
</problem>
<problem>
<file>Test.java</file>
<line>11</line>
<description>'foo()' is deprecated and marked for removal</description>
</problem>
<problem>
<file>Test.java</file>
<line>16</line>
<description>'foo()' is deprecated and marked for removal</description>
</problem>
<problem>
<file>Test.java</file>
<line>21</line>
<description>'foo()' is deprecated and marked for removal</description>
</problem>
</problems>
@@ -0,0 +1,28 @@
public class Test {
@Deprecated(forRemoval = true)
void foo() {}
void normal() {
foo();
}
@Deprecated()
void normallyDeprecated() {
foo();
}
@Deprecated(forRemoval = 1 + 1 == 2)
void deprecatedForRemoval() {
foo();
}
@SuppressWarnings("deprecation")
void suppressDeprecation() {
foo();
}
@SuppressWarnings("removal")
void suppressRemoval() {
foo();
}
}
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<problems>
<problem>
<file>Test.java</file>
<line>7</line>
<description>Overrides method marked for removal in 'Test'</description>
</problem>
<problem>
<file>Test.java</file>
<line>8</line>
<description>'foo()' is deprecated and marked for removal</description>
</problem>
</problems>
@@ -0,0 +1,17 @@
public class Test {
@Deprecated(forRemoval = true)
void foo() {}
}
class Overrides extends Test {
void foo() {
super.foo();
}
}
@SuppressWarnings("removal")
class Suppressed extends Test {
void foo() {
super.foo();
}
}
@@ -17,6 +17,7 @@ package com.intellij.java.codeInsight.daemon
import com.intellij.codeInsight.daemon.impl.JavaHighlightInfoTypes
import com.intellij.codeInspection.deprecation.DeprecationInspection
import com.intellij.codeInspection.deprecation.MarkedForRemovalInspection
import com.intellij.java.testFramework.fixtures.LightJava9ModulesCodeInsightFixtureTestCase
import com.intellij.java.testFramework.fixtures.MultiModuleJava9ProjectDescriptor.ModuleDescriptor.*
import com.intellij.openapi.util.TextRange
@@ -296,11 +297,17 @@ class ModuleHighlightingTest : LightJava9ModulesCodeInsightFixtureTestCase() {
}
fun testDeprecations() {
myFixture.enableInspections(DeprecationInspection())
myFixture.enableInspections(DeprecationInspection(), MarkedForRemovalInspection())
addFile("module-info.java", "@Deprecated module M2 { }", M2)
highlight("""module M { requires <warning descr="'M2' is deprecated">M2</warning>; }""")
}
fun testMarkedForRemoval() {
myFixture.enableInspections(DeprecationInspection(), MarkedForRemovalInspection())
addFile("module-info.java", "@Deprecated(forRemoval=true) module M2 { }", M2)
highlight("""module M { requires <warning descr="'M2' is deprecated and marked for removal">M2</warning>; }""")
}
fun testPackageConflicts() {
addFile("pkg/collision2/C2.java", "package pkg.collision2;\npublic class C2 { }", M2)
addFile("pkg/collision4/C4.java", "package pkg.collision4;\npublic class C4 { }", M4)
@@ -0,0 +1,72 @@
/*
* 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.JavaTestUtil;
import com.intellij.codeInspection.deprecation.MarkedForRemovalInspection;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.LanguageLevelProjectExtension;
import com.intellij.openapi.roots.ModuleRootModificationUtil;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.testFramework.IdeaTestUtil;
import com.intellij.testFramework.InspectionTestCase;
/**
* @author max
*/
public class MarkedForRemovalInspectionTest extends InspectionTestCase {
@Override
protected String getTestDataPath() {
return JavaTestUtil.getJavaTestDataPath() + "/inspection";
}
@Override
protected void setUp() throws Exception {
super.setUp();
LanguageLevelProjectExtension.getInstance(myJavaFacade.getProject()).setLanguageLevel(LanguageLevel.JDK_1_9);
ModuleRootModificationUtil.setModuleSdk(getModule(), getTestProjectSdk());
}
@Override
protected Sdk getTestProjectSdk() {
return IdeaTestUtil.getMockJdk9();
}
private void doTest() {
doTest("forRemoval/" + getTestName(true), new MarkedForRemovalInspection());
}
public void testForRemovalClass() {
doTest();
}
public void testForRemovalField() {
doTest();
}
public void testForRemovalMethod() {
doTest();
}
public void testForRemovalOverride() {
doTest();
}
public void testForRemovalDefaultConstructorInSuper() {
doTest();
}
}
@@ -21,4 +21,8 @@ public class DeprecationUtil {
@NonNls public static final String DEPRECATION_SHORT_NAME = "Deprecation";
@NonNls public static final String DEPRECATION_DISPLAY_NAME = InspectionsBundle.message("inspection.deprecated.display.name");
@NonNls public static final String DEPRECATION_ID = "deprecation";
@NonNls public static final String FOR_REMOVAL_SHORT_NAME = "MarkedForRemoval";
@NonNls public static final String FOR_REMOVAL_DISPLAY_NAME = InspectionsBundle.message("inspection.marked.for.removal.display.name");
@NonNls public static final String FOR_REMOVAL_ID = "removal";
}
@@ -100,6 +100,7 @@ dataflow.message.array.index.out.of.bounds=Array index is out of bounds
#deprecated
inspection.deprecated.display.name=Deprecated API usage
inspection.marked.for.removal.display.name=Usage of API marked for removal
#empty method
inspection.empty.method.display.name=Empty method
inspection.empty.method.delete.quickfix=Delete unnecessary method(s)
@@ -0,0 +1,5 @@
<html>
<body>
This inspection reports usages of deprecated classes, fields, and methods which are marked for removal (Java 9+).
</body>
</html>
+7 -1
View File
@@ -704,9 +704,15 @@
implementationClass="com.intellij.codeInspection.javaDoc.JavadocHtmlLintInspection"/>
<externalAnnotator language="JAVA" implementationClass="com.intellij.codeInspection.javaDoc.JavadocHtmlLintAnnotator"/>
<localInspection groupPath="Java" language="JAVA" suppressId="deprecation" shortName="Deprecation" displayName="Deprecated API usage" groupKey="group.names.code.maturity.issues" groupBundle="messages.InspectionsBundle"
<localInspection groupPath="Java" language="JAVA" suppressId="deprecation" shortName="Deprecation"
key="inspection.deprecated.display.name" bundle="messages.InspectionsBundle"
groupKey="group.names.code.maturity.issues" groupBundle="messages.InspectionsBundle"
enabledByDefault="true" level="WARNING"
implementationClass="com.intellij.codeInspection.deprecation.DeprecationInspection"/>
<localInspection groupPath="Java" language="JAVA" suppressId="removal" shortName="MarkedForRemoval"
key="inspection.marked.for.removal.display.name" bundle="messages.InspectionsBundle"
groupKey="group.names.code.maturity.issues" groupBundle="messages.InspectionsBundle"
enabledByDefault="true" level="WARNING" implementationClass="com.intellij.codeInspection.deprecation.MarkedForRemovalInspection"/>
<localInspection groupPath="Java" language="JAVA" shortName="DeprecatedIsStillUsed" displayName="Deprecated member is still used"
groupKey="group.names.code.maturity.issues" groupBundle="messages.InspectionsBundle" enabledByDefault="true" level="WARNING"
implementationClass="com.intellij.codeInspection.DeprecatedIsStillUsedInspection" />