[java-highlighting] IDEA-352187 Support JEP 455: highlighting for instanceof with primitives

GitOrigin-RevId: cbd688d0945b03bffb05af85825c610889383fc4
This commit is contained in:
Mikhail Pyltsin
2024-04-29 21:17:42 +00:00
committed by intellij-monorepo-bot
parent 882dbec1fb
commit c8995f3cc8
11 changed files with 4566 additions and 10 deletions
@@ -206,19 +206,26 @@ public final class HighlightUtil {
PsiType checkType = typeElement.getType();
PsiType operandType = operand.getType();
if (operandType == null) return;
if (TypeConversionUtil.isPrimitiveAndNotNull(operandType)
|| TypeConversionUtil.isPrimitiveAndNotNull(checkType)
|| !TypeConversionUtil.areTypesConvertible(operandType, checkType)) {
boolean operandIsPrimitive = TypeConversionUtil.isPrimitiveAndNotNull(operandType);
boolean checkIsPrimitive = TypeConversionUtil.isPrimitiveAndNotNull(checkType);
boolean convertible = TypeConversionUtil.areTypesConvertible(operandType, checkType);
boolean primitiveInPatternsEnabled = JavaFeature.PRIMITIVE_TYPES_IN_PATTERNS.isSufficient(PsiUtil.getLanguageLevel(expression));
if (((operandIsPrimitive || checkIsPrimitive) && !primitiveInPatternsEnabled) || !convertible) {
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 (TypeConversionUtil.isPrimitiveAndNotNull(checkType)) {
if (checkIsPrimitive) {
IntentionAction action = getFixFactory().createReplacePrimitiveWithBoxedTypeAction(operandType, typeElement);
if (action != null) {
info.registerFix(action, null, null, null, null);
}
}
if (((operandIsPrimitive || checkIsPrimitive) && !primitiveInPatternsEnabled) && convertible) {
registerIncreaseLanguageLevelFixes(expression, JavaFeature.PRIMITIVE_TYPES_IN_PATTERNS, info);
}
errorSink.accept(info);
return;
}
@@ -12,6 +12,8 @@ import com.intellij.codeInsight.intention.QuickFixFactory;
import com.intellij.modcommand.ModCommandAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.TextRange;
import com.intellij.pom.java.JavaFeature;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.PsiClassType.ClassResolveResult;
import com.intellij.psi.util.*;
@@ -66,12 +68,16 @@ final class PatternHighlightingModel {
PsiType recordComponentType = recordComponents[i].getType();
PsiType substitutedRecordComponentType = substitutor.substitute(recordComponentType);
PsiType deconstructionComponentType = JavaPsiPatternUtil.getPatternType(deconstructionComponent);
if (!isApplicable(substitutedRecordComponentType, deconstructionComponentType)) {
if (!isApplicable(substitutedRecordComponentType, deconstructionComponentType, PsiUtil.getLanguageLevel(deconstructionPattern))) {
hasMismatchedPattern = true;
if (recordComponents.length == deconstructionComponents.length) {
HighlightInfo.Builder
builder = HighlightUtil.createIncompatibleTypeHighlightInfo(substitutedRecordComponentType, deconstructionComponentType,
deconstructionComponent.getTextRange(), 0);
if (isApplicable(substitutedRecordComponentType, deconstructionComponentType,
JavaFeature.PRIMITIVE_TYPES_IN_PATTERNS.getMinimumLevel())) {
HighlightUtil.registerIncreaseLanguageLevelFixes(deconstructionComponent, JavaFeature.PRIMITIVE_TYPES_IN_PATTERNS, builder);
}
errorSink.accept(builder);
reported = true;
}
@@ -121,8 +127,9 @@ final class PatternHighlightingModel {
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(pattern).descriptionAndTooltip(message);
}
private static boolean isApplicable(@NotNull PsiType recordType, @Nullable PsiType patternType) {
if (recordType instanceof PsiPrimitiveType || patternType instanceof PsiPrimitiveType) {
private static boolean isApplicable(@NotNull PsiType recordType, @Nullable PsiType patternType, @NotNull LanguageLevel languageLevel) {
if ((recordType instanceof PsiPrimitiveType || patternType instanceof PsiPrimitiveType) &&
!JavaFeature.PRIMITIVE_TYPES_IN_PATTERNS.isSufficient(languageLevel)) {
return recordType.equals(patternType);
}
return patternType != null && TypeConversionUtil.areTypesConvertible(recordType, patternType);
@@ -921,8 +928,8 @@ final class PatternHighlightingModel {
}
static class RecordExhaustivenessResult {
private boolean isExhaustive;
private boolean canBeAdded;
private final boolean isExhaustive;
private final boolean canBeAdded;
final Map<PsiType, Set<List<PsiType>>> missedBranchesByType = new HashMap<>();
@@ -105,6 +105,7 @@ feature.patterns.in.switch=Patterns in switch
feature.javadoc.snippets=@snippet in Javadoc
feature.pattern.guard.and.record.patterns=Pattern guards and record patterns
feature.record.patterns.in.for.each=Record patterns in for-each loops
feature.primitive.types.in.patterns=Primitive types in patterns, instanceof and switch
feature.enum.qualified.name.in.switch=Qualified enum as a constant in switch
feature.string.templates=String templates
feature.unnamed.vars=Unnamed patterns and variables
@@ -97,6 +97,10 @@ public enum JavaFeature {
*/
RECORD_PATTERNS_IN_FOR_EACH(LanguageLevel.JDK_X, "feature.record.patterns.in.for.each",
LanguageLevel.JDK_20_PREVIEW),
/**
* Implementation for java 23
*/
PRIMITIVE_TYPES_IN_PATTERNS(LanguageLevel.JDK_X, "feature.primitive.types.in.patterns"),
;
private final @NotNull LanguageLevel myLevel;
@@ -0,0 +1,81 @@
package dfa;
public class InstanceofPrimitivesNotAllowed {
public static void main(String[] args) {
testPrimitiveToPrimitive();
testPrimitiveToObject();
testObjectToPrimitive();
}
private static void testObjectToPrimitive() {
Integer i = 1;
Object l = 1;
if (<error descr="Inconvertible types; cannot cast 'java.lang.Integer' to 'double'">i instanceof double</error>) { //error
System.out.println("double");
}
if (<error descr="Inconvertible types; cannot cast 'java.lang.Integer' to 'int'">i instanceof int</error>) { //error
System.out.println("int");
}
if (<error descr="Inconvertible types; cannot cast 'java.lang.Integer' to 'int'">i instanceof int ii</error>) { //error
System.out.println("int pattern");
}
if (<error descr="Inconvertible types; cannot cast 'java.lang.Object' to 'double'">l instanceof double</error>) { //error
System.out.println("double");
}
if (<error descr="Inconvertible types; cannot cast 'java.lang.Object' to 'int'">l instanceof int</error>) { //error
System.out.println("int");
}
if (<error descr="Inconvertible types; cannot cast 'java.lang.Object' to 'int'">l instanceof int ii</error>) { //error
System.out.println("int pattern");
}
}
private static void testPrimitiveToPrimitive() {
int i = 1;
long l = 1;
if (<error descr="Inconvertible types; cannot cast 'int' to 'double'">i instanceof double</error>) { //error
System.out.println("double");
}
if (<error descr="Inconvertible types; cannot cast 'int' to 'int'">i instanceof int</error>) { //error
System.out.println("int");
}
if (<error descr="Inconvertible types; cannot cast 'int' to 'int'">i instanceof int ii</error>) { //error
System.out.println("int pattern");
}
if (<error descr="Inconvertible types; cannot cast 'long' to 'double'">l instanceof double</error>) { //error
System.out.println("double");
}
if (<error descr="Inconvertible types; cannot cast 'long' to 'int'">l instanceof int</error>) { //error
System.out.println("int");
}
if (<error descr="Inconvertible types; cannot cast 'long' to 'int'">l instanceof int ii</error>) { //error
System.out.println("int pattern");
}
}
private static void testPrimitiveToObject() {
int i = 1;
long l = 1;
if (<error descr="Inconvertible types; cannot cast 'int' to 'java.lang.Double'">i instanceof Double</error>) { //error
System.out.println("Double");
}
if (<error descr="Inconvertible types; cannot cast 'int' to 'java.lang.Integer'">i instanceof Integer</error>) { //error
System.out.println("Integer");
}
if (<error descr="Inconvertible types; cannot cast 'int' to 'java.lang.Object'">i instanceof Object</error>) { //error
System.out.println("Object");
}
if (<error descr="Inconvertible types; cannot cast 'int' to 'java.lang.Integer'">i instanceof Integer ii</error>) { //error
System.out.println("Integer ii");
}
if (<error descr="Inconvertible types; cannot cast 'long' to 'java.lang.Double'">l instanceof Double</error>) { //error
System.out.println("Double");
}
if (<error descr="Inconvertible types; cannot cast 'long' to 'java.lang.Integer'">l instanceof Integer</error>) { //error
System.out.println("Integer");
}
if (<error descr="Inconvertible types; cannot cast 'long' to 'java.lang.Object'">l instanceof Object</error>) { //error
System.out.println("Object");
}
}
}
@@ -103,7 +103,15 @@ public class LightPatternsHighlightingTest extends LightJavaCodeInsightFixtureTe
public void testUnnamedPatternsUnavailable() {
IdeaTestUtil.withLevel(getModule(), LanguageLevel.JDK_20, this::doTest);
}
}
public void testInstanceofPrimitivesNotAllowed() {
IdeaTestUtil.withLevel(getModule(), LanguageLevel.JDK_22, this::doTest);
}
public void testRecordPrimitiveInstanceOfPatternNotAllowed() {
IdeaTestUtil.withLevel(getModule(), LanguageLevel.JDK_22, this::doTest);
}
private void doTest() {
myFixture.configureByFile(getTestName(false) + ".java");
@@ -0,0 +1,38 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.java.codeInsight.daemon;
import com.intellij.JavaTestUtil;
import com.intellij.testFramework.LightProjectDescriptor;
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
import org.jetbrains.annotations.NotNull;
public class LightPrimitivePatternsHighlightingTest extends LightJavaCodeInsightFixtureTestCase {
@Override
protected String getBasePath() {
return JavaTestUtil.getRelativeJavaTestDataPath() + "/codeInsight/daemonCodeAnalyzer/advHighlightingPatterns";
}
@NotNull
@Override
protected LightProjectDescriptor getProjectDescriptor() {
//todo change after new Java
return JAVA_X;
}
public void testRecordPrimitiveInstanceOfPattern() {
doTest();
}
public void testSimplePrimitiveInstanceOf() {
doTest();
}
public void testSimplePrimitiveInstanceOfPattern() {
doTest();
}
private void doTest() {
myFixture.configureByFile(getTestName(false) + ".java");
myFixture.checkHighlighting();
}
}