[java-highlighting] More instanceof/cast/pattern errors migrated

Part of IDEA-365344 Create a new Java error highlighter with minimal dependencies (PSI only)

GitOrigin-RevId: e137879fe6320d4a01b0595a7399b29148161450
This commit is contained in:
Tagir Valeev
2025-01-31 15:31:12 +00:00
committed by intellij-monorepo-bot
parent 72809f5127
commit cd9760a15f
12 changed files with 332 additions and 317 deletions
@@ -311,6 +311,7 @@ call.ambiguous.tooltip=\
<tr>{1}<td>in <b>{2}</b>\\&nbsp;and</td></tr>\
<tr>{3}<td>in <b>{4}</b>\\&nbsp;match</td></tr>\
</table></body></html>
call.parsed.as.deconstruction.pattern=Constant expression, pattern or null is required
array.illegal.initializer=Illegal initializer for ''{0}''
array.initializer.not.allowed=Array initializer is not allowed here
@@ -325,12 +326,22 @@ pattern.type.pattern.expected=Type pattern expected
pattern.deconstruction.variable=Identifier is not allowed here
pattern.deconstruction.annotation=Annotations are not allowed in deconstruction pattern types
pattern.deconstruction.requires.record=Deconstruction pattern can only be applied to a record, ''{0}'' is not a record
pattern.deconstruction.count.mismatch=Incorrect number of nested patterns: expected {0} but found {1}
pattern.not.exhaustive=Pattern ''{0}'' is not exhaustive on ''{1}''
pattern.unsafe.cast=''{0}'' cannot be safely cast to ''{1}''
pattern.cannot.infer.type=Cannot infer pattern type: {0}
pattern.deconstruction.count.mismatch=Incorrect number of nested patterns: expected {0} but found {1}
pattern.instanceof.supertype=Pattern type ''{0}'' is a supertype of expression type ''{1}''
pattern.instanceof.equals=Pattern type ''{0}'' is the same as expression type
instanceof.type.parameter=Class or array expected
instanceof.illegal.generic.type=Illegal generic type for instanceof
instanceof.unsafe.cast=''{0}'' cannot be safely cast to ''{1}''
cast.inconvertible=Inconvertible types; cannot cast ''{0}'' to ''{1}''
cast.intersection.not.interface=Interface expected here
cast.intersection.unexpected.type=Unexpected type: class is expected
cast.intersection.repeated.interface=Repeated interface
cast.intersection.inheritance.clash={0} cannot be inherited with different arguments: {1} and {2}
expression.expected=Expression expected
expression.super.dot.expected='.' expected
@@ -6,6 +6,7 @@ import com.intellij.core.JavaPsiBundle;
import com.intellij.java.codeserver.highlighting.errors.*;
import com.intellij.openapi.project.IndexNotReadyException;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Ref;
import com.intellij.pom.java.JavaFeature;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
@@ -720,6 +721,87 @@ final class ExpressionChecker {
}
}
void checkInconvertibleTypeCast(@NotNull PsiTypeCastExpression expression) {
PsiTypeElement castTypeElement = expression.getCastType();
if (castTypeElement == null) return;
PsiType castType = castTypeElement.getType();
PsiExpression operand = expression.getOperand();
if (operand == null) return;
PsiType operandType = operand.getType();
if (operandType != null &&
!TypeConversionUtil.areTypesConvertible(operandType, castType, PsiUtil.getLanguageLevel(expression)) &&
!PsiUtil.isInSignaturePolymorphicCall(expression)) {
if (IncompleteModelUtil.isIncompleteModel(expression) && IncompleteModelUtil.isPotentiallyConvertible(castType, operand)) {
return;
}
myVisitor.report(JavaErrorKinds.CAST_INCONVERTIBLE.create(
expression, new JavaIncompatibleTypeErrorContext(operandType, castType)));
}
}
/**
* 15.16 Cast Expressions
* ( ReferenceType {AdditionalBound} ) expression, where AdditionalBound: & InterfaceType then all must be true
* - ReferenceType must denote a class or interface type.
* - The erasures of all the listed types must be pairwise different.
* - No two listed types may be subtypes of different parameterization of the same generic interface.
*/
void checkIntersectionInTypeCast(@NotNull PsiTypeCastExpression expression) {
PsiTypeElement castTypeElement = expression.getCastType();
if (castTypeElement == null || !isIntersection(castTypeElement, castTypeElement.getType())) return;
myVisitor.checkFeature(expression, JavaFeature.INTERSECTION_CASTS);
if (myVisitor.hasErrorResults()) return;
PsiTypeElement[] conjuncts = PsiTreeUtil.getChildrenOfType(castTypeElement, PsiTypeElement.class);
if (conjuncts != null) {
Set<PsiType> erasures = new HashSet<>(conjuncts.length);
erasures.add(TypeConversionUtil.erasure(conjuncts[0].getType()));
List<PsiTypeElement> conjList = new ArrayList<>(Arrays.asList(conjuncts));
for (int i = 1; i < conjuncts.length; i++) {
PsiTypeElement conjunct = conjuncts[i];
PsiType conjType = conjunct.getType();
if (conjType instanceof PsiClassType classType) {
PsiClass aClass = classType.resolve();
if (aClass != null && !aClass.isInterface()) {
myVisitor.report(JavaErrorKinds.CAST_INTERSECTION_NOT_INTERFACE.create(conjunct));
continue;
}
}
else {
myVisitor.report(JavaErrorKinds.CAST_INTERSECTION_UNEXPECTED_TYPE.create(conjunct));
continue;
}
if (!erasures.add(TypeConversionUtil.erasure(conjType))) {
myVisitor.report(JavaErrorKinds.CAST_INTERSECTION_REPEATED_INTERFACE.create(conjunct));
}
}
if (myVisitor.hasErrorResults()) return;
List<PsiType> typeList = ContainerUtil.map(conjList, PsiTypeElement::getType);
Ref<Pair<PsiType, PsiType>> differentArguments = new Ref<>();
PsiClass sameGenericParameterization =
InferenceSession.findParameterizationOfTheSameGenericClass(typeList, pair -> {
if (!TypesDistinctProver.provablyDistinct(pair.first, pair.second)) {
return true;
}
differentArguments.set(pair);
return false;
});
if (differentArguments.get() != null && sameGenericParameterization != null) {
var context = new JavaErrorKinds.InheritTypeClashContext(
sameGenericParameterization, differentArguments.get().getFirst(), differentArguments.get().getSecond());
myVisitor.report(JavaErrorKinds.CAST_INTERSECTION_INHERITANCE_CLASH.create(expression, context));
}
}
}
private static boolean isIntersection(@NotNull PsiTypeElement castTypeElement, @NotNull PsiType castType) {
if (castType instanceof PsiIntersectionType) return true;
return castType instanceof PsiClassType && PsiTreeUtil.getChildrenOfType(castTypeElement, PsiTypeElement.class) != null;
}
static boolean isArrayDeclaration(@NotNull PsiVariable variable) {
// Java-style 'var' arrays are prohibited by the parser; for C-style ones, looking for a bracket is enough
return ContainerUtil.or(variable.getChildren(), e -> PsiUtil.isJavaToken(e, JavaTokenType.LBRACKET));
@@ -1211,4 +1293,36 @@ final class ExpressionChecker {
myVisitor.report(JavaErrorKinds.TYPE_RESTRICTED_IDENTIFIER.create(ref));
}
}
void checkInstanceOfApplicable(@NotNull PsiInstanceOfExpression expression) {
PsiExpression operand = expression.getOperand();
PsiTypeElement typeElement = expression.getCheckType();
if (typeElement == null) {
typeElement = JavaPsiPatternUtil.getPatternTypeElement(expression.getPattern());
}
if (typeElement == null) return;
PsiType checkType = typeElement.getType();
PsiType operandType = operand.getType();
if (operandType == null) return;
boolean operandIsPrimitive = TypeConversionUtil.isPrimitiveAndNotNull(operandType);
boolean checkIsPrimitive = TypeConversionUtil.isPrimitiveAndNotNull(checkType);
boolean convertible = TypeConversionUtil.areTypesConvertible(operandType, checkType);
boolean primitiveInPatternsEnabled = PsiUtil.isAvailable(JavaFeature.PRIMITIVE_TYPES_IN_PATTERNS, expression);
if (((operandIsPrimitive || checkIsPrimitive) && !primitiveInPatternsEnabled) || !convertible) {
if (!convertible && IncompleteModelUtil.isIncompleteModel(expression) &&
IncompleteModelUtil.isPotentiallyConvertible(checkType, operand)) {
return;
}
if (((operandIsPrimitive || checkIsPrimitive) && !primitiveInPatternsEnabled) && convertible) {
myVisitor.checkFeature(expression, JavaFeature.PRIMITIVE_TYPES_IN_PATTERNS);
if (myVisitor.hasErrorResults()) return;
}
myVisitor.report(JavaErrorKinds.CAST_INCONVERTIBLE.create(
expression, new JavaIncompatibleTypeErrorContext(operandType, checkType)));
}
PsiPrimaryPattern pattern = expression.getPattern();
if (pattern instanceof PsiDeconstructionPattern deconstruction) {
myVisitor.myPatternChecker.checkDeconstructionErrors(deconstruction);
}
}
}
@@ -9,6 +9,7 @@ import com.intellij.openapi.projectRoots.JavaVersionService;
import com.intellij.openapi.roots.FileIndexFacade;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.java.JavaFeature;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.impl.IncompleteModelUtil;
@@ -484,6 +485,46 @@ final class GenericsChecker {
}
}
void checkInstanceOfGenericType(@NotNull PsiInstanceOfExpression expression) {
PsiTypeElement typeElement = expression.getCheckType();
if (typeElement == null) {
typeElement = JavaPsiPatternUtil.getPatternTypeElement(expression.getPattern());
}
if (typeElement == null) return;
PsiType checkType = typeElement.getType();
if (myVisitor.isApplicable(JavaFeature.PATTERNS)) {
PsiPrimaryPattern pattern = expression.getPattern();
if (pattern != null) {
myVisitor.myPatternChecker.checkUncheckedPatternConversion(pattern);
} else {
checkUnsafeCastInInstanceOf(typeElement, checkType, expression.getOperand().getType());
}
} else {
checkIllegalForInstanceOf(checkType, typeElement);
}
}
private void checkUnsafeCastInInstanceOf(@NotNull PsiTypeElement checkTypeElement, @NotNull PsiType checkType, @Nullable PsiType expressionType) {
if (expressionType != null && JavaGenericsUtil.isUncheckedCast(checkType, expressionType)) {
myVisitor.report(JavaErrorKinds.INSTANCEOF_UNSAFE_CAST.create(
checkTypeElement, new JavaIncompatibleTypeErrorContext(expressionType, checkType)));
}
}
/**
* 15.20.2 Type Comparison Operator instanceof
* ReferenceType mentioned after the instanceof operator is reifiable
*/
private void checkIllegalForInstanceOf(@Nullable PsiType type, @NotNull PsiTypeElement typeElement) {
PsiClass resolved = PsiUtil.resolveClassInClassTypeOnly(type);
if (resolved instanceof PsiTypeParameter) {
myVisitor.report(JavaErrorKinds.INSTANCEOF_TYPE_PARAMETER.create(typeElement));
}
else if (!JavaGenericsUtil.isReifiableType(type)) {
myVisitor.report(JavaErrorKinds.INSTANCEOF_ILLEGAL_GENERIC_TYPE.create(typeElement));
}
}
private static PsiType detectExpectedType(@NotNull PsiReferenceParameterList referenceParameterList) {
PsiNewExpression newExpression = requireNonNull(PsiTreeUtil.getParentOfType(referenceParameterList, PsiNewExpression.class));
PsiElement parent = newExpression.getParent();
@@ -49,7 +49,7 @@ final class JavaErrorVisitor extends JavaElementVisitor {
final @NotNull TypeChecker myTypeChecker = new TypeChecker(this);
final @NotNull MethodChecker myMethodChecker = new MethodChecker(this);
private final @NotNull ReceiverChecker myReceiverChecker = new ReceiverChecker(this);
private final @NotNull PatternChecker myPatternChecker = new PatternChecker(this);
final @NotNull PatternChecker myPatternChecker = new PatternChecker(this);
final @NotNull ModifierChecker myModifierChecker = new ModifierChecker(this);
final @NotNull ExpressionChecker myExpressionChecker = new ExpressionChecker(this);
private final @NotNull StatementChecker myStatementChecker = new StatementChecker(this);
@@ -684,6 +684,33 @@ final class JavaErrorVisitor extends JavaElementVisitor {
myPatternChecker.checkDeconstructionPattern(deconstructionPattern);
}
@Override
public void visitDeconstructionList(@NotNull PsiDeconstructionList deconstructionList) {
super.visitDeconstructionList(deconstructionList);
if (deconstructionList.getParent() instanceof PsiDeconstructionPattern pattern) {
myPatternChecker.checkMalformedDeconstructionPatternInCase(pattern);
}
}
@Override
public void visitInstanceOfExpression(@NotNull PsiInstanceOfExpression expression) {
super.visitInstanceOfExpression(expression);
if (!hasErrorResults()) myExpressionChecker.checkInstanceOfApplicable(expression);
if (!hasErrorResults()) myGenericsChecker.checkInstanceOfGenericType(expression);
if (!hasErrorResults() && isApplicable(JavaFeature.PATTERNS) &&
// 5.20.2 Removed restriction on pattern instanceof for unconditional patterns (JEP 432, 440)
!isApplicable(JavaFeature.PATTERN_GUARDS_AND_RECORD_PATTERNS)) {
myPatternChecker.checkInstanceOfPatternSupertype(expression);
}
}
@Override
public void visitTypeCastExpression(@NotNull PsiTypeCastExpression expression) {
super.visitTypeCastExpression(expression);
if (!hasErrorResults()) myExpressionChecker.checkIntersectionInTypeCast(expression);
if (!hasErrorResults()) myExpressionChecker.checkInconvertibleTypeCast(expression);
}
@Override
public void visitReferenceElement(@NotNull PsiJavaCodeReferenceElement ref) {
JavaResolveResult result = ref instanceof PsiExpression ? resolveOptimised(ref, myFile) : doVisitReferenceElement(ref);
@@ -85,7 +85,7 @@ final class PatternChecker {
}
}
private boolean checkUncheckedPatternConversion(@NotNull PsiPattern pattern) {
boolean checkUncheckedPatternConversion(@NotNull PsiPattern pattern) {
PsiType patternType = JavaPsiPatternUtil.getPatternType(pattern);
if (patternType == null) return false;
if (pattern instanceof PsiDeconstructionPattern subPattern) {
@@ -119,7 +119,7 @@ final class PatternChecker {
checkDeconstructionErrors(pattern);
}
private void checkDeconstructionErrors(@Nullable PsiDeconstructionPattern deconstructionPattern) {
void checkDeconstructionErrors(@Nullable PsiDeconstructionPattern deconstructionPattern) {
if (deconstructionPattern == null) return;
PsiTypeElement typeElement = deconstructionPattern.getTypeElement();
PsiType recordType = typeElement.getType();
@@ -186,6 +186,52 @@ final class PatternChecker {
}
}
void checkMalformedDeconstructionPatternInCase(@NotNull PsiDeconstructionPattern pattern) {
// We are checking the case when the pattern looks similar to method call in switch and want to show user-friendly message that here
// only constant expressions are expected.
// it is required to do it in deconstruction list because unresolved reference won't let any parents show any highlighting,
// so we need element which is not parent
PsiElement grandParent = pattern.getParent();
if (!(grandParent instanceof PsiCaseLabelElementList)) return;
PsiTypeElement typeElement = pattern.getTypeElement();
PsiJavaCodeReferenceElement ref = PsiTreeUtil.getChildOfType(typeElement, PsiJavaCodeReferenceElement.class);
if (ref == null) return;
if (ref.multiResolve(true).length == 0) {
PsiElementFactory elementFactory = myVisitor.factory();
if (pattern.getPatternVariable() == null && pattern.getDeconstructionList().getDeconstructionComponents().length == 0) {
PsiClassType type = tryCast(pattern.getTypeElement().getType(), PsiClassType.class);
if (type != null && ContainerUtil.exists(type.getParameters(), PsiWildcardType.class::isInstance)) return;
PsiExpression expression = elementFactory.createExpressionFromText(pattern.getText(), grandParent);
PsiMethodCallExpression call = tryCast(expression, PsiMethodCallExpression.class);
if (call == null) return;
if (call.getMethodExpression().resolve() != null) {
myVisitor.report(JavaErrorKinds.CALL_PARSED_AS_DECONSTRUCTION_PATTERN.create(pattern));
}
}
}
}
void checkInstanceOfPatternSupertype(@NotNull PsiInstanceOfExpression expression) {
@Nullable PsiPattern expressionPattern = expression.getPattern();
PsiTypeTestPattern pattern = tryCast(expressionPattern, PsiTypeTestPattern.class);
if (pattern == null) return;
PsiPatternVariable variable = pattern.getPatternVariable();
if (variable == null) return;
PsiTypeElement typeElement = pattern.getCheckType();
if (typeElement == null) return;
PsiType checkType = typeElement.getType();
PsiType expressionType = expression.getOperand().getType();
if (expressionType != null && checkType.isAssignableFrom(expressionType)) {
if (checkType.equals(expressionType)) {
myVisitor.report(JavaErrorKinds.PATTERN_INSTANCEOF_EQUALS.create(expression, checkType));
}
else {
myVisitor.report(JavaErrorKinds.PATTERN_INSTANCEOF_SUPERTYPE.create(
expression, new JavaIncompatibleTypeErrorContext(checkType, expressionType)));
}
}
}
private static @NotNull RecordExhaustivenessResult checkRecordExhaustiveness(@NotNull List<? extends PsiCaseLabelElement> elements,
@NotNull PsiType selectorType,
@NotNull PsiElement context) {
@@ -714,10 +714,33 @@ public final class JavaErrorKinds {
})
.withRawDescription((list, ctx) -> message("pattern.deconstruction.count.mismatch",
ctx.recordComponents().length, ctx.patternComponents().length));
public static final Parameterized<PsiInstanceOfExpression, JavaIncompatibleTypeErrorContext> PATTERN_INSTANCEOF_SUPERTYPE =
parameterized(PsiInstanceOfExpression.class, JavaIncompatibleTypeErrorContext.class, "pattern.instanceof.supertype")
.withAnchor((expr, context) -> expr.getCheckType())
.withRawDescription((expr, context) -> message(
"pattern.instanceof.supertype", context.lType().getPresentableText(), requireNonNull(context.rType()).getPresentableText()));
public static final Parameterized<PsiInstanceOfExpression, PsiType> PATTERN_INSTANCEOF_EQUALS =
parameterized(PsiInstanceOfExpression.class, PsiType.class, "pattern.instanceof.equals")
.withAnchor((expr, context) -> expr.getCheckType())
.withRawDescription((expr, context) -> message("pattern.instanceof.equals", context.getPresentableText()));
public static final Simple<PsiTypeElement> INSTANCEOF_TYPE_PARAMETER = error("instanceof.type.parameter");
public static final Simple<PsiTypeElement> INSTANCEOF_ILLEGAL_GENERIC_TYPE = error("instanceof.illegal.generic.type");
public static final Parameterized<PsiTypeElement, JavaIncompatibleTypeErrorContext> INSTANCEOF_UNSAFE_CAST =
parameterized(PsiTypeElement.class, JavaIncompatibleTypeErrorContext.class, "instanceof.unsafe.cast")
.withRawDescription((expr, context) -> message(
"instanceof.unsafe.cast", context.lType().getPresentableText(), requireNonNull(context.rType()).getPresentableText()));
public static final Parameterized<PsiElement, JavaIncompatibleTypeErrorContext> CAST_INCONVERTIBLE =
parameterized(PsiElement.class, JavaIncompatibleTypeErrorContext.class, "cast.inconvertible")
.withRawDescription((psi, ctx) -> message("cast.inconvertible", formatType(ctx.lType()), formatType(ctx.rType())));
public static final Simple<PsiTypeElement> CAST_INTERSECTION_NOT_INTERFACE = error("cast.intersection.not.interface");
public static final Simple<PsiTypeElement> CAST_INTERSECTION_UNEXPECTED_TYPE = error("cast.intersection.unexpected.type");
public static final Simple<PsiTypeElement> CAST_INTERSECTION_REPEATED_INTERFACE = error("cast.intersection.repeated.interface");
public static final Parameterized<PsiTypeCastExpression, InheritTypeClashContext> CAST_INTERSECTION_INHERITANCE_CLASH =
parameterized(PsiTypeCastExpression.class, InheritTypeClashContext.class, "cast.intersection.inheritance.clash")
.withRawDescription((cast, ctx) -> message("cast.intersection.inheritance.clash", formatClass(ctx.superClass()),
ctx.type1().getPresentableText(), ctx.type2().getPresentableText()));
public static final Simple<PsiReferenceExpression> EXPRESSION_EXPECTED = error("expression.expected");
public static final Parameterized<PsiReferenceExpression, PsiSuperExpression> EXPRESSION_SUPER_UNQUALIFIED_DEFAULT_METHOD =
@@ -912,6 +935,8 @@ public final class JavaErrorKinds {
parameterized(PsiExpression.class, PsiClass.class, "call.super.qualifier.not.inner.class")
.withRawDescription((psi, cls) -> message("call.super.qualifier.not.inner.class", formatClass(cls)));
public static final Simple<PsiMethodCallExpression> CALL_EXPECTED = error("call.expected");
public static final Simple<PsiDeconstructionPattern> CALL_PARSED_AS_DECONSTRUCTION_PATTERN =
error("call.parsed.as.deconstruction.pattern");
public static final Simple<PsiJavaCodeReferenceElement> CALL_STATIC_INTERFACE_METHOD_QUALIFIER =
error(PsiJavaCodeReferenceElement.class, "call.static.interface.method.qualifier")
.withRange(JavaErrorFormatUtil::getRange);
@@ -26,7 +26,6 @@ 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 org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -554,48 +553,6 @@ public final class GenericsHighlightUtil {
return enumClass != null && enumClass.isEnum() ? enumClass : null;
}
static HighlightInfo.Builder checkInstanceOfGenericType(@NotNull LanguageLevel languageLevel, @NotNull PsiInstanceOfExpression expression) {
PsiTypeElement checkTypeElement = InstanceOfUtils.findCheckTypeElement(expression);
if (checkTypeElement == null) return null;
PsiType checkType = checkTypeElement.getType();
if (JavaFeature.PATTERNS.isSufficient(languageLevel)) {
PsiPrimaryPattern pattern = expression.getPattern();
if (pattern != null) {
return PatternHighlightingModel.getUncheckedPatternConversionError(pattern);
}
return isUnsafeCastInInstanceOf(checkTypeElement, checkType, expression.getOperand().getType());
}
return isIllegalForInstanceOf(checkType, checkTypeElement);
}
private static HighlightInfo.Builder isUnsafeCastInInstanceOf(@NotNull PsiTypeElement checkTypeElement, @NotNull PsiType checkType, @Nullable PsiType expressionType) {
if (expressionType != null && JavaGenericsUtil.isUncheckedCast(checkType, expressionType)) {
String description = JavaErrorBundle.message("unsafe.cast.in.instanceof",
expressionType.getPresentableText(), checkType.getPresentableText());
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(checkTypeElement).descriptionAndTooltip(description);
}
return null;
}
/**
* 15.20.2 Type Comparison Operator instanceof
* ReferenceType mentioned after the instanceof operator is reifiable
*/
private static HighlightInfo.Builder isIllegalForInstanceOf(@Nullable PsiType type, @NotNull PsiTypeElement typeElement) {
PsiClass resolved = PsiUtil.resolveClassInClassTypeOnly(type);
if (resolved instanceof PsiTypeParameter) {
String description = JavaErrorBundle.message("generics.cannot.instanceof.type.parameters");
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeElement).descriptionAndTooltip(description);
}
if (!JavaGenericsUtil.isReifiableType(type)) {
String description = JavaErrorBundle.message("illegal.generic.type.for.instanceof");
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeElement).descriptionAndTooltip(description);
}
return null;
}
static HighlightInfo.Builder checkClassObjectAccessExpression(@NotNull PsiClassObjectAccessExpression expression) {
PsiType type = expression.getOperand().getType();
if (type instanceof PsiClassType classType) {
@@ -4,18 +4,17 @@ package com.intellij.codeInsight.daemon.impl.analysis;
import com.intellij.codeInsight.ContainerProvider;
import com.intellij.codeInsight.JavaModuleSystemEx;
import com.intellij.codeInsight.JavaModuleSystemEx.ErrorWithFixes;
import com.intellij.codeInsight.daemon.HighlightDisplayKey;
import com.intellij.codeInsight.daemon.JavaErrorBundle;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
import com.intellij.codeInsight.daemon.impl.quickfix.*;
import com.intellij.codeInsight.daemon.impl.quickfix.AddTypeArgumentsConditionalFix;
import com.intellij.codeInsight.daemon.impl.quickfix.ChangeNewOperatorTypeFix;
import com.intellij.codeInsight.daemon.impl.quickfix.QualifyWithThisFix;
import com.intellij.codeInsight.highlighting.HighlightUsagesDescriptionLocation;
import com.intellij.codeInsight.intention.CommonIntentionAction;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.QuickFixFactory;
import com.intellij.codeInsight.quickfix.UnresolvedReferenceQuickFixProvider;
import com.intellij.codeInspection.dataFlow.fix.RedundantInstanceofFix;
import com.intellij.ide.IdeBundle;
import com.intellij.modcommand.ModCommandAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.LanguageLevelUtil;
@@ -34,7 +33,6 @@ import com.intellij.pom.java.JavaFeature;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.impl.IncompleteModelUtil;
import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession;
import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil;
import com.intellij.psi.scope.PatternResolveState;
import com.intellij.psi.scope.processor.VariablesNotProcessor;
@@ -51,16 +49,15 @@ import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.NamedColorUtil;
import com.intellij.util.ui.UIUtil;
import com.siyeh.ig.psiutils.ControlFlowUtils;
import com.siyeh.ig.psiutils.InstanceOfUtils;
import com.siyeh.ig.psiutils.VariableAccessUtils;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.util.*;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -79,166 +76,11 @@ public final class HighlightUtil {
}
static void checkInstanceOfApplicable(@NotNull PsiInstanceOfExpression expression, @NotNull Consumer<? super HighlightInfo.Builder> errorSink) {
PsiExpression operand = expression.getOperand();
PsiTypeElement typeElement = InstanceOfUtils.findCheckTypeElement(expression);
if (typeElement == null) return;
PsiType checkType = typeElement.getType();
PsiType operandType = operand.getType();
if (operandType == null) return;
boolean operandIsPrimitive = TypeConversionUtil.isPrimitiveAndNotNull(operandType);
boolean checkIsPrimitive = TypeConversionUtil.isPrimitiveAndNotNull(checkType);
boolean convertible = TypeConversionUtil.areTypesConvertible(operandType, checkType);
boolean primitiveInPatternsEnabled = PsiUtil.isAvailable(JavaFeature.PRIMITIVE_TYPES_IN_PATTERNS, expression);
if (((operandIsPrimitive || checkIsPrimitive) && !primitiveInPatternsEnabled) || !convertible) {
if (!convertible && IncompleteModelUtil.isIncompleteModel(expression) &&
IncompleteModelUtil.isPotentiallyConvertible(checkType, operand)) {
return;
}
String message = JavaErrorBundle.message("inconvertible.type.cast", JavaHighlightUtil.formatType(operandType), JavaHighlightUtil
.formatType(checkType));
HighlightInfo.Builder info =
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(message);
if (((operandIsPrimitive || checkIsPrimitive) && !primitiveInPatternsEnabled) && convertible) {
HighlightInfo.Builder infoFeature =
checkFeature(expression, JavaFeature.PRIMITIVE_TYPES_IN_PATTERNS,
PsiUtil.getLanguageLevel(expression), expression.getContainingFile());
if (infoFeature != null) {
info = infoFeature;
}
}
if (checkIsPrimitive) {
IntentionAction action = getFixFactory().createReplacePrimitiveWithBoxedTypeAction(operandType, typeElement);
if (action != null) {
info.registerFix(action, null, null, null, null);
}
}
errorSink.accept(info);
return;
}
PsiPrimaryPattern pattern = expression.getPattern();
if (pattern instanceof PsiDeconstructionPattern deconstruction) {
PatternHighlightingModel.createDeconstructionErrors(deconstruction, errorSink);
}
}
/**
* 15.16 Cast Expressions
* ( ReferenceType {AdditionalBound} ) expression, where AdditionalBound: & InterfaceType then all must be true
* - ReferenceType must denote a class or interface type.
* - The erasures of all the listed types must be pairwise different.
* - No two listed types may be subtypes of different parameterization of the same generic interface.
*/
static HighlightInfo.Builder checkIntersectionInTypeCast(@NotNull PsiTypeCastExpression expression,
@NotNull LanguageLevel languageLevel,
@NotNull PsiFile file) {
PsiTypeElement castTypeElement = expression.getCastType();
if (castTypeElement == null || !isIntersection(castTypeElement, castTypeElement.getType())) {
return null;
}
HighlightInfo.Builder info = checkFeature(expression, JavaFeature.INTERSECTION_CASTS, languageLevel, file);
if (info != null) return info;
PsiTypeElement[] conjuncts = PsiTreeUtil.getChildrenOfType(castTypeElement, PsiTypeElement.class);
if (conjuncts != null) {
Set<PsiType> erasures = new HashSet<>(conjuncts.length);
erasures.add(TypeConversionUtil.erasure(conjuncts[0].getType()));
List<PsiTypeElement> conjList = new ArrayList<>(Arrays.asList(conjuncts));
for (int i = 1; i < conjuncts.length; i++) {
PsiTypeElement conjunct = conjuncts[i];
PsiType conjType = conjunct.getType();
if (conjType instanceof PsiClassType classType) {
PsiClass aClass = classType.resolve();
if (aClass != null && !aClass.isInterface()) {
HighlightInfo.Builder errorResult = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.range(conjunct)
.descriptionAndTooltip(JavaErrorBundle.message("interface.expected"));
var action = new FlipIntersectionSidesFix(aClass.getName(), conjunct, castTypeElement);
errorResult.registerFix(action, null, HighlightDisplayKey.getDisplayNameByKey(null), null, null);
return errorResult;
}
}
else {
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.range(conjunct)
.descriptionAndTooltip(JavaErrorBundle.message("unexpected.type.class.expected"));
}
if (!erasures.add(TypeConversionUtil.erasure(conjType))) {
HighlightInfo.Builder highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.range(conjunct)
.descriptionAndTooltip(JavaErrorBundle.message("repeated.interface"));
var action = new DeleteRepeatedInterfaceFix(conjunct);
highlightInfo.registerFix(action, null, HighlightDisplayKey.getDisplayNameByKey(null), null, null);
return highlightInfo;
}
}
List<PsiType> typeList = ContainerUtil.map(conjList, PsiTypeElement::getType);
Ref<@Nls String> differentArgumentsMessage = new Ref<>();
PsiClass sameGenericParameterization =
InferenceSession.findParameterizationOfTheSameGenericClass(typeList, pair -> {
if (!TypesDistinctProver.provablyDistinct(pair.first, pair.second)) {
return true;
}
differentArgumentsMessage.set(IdeBundle.message("x.and.y", pair.first.getPresentableText(),
pair.second.getPresentableText()));
return false;
});
if (sameGenericParameterization != null) {
String message = JavaErrorBundle
.message("class.cannot.be.inherited.with.different.arguments", formatClass(sameGenericParameterization),
differentArgumentsMessage.get());
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.range(expression)
.descriptionAndTooltip(message);
}
}
return null;
}
private static boolean isIntersection(@NotNull PsiTypeElement castTypeElement, @NotNull PsiType castType) {
if (castType instanceof PsiIntersectionType) return true;
return castType instanceof PsiClassType && PsiTreeUtil.getChildrenOfType(castTypeElement, PsiTypeElement.class) != null;
}
static HighlightInfo.Builder checkInconvertibleTypeCast(@NotNull PsiTypeCastExpression expression) {
PsiTypeElement castTypeElement = expression.getCastType();
if (castTypeElement == null) return null;
PsiType castType = castTypeElement.getType();
PsiExpression operand = expression.getOperand();
if (operand == null) return null;
PsiType operandType = operand.getType();
if (operandType != null &&
!TypeConversionUtil.areTypesConvertible(operandType, castType, PsiUtil.getLanguageLevel(expression)) &&
!RedundantCastUtil.isInPolymorphicCall(expression)) {
if (IncompleteModelUtil.isIncompleteModel(expression) && IncompleteModelUtil.isPotentiallyConvertible(castType, operand)) {
return null;
}
String message = JavaErrorBundle.message("inconvertible.type.cast", JavaHighlightUtil.formatType(operandType), JavaHighlightUtil
.formatType(castType));
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(message);
}
return null;
}
static HighlightInfo.Builder checkAssignability(@Nullable PsiType lType,
@Nullable PsiType rType,
@Nullable PsiExpression expression,
@NotNull PsiElement elementToHighlight) {
TextRange textRange = elementToHighlight.getTextRange();
return checkAssignability(lType, rType, expression, textRange, 0);
}
private static HighlightInfo.Builder checkAssignability(@Nullable PsiType lType,
@Nullable PsiType rType,
@Nullable PsiExpression expression,
@NotNull TextRange textRange,
int navigationShift) {
if (lType == rType) return null;
if (expression == null) {
if (rType == null || lType == null || TypeConversionUtil.isAssignable(lType, rType)) return null;
@@ -256,7 +98,7 @@ public final class HighlightUtil {
IncompleteModelUtil.isPotentiallyConvertible(lType, expression)) {
return null;
}
HighlightInfo.Builder highlightInfo = createIncompatibleTypeHighlightInfo(lType, rType, textRange, navigationShift);
HighlightInfo.Builder highlightInfo = createIncompatibleTypeHighlightInfo(lType, rType, textRange, 0);
AddTypeArgumentsConditionalFix.register(asConsumer(highlightInfo), expression, lType);
if (expression != null) {
AdaptExpressionTypeFixUtil.registerExpectedTypeFixes(asConsumer(highlightInfo), expression, lType, rType);
@@ -495,32 +337,6 @@ public final class HighlightUtil {
}
}
static HighlightInfo.Builder checkInstanceOfPatternSupertype(@NotNull PsiInstanceOfExpression expression) {
@Nullable PsiPattern expressionPattern = expression.getPattern();
PsiTypeTestPattern pattern = tryCast(expressionPattern, PsiTypeTestPattern.class);
if (pattern == null) return null;
PsiPatternVariable variable = pattern.getPatternVariable();
if (variable == null) return null;
PsiTypeElement typeElement = pattern.getCheckType();
if (typeElement == null) return null;
PsiType checkType = typeElement.getType();
PsiType expressionType = expression.getOperand().getType();
if (expressionType != null && checkType.isAssignableFrom(expressionType)) {
String description =
checkType.equals(expressionType) ?
JavaErrorBundle.message("instanceof.pattern.equals", checkType.getPresentableText()) :
JavaErrorBundle.message("instanceof.pattern.supertype", checkType.getPresentableText(), expressionType.getPresentableText());
HighlightInfo.Builder info =
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeElement).descriptionAndTooltip(description);
if (!VariableAccessUtils.variableIsUsed(variable, variable.getDeclarationScope())) {
var action = new RedundantInstanceofFix(expression);
info.registerFix(action, null, null, null, null);
}
return info;
}
return null;
}
static @NotNull @NlsContexts.DetailedDescription String staticContextProblemDescription(@NotNull PsiElement refElement) {
String type = JavaElementKind.fromElement(refElement).lessDescriptive().subject();
String name = HighlightMessageUtil.getSymbolName(refElement, PsiSubstitutor.EMPTY);
@@ -47,7 +47,6 @@ import com.intellij.psi.util.*;
import com.intellij.ui.ColorUtil;
import com.intellij.ui.NewUI;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.NamedColorUtil;
import com.intellij.util.ui.UIUtil;
@@ -499,19 +498,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
}
}
@Override
public void visitInstanceOfExpression(@NotNull PsiInstanceOfExpression expression) {
super.visitInstanceOfExpression(expression);
if (!hasErrorResults()) HighlightUtil.checkInstanceOfApplicable(expression, myErrorSink);
if (!hasErrorResults()) add(GenericsHighlightUtil.checkInstanceOfGenericType(myLanguageLevel, expression));
if (!hasErrorResults() &&
JavaFeature.PATTERNS.isSufficient(myLanguageLevel) &&
// 5.20.2 Removed restriction on pattern instanceof for unconditional patterns (JEP 432, 440)
!JavaFeature.PATTERN_GUARDS_AND_RECORD_PATTERNS.isSufficient(myLanguageLevel)) {
add(HighlightUtil.checkInstanceOfPatternSupertype(expression));
}
}
@Override
public void visitImportModuleStatement(@NotNull PsiImportModuleStatement statement) {
super.visitImportModuleStatement(statement);
@@ -1024,17 +1010,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
}
}
@Override
public void visitTypeCastExpression(@NotNull PsiTypeCastExpression typeCast) {
super.visitTypeCastExpression(typeCast);
try {
if (!hasErrorResults()) add(HighlightUtil.checkIntersectionInTypeCast(typeCast, myLanguageLevel, myFile));
if (!hasErrorResults()) add(HighlightUtil.checkInconvertibleTypeCast(typeCast));
}
catch (IndexNotReadyException ignored) {
}
}
@Override
public void visitConditionalExpression(@NotNull PsiConditionalExpression expression) {
super.visitConditionalExpression(expression);
@@ -1132,37 +1107,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
}
}
@Override
public void visitDeconstructionList(@NotNull PsiDeconstructionList deconstructionList) {
super.visitDeconstructionList(deconstructionList);
// We are checking the case when the pattern looks similar to method call in switch and want to show user-friendly message that here
// only constant expressions are expected.
// it is required to do it in deconstruction list because unresolved reference won't let any parents show any highlighting,
// so we need element which is not parent
PsiElement parent = deconstructionList.getParent();
PsiDeconstructionPattern pattern = tryCast(parent, PsiDeconstructionPattern.class);
if (pattern == null) return;
PsiElement grandParent = parent.getParent();
if (!(grandParent instanceof PsiCaseLabelElementList)) return;
PsiTypeElement typeElement = pattern.getTypeElement();
PsiJavaCodeReferenceElement ref = PsiTreeUtil.getChildOfType(typeElement, PsiJavaCodeReferenceElement.class);
if (ref == null) return;
if (ref.multiResolve(true).length == 0) {
PsiElementFactory elementFactory = PsiElementFactory.getInstance(myFile.getProject());
if (pattern.getPatternVariable() == null && pattern.getDeconstructionList().getDeconstructionComponents().length == 0) {
PsiClassType type = tryCast(pattern.getTypeElement().getType(), PsiClassType.class);
if (type != null && ContainerUtil.exists(type.getParameters(), PsiWildcardType.class::isInstance)) return;
PsiExpression expression = elementFactory.createExpressionFromText(pattern.getText(), grandParent);
PsiMethodCallExpression call = tryCast(expression, PsiMethodCallExpression.class);
if (call == null) return;
if (call.getMethodExpression().resolve() != null) {
add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(pattern.getTextRange())
.descriptionAndTooltip(JavaErrorBundle.message("switch.constant.expression.required")));
}
}
}
}
private @Nullable HighlightInfo.Builder checkFeature(@NotNull PsiElement element, @NotNull JavaFeature feature) {
return HighlightUtil.checkFeature(element, feature, myLanguageLevel, myFile);
}
@@ -9,6 +9,7 @@ import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.QuickFixFactory;
import com.intellij.codeInsight.intention.impl.BaseIntentionAction;
import com.intellij.codeInsight.intention.impl.PriorityIntentionActionWrapper;
import com.intellij.codeInspection.dataFlow.fix.RedundantInstanceofFix;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.java.analysis.JavaAnalysisBundle;
import com.intellij.java.codeserver.highlighting.JavaErrorCollector;
@@ -34,7 +35,9 @@ import com.intellij.psi.util.*;
import com.intellij.refactoring.util.RefactoringChangeUtil;
import com.intellij.util.VisibilityUtil;
import com.intellij.util.containers.ContainerUtil;
import com.siyeh.ig.psiutils.InstanceOfUtils;
import com.siyeh.ig.psiutils.TypeUtils;
import com.siyeh.ig.psiutils.VariableAccessUtils;
import com.siyeh.ig.psiutils.VariableNameGenerator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -321,6 +324,25 @@ final class JavaErrorFixProvider {
sink.accept(QuickFixFactory.getInstance().createDeleteFix(elementsToDelete, text));
}
});
fix(CAST_INCONVERTIBLE, error -> {
if (error.psi() instanceof PsiInstanceOfExpression instanceOfExpression &&
TypeConversionUtil.isPrimitiveAndNotNull(error.context().rType())) {
return myFactory.createReplacePrimitiveWithBoxedTypeAction(
error.context().lType(), requireNonNull(InstanceOfUtils.findCheckTypeElement(instanceOfExpression)));
}
return null;
});
JavaFixProvider<PsiInstanceOfExpression, Object> redundantInstanceOfFix = error -> {
if (error.psi().getPattern() instanceof PsiTypeTestPattern pattern) {
PsiPatternVariable variable = pattern.getPatternVariable();
if (variable != null && !VariableAccessUtils.variableIsUsed(variable, variable.getDeclarationScope())) {
return new RedundantInstanceofFix(error.psi());
}
}
return null;
};
fix(PATTERN_INSTANCEOF_EQUALS, redundantInstanceOfFix);
fix(PATTERN_INSTANCEOF_SUPERTYPE, redundantInstanceOfFix);
}
private void createVariableFixes() {
@@ -480,6 +502,12 @@ final class JavaErrorFixProvider {
HighlightUtil.isCallToStaticMember(newExpression) ? new RemoveNewKeywordFix(newExpression) : null);
fix(REFERENCE_QUALIFIER_PRIMITIVE,
error -> error.psi() instanceof PsiReferenceExpression ref ? myFactory.createRenameWrongRefFix(ref) : null);
fix(CAST_INTERSECTION_NOT_INTERFACE, error -> {
PsiTypeElement conjunct = error.psi();
return new FlipIntersectionSidesFix(((PsiClassType)conjunct.getType()).getClassName(), conjunct,
PsiTreeUtil.getParentOfType(conjunct, PsiTypeElement.class, true));
});
fix(CAST_INTERSECTION_REPEATED_INTERFACE, error -> new DeleteRepeatedInterfaceFix(error.psi()));
}
private void createAccessFixes() {
@@ -1,7 +1,6 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.psi.util;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.ExceptionUtil;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil;
@@ -1053,7 +1052,7 @@ public final class RedundantCastUtil {
PsiExpression operand = typeCast.getOperand();
if (operand == null) return false;
if (isInPolymorphicCall(typeCast)) return true;
if (PsiUtil.isInSignaturePolymorphicCall(typeCast)) return true;
PsiType opType = operand.getType();
PsiTypeElement typeElement = typeCast.getCastType();
@@ -1136,28 +1135,6 @@ public final class RedundantCastUtil {
}
}
// see http://download.java.net/jdk7/docs/api/java/lang/invoke/MethodHandle.html#sigpoly
public static boolean isInPolymorphicCall(PsiTypeCastExpression typeCast) {
if (!PsiUtil.isLanguageLevel7OrHigher(typeCast)) return false;
// return type
final PsiExpression operand = typeCast.getOperand();
if (operand instanceof PsiMethodCallExpression methodCallExpression && isPolymorphicMethod(methodCallExpression)) {
return true;
}
// argument type
final PsiElement exprList = PsiUtil.skipParenthesizedExprUp(typeCast.getParent());
return exprList instanceof PsiExpressionList &&
exprList.getParent() instanceof PsiMethodCallExpression methodCallExpression &&
isPolymorphicMethod(methodCallExpression);
}
private static boolean isPolymorphicMethod(PsiMethodCallExpression expression) {
return expression.getMethodExpression().resolve() instanceof PsiMethod method &&
AnnotationUtil.isAnnotated(method, CommonClassNames.JAVA_LANG_INVOKE_MH_POLYMORPHIC, 0);
}
private static boolean areNullabilityCompatible(PsiMethod oldTargetMethod, PsiMethod newTargetMethod) {
// the cast may be for the @NotNull which newTargetMethod has whereas the oldTargetMethod doesn't
return NullableNotNullManager.getNullability(oldTargetMethod) == NullableNotNullManager.getNullability(newTargetMethod);
@@ -1,6 +1,7 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.psi.util;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.core.JavaPsiBundle;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.diagnostic.Logger;
@@ -416,6 +417,34 @@ public final class PsiUtil extends PsiUtilCore {
}
}
/**
* @param typeCast cast to check
* @return true if the supplied cast is necessary to fulfill the signature polymorphic method contract
* @see <a href="https://docs.oracle.com/en/java/javase/21/docs//api/java.base/java/lang/invoke/MethodHandle.html#sigpoly">Signature polymorphism</a> in Java API reference
*/
// see http://download.java.net/jdk7/docs/api/java/lang/invoke/MethodHandle.html#sigpoly
public static boolean isInSignaturePolymorphicCall(@NotNull PsiTypeCastExpression typeCast) {
if (!isLanguageLevel7OrHigher(typeCast)) return false;
// return type
final PsiExpression operand = typeCast.getOperand();
if (operand instanceof PsiMethodCallExpression && isPolymorphicMethod((PsiMethodCallExpression)operand)) {
return true;
}
// argument type
final PsiElement exprList = skipParenthesizedExprUp(typeCast.getParent());
return exprList instanceof PsiExpressionList &&
exprList.getParent() instanceof PsiMethodCallExpression &&
isPolymorphicMethod((PsiMethodCallExpression)exprList.getParent());
}
private static boolean isPolymorphicMethod(PsiMethodCallExpression expression) {
PsiElement target = expression.getMethodExpression().resolve();
return target instanceof PsiMethod &&
AnnotationUtil.isAnnotated((PsiMethod)target, CommonClassNames.JAVA_LANG_INVOKE_MH_POLYMORPHIC, 0);
}
@MagicConstant(intValues = {ACCESS_LEVEL_PUBLIC, ACCESS_LEVEL_PROTECTED, ACCESS_LEVEL_PACKAGE_LOCAL, ACCESS_LEVEL_PRIVATE})
public @interface AccessLevel {}