diff --git a/java/java-psi-api/src/com/intellij/psi/impl/source/resolve/graphInference/PsiPolyExpressionUtil.java b/java/java-psi-api/src/com/intellij/psi/impl/source/resolve/graphInference/PsiPolyExpressionUtil.java index d5731d0852b1..dba8832aae0e 100644 --- a/java/java-psi-api/src/com/intellij/psi/impl/source/resolve/graphInference/PsiPolyExpressionUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/impl/source/resolve/graphInference/PsiPolyExpressionUtil.java @@ -18,6 +18,7 @@ public class PsiPolyExpressionUtil { return !(expression instanceof PsiFunctionalExpression) && !(expression instanceof PsiParenthesizedExpression) && !(expression instanceof PsiConditionalExpression) && + !(expression instanceof PsiSwitchExpression) && !(expression instanceof PsiCallExpression); } @@ -44,6 +45,9 @@ public class PsiPolyExpressionUtil { return isInAssignmentOrInvocationContext(expression); } } + if (expression instanceof PsiSwitchExpression) { + return isInAssignmentOrInvocationContext(expression); + } return false; } @@ -159,6 +163,9 @@ public class PsiPolyExpressionUtil { final PsiMethod method = ((PsiMethodCallExpression)arg).resolveMethod(); return method != null && method.getReturnType() instanceof PsiPrimitiveType; } + else if (arg instanceof PsiSwitchExpression) { + return isBooleanOrNumeric(arg) != null; + } else { assert false : arg; return false; @@ -201,6 +208,25 @@ public class PsiPolyExpressionUtil { if (thenKind == elseKind || elseKind == ConditionalKind.NULL) return thenKind; if (thenKind == ConditionalKind.NULL) return elseKind; } + + if (expr instanceof PsiSwitchExpression) { + ConditionalKind switchKind = null; + for (PsiExpression resultExpression : PsiUtil.getSwitchResultExpressions((PsiSwitchExpression)expr)) { + ConditionalKind resultKind = isBooleanOrNumeric(resultExpression); + if (resultKind == null) return null; + if (switchKind == null) { + switchKind = resultKind; + } + else if (switchKind != resultKind) { + if (switchKind == ConditionalKind.NULL) { + switchKind = resultKind; + } + else if (resultKind != ConditionalKind.NULL) { + return null; + } + } + } + } return null; } diff --git a/java/java-psi-api/src/com/intellij/psi/infos/MethodCandidateInfo.java b/java/java-psi-api/src/com/intellij/psi/infos/MethodCandidateInfo.java index 6fd650c5dc84..8bd1586864e6 100644 --- a/java/java-psi-api/src/com/intellij/psi/infos/MethodCandidateInfo.java +++ b/java/java-psi-api/src/com/intellij/psi/infos/MethodCandidateInfo.java @@ -33,6 +33,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; /** * @author ik, dsl @@ -256,6 +258,12 @@ public class MethodCandidateInfo extends CandidateInfo{ return ThreeState.UNSURE; } } + else if (expression instanceof PsiSwitchExpression) { + Set states = + PsiUtil.getSwitchResultExpressions((PsiSwitchExpression)expression).stream().map(expr -> isPotentialCompatible(expr, formalType, method)).collect(Collectors.toSet()); + if (states.contains(ThreeState.NO)) return ThreeState.NO; + if (states.contains(ThreeState.UNSURE)) return ThreeState.UNSURE; + } return ThreeState.YES; } diff --git a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java index 5f9dd9461a74..ec954556d6e4 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java @@ -347,6 +347,25 @@ public final class PsiUtil extends PsiUtilCore { return false; } + public static List getSwitchResultExpressions(PsiSwitchExpression switchExpression) { + PsiCodeBlock body = switchExpression.getBody(); + if (body != null) { + List result = new ArrayList<>(); + PsiStatement[] statements = body.getStatements(); + for (PsiStatement statement : statements) { + if (statement instanceof PsiSwitchLabeledRuleStatement) { + PsiStatement ruleBody = ((PsiSwitchLabeledRuleStatement)statement).getBody(); + if (ruleBody instanceof PsiExpressionStatement) {//todo break statements + result.add(((PsiExpressionStatement)ruleBody).getExpression()); + + } + } + } + return result; + } + return Collections.emptyList(); + } + @MagicConstant(intValues = {ACCESS_LEVEL_PUBLIC, ACCESS_LEVEL_PROTECTED, ACCESS_LEVEL_PACKAGE_LOCAL, ACCESS_LEVEL_PRIVATE}) public @interface AccessLevel {} diff --git a/java/java-psi-api/src/com/intellij/psi/util/TypeConversionUtil.java b/java/java-psi-api/src/com/intellij/psi/util/TypeConversionUtil.java index 6fc8624339ed..7b2405bc475c 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/TypeConversionUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/TypeConversionUtil.java @@ -40,8 +40,8 @@ public class TypeConversionUtil { public static final int CHAR_RANK = 3; public static final int INT_RANK = 4; public static final int LONG_RANK = 5; - private static final int FLOAT_RANK = 6; - private static final int DOUBLE_RANK = 7; + public static final int FLOAT_RANK = 6; + public static final int DOUBLE_RANK = 7; private static final int BOOL_RANK = 10; private static final int STRING_RANK = 100; private static final int MAX_NUMERIC_RANK = DOUBLE_RANK; diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java index 7966f0d8ab0c..68420601f405 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java @@ -528,6 +528,11 @@ public class InferenceSession { processReturnExpression(additionalConstraints, ignoredConstraints, ((PsiConditionalExpression)returnExpression).getThenExpression(), functionalType, addConstraint, initialSubstitutor); processReturnExpression(additionalConstraints, ignoredConstraints, ((PsiConditionalExpression)returnExpression).getElseExpression(), functionalType, addConstraint, initialSubstitutor); } + else if (returnExpression instanceof PsiSwitchExpression) { + for (PsiExpression resultExpression : PsiUtil.getSwitchResultExpressions((PsiSwitchExpression)returnExpression)) { + processReturnExpression(additionalConstraints, ignoredConstraints, resultExpression, functionalType, addConstraint, initialSubstitutor); + } + } else if (returnExpression instanceof PsiLambdaExpression) { collectLambdaReturnExpression(additionalConstraints, ignoredConstraints, (PsiLambdaExpression)returnExpression, functionalType, myErased, initialSubstitutor); } @@ -796,9 +801,13 @@ public class InferenceSession { } return false; } - + public static PsiType getTargetType(final PsiElement context) { - return getTargetTypeFromParent(context, new Ref<>(), true); + PsiType targetType = getTargetTypeFromParent(context, new Ref<>(), true); + if (targetType instanceof PsiClassType) { + return ((PsiClassType)targetType).setLanguageLevel(PsiUtil.getLanguageLevel(context)); + } + return targetType; } /** @@ -1887,6 +1896,11 @@ public class InferenceSession { return argConstraints(thenExpression, session, sInterfaceMethod, sSubstitutor, tInterfaceMethod, tSubstitutor) && argConstraints(elseExpression, session, sInterfaceMethod, sSubstitutor, tInterfaceMethod, tSubstitutor); } + + if (arg instanceof PsiSwitchExpression) { + return PsiUtil.getSwitchResultExpressions((PsiSwitchExpression)arg).stream() + .allMatch(resultExpression -> argConstraints(resultExpression, session, sInterfaceMethod, sSubstitutor, tInterfaceMethod, tSubstitutor)); + } return false; } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/ExpressionCompatibilityConstraint.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/ExpressionCompatibilityConstraint.java index 5431414ba1ed..a207da47bdb5 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/ExpressionCompatibilityConstraint.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/ExpressionCompatibilityConstraint.java @@ -8,6 +8,7 @@ import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession; import com.intellij.psi.impl.source.resolve.graphInference.InferenceVariable; import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil; import com.intellij.psi.infos.MethodCandidateInfo; +import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.ArrayUtil; import org.jetbrains.annotations.NotNull; @@ -82,6 +83,11 @@ public class ExpressionCompatibilityConstraint extends InputOutputConstraintForm return true; } + if (myExpression instanceof PsiSwitchExpression) { + PsiUtil.getSwitchResultExpressions((PsiSwitchExpression)myExpression).forEach(expression -> constraints.add(new ExpressionCompatibilityConstraint(expression,myT))); + return true; + } + if (myExpression instanceof PsiCall) { final InferenceSession callSession = reduceExpressionCompatibilityConstraint(session, myExpression, myT, true); if (callSession == null) { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/InputOutputConstraintFormula.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/InputOutputConstraintFormula.java index 8c4584f68a6f..226f8b5ff672 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/InputOutputConstraintFormula.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/InputOutputConstraintFormula.java @@ -24,6 +24,8 @@ import org.jetbrains.annotations.Nullable; import java.util.HashSet; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; public abstract class InputOutputConstraintFormula implements ConstraintFormula { @@ -93,6 +95,15 @@ public abstract class InputOutputConstraintFormula implements ConstraintFormula return thenResult; } } + + if (psiExpression instanceof PsiSwitchExpression) { + Set variables = + PsiUtil.getSwitchResultExpressions((PsiSwitchExpression)psiExpression).stream().flatMap(expression -> { + Set inputVariables = createSelfConstraint(type, expression).getInputVariables(session); + return inputVariables != null ? inputVariables.stream() : Stream.empty(); + }).collect(Collectors.toSet()); + return variables.isEmpty() ? null : variables; + } return null; } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiConditionalExpressionImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiConditionalExpressionImpl.java index e8af17c4fd49..c1a6d7d51760 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiConditionalExpressionImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiConditionalExpressionImpl.java @@ -71,11 +71,7 @@ public class PsiConditionalExpressionImpl extends ExpressionPsiElement implement !MethodCandidateInfo.ourOverloadGuard.currentStack().contains(PsiUtil.skipParenthesizedExprUp(this.getParent()))) { //15.25.3 Reference Conditional Expressions // The type of a poly reference conditional expression is the same as its target type. - final PsiType targetType = InferenceSession.getTargetType(this); - if (targetType instanceof PsiClassType) { - return ((PsiClassType)targetType).setLanguageLevel(PsiUtil.getLanguageLevel(this)); - } - return targetType; + return InferenceSession.getTargetType(this); } final int typeRank1 = TypeConversionUtil.getTypeRank(type1); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiSwitchExpressionImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiSwitchExpressionImpl.java index fa0825026ec6..a9aa2ce0bcbb 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiSwitchExpressionImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiSwitchExpressionImpl.java @@ -3,13 +3,23 @@ package com.intellij.psi.impl.source.tree.java; import com.intellij.lang.ASTNode; import com.intellij.psi.*; -import com.intellij.psi.impl.source.PsiImmediateClassType; +import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession; +import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil; import com.intellij.psi.impl.source.tree.ElementType; import com.intellij.psi.impl.source.tree.JavaElementType; import com.intellij.psi.impl.source.tree.JavaSourceUtil; import com.intellij.psi.impl.source.tree.TreeElement; +import com.intellij.psi.infos.MethodCandidateInfo; +import com.intellij.psi.util.PsiUtil; +import com.intellij.psi.util.TypeConversionUtil; +import com.intellij.util.ArrayUtil; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + public class PsiSwitchExpressionImpl extends PsiSwitchBlockImpl implements PsiSwitchExpression { public PsiSwitchExpressionImpl() { super(JavaElementType.SWITCH_EXPRESSION); @@ -22,9 +32,80 @@ public class PsiSwitchExpressionImpl extends PsiSwitchBlockImpl implements PsiSw @Override public PsiType getType() { - //todo[ann] http://cr.openjdk.java.net/~gbierman/switch-expressions.html#jep325-15.29.1 - PsiClass objClass = JavaPsiFacade.getInstance(getProject()).findClass(CommonClassNames.JAVA_LANG_OBJECT, getResolveScope()); - return objClass != null ? new PsiImmediateClassType(objClass, PsiSubstitutor.EMPTY) : null; + if (PsiPolyExpressionUtil.isPolyExpression(this) && + !MethodCandidateInfo.ourOverloadGuard.currentStack().contains(PsiUtil.skipParenthesizedExprUp(getParent()))) { + return InferenceSession.getTargetType(this); + } + + List resultExpressions = PsiUtil.getSwitchResultExpressions(this); + + Set resultTypes = new HashSet<>(); + for (PsiExpression expression : resultExpressions) { + PsiType resultExpressionType = expression.getType(); + if (resultExpressionType == null) return null; + resultTypes.add(resultExpressionType); + } + + //If the result expressions all have the same type (which may be the null type), then that is the type of the switch expression. + if (resultTypes.size() == 1) { + return ContainerUtil.getFirstItem(resultTypes); + } + + //Otherwise, if the type of each result expression is boolean or Boolean, + //an unboxing conversion (5.1.8) is applied to each result expression of type Boolean, and the switch expression has type boolean. + if (resultTypes.stream().allMatch(type -> PsiType.BOOLEAN.isAssignableFrom(type))) { + return PsiType.BOOLEAN; + } + + //Otherwise, if the type of each result expression is convertible to a numeric type (5.1.8), + // the type of the switch expression is given by numeric promotion (5.6.3) applied to the result expressions. + int[] ranks = resultTypes.stream().mapToInt(type -> TypeConversionUtil.getTypeRank(type)).toArray(); + int maxRank = ArrayUtil.max(ranks); + if (TypeConversionUtil.isNumericType(maxRank)) { + if (maxRank == TypeConversionUtil.DOUBLE_RANK) { + return PsiType.DOUBLE; + } + if (maxRank == TypeConversionUtil.FLOAT_RANK) { + return PsiType.FLOAT; + } + if (maxRank == TypeConversionUtil.LONG_RANK) { + return PsiType.LONG; + } + + if (isNumericPromotion(resultExpressions, ranks, PsiType.CHAR)) { + return PsiType.CHAR; + } + + if (isNumericPromotion(resultExpressions, ranks, PsiType.SHORT)) { + return PsiType.SHORT; + } + + if (isNumericPromotion(resultExpressions, ranks, PsiType.BYTE)) { + return PsiType.BYTE; + } + return PsiType.INT; + } + + //Otherwise, boxing conversion (5.1.7) is applied to each result expression that has a primitive type, after which the type of the switch expression is the result of applying capture conversion (5.1.10) + // to the least upper bound (4.10.4) of the types of the result expressions. + PsiType leastUpperBound = PsiType.NULL; + for (PsiType type : resultTypes) { + if (TypeConversionUtil.isPrimitiveAndNotNull(type)) { + type = ((PsiPrimitiveType)type).getBoxedType(this); + } + if (leastUpperBound == PsiType.NULL) { + leastUpperBound = type; + } + else { + leastUpperBound = GenericsUtil.getLeastUpperBound(leastUpperBound, leastUpperBound, getManager()); + } + } + return leastUpperBound != null ? PsiUtil.captureToplevelWildcards(leastUpperBound, this) : null; + } + + private static boolean isNumericPromotion(List resultExpressions, int[] ranks, final PsiPrimitiveType type) { + return ArrayUtil.find(ranks, TypeConversionUtil.getTypeRank(type)) > -1 && + resultExpressions.stream().allMatch(expression -> TypeConversionUtil.areTypesAssignmentCompatible(type, expression)); } @Override diff --git a/java/java-psi-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java b/java/java-psi-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java index 892c43547fb1..f89a93de6aae 100644 --- a/java/java-psi-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java +++ b/java/java-psi-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java @@ -749,6 +749,10 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{ isFunctionalTypeMoreSpecific(((PsiConditionalExpression)expr).getElseExpression(), sType, tType); } + if (expr instanceof PsiSwitchExpression) { + return PsiUtil.getSwitchResultExpressions((PsiSwitchExpression)expr).stream().allMatch(resultExpr -> isFunctionalTypeMoreSpecific(resultExpr, sType, tType)); + } + if (expr instanceof PsiFunctionalExpression) { if (expr instanceof PsiLambdaExpression && !((PsiLambdaExpression)expr).hasFormalParameterTypes()) { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting12/SimpleInferenceCases.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting12/SimpleInferenceCases.java new file mode 100644 index 000000000000..03373b102e65 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting12/SimpleInferenceCases.java @@ -0,0 +1,11 @@ +class MyTest { + T foo(T t) { + return t; + } + + void m(int i) { + String s = foo(switch (i) {default -> "str";}); + String s1 = foo(switch (i) {case 1 -> new Object(); default -> "str";}); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting12/SwitchNumericPromotion.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting12/SwitchNumericPromotion.java new file mode 100644 index 000000000000..9d1d16afd4cf --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting12/SwitchNumericPromotion.java @@ -0,0 +1,49 @@ + +class SwitchExpressions { + + byte B = 1; + short S = 1; + char C = 1; + final int I = 1; + + void m(int i) { + var v1 = switch(i) { + case 1 -> 1; + default -> 1.0; + }; + + double d = v1; + float f = v1; + int in = v1; + + var v2 = switch (i) { + case 1 -> C; + default -> I; + }; + + in = v2; + byte b = v2; + short s = v2; + char c = v2; + + var v3 = switch (i) { + case 1 -> B; + default -> I; + }; + + in = v3; + b = v3; + s = v3; + c = v3; + + var v4 = switch (i) { + case 1 -> B; + default -> Integer.MAX_VALUE; + }; + + in = v4; + b = v4; + s = v4; + c = v4; + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/LightJava12HighlightingTest.kt b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/LightJava12HighlightingTest.kt index 2fce45d478ef..1421acc8d798 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/LightJava12HighlightingTest.kt +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/LightJava12HighlightingTest.kt @@ -10,6 +10,8 @@ class LightJava12HighlightingTest : LightCodeInsightFixtureTestCase() { fun testEnhancedSwitchStatements() = doTest() fun testSwitchExpressions() = doTest() + fun testSwitchNumericPromotion() = doTest() + fun testSimpleInferenceCases() = doTest() private fun doTest() { myFixture.configureByFile(getTestName(false) + ".java") diff --git a/java/java-tests/testSrc/com/intellij/java/propertyBased/Java12SwitchExpressionSanityTest.java b/java/java-tests/testSrc/com/intellij/java/propertyBased/Java12SwitchExpressionSanityTest.java new file mode 100644 index 000000000000..7473821cd2e1 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/java/propertyBased/Java12SwitchExpressionSanityTest.java @@ -0,0 +1,82 @@ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.java.propertyBased; + +import com.intellij.openapi.application.PathManager; +import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiSwitchBlock; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.testFramework.LightProjectDescriptor; +import com.intellij.testFramework.SkipSlowTestLocally; +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; +import com.intellij.testFramework.propertyBased.InvokeIntention; +import com.intellij.testFramework.propertyBased.MadTestingAction; +import com.intellij.testFramework.propertyBased.MadTestingUtil; +import com.intellij.testFramework.propertyBased.StripTestDataMarkup; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jetCheck.Generator; +import org.jetbrains.jetCheck.PropertyChecker; + +import java.io.IOException; +import java.util.Collection; +import java.util.List; +import java.util.function.Function; +import java.util.function.Supplier; + +@SkipSlowTestLocally +public class Java12SwitchExpressionSanityTest extends LightCodeInsightFixtureTestCase { + + @Override + protected void tearDown() throws Exception { + // remove jdk if it was created during highlighting to avoid leaks + try { + JavaAwareProjectJdkTableImpl.removeInternalJdkInTests(); + } + finally { + super.tearDown(); + } + } + + @NotNull + @Override + protected LightProjectDescriptor getProjectDescriptor() { + return JAVA_12; + } + + public void testIntentionsAroundSwitch() { + MadTestingUtil.enableAllInspections(getProject(), getTestRootDisposable(), + "BoundedWildcard" // IDEA-194460 + ); + + Function> fileActions = + file -> Generator.sampledFrom(new InvokeIntention(file, new JavaIntentionPolicy()) { + @Override + protected int generateDocOffset(@NotNull Environment env, @Nullable String logMessage) { + Collection children = PsiTreeUtil.findChildrenOfType(getFile(), PsiSwitchBlock.class); + if (children.isEmpty()) { + return super.generateDocOffset(env, logMessage); + } + + List> generators = + ContainerUtil.map(children, stmt -> + Generator.integers(stmt.getTextRange().getStartOffset(), + stmt.getTextRange().getEndOffset()).noShrink()); + return env.generateValue(Generator.anyOf(generators), logMessage); + } + }, new StripTestDataMarkup(file)); + + Supplier fileChooser = MadTestingUtil.actionsOnFileContents(myFixture, PathManager.getHomePath(), f -> { + try { + return f.getName().endsWith(".java") && FileUtil.loadFile(f).contains(" switch"); + } + catch (IOException e) { + return false; + } + }, fileActions); + PropertyChecker.checkScenarios(fileChooser); + } + +} diff --git a/platform/testFramework/src/com/intellij/testFramework/propertyBased/InvokeIntention.java b/platform/testFramework/src/com/intellij/testFramework/propertyBased/InvokeIntention.java index 1b1391c15caf..91a6e426415d 100644 --- a/platform/testFramework/src/com/intellij/testFramework/propertyBased/InvokeIntention.java +++ b/platform/testFramework/src/com/intellij/testFramework/propertyBased/InvokeIntention.java @@ -80,7 +80,7 @@ public class InvokeIntention extends ActionOnFile { return result; } - private void doInvokeIntention(int offset, Environment env) { + protected void doInvokeIntention(int offset, Environment env) { Project project = getProject(); Editor editor = FileEditorManager.getInstance(project).openTextEditor(new OpenFileDescriptor(project, getVirtualFile(), offset), true); assert editor != null; diff --git a/platform/util/src/com/intellij/util/ArrayUtil.java b/platform/util/src/com/intellij/util/ArrayUtil.java index 92a75551ce4b..7e81dd6f4abe 100644 --- a/platform/util/src/com/intellij/util/ArrayUtil.java +++ b/platform/util/src/com/intellij/util/ArrayUtil.java @@ -932,6 +932,15 @@ public class ArrayUtil extends ArrayUtilRt { return min; } + @Contract(pure = true) + public static int max(int[] values) { + int max = Integer.MIN_VALUE; + for (int value : values) { + if (value > max) max = value; + } + return max; + } + @Contract(pure = true) public static int[] mergeSortedArrays(int[] a1, int[] a2, boolean mergeEqualItems) { int newSize = a1.length + a2.length;