mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[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:
committed by
intellij-monorepo-bot
parent
72809f5127
commit
cd9760a15f
+12
-1
@@ -311,6 +311,7 @@ call.ambiguous.tooltip=\
|
||||
<tr>{1}<td>in <b>{2}</b>\\ and</td></tr>\
|
||||
<tr>{3}<td>in <b>{4}</b>\\ 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
|
||||
|
||||
+114
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+41
@@ -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();
|
||||
|
||||
+28
-1
@@ -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);
|
||||
|
||||
+48
-2
@@ -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) {
|
||||
|
||||
+25
@@ -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);
|
||||
|
||||
-43
@@ -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) {
|
||||
|
||||
+6
-190
@@ -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);
|
||||
|
||||
-56
@@ -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);
|
||||
}
|
||||
|
||||
+28
@@ -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 {}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user