mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-20 21:41:24 +07:00
[java-highlighting] checkSwitchSelectorType migrated
Error messages unified; do not list allowed switch types anymore Part of IDEA-365344 Create a new Java error highlighter with minimal dependencies (PSI only) GitOrigin-RevId: 69675af87ac8866fb5ba1475094b93af07aa1ad5
This commit is contained in:
committed by
intellij-monorepo-bot
parent
16c58ccd5d
commit
cb626d5749
@@ -316,6 +316,8 @@ switch.expression.incompatible.type=Bad type in switch expression: {0} cannot be
|
||||
switch.expression.cannot.be.void=Target type for switch expression cannot be void
|
||||
switch.label.expected=Statement must be prepended with a case label
|
||||
switch.different.case.kinds=Different 'case' kinds used in 'switch'
|
||||
switch.selector.type.invalid=Selector type of ''{0}'' is not supported
|
||||
switch.selector.type.invalid.level=Selector type of ''{0}'' is not supported at language level ''{1}''
|
||||
|
||||
guard.misplaced=Guard is allowed after patterns only
|
||||
guard.evaluated.to.false=Case label has a guard that is a constant expression with value 'false'
|
||||
|
||||
@@ -1789,4 +1789,23 @@ final class ExpressionChecker {
|
||||
}
|
||||
myVisitor.report(JavaErrorKinds.SWITCH_DIFFERENT_CASE_KINDS.create(alien));
|
||||
}
|
||||
|
||||
void checkSwitchSelectorType(@NotNull PsiSwitchBlock block) {
|
||||
PsiExpression selector = block.getExpression();
|
||||
if (selector == null) return;
|
||||
PsiType selectorType = selector.getType();
|
||||
if (selectorType == null) return;
|
||||
JavaPsiSwitchUtil.SelectorKind kind = JavaPsiSwitchUtil.getSwitchSelectorKind(selectorType);
|
||||
|
||||
JavaFeature requiredFeature = kind.getFeature();
|
||||
|
||||
if ((kind == JavaPsiSwitchUtil.SelectorKind.INVALID || requiredFeature != null && !myVisitor.isApplicable(requiredFeature)) &&
|
||||
!PsiTreeUtil.hasErrorElements(block)) {
|
||||
myVisitor.report(JavaErrorKinds.SWITCH_SELECTOR_TYPE_INVALID.create(selector, kind));
|
||||
}
|
||||
PsiClass member = PsiUtil.resolveClassInClassTypeOnly(selectorType);
|
||||
if (member != null && !PsiUtil.isAccessible(member.getProject(), member, selector, null)) {
|
||||
myVisitor.report(JavaErrorKinds.TYPE_INACCESSIBLE.create(selector, member));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -625,10 +625,10 @@ final class JavaErrorVisitor extends JavaElementVisitor {
|
||||
@Override
|
||||
public void visitSwitchExpression(@NotNull PsiSwitchExpression expression) {
|
||||
super.visitSwitchExpression(expression);
|
||||
if (!hasErrorResults()) checkFeature(expression, JavaFeature.SWITCH_EXPRESSION);
|
||||
checkSwitchBlock(expression);
|
||||
if (!hasErrorResults()) checkFeature(expression.getFirstChild(), JavaFeature.SWITCH_EXPRESSION);
|
||||
if (!hasErrorResults()) myExpressionChecker.checkSwitchExpressionReturnTypeCompatible(expression);
|
||||
if (!hasErrorResults()) myExpressionChecker.checkSwitchExpressionHasResult(expression);
|
||||
checkSwitchBlock(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -639,6 +639,7 @@ final class JavaErrorVisitor extends JavaElementVisitor {
|
||||
|
||||
private void checkSwitchBlock(@NotNull PsiSwitchBlock block) {
|
||||
if (!hasErrorResults()) myExpressionChecker.checkSwitchBlockStatements(block);
|
||||
if (!hasErrorResults()) myExpressionChecker.checkSwitchSelectorType(block);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -925,6 +925,12 @@ public final class JavaErrorKinds {
|
||||
.withRawDescription((expr, context) -> message("switch.expression.incompatible.type", formatType(context.rType()), formatType(context.lType())));
|
||||
public static final Simple<PsiElement> SWITCH_LABEL_EXPECTED = error(PsiElement.class, "switch.label.expected");
|
||||
public static final Simple<PsiElement> SWITCH_DIFFERENT_CASE_KINDS = error("switch.different.case.kinds");
|
||||
public static final Parameterized<PsiExpression, JavaPsiSwitchUtil.SelectorKind> SWITCH_SELECTOR_TYPE_INVALID =
|
||||
parameterized(PsiExpression.class, JavaPsiSwitchUtil.SelectorKind.class, "switch.selector.type.invalid")
|
||||
.withRawDescription((expr, kind) -> kind.getFeature() == null ?
|
||||
message("switch.selector.type.invalid", formatType(expr.getType())) :
|
||||
message("switch.selector.type.invalid.level", formatType(expr.getType()),
|
||||
PsiUtil.getLanguageLevel(expr).getShortText()));
|
||||
|
||||
public static final Simple<PsiReferenceExpression> EXPRESSION_EXPECTED = error("expression.expected");
|
||||
public static final Parameterized<PsiReferenceExpression, PsiSuperExpression> EXPRESSION_SUPER_UNQUALIFIED_DEFAULT_METHOD =
|
||||
|
||||
@@ -899,6 +899,21 @@ public final class HighlightFixUtil {
|
||||
factory.createShowModulePropertiesFix(element));
|
||||
}
|
||||
|
||||
static void registerFixesOnInvalidSelector(@NotNull PsiExpression selector,
|
||||
@NotNull Consumer<? super @Nullable CommonIntentionAction> sink) {
|
||||
QuickFixFactory factory = QuickFixFactory.getInstance();
|
||||
if (selector.getParent() instanceof PsiSwitchStatement switchStatement) {
|
||||
sink.accept(factory.createConvertSwitchToIfIntention(switchStatement));
|
||||
}
|
||||
PsiType selectorType = selector.getType();
|
||||
if (PsiTypes.longType().equals(selectorType) ||
|
||||
PsiTypes.floatType().equals(selectorType) ||
|
||||
PsiTypes.doubleType().equals(selectorType)) {
|
||||
sink.accept(factory.createAddTypeCastFix(PsiTypes.intType(), selector));
|
||||
sink.accept(factory.createWrapWithAdapterFix(PsiTypes.intType(), selector));
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ReturnModel {
|
||||
final PsiReturnStatement myStatement;
|
||||
final PsiType myType;
|
||||
|
||||
@@ -327,7 +327,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
private void checkSwitchBlock(@NotNull PsiSwitchBlock switchBlock) {
|
||||
SwitchBlockHighlightingModel model = SwitchBlockHighlightingModel.createInstance(myLanguageLevel, switchBlock, myFile);
|
||||
if (model == null) return;
|
||||
if (!hasErrorResults()) model.checkSwitchSelectorType(myErrorSink);
|
||||
if (!hasErrorResults()) model.checkSwitchLabelValues(myErrorSink);
|
||||
}
|
||||
|
||||
|
||||
@@ -228,6 +228,13 @@ final class JavaErrorFixProvider {
|
||||
PsiSwitchLabeledRuleStatement previousRule = PsiTreeUtil.getPrevSiblingOfType(error.psi(), PsiSwitchLabeledRuleStatement.class);
|
||||
return previousRule == null ? null : myFactory.createWrapSwitchRuleStatementsIntoBlockFix(previousRule);
|
||||
});
|
||||
fixes(SWITCH_SELECTOR_TYPE_INVALID, (error, sink) -> {
|
||||
HighlightFixUtil.registerFixesOnInvalidSelector(error.psi(), sink);
|
||||
JavaFeature feature = error.context().getFeature();
|
||||
if (feature != null) {
|
||||
HighlightFixUtil.getIncreaseLanguageLevelFixes(error.psi(), feature).forEach(sink);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void createMethodFixes() {
|
||||
|
||||
@@ -12,7 +12,10 @@ import com.intellij.openapi.util.NlsContexts;
|
||||
import com.intellij.pom.java.JavaFeature;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.*;
|
||||
import com.intellij.psi.util.ConstantExpressionUtil;
|
||||
import com.intellij.psi.util.JavaPsiSwitchUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
@@ -80,48 +83,6 @@ public class SwitchBlockHighlightingModel {
|
||||
return found.get();
|
||||
}
|
||||
|
||||
void checkSwitchSelectorType(@NotNull Consumer<? super HighlightInfo.Builder> errorSink) {
|
||||
JavaPsiSwitchUtil.SelectorKind kind = getSwitchSelectorKind();
|
||||
if (kind == JavaPsiSwitchUtil.SelectorKind.INT) return;
|
||||
|
||||
JavaFeature requiredFeature = kind.getFeature();
|
||||
|
||||
if (kind == JavaPsiSwitchUtil.SelectorKind.INVALID || requiredFeature != null && !requiredFeature.isSufficient(myLevel)) {
|
||||
String message;
|
||||
if (JavaFeature.PATTERNS_IN_SWITCH.isSufficient(myLevel)) {
|
||||
message = JavaErrorBundle.message("switch.invalid.selector.types",
|
||||
JavaHighlightUtil.formatType(mySelectorType));
|
||||
}
|
||||
else {
|
||||
boolean is7 = JavaFeature.STRING_SWITCH.isSufficient(myLevel);
|
||||
String expected = JavaErrorBundle.message(is7 ? "valid.switch.1_7.selector.types" : "valid.switch.selector.types");
|
||||
message = JavaErrorBundle.message("incompatible.types", expected, JavaHighlightUtil.formatType(mySelectorType));
|
||||
}
|
||||
HighlightInfo.Builder info = createError(mySelector, message);
|
||||
registerFixesOnInvalidSelector(info);
|
||||
if (requiredFeature != null) {
|
||||
HighlightUtil.registerIncreaseLanguageLevelFixes(mySelector, requiredFeature, info);
|
||||
}
|
||||
errorSink.accept(info);
|
||||
}
|
||||
checkIfAccessibleType(errorSink);
|
||||
}
|
||||
|
||||
private void registerFixesOnInvalidSelector(HighlightInfo.Builder builder) {
|
||||
if (myBlock instanceof PsiSwitchStatement switchStatement) {
|
||||
IntentionAction action = getFixFactory().createConvertSwitchToIfIntention(switchStatement);
|
||||
builder.registerFix(action, null, null, null, null);
|
||||
}
|
||||
if (PsiTypes.longType().equals(mySelectorType) ||
|
||||
PsiTypes.floatType().equals(mySelectorType) ||
|
||||
PsiTypes.doubleType().equals(mySelectorType)) {
|
||||
IntentionAction addTypeCastFix = getFixFactory().createAddTypeCastFix(PsiTypes.intType(), mySelector);
|
||||
builder.registerFix(addTypeCastFix, null, null, null, null);
|
||||
IntentionAction wrapWithAdapterFix = getFixFactory().createWrapWithAdapterFix(PsiTypes.intType(), mySelector);
|
||||
builder.registerFix(wrapWithAdapterFix, null, null, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
void checkSwitchLabelValues(@NotNull Consumer<? super HighlightInfo.Builder> errorSink) {
|
||||
PsiCodeBlock body = myBlock.getBody();
|
||||
if (body == null) return;
|
||||
@@ -231,14 +192,6 @@ public class SwitchBlockHighlightingModel {
|
||||
return QuickFixFactory.getInstance();
|
||||
}
|
||||
|
||||
void checkIfAccessibleType(@NotNull Consumer<? super HighlightInfo.Builder> errorSink) {
|
||||
PsiClass member = PsiUtil.resolveClassInClassTypeOnly(mySelectorType);
|
||||
if (member != null && !PsiUtil.isAccessible(member.getProject(), member, mySelector, null)) {
|
||||
String className = PsiFormatUtil.formatClass(member, PsiFormatUtilBase.SHOW_NAME | PsiFormatUtilBase.SHOW_FQ_NAME);
|
||||
errorSink.accept(createError(mySelector, JavaErrorBundle.message("inaccessible.type", className)));
|
||||
}
|
||||
}
|
||||
|
||||
void fillElementsToCheckDuplicates(@NotNull MultiMap<Object, PsiElement> elements, @NotNull PsiCaseLabelElement labelElement) {
|
||||
PsiExpression expr = ObjectUtils.tryCast(labelElement, PsiExpression.class);
|
||||
if (expr == null) return;
|
||||
|
||||
@@ -54,8 +54,6 @@ unexpected.type=Unexpected type. Found: ''{1}'', required: ''{0}''
|
||||
incompatible.types.reason.ambiguous.method.reference=<br/>reason: method reference is ambiguous: both ''{0}'' and ''{1}'' match
|
||||
incompatible.switch.null.type=''{0}'' cannot be converted to ''{1}''
|
||||
inaccessible.type=''{0}'' is inaccessible from here
|
||||
valid.switch.selector.types=byte, char, short or int
|
||||
valid.switch.1_7.selector.types=char, byte, short, int, Character, Byte, Short, Integer, String, or an enum
|
||||
switch.illegal.fall.through.to=Illegal fall-through to a pattern
|
||||
invalid.case.label.combination.constants.and.patterns=Invalid case label combination: a case label must consist of either a list of case constants or a single case pattern
|
||||
invalid.case.label.combination.constants.and.patterns.unnamed=Invalid case label combination: a case label must consist of either a list of case constants or a list of case patterns
|
||||
|
||||
@@ -89,8 +89,11 @@ public final class JavaPsiSwitchUtil {
|
||||
/**
|
||||
* Returns the selector kind based on the type.
|
||||
* <p>
|
||||
* It's not checked whether this particular kind is supported at a given location
|
||||
* (the method does not have location information anyway).
|
||||
* The result may depend on type language level for boxed primitive types.
|
||||
* E.g., if selector type is {@link Double} then {@link SelectorKind#DOUBLE} will be returned
|
||||
* if primitives in patterns are supported, but {@link SelectorKind#CLASS_OR_ARRAY} will be returned otherwise.
|
||||
* <p>
|
||||
* It's not checked whether this particular kind is supported at a given location.
|
||||
* It's up to the caller to check this using the {@link SelectorKind#getFeature()} method.
|
||||
*
|
||||
* @param selectorType type of switch selector expression
|
||||
@@ -100,7 +103,9 @@ public final class JavaPsiSwitchUtil {
|
||||
if (TypeConversionUtil.getTypeRank(selectorType) <= TypeConversionUtil.INT_RANK) {
|
||||
return SelectorKind.INT;
|
||||
}
|
||||
PsiType unboxedType = PsiPrimitiveType.getOptionallyUnboxedType(selectorType);
|
||||
PsiType unboxedType = selectorType instanceof PsiClassType &&
|
||||
JavaFeature.PRIMITIVE_TYPES_IN_PATTERNS.isSufficient(((PsiClassType)selectorType).getLanguageLevel()) ?
|
||||
PsiPrimitiveType.getOptionallyUnboxedType(selectorType) : selectorType;
|
||||
if (unboxedType != null) {
|
||||
if (unboxedType.equals(PsiTypes.longType())) {
|
||||
return SelectorKind.LONG;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
class c {
|
||||
void f() {
|
||||
Integer x = new Integer(0);
|
||||
//---- switch --------------------------------------------------------
|
||||
switch (<error descr="Incompatible types. Found: 'java.lang.String', required: 'byte, char, short or int'">"s"</error>)
|
||||
switch (<error descr="Selector type of 'java.lang.Integer' is not supported at language level '1.4'">x</error>) {default:}
|
||||
switch (<error descr="Selector type of 'java.lang.String' is not supported at language level '1.4'">"s"</error>)
|
||||
{default:}
|
||||
byte bt = 0;
|
||||
switch (bt) {
|
||||
|
||||
@@ -20,7 +20,7 @@ class UnsupportedFeatures {
|
||||
boolean b1 = Boolean.<error descr="Incompatible types. Found: 'java.lang.Boolean', required: 'boolean'">TRUE</error>;
|
||||
|
||||
java.lang.annotation.ElementType t = null;
|
||||
switch (<error descr="Incompatible types. Found: 'java.lang.annotation.ElementType', required: 'byte, char, short or int'">t</error>) { }
|
||||
switch (<error descr="Selector type of 'java.lang.annotation.ElementType' is not supported at language level '1.4'">t</error>) { }
|
||||
|
||||
String raw = <error descr="Text block literals are not supported at language level '1.4'">"""hi there"""</error>;
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ class UnsupportedFeatures {
|
||||
I i1 = <error descr="Method references are not supported at language level '6'">UnsupportedFeatures::m</error>;
|
||||
I i2 = <error descr="Lambda expressions are not supported at language level '6'">() -> { }</error>;
|
||||
|
||||
switch (<error descr="Incompatible types. Found: 'java.lang.String', required: 'byte, char, short or int'">list.get(0)</error>) {
|
||||
switch (<error descr="Selector type of 'java.lang.String' is not supported at language level '6'">list.get(0)</error>) {
|
||||
case "foo": break;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ public class SwitchWithPrimitivesNotAllowed {
|
||||
|
||||
private static void testPrimitives() {
|
||||
boolean b = true;
|
||||
switch (<error descr="Selector type of 'boolean' is not supported">b</error>) {//error
|
||||
switch (<error descr="Selector type of 'boolean' is not supported at language level '22'">b</error>) {//error
|
||||
default -> System.out.println("1");
|
||||
}
|
||||
int i = 1;
|
||||
@@ -63,15 +63,15 @@ public class SwitchWithPrimitivesNotAllowed {
|
||||
default -> System.out.println("1");
|
||||
}
|
||||
long l = 1L;
|
||||
switch (<error descr="Selector type of 'long' is not supported">l</error>) { //error
|
||||
switch (<error descr="Selector type of 'long' is not supported at language level '22'">l</error>) { //error
|
||||
default -> System.out.println("1");
|
||||
}
|
||||
double d = 1.0;
|
||||
switch (<error descr="Selector type of 'double' is not supported">d</error>) {//error
|
||||
switch (<error descr="Selector type of 'double' is not supported at language level '22'">d</error>) {//error
|
||||
default -> System.out.println("1");
|
||||
}
|
||||
float f = 1.0F;
|
||||
switch (<error descr="Selector type of 'float' is not supported">f</error>) {//error
|
||||
switch (<error descr="Selector type of 'float' is not supported at language level '22'">f</error>) {//error
|
||||
default -> System.out.println("1");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ class EnhancedSwitchStatements {
|
||||
noop(); break;
|
||||
}
|
||||
|
||||
switch (<error descr="Incompatible types. Found: 'java.lang.Object', required: 'char, byte, short, int, Character, Byte, Short, Integer, String, or an enum'">new Object()</error>) { }
|
||||
switch (<error descr="Selector type of 'java.lang.Object' is not supported at language level '15'">new Object()</error>) { }
|
||||
}
|
||||
|
||||
private static void noop() { }
|
||||
|
||||
@@ -16,7 +16,7 @@ class SwitchExpressions {
|
||||
});
|
||||
|
||||
System.out.println(
|
||||
switch (<error descr="Incompatible types. Found: 'java.lang.Object', required: 'char, byte, short, int, Character, Byte, Short, Integer, String, or an enum'">new Object()</error>) {
|
||||
switch (<error descr="Selector type of 'java.lang.Object' is not supported at language level '15'">new Object()</error>) {
|
||||
default -> "whatever";
|
||||
}
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user