mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-199625 Support of Java11 Optional#isEmpty method
This commit is contained in:
+76
-11
@@ -6,6 +6,7 @@ import com.intellij.codeInspection.dataFlow.DfaFactType;
|
||||
import com.intellij.codeInspection.util.LambdaGenerationUtil;
|
||||
import com.intellij.codeInspection.util.OptionalUtil;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.PsiDiamondTypeUtil;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
@@ -23,31 +24,48 @@ import org.jetbrains.annotations.Nullable;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.intellij.codeInspection.util.OptionalUtil.*;
|
||||
import static com.intellij.psi.CommonClassNames.JAVA_UTIL_OPTIONAL;
|
||||
import static com.intellij.util.ObjectUtils.tryCast;
|
||||
|
||||
|
||||
public class SimplifyOptionalCallChainsInspection extends AbstractBaseJavaLocalInspectionTool {
|
||||
private static final CallMatcher OPTIONAL_OR_ELSE =
|
||||
CallMatcher.instanceCall(CommonClassNames.JAVA_UTIL_OPTIONAL, "orElse").parameterCount(1);
|
||||
CallMatcher.instanceCall(JAVA_UTIL_OPTIONAL, "orElse").parameterCount(1);
|
||||
private static final CallMatcher OPTIONAL_GET =
|
||||
CallMatcher.instanceCall(CommonClassNames.JAVA_UTIL_OPTIONAL, "get").parameterCount(0);
|
||||
CallMatcher.instanceCall(JAVA_UTIL_OPTIONAL, "get").parameterCount(0);
|
||||
private static final CallMatcher OPTIONAL_OR_ELSE_GET =
|
||||
CallMatcher.instanceCall(CommonClassNames.JAVA_UTIL_OPTIONAL, "orElseGet").parameterCount(1);
|
||||
CallMatcher.instanceCall(JAVA_UTIL_OPTIONAL, "orElseGet").parameterCount(1);
|
||||
private static final CallMatcher OPTIONAL_MAP =
|
||||
CallMatcher.instanceCall(CommonClassNames.JAVA_UTIL_OPTIONAL, "map").parameterCount(1);
|
||||
CallMatcher.instanceCall(JAVA_UTIL_OPTIONAL, "map").parameterCount(1);
|
||||
private static final CallMatcher OPTIONAL_OF_NULLABLE =
|
||||
CallMatcher.staticCall(CommonClassNames.JAVA_UTIL_OPTIONAL, "ofNullable").parameterCount(1);
|
||||
CallMatcher.staticCall(JAVA_UTIL_OPTIONAL, "ofNullable").parameterCount(1);
|
||||
private static final CallMatcher OPTIONAL_OF_OF_NULLABLE =
|
||||
CallMatcher.staticCall(CommonClassNames.JAVA_UTIL_OPTIONAL, "ofNullable", "of").parameterCount(1);
|
||||
CallMatcher.staticCall(JAVA_UTIL_OPTIONAL, "ofNullable", "of").parameterCount(1);
|
||||
private static final CallMatcher OPTIONAL_IS_PRESENT =
|
||||
CallMatcher.anyOf(
|
||||
CallMatcher.exactInstanceCall(JAVA_UTIL_OPTIONAL, "isPresent").parameterCount(0),
|
||||
CallMatcher.exactInstanceCall(OPTIONAL_INT, "isPresent").parameterCount(0),
|
||||
CallMatcher.exactInstanceCall(OPTIONAL_LONG, "isPresent").parameterCount(0),
|
||||
CallMatcher.exactInstanceCall(OPTIONAL_DOUBLE, "isPresent").parameterCount(0)
|
||||
);
|
||||
private static final CallMatcher OPTIONAL_IS_EMPTY =
|
||||
CallMatcher.anyOf(
|
||||
CallMatcher.exactInstanceCall(JAVA_UTIL_OPTIONAL, "isEmpty").parameterCount(0),
|
||||
CallMatcher.exactInstanceCall(OPTIONAL_INT, "isEmpty").parameterCount(0),
|
||||
CallMatcher.exactInstanceCall(OPTIONAL_LONG, "isEmpty").parameterCount(0),
|
||||
CallMatcher.exactInstanceCall(OPTIONAL_DOUBLE, "isEmpty").parameterCount(0)
|
||||
);
|
||||
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
|
||||
if (!PsiUtil.isLanguageLevel8OrHigher(holder.getFile())) {
|
||||
LanguageLevel level = PsiUtil.getLanguageLevel(holder.getFile());
|
||||
if (level.isLessThan(LanguageLevel.JDK_1_8)) {
|
||||
return PsiElementVisitor.EMPTY_VISITOR;
|
||||
}
|
||||
return new OptionalChainVisitor() {
|
||||
return new OptionalChainVisitor(level) {
|
||||
@Override
|
||||
protected void handleSimplification(@NotNull PsiElement element,
|
||||
@NotNull OptionalChainSimplification simplification) {
|
||||
@@ -57,12 +75,19 @@ public class SimplifyOptionalCallChainsInspection extends AbstractBaseJavaLocalI
|
||||
}
|
||||
|
||||
private static abstract class OptionalChainVisitor extends JavaElementVisitor {
|
||||
private final LanguageLevel myLevel;
|
||||
|
||||
private OptionalChainVisitor(LanguageLevel level) {
|
||||
myLevel = level;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodCallExpression(PsiMethodCallExpression call) {
|
||||
if (OPTIONAL_GET.test(call)) {
|
||||
handleRewrapping(call, OPTIONAL_OF_OF_NULLABLE);
|
||||
return;
|
||||
}
|
||||
handleInvertedPresentOrEmpty(call);
|
||||
PsiExpression falseArg = null;
|
||||
boolean useOrElseGet = false;
|
||||
if (OPTIONAL_OR_ELSE.test(call)) {
|
||||
@@ -83,6 +108,19 @@ public class SimplifyOptionalCallChainsInspection extends AbstractBaseJavaLocalI
|
||||
handleOrElseNullConditionalAction(call, falseArg);
|
||||
}
|
||||
|
||||
private void handleInvertedPresentOrEmpty(PsiMethodCallExpression call) {
|
||||
if (myLevel.isLessThan(LanguageLevel.JDK_11)) return;
|
||||
if (!BoolUtils.isNegated(call)) return;
|
||||
PsiElement nameElement = call.getMethodExpression().getReferenceNameElement();
|
||||
if (nameElement == null) return;
|
||||
if (OPTIONAL_IS_EMPTY.test(call)) {
|
||||
handleSimplification(nameElement, new FlipEmptyPresentFix("isPresent"));
|
||||
}
|
||||
else if (OPTIONAL_IS_PRESENT.test(call)) {
|
||||
handleSimplification(nameElement, new FlipEmptyPresentFix("isEmpty"));
|
||||
}
|
||||
}
|
||||
|
||||
private void handleRewrapping(PsiMethodCallExpression call, CallMatcher wrapper) {
|
||||
PsiElement parent = PsiUtil.skipParenthesizedExprUp(call.getParent());
|
||||
if (!(parent instanceof PsiExpressionList)) return;
|
||||
@@ -471,8 +509,6 @@ public class SimplifyOptionalCallChainsInspection extends AbstractBaseJavaLocalI
|
||||
PsiTreeUtil.isAncestor(nextStatement, reference.getElement(), false))) return null;
|
||||
return new Context(lambdaExpr, nextStatement, statement, returnVar, orElseCall);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -497,6 +533,35 @@ public class SimplifyOptionalCallChainsInspection extends AbstractBaseJavaLocalI
|
||||
}
|
||||
}
|
||||
|
||||
private static class FlipEmptyPresentFix implements OptionalChainSimplification {
|
||||
private final String myReplacement;
|
||||
|
||||
private FlipEmptyPresentFix(String replacement) {
|
||||
myReplacement = replacement;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return CommonQuickFixBundle.message("fix.replace.with.x", myReplacement + "()");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "'" + myReplacement + "()' can be used instead";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull PsiElement element) {
|
||||
PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class);
|
||||
if (call == null) return;
|
||||
PsiPrefixExpression negation = tryCast(PsiUtil.skipParenthesizedExprUp(call.getParent()), PsiPrefixExpression.class);
|
||||
if (negation == null || BoolUtils.getNegated(negation) != call) return;
|
||||
ExpressionUtils.bindCallTo(call, myReplacement);
|
||||
new CommentTracker().replaceAndRestoreComments(negation, call);
|
||||
}
|
||||
}
|
||||
|
||||
private static class SimplifyOptionalChainFix implements OptionalChainSimplification {
|
||||
private final String myReplacement;
|
||||
@@ -531,4 +596,4 @@ public class SimplifyOptionalCallChainsInspection extends AbstractBaseJavaLocalI
|
||||
PsiDiamondTypeUtil.removeRedundantTypeArguments(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,9 +18,9 @@ import org.jetbrains.annotations.Nullable;
|
||||
import java.util.Objects;
|
||||
|
||||
public class OptionalUtil {
|
||||
private static final String OPTIONAL_INT = "java.util.OptionalInt";
|
||||
private static final String OPTIONAL_LONG = "java.util.OptionalLong";
|
||||
private static final String OPTIONAL_DOUBLE = "java.util.OptionalDouble";
|
||||
public static final String OPTIONAL_INT = "java.util.OptionalInt";
|
||||
public static final String OPTIONAL_LONG = "java.util.OptionalLong";
|
||||
public static final String OPTIONAL_DOUBLE = "java.util.OptionalDouble";
|
||||
|
||||
private static final CallMatcher OPTIONAL_OF =
|
||||
CallMatcher.staticCall(CommonClassNames.JAVA_UTIL_OPTIONAL, "of", "ofNullable").parameterCount(1);
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Replace with 'isEmpty()'" "true"
|
||||
import java.util.Optional;
|
||||
|
||||
public class Test {
|
||||
void test(Optional<String> opt) {
|
||||
if(!opt.is<caret>Present()) return;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Replace with 'isEmpty()'" "true"
|
||||
import java.util.Optional;
|
||||
|
||||
public class Test {
|
||||
void test(Optional<String> opt) {
|
||||
if(!opt.is<caret>Present()) return;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Replace with 'isEmpty()'" "false"
|
||||
import java.util.Optional;
|
||||
|
||||
public class Test {
|
||||
void test(Optional<String> opt) {
|
||||
if(!opt.is<caret>Present()) return;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user