mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-21 22:11:40 +07:00
[java-highlighting] SafeVarargs checks -> AnnotationChecker
Also, warnings are moved out to inspections Part of IDEA-365344 Create a new Java error highlighter with minimal dependencies (PSI only) GitOrigin-RevId: 2292723fde787454467307c213ea38e730aa0ff5
This commit is contained in:
committed by
intellij-monorepo-bot
parent
6a2b751ffd
commit
c7edfdbe4a
@@ -15,3 +15,10 @@ lambda.not.a.functional.interface={0} is not a functional interface
|
||||
lambda.no.target.method.found=No target method found
|
||||
lambda.multiple.sam.candidates=Multiple non-overriding abstract methods found in interface {0}
|
||||
lambda.sealed.functional.interface=Functional interface can't be declared as 'sealed'
|
||||
|
||||
safe.varargs.on.record.component=@SafeVarargs is not allowed on a record component
|
||||
safe.varargs.on.fixed.arity=@SafeVarargs is not allowed on methods with fixed arity
|
||||
safe.varargs.on.non.final.method=@SafeVarargs is not allowed on non-final instance methods
|
||||
|
||||
override.on.static.method=Static methods cannot be annotated with @Override
|
||||
override.on.non-overriding.method=Method does not override method from its superclass
|
||||
|
||||
@@ -7,7 +7,11 @@ import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.patterns.ElementPattern;
|
||||
import com.intellij.pom.java.JavaFeature;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.IncompleteModelUtil;
|
||||
import com.intellij.psi.impl.source.PsiClassReferenceType;
|
||||
import com.intellij.psi.search.searches.SuperMethodsSearch;
|
||||
import com.intellij.psi.util.JavaPsiRecordUtil;
|
||||
import com.intellij.psi.util.MethodSignatureBackedByPsiMethod;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
@@ -72,8 +76,10 @@ final class AnnotationChecker {
|
||||
if (!myVisitor.hasErrorResults()) checkAnnotationApplicability(annotation);
|
||||
if (!myVisitor.hasErrorResults()) checkAnnotationType(annotation);
|
||||
if (!myVisitor.hasErrorResults()) checkMissingAttributes(annotation);
|
||||
if (!myVisitor.hasErrorResults()) checkSafeVarargAnnotation(annotation);
|
||||
if (!myVisitor.hasErrorResults()) checkTargetAnnotationDuplicates(annotation);
|
||||
if (!myVisitor.hasErrorResults()) checkFunctionalInterface(annotation);
|
||||
if (!myVisitor.hasErrorResults()) checkOverrideAnnotation(annotation);
|
||||
}
|
||||
|
||||
private void checkFunctionalInterface(@NotNull PsiAnnotation annotation) {
|
||||
@@ -96,6 +102,23 @@ final class AnnotationChecker {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkSafeVarargAnnotation(@NotNull PsiAnnotation annotation) {
|
||||
if (!Comparing.strEqual(annotation.getQualifiedName(), CommonClassNames.JAVA_LANG_SAFE_VARARGS)) return;
|
||||
PsiAnnotationOwner owner = annotation.getOwner();
|
||||
if (!(owner instanceof PsiModifierList list)) return;
|
||||
PsiElement parent = list.getParent();
|
||||
if (parent instanceof PsiRecordComponent) {
|
||||
myVisitor.report(JavaErrorKinds.SAFE_VARARGS_ON_RECORD_COMPONENT.create(annotation));
|
||||
} else if (parent instanceof PsiMethod method) {
|
||||
if (!method.isVarArgs()) {
|
||||
myVisitor.report(JavaErrorKinds.SAFE_VARARGS_ON_FIXED_ARITY.create(annotation, method));
|
||||
}
|
||||
else if (!GenericsUtil.isSafeVarargsNoOverridingCondition(method)) {
|
||||
myVisitor.report(JavaErrorKinds.SAFE_VARARGS_ON_NON_FINAL_METHOD.create(annotation, method));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkMissingAttributes(@NotNull PsiAnnotation annotation) {
|
||||
PsiJavaCodeReferenceElement nameRef = annotation.getNameReferenceElement();
|
||||
if (nameRef == null) return;
|
||||
@@ -236,4 +259,50 @@ final class AnnotationChecker {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkOverrideAnnotation(@NotNull PsiAnnotation annotation) {
|
||||
if (!CommonClassNames.JAVA_LANG_OVERRIDE.equals(annotation.getQualifiedName())) return;
|
||||
if (!(annotation.getOwner() instanceof PsiModifierList list)) return;
|
||||
if (!(list.getParent() instanceof PsiMethod method)) return;
|
||||
if (method.hasModifierProperty(PsiModifier.STATIC)) {
|
||||
myVisitor.report(JavaErrorKinds.OVERRIDE_ON_STATIC_METHOD.create(annotation, method));
|
||||
}
|
||||
MethodSignatureBackedByPsiMethod superMethod = SuperMethodsSearch.search(method, null, true, false).findFirst();
|
||||
PsiClass psiClass = method.getContainingClass();
|
||||
if (psiClass != null) {
|
||||
if (superMethod != null && psiClass.isInterface()) {
|
||||
PsiMethod psiMethod = superMethod.getMethod();
|
||||
PsiClass superClass = psiMethod.getContainingClass();
|
||||
if (superClass != null &&
|
||||
CommonClassNames.JAVA_LANG_OBJECT.equals(superClass.getQualifiedName()) &&
|
||||
psiMethod.hasModifierProperty(PsiModifier.PROTECTED)) {
|
||||
superMethod = null;
|
||||
}
|
||||
}
|
||||
else if (superMethod == null) {
|
||||
if (IncompleteModelUtil.isIncompleteModel(psiClass)) {
|
||||
if (!IncompleteModelUtil.isHierarchyResolved(psiClass)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (PsiClassType type : psiClass.getSuperTypes()) {
|
||||
// There's an unresolvable superclass: likely the error on @Override is induced.
|
||||
// Do not show an error on override, as it's reasonable to fix hierarchy first.
|
||||
if (type.resolve() == null) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (superMethod == null) {
|
||||
if (JavaPsiRecordUtil.getRecordComponentForAccessor(method) == null) {
|
||||
myVisitor.report(JavaErrorKinds.OVERRIDE_ON_NON_OVERRIDING_METHOD.create(annotation, method));
|
||||
}
|
||||
return;
|
||||
}
|
||||
PsiClass superClass = superMethod.getMethod().getContainingClass();
|
||||
if (superClass != null && superClass.isInterface()) {
|
||||
myVisitor.checkFeature(annotation, JavaFeature.OVERRIDE_INTERFACE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,7 @@ import com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.pom.java.JavaFeature;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.psi.JavaElementVisitor;
|
||||
import com.intellij.psi.PsiAnnotation;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -59,6 +56,28 @@ final class JavaErrorVisitor extends JavaElementVisitor {
|
||||
myAnnotationChecker.checkAnnotation(annotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitNameValuePair(@NotNull PsiNameValuePair pair) {
|
||||
super.visitNameValuePair(pair);
|
||||
myAnnotationChecker.checkNameValuePair(pair);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitAnnotationArrayInitializer(@NotNull PsiArrayInitializerMemberValue initializer) {
|
||||
super.visitAnnotationArrayInitializer(initializer);
|
||||
myAnnotationChecker.checkArrayInitializer(initializer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitAnnotationMethod(@NotNull PsiAnnotationMethod method) {
|
||||
super.visitAnnotationMethod(method);
|
||||
PsiType returnType = method.getReturnType();
|
||||
PsiAnnotationMemberValue value = method.getDefaultValue();
|
||||
if (returnType != null && value != null) {
|
||||
myAnnotationChecker.checkMemberValueType(value, returnType, method);
|
||||
}
|
||||
}
|
||||
|
||||
void checkFeature(@NotNull PsiElement element, @NotNull JavaFeature feature) {
|
||||
if (!feature.isSufficient(myLanguageLevel)) {
|
||||
report(JavaErrorKinds.UNSUPPORTED_FEATURE.create(element, feature));
|
||||
|
||||
@@ -56,7 +56,7 @@ public final class JavaErrorKinds {
|
||||
};
|
||||
// Can be anchored on @FunctionalInterface annotation or at call site
|
||||
public static final JavaErrorKind<PsiElement, PsiClass> LAMBDA_NO_TARGET_METHOD =
|
||||
new Parameterized<>("lambda.no.target.method.found") {};
|
||||
new Parameterized<>("lambda.no.target.method.found");
|
||||
// Can be anchored on @FunctionalInterface annotation or at call site
|
||||
public static final JavaErrorKind<PsiElement, PsiClass> LAMBDA_MULTIPLE_TARGET_METHODS =
|
||||
new Parameterized<>("lambda.multiple.sam.candidates") {
|
||||
@@ -66,7 +66,7 @@ public final class JavaErrorKinds {
|
||||
}
|
||||
};
|
||||
public static final JavaErrorKind<PsiAnnotation, PsiClass> LAMBDA_FUNCTIONAL_INTERFACE_SEALED =
|
||||
new Parameterized<>("lambda.sealed.functional.interface") {};
|
||||
new Parameterized<>("lambda.sealed.functional.interface");
|
||||
public static final JavaErrorKind<PsiAnnotation, @NotNull List<PsiAnnotation.@NotNull TargetType>> ANNOTATION_NOT_APPLICABLE =
|
||||
new Parameterized<>("annotation.not.applicable") {
|
||||
@Override
|
||||
@@ -98,4 +98,14 @@ public final class JavaErrorKinds {
|
||||
"annotation.missing.attribute", attributeNames.stream().map(attr -> "'" + attr + "'").collect(Collectors.joining(", "))));
|
||||
}
|
||||
};
|
||||
public static final JavaSimpleErrorKind<PsiAnnotation> SAFE_VARARGS_ON_RECORD_COMPONENT =
|
||||
new JavaSimpleErrorKind<>("safe.varargs.on.record.component");
|
||||
public static final JavaErrorKind<PsiAnnotation, PsiMethod> SAFE_VARARGS_ON_FIXED_ARITY =
|
||||
new Parameterized<>("safe.varargs.on.fixed.arity");
|
||||
public static final JavaErrorKind<PsiAnnotation, PsiMethod> SAFE_VARARGS_ON_NON_FINAL_METHOD =
|
||||
new Parameterized<>("safe.varargs.on.non.final.method");
|
||||
public static final JavaErrorKind<PsiAnnotation, PsiMethod> OVERRIDE_ON_STATIC_METHOD =
|
||||
new Parameterized<>("override.on.static.method");
|
||||
public static final JavaErrorKind<PsiAnnotation, PsiMethod> OVERRIDE_ON_NON_OVERRIDING_METHOD =
|
||||
new Parameterized<>("override.on.non-overriding.method");
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.PropertyKey;
|
||||
|
||||
non-sealed abstract class Parameterized<Psi extends PsiElement, Context> implements JavaErrorKind<Psi, Context> {
|
||||
non-sealed class Parameterized<Psi extends PsiElement, Context> implements JavaErrorKind<Psi, Context> {
|
||||
private final @NotNull @PropertyKey(resourceBundle = JavaCompilationErrorBundle.BUNDLE) String myKey;
|
||||
|
||||
Parameterized(@NotNull @PropertyKey(resourceBundle = JavaCompilationErrorBundle.BUNDLE) String key) {
|
||||
|
||||
@@ -611,3 +611,5 @@ inspection.message.expression.compared.to.itself.description=Expression is compa
|
||||
intention.name.do.not.report.conditions.with.possible.side.effect=Do not report conditions with possible side-effect
|
||||
dfa.find.cause.or.another=or {0}
|
||||
dfa.find.cause.and.another=and {0}
|
||||
safe.varargs.not.suppress.potentially.unsafe.operations=@SafeVarargs do not suppress potentially unsafe operations
|
||||
safe.varargs.on.reifiable.type=@SafeVarargs is not applicable for reifiable types
|
||||
@@ -134,6 +134,16 @@
|
||||
enabledByDefault="true" level="WARNING"
|
||||
implementationClass="com.intellij.codeInspection.PossibleHeapPollutionVarargsInspection"
|
||||
key="inspection.safe.varargs.detector.display.name" bundle="messages.JavaAnalysisBundle"/>
|
||||
<localInspection groupPath="Java" language="JAVA" shortName="SafeVarargsHasNoEffect"
|
||||
groupKey="group.names.probable.bugs" groupBundle="messages.InspectionsBundle"
|
||||
enabledByDefault="true" level="WARNING"
|
||||
implementationClass="com.intellij.codeInspection.SafeVarargsHasNoEffectInspection"
|
||||
key="safe.varargs.not.suppress.potentially.unsafe.operations" bundle="messages.JavaAnalysisBundle"/>
|
||||
<localInspection groupPath="Java" language="JAVA" shortName="SafeVarargsOnNonReifiableType"
|
||||
groupKey="group.names.declaration.redundancy" groupBundle="messages.InspectionsBundle"
|
||||
enabledByDefault="true" level="WARNING"
|
||||
implementationClass="com.intellij.codeInspection.SafeVarargsOnNonReifiableTypeInspection"
|
||||
key="safe.varargs.on.reifiable.type" bundle="messages.JavaAnalysisBundle"/>
|
||||
<localInspection groupPathKey="group.path.names.java.language.level.specific.issues.and.migration.aids" language="JAVA" shortName="CodeBlock2Expr"
|
||||
groupKey="group.names.language.level.specific.issues.and.migration.aids8" groupBundle="messages.InspectionsBundle" enabledByDefault="true" level="WARNING"
|
||||
implementationClass="com.intellij.codeInspection.RedundantLambdaCodeBlockInspection"
|
||||
|
||||
@@ -14,7 +14,6 @@ import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement;
|
||||
import com.intellij.core.JavaPsiBundle;
|
||||
import com.intellij.java.analysis.JavaAnalysisBundle;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.NlsContexts;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
@@ -434,16 +433,6 @@ public final class AnnotationsHighlightUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
static HighlightInfo.Builder checkInvalidAnnotationOnRecordComponent(@NotNull PsiAnnotation annotation) {
|
||||
if (!Comparing.strEqual(annotation.getQualifiedName(), CommonClassNames.JAVA_LANG_SAFE_VARARGS)) return null;
|
||||
PsiAnnotationOwner owner = annotation.getOwner();
|
||||
if (!(owner instanceof PsiModifierList list)) return null;
|
||||
PsiElement parent = list.getParent();
|
||||
if (!(parent instanceof PsiRecordComponent)) return null;
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(annotation)
|
||||
.descriptionAndTooltip(JavaErrorBundle.message("safevararg.annotation.cannot.be.applied.for.record.component"));
|
||||
}
|
||||
|
||||
static HighlightInfo.Builder checkRepeatableAnnotation(@NotNull PsiAnnotation annotation) {
|
||||
String qualifiedName = annotation.getQualifiedName();
|
||||
if (!CommonClassNames.JAVA_LANG_ANNOTATION_REPEATABLE.equals(qualifiedName)) return null;
|
||||
|
||||
@@ -12,7 +12,6 @@ import com.intellij.core.JavaPsiBundle;
|
||||
import com.intellij.ide.IdeBundle;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.DumbService;
|
||||
import com.intellij.openapi.project.IndexNotReadyException;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.projectRoots.JavaSdkVersion;
|
||||
import com.intellij.openapi.projectRoots.JavaVersionService;
|
||||
@@ -30,13 +29,11 @@ import com.intellij.psi.impl.PsiClassImplUtil;
|
||||
import com.intellij.psi.impl.PsiImplUtil;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.PsiShortNamesCache;
|
||||
import com.intellij.psi.search.searches.SuperMethodsSearch;
|
||||
import com.intellij.psi.util.*;
|
||||
import com.intellij.util.ArrayUtilRt;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.siyeh.ig.psiutils.InstanceOfUtils;
|
||||
import com.siyeh.ig.psiutils.VariableAccessUtils;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -1115,123 +1112,6 @@ public final class GenericsHighlightUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
static HighlightInfo.Builder checkOverrideAnnotation(@NotNull PsiMethod method,
|
||||
@NotNull PsiAnnotation overrideAnnotation,
|
||||
@NotNull LanguageLevel languageLevel) {
|
||||
if (method.hasModifierProperty(PsiModifier.STATIC)) {
|
||||
QuickFixFactory factory = QuickFixFactory.getInstance();
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(overrideAnnotation)
|
||||
.registerFix(factory.createDeleteFix(overrideAnnotation), null, null, null, null)
|
||||
.descriptionAndTooltip(
|
||||
JavaErrorBundle.message("static.method.cannot.be.annotated.with.override"));
|
||||
}
|
||||
try {
|
||||
MethodSignatureBackedByPsiMethod superMethod = SuperMethodsSearch.search(method, null, true, false).findFirst();
|
||||
PsiClass psiClass = method.getContainingClass();
|
||||
if (psiClass != null) {
|
||||
if (superMethod != null && psiClass.isInterface()) {
|
||||
PsiMethod psiMethod = superMethod.getMethod();
|
||||
PsiClass superClass = psiMethod.getContainingClass();
|
||||
if (superClass != null &&
|
||||
CommonClassNames.JAVA_LANG_OBJECT.equals(superClass.getQualifiedName()) &&
|
||||
psiMethod.hasModifierProperty(PsiModifier.PROTECTED)) {
|
||||
superMethod = null;
|
||||
}
|
||||
} else if (superMethod == null) {
|
||||
if (IncompleteModelUtil.isIncompleteModel(psiClass)) {
|
||||
if (!IncompleteModelUtil.isHierarchyResolved(psiClass)) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
for (PsiClassType type : psiClass.getSuperTypes()) {
|
||||
// There's an unresolvable superclass: likely the error on @Override is induced.
|
||||
// Do not show an error on override, as it's reasonable to fix hierarchy first.
|
||||
if (type.resolve() == null) return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (superMethod == null) {
|
||||
if (JavaPsiRecordUtil.getRecordComponentForAccessor(method) != null) {
|
||||
return null;
|
||||
}
|
||||
String description = JavaErrorBundle.message("method.does.not.override.super");
|
||||
HighlightInfo.Builder builder =
|
||||
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(overrideAnnotation).descriptionAndTooltip(description);
|
||||
List<IntentionAction> registrar = new ArrayList<>();
|
||||
QuickFixFactory factory = QuickFixFactory.getInstance();
|
||||
factory.registerPullAsAbstractUpFixes(method, registrar);
|
||||
for (IntentionAction action : registrar) {
|
||||
builder.registerFix(action, null, null, null, null);
|
||||
}
|
||||
builder.registerFix(factory.createDeleteFix(overrideAnnotation), null, null, null, null);
|
||||
return builder;
|
||||
}
|
||||
PsiClass superClass = superMethod.getMethod().getContainingClass();
|
||||
if (superClass != null && superClass.isInterface()) {
|
||||
return HighlightUtil.checkFeature(overrideAnnotation, JavaFeature.OVERRIDE_INTERFACE, languageLevel,
|
||||
overrideAnnotation.getContainingFile());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
catch (IndexNotReadyException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static HighlightInfo.Builder checkSafeVarargsAnnotation(@NotNull PsiMethod method, @NotNull LanguageLevel languageLevel) {
|
||||
PsiModifierList list = method.getModifierList();
|
||||
PsiAnnotation safeVarargsAnnotation = list.findAnnotation(CommonClassNames.JAVA_LANG_SAFE_VARARGS);
|
||||
if (safeVarargsAnnotation == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
if (!method.isVarArgs()) {
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(safeVarargsAnnotation).descriptionAndTooltip(
|
||||
JavaErrorBundle.message("safevarargs.not.allowed.on.methods.with.fixed.arity"));
|
||||
}
|
||||
if (!isSafeVarargsNoOverridingCondition(method, languageLevel)) {
|
||||
HighlightInfo.Builder info =
|
||||
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(safeVarargsAnnotation).descriptionAndTooltip(
|
||||
JavaErrorBundle.message("safevarargs.not.allowed.non.final.instance.methods"));
|
||||
IntentionAction action = QuickFixFactory.getInstance().createModifierListFix(method, PsiModifier.FINAL, true, true);
|
||||
info.registerFix(action, null, null, null, null);
|
||||
return info;
|
||||
}
|
||||
|
||||
PsiParameterList parameterList = method.getParameterList();
|
||||
PsiParameter varParameter = Objects.requireNonNull(parameterList.getParameter(parameterList.getParametersCount() - 1));
|
||||
|
||||
for (PsiReferenceExpression element : VariableAccessUtils.getVariableReferences(varParameter, method.getBody())) {
|
||||
if (!PsiUtil.isAccessedForReading(element)) {
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.WARNING).range(element).descriptionAndTooltip(
|
||||
JavaErrorBundle.message("safevarargs.not.suppress.potentially.unsafe.operations"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LOG.assertTrue(varParameter.isVarArgs());
|
||||
PsiEllipsisType ellipsisType = (PsiEllipsisType)varParameter.getType();
|
||||
PsiType componentType = ellipsisType.getComponentType();
|
||||
if (JavaGenericsUtil.isReifiableType(componentType)) {
|
||||
PsiElement element = varParameter.getTypeElement();
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.WARNING).range(element).descriptionAndTooltip(
|
||||
JavaErrorBundle.message("safevarargs.not.applicable.for.reifiable.types"));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
catch (IndexNotReadyException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isSafeVarargsNoOverridingCondition(@NotNull PsiMethod method, @NotNull LanguageLevel languageLevel) {
|
||||
return method.hasModifierProperty(PsiModifier.FINAL) ||
|
||||
method.hasModifierProperty(PsiModifier.STATIC) ||
|
||||
method.isConstructor() ||
|
||||
method.hasModifierProperty(PsiModifier.PRIVATE) && languageLevel.isAtLeast(LanguageLevel.JDK_1_9);
|
||||
}
|
||||
|
||||
static void checkEnumConstantForConstructorProblems(@NotNull Project project,
|
||||
@NotNull PsiEnumConstant enumConstant,
|
||||
@NotNull JavaSdkVersion javaSdkVersion, @NotNull Consumer<? super HighlightInfo.Builder> errorSink) {
|
||||
|
||||
@@ -245,15 +245,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
public void visitAnnotation(@NotNull PsiAnnotation annotation) {
|
||||
super.visitAnnotation(annotation);
|
||||
if (!hasErrorResults()) add(AnnotationsHighlightUtil.checkDuplicateAnnotations(annotation, myLanguageLevel));
|
||||
if (!hasErrorResults()) add(AnnotationsHighlightUtil.checkInvalidAnnotationOnRecordComponent(annotation));
|
||||
if (!hasErrorResults()) add(AnnotationsHighlightUtil.checkRepeatableAnnotation(annotation));
|
||||
if (CommonClassNames.JAVA_LANG_OVERRIDE.equals(annotation.getQualifiedName())) {
|
||||
PsiAnnotationOwner owner = annotation.getOwner();
|
||||
PsiElement parent = owner instanceof PsiModifierList list ? list.getParent() : null;
|
||||
if (parent instanceof PsiMethod psiMethod) {
|
||||
add(GenericsHighlightUtil.checkOverrideAnnotation(psiMethod, annotation, myLanguageLevel));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean add(@Nullable HighlightInfo.Builder builder) {
|
||||
@@ -938,7 +930,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
super.visitMethod(method);
|
||||
if (!hasErrorResults()) add(HighlightControlFlowUtil.checkUnreachableStatement(method.getBody()));
|
||||
if (!hasErrorResults()) add(HighlightMethodUtil.checkConstructorHandleSuperClassExceptions(method));
|
||||
if (!hasErrorResults()) add(GenericsHighlightUtil.checkSafeVarargsAnnotation(method, myLanguageLevel));
|
||||
if (!hasErrorResults()) add(HighlightMethodUtil.checkRecordAccessorDeclaration(method));
|
||||
if (!hasErrorResults()) HighlightMethodUtil.checkRecordConstructorDeclaration(method, myErrorSink);
|
||||
|
||||
|
||||
@@ -40,6 +40,13 @@ final class JavaErrorFixProvider {
|
||||
private static final Map<JavaErrorKind<?, ?>, List<JavaFixesProvider<?, ?>>> FIXES = new HashMap<>();
|
||||
|
||||
static {
|
||||
single(SAFE_VARARGS_ON_NON_FINAL_METHOD,
|
||||
error -> QuickFixFactory.getInstance().createModifierListFix(error.context(), PsiModifier.FINAL, true, true));
|
||||
multi(OVERRIDE_ON_NON_OVERRIDING_METHOD, error -> {
|
||||
List<CommonIntentionAction> registrar = new ArrayList<>();
|
||||
QuickFixFactory.getInstance().registerPullAsAbstractUpFixes(error.context(), registrar);
|
||||
return registrar;
|
||||
});
|
||||
JavaFixProvider<PsiElement, Object> annotationRemover = error ->
|
||||
error.psi() instanceof PsiAnnotation annotation ? QuickFixFactory.getInstance()
|
||||
.createDeleteFix(annotation, JavaAnalysisBundle.message("intention.text.remove.annotation")) : null;
|
||||
@@ -47,7 +54,9 @@ final class JavaErrorFixProvider {
|
||||
ANNOTATION_NOT_ALLOWED_REF, ANNOTATION_NOT_ALLOWED_VAR,
|
||||
ANNOTATION_NOT_ALLOWED_VOID, LAMBDA_MULTIPLE_TARGET_METHODS, LAMBDA_NO_TARGET_METHOD,
|
||||
LAMBDA_NOT_FUNCTIONAL_INTERFACE, ANNOTATION_NOT_APPLICABLE,
|
||||
LAMBDA_FUNCTIONAL_INTERFACE_SEALED)) {
|
||||
LAMBDA_FUNCTIONAL_INTERFACE_SEALED, OVERRIDE_ON_STATIC_METHOD,
|
||||
OVERRIDE_ON_NON_OVERRIDING_METHOD, SAFE_VARARGS_ON_FIXED_ARITY,
|
||||
SAFE_VARARGS_ON_NON_FINAL_METHOD, SAFE_VARARGS_ON_RECORD_COMPONENT)) {
|
||||
single(kind, annotationRemover);
|
||||
}
|
||||
single(ANNOTATION_NOT_ALLOWED_VAR, error -> {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
package com.intellij.codeInspection;
|
||||
|
||||
import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.GenericsHighlightUtil;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil;
|
||||
import com.intellij.java.analysis.JavaAnalysisBundle;
|
||||
import com.intellij.modcommand.ModPsiUpdater;
|
||||
@@ -154,7 +153,8 @@ public final class PossibleHeapPollutionVarargsInspection extends AbstractBaseJa
|
||||
|
||||
protected void registerProblem(PsiMethod method, PsiIdentifier nameIdentifier) {
|
||||
final LocalQuickFix quickFix;
|
||||
if (GenericsHighlightUtil.isSafeVarargsNoOverridingCondition(method, PsiUtil.getLanguageLevel(method))) {
|
||||
PsiUtil.getLanguageLevel(method);
|
||||
if (GenericsUtil.isSafeVarargsNoOverridingCondition(method)) {
|
||||
quickFix = new AnnotateAsSafeVarargsQuickFix(false);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInspection;
|
||||
|
||||
import com.intellij.java.analysis.JavaAnalysisBundle;
|
||||
import com.intellij.pom.java.JavaFeature;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public final class SafeVarargsHasNoEffectInspection extends AbstractBaseJavaLocalInspectionTool {
|
||||
@Override
|
||||
public @NotNull Set<@NotNull JavaFeature> requiredFeatures() {
|
||||
return Set.of(JavaFeature.ANNOTATIONS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
|
||||
return new JavaElementVisitor() {
|
||||
@Override
|
||||
public void visitReferenceExpression(@NotNull PsiReferenceExpression expression) {
|
||||
if (PsiUtil.isAccessedForReading(expression)) return;
|
||||
if (!(expression.resolve() instanceof PsiParameter parameter)) return;
|
||||
if (!(parameter.getDeclarationScope() instanceof PsiMethod method)) return;
|
||||
if (!(parameter.getType() instanceof PsiEllipsisType)) return;
|
||||
if (!method.getModifierList().hasAnnotation(CommonClassNames.JAVA_LANG_SAFE_VARARGS)) return;
|
||||
holder.registerProblem(expression, JavaAnalysisBundle.message("safe.varargs.not.suppress.potentially.unsafe.operations"));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInspection;
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil;
|
||||
import com.intellij.java.analysis.JavaAnalysisBundle;
|
||||
import com.intellij.pom.java.JavaFeature;
|
||||
import com.intellij.psi.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
public final class SafeVarargsOnNonReifiableTypeInspection extends AbstractBaseJavaLocalInspectionTool {
|
||||
@Override
|
||||
public @NotNull Set<@NotNull JavaFeature> requiredFeatures() {
|
||||
return Set.of(JavaFeature.ANNOTATIONS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
|
||||
return new JavaElementVisitor() {
|
||||
@Override
|
||||
public void visitParameter(@NotNull PsiParameter parameter) {
|
||||
super.visitParameter(parameter);
|
||||
if (!parameter.isVarArgs()) return;
|
||||
if (!(parameter.getDeclarationScope() instanceof PsiMethod method)) return;
|
||||
if (!method.getModifierList().hasAnnotation(CommonClassNames.JAVA_LANG_SAFE_VARARGS)) return;
|
||||
if (!method.isVarArgs() || !GenericsUtil.isSafeVarargsNoOverridingCondition(method)) return;
|
||||
PsiEllipsisType ellipsisType = (PsiEllipsisType)parameter.getType();
|
||||
PsiType componentType = ellipsisType.getComponentType();
|
||||
if (JavaGenericsUtil.isReifiableType(componentType)) {
|
||||
PsiTypeElement element = Objects.requireNonNull(parameter.getTypeElement());
|
||||
holder.registerProblem(element, JavaAnalysisBundle.message("safe.varargs.on.reifiable.type"));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<html>
|
||||
<body>
|
||||
Reports write operations inside <code>@SafeVarargs</code> methods where vararg parameter is reassigned to something else.
|
||||
In this case, newly assigned array is not related to the variable arity arguments at the call site and might not be safe anymore.
|
||||
<p><b>Example:</b></p>
|
||||
<pre><code>
|
||||
@SafeVarargs
|
||||
public final <T> void test(T[] tt, T... t) {
|
||||
t = tt;
|
||||
System.out.println(t[0]);
|
||||
}
|
||||
</code></pre>
|
||||
<!-- tooltip end -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,13 @@
|
||||
<html>
|
||||
<body>
|
||||
Reports reifiable type of variable arity method annotated with <code>@SafeVarargs</code>.
|
||||
In this case, the annotation is redundant.
|
||||
<p><b>Example:</b></p>
|
||||
<pre><code>
|
||||
@SafeVarargs
|
||||
public final void processStrings(String... strings) {
|
||||
}
|
||||
</code></pre>
|
||||
<!-- tooltip end -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -5,6 +5,7 @@ import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Couple;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.util.*;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
@@ -646,4 +647,15 @@ public final class GenericsUtil {
|
||||
return JavaPsiFacade.getElementFactory(target.getProject())
|
||||
.createType(target, parameters).annotate(classType.getAnnotationProvider());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param method method to check
|
||||
* @return true if a given method can be annotated as safe-varargs according to its overridability
|
||||
*/
|
||||
public static boolean isSafeVarargsNoOverridingCondition(@NotNull PsiMethod method) {
|
||||
return method.hasModifierProperty(PsiModifier.FINAL) ||
|
||||
method.hasModifierProperty(PsiModifier.STATIC) ||
|
||||
method.isConstructor() ||
|
||||
method.hasModifierProperty(PsiModifier.PRIVATE) && PsiUtil.getLanguageLevel(method).isAtLeast(LanguageLevel.JDK_1_9);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ package com.intellij.java.codeInsight.daemon;
|
||||
import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase;
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
|
||||
import com.intellij.codeInspection.InspectionProfileEntry;
|
||||
import com.intellij.codeInspection.SafeVarargsHasNoEffectInspection;
|
||||
import com.intellij.codeInspection.SafeVarargsOnNonReifiableTypeInspection;
|
||||
import com.intellij.codeInspection.compiler.JavacQuirksInspection;
|
||||
import com.intellij.codeInspection.deadCode.UnusedDeclarationInspection;
|
||||
import com.intellij.codeInspection.deadCode.UnusedDeclarationInspectionBase;
|
||||
@@ -35,7 +37,8 @@ public class LightAdvHighlightingJdk7Test extends LightDaemonAnalyzerTestCase {
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
enableInspectionTools(new UnusedDeclarationInspection(), new UncheckedWarningLocalInspection(), new JavacQuirksInspection(), new RedundantCastInspection());
|
||||
enableInspectionTools(new UnusedDeclarationInspection(), new UncheckedWarningLocalInspection(), new JavacQuirksInspection(), new RedundantCastInspection(),
|
||||
new SafeVarargsHasNoEffectInspection(), new SafeVarargsOnNonReifiableTypeInspection());
|
||||
setLanguageLevel(LanguageLevel.JDK_1_7);
|
||||
IdeaTestUtil.setTestVersion(JavaSdkVersion.JDK_1_7, getModule(), getTestRootDisposable());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user