mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-191905 Optional.get without Optional.isPresent can suggest quick fix when Optional.get returns Optional
This commit is contained in:
+84
-4
@@ -2,18 +2,28 @@
|
||||
package com.intellij.codeInspection.java18api;
|
||||
|
||||
import com.intellij.codeInsight.PsiEquivalenceUtil;
|
||||
import com.intellij.codeInspection.AbstractBaseJavaLocalInspectionTool;
|
||||
import com.intellij.codeInspection.InspectionsBundle;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.codeInspection.*;
|
||||
import com.intellij.codeInspection.dataFlow.CommonDataflow;
|
||||
import com.intellij.codeInspection.dataFlow.DfaFactType;
|
||||
import com.intellij.codeInspection.dataFlow.DfaOptionalSupport;
|
||||
import com.intellij.codeInspection.util.LambdaGenerationUtil;
|
||||
import com.intellij.codeInspection.util.OptionalUtil;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.codeStyle.SuggestedNameInfo;
|
||||
import com.intellij.psi.codeStyle.VariableKind;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.siyeh.ig.psiutils.CommentTracker;
|
||||
import com.siyeh.ig.psiutils.ExpressionUtils;
|
||||
import com.siyeh.ig.psiutils.TypeUtils;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class OptionalGetWithoutIsPresentInspection extends AbstractBaseJavaLocalInspectionTool {
|
||||
@NotNull
|
||||
@Override
|
||||
@@ -37,7 +47,8 @@ public class OptionalGetWithoutIsPresentInspection extends AbstractBaseJavaLocal
|
||||
result.getExpressionFact(qualifier, DfaFactType.OPTIONAL_PRESENCE) == null &&
|
||||
!isPresentCallWithSameQualifierExists(qualifier)) {
|
||||
holder.registerProblem(nameElement,
|
||||
InspectionsBundle.message("inspection.optional.get.without.is.present.message", optionalClass.getName()));
|
||||
InspectionsBundle.message("inspection.optional.get.without.is.present.message", optionalClass.getName()),
|
||||
tryCreateFix(call));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,4 +71,73 @@ public class OptionalGetWithoutIsPresentInspection extends AbstractBaseJavaLocal
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static LocalQuickFix tryCreateFix(PsiMethodCallExpression call) {
|
||||
PsiExpression qualifier = call.getMethodExpression().getQualifierExpression();
|
||||
if (qualifier == null) return null;
|
||||
PsiClass optionalClass = PsiUtil.resolveClassInClassTypeOnly(qualifier.getType());
|
||||
if (optionalClass == null || !CommonClassNames.JAVA_UTIL_OPTIONAL.equals(optionalClass.getQualifiedName())) return null;
|
||||
PsiType optionalElementType = OptionalUtil.getOptionalElementType(qualifier.getType());
|
||||
if (optionalElementType == null) return null;
|
||||
PsiMethodCallExpression nextCall = ExpressionUtils.getCallForQualifier(call);
|
||||
if (nextCall != null) {
|
||||
if (optionalClass.equals(PsiUtil.resolveClassInClassTypeOnly(nextCall.getType()))) {
|
||||
if (!LambdaGenerationUtil.canBeUncheckedLambda(nextCall)) {
|
||||
// Probably qualifier accesses non-final vars or throws exception: we will replace qualifier, so this is not a problem
|
||||
PsiMethodCallExpression copy = (PsiMethodCallExpression)nextCall.copy();
|
||||
PsiExpression copyQualifier = Objects.requireNonNull(copy.getMethodExpression().getQualifierExpression());
|
||||
try {
|
||||
copyQualifier.replace(JavaPsiFacade.getElementFactory(call.getProject())
|
||||
.createExpressionFromText("((" + optionalElementType.getCanonicalText() + ")null)",
|
||||
copyQualifier));
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
return null;
|
||||
}
|
||||
if (!LambdaGenerationUtil.canBeUncheckedLambda(copy)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return new UseFlatMapFix();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class UseFlatMapFix implements LocalQuickFix {
|
||||
@Nls(capitalization = Nls.Capitalization.Sentence)
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return "Use 'flatMap'";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(descriptor.getStartElement(), PsiMethodCallExpression.class);
|
||||
if (call == null) return;
|
||||
PsiExpression qualifier = call.getMethodExpression().getQualifierExpression();
|
||||
if (qualifier == null) return;
|
||||
PsiType elementType = OptionalUtil.getOptionalElementType(qualifier.getType());
|
||||
PsiMethodCallExpression nextCall = ExpressionUtils.getCallForQualifier(call);
|
||||
if (nextCall == null) return;
|
||||
JavaCodeStyleManager manager = JavaCodeStyleManager.getInstance(project);
|
||||
SuggestedNameInfo info = manager.suggestVariableName(VariableKind.PARAMETER, null, qualifier, elementType, true);
|
||||
String name = info.names.length == 0 ? "value" : info.names[0];
|
||||
name = manager.suggestUniqueVariableName(name, call, true);
|
||||
CommentTracker ct = new CommentTracker();
|
||||
PsiReferenceExpression methodExpression = nextCall.getMethodExpression();
|
||||
ct.markRangeUnchanged(Objects.requireNonNull(methodExpression.getQualifierExpression()).getNextSibling(),
|
||||
methodExpression.getLastChild());
|
||||
ct.markRangeUnchanged(methodExpression.getNextSibling(), nextCall.getLastChild());
|
||||
PsiMethodCallExpression newNextCall = (PsiMethodCallExpression)nextCall.copy();
|
||||
PsiExpression newQualifier = Objects.requireNonNull(newNextCall.getMethodExpression().getQualifierExpression());
|
||||
newQualifier.replace(JavaPsiFacade.getElementFactory(project).createExpressionFromText(name, newNextCall));
|
||||
String lambda = name + "->" + newNextCall.getText();
|
||||
String replacement = ct.text(qualifier) + ".flatMap(" + lambda + ")";
|
||||
PsiMethodCallExpression result = (PsiMethodCallExpression)ct.replaceAndRestoreComments(nextCall, replacement);
|
||||
LambdaCanBeMethodReferenceInspection.replaceLambdaWithMethodReference(
|
||||
(PsiLambdaExpression)result.getArgumentList().getExpressions()[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
// "Fix all 'Optional.get() is called without isPresent() check' problems in file" "true"
|
||||
import java.util.Optional;
|
||||
|
||||
class Test {
|
||||
class PropertyHolder<T> {
|
||||
T property;
|
||||
|
||||
Optional<T> getProperty() {
|
||||
return Optional.ofNullable(property);
|
||||
}
|
||||
|
||||
Optional<T> getProperty(String s) {
|
||||
return Optional.ofNullable(property);
|
||||
}
|
||||
Optional<T> getPropertyEx() throws Exception {
|
||||
return Optional.ofNullable(property);
|
||||
}
|
||||
}
|
||||
|
||||
class Smth<T> {
|
||||
PropertyHolder<T> propertyHolder;
|
||||
|
||||
Optional<PropertyHolder<T>> getPropertyHolder() {
|
||||
return Optional.ofNullable(propertyHolder);
|
||||
}
|
||||
|
||||
Optional<PropertyHolder<T>> getPropertyHolderEx() throws Exception {
|
||||
return Optional.ofNullable(propertyHolder);
|
||||
}
|
||||
}
|
||||
|
||||
void test() throws Exception {
|
||||
Optional<Long> property = new Smth<Long>().getPropertyHolder().flatMap(PropertyHolder::getProperty);
|
||||
Optional<Long> property1 = new Smth<Long>().getPropertyHolderEx().flatMap(propertyHolderEx -> propertyHolderEx.getProperty("foo"));
|
||||
Optional<Long> property2 = new Smth<Long>().getPropertyHolderEx().get().getPropertyEx();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// "Fix all 'Optional.get() is called without isPresent() check' problems in file" "true"
|
||||
import java.util.Optional;
|
||||
|
||||
class Test {
|
||||
class PropertyHolder<T> {
|
||||
T property;
|
||||
|
||||
Optional<T> getProperty() {
|
||||
return Optional.ofNullable(property);
|
||||
}
|
||||
|
||||
Optional<T> getProperty(String s) {
|
||||
return Optional.ofNullable(property);
|
||||
}
|
||||
Optional<T> getPropertyEx() throws Exception {
|
||||
return Optional.ofNullable(property);
|
||||
}
|
||||
}
|
||||
|
||||
class Smth<T> {
|
||||
PropertyHolder<T> propertyHolder;
|
||||
|
||||
Optional<PropertyHolder<T>> getPropertyHolder() {
|
||||
return Optional.ofNullable(propertyHolder);
|
||||
}
|
||||
|
||||
Optional<PropertyHolder<T>> getPropertyHolderEx() throws Exception {
|
||||
return Optional.ofNullable(propertyHolder);
|
||||
}
|
||||
}
|
||||
|
||||
void test() throws Exception {
|
||||
Optional<Long> property = new Smth<Long>().getPropertyHolder().ge<caret>t().getProperty();
|
||||
Optional<Long> property1 = new Smth<Long>().getPropertyHolderEx().get().getProperty("foo");
|
||||
Optional<Long> property2 = new Smth<Long>().getPropertyHolderEx().get().getPropertyEx();
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// 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.codeInspection;
|
||||
|
||||
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
|
||||
import com.intellij.codeInspection.LocalInspectionTool;
|
||||
import com.intellij.codeInspection.java18api.OptionalGetWithoutIsPresentInspection;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase.JAVA_8;
|
||||
|
||||
public class OptionalGetWithoutIsPresentInspectionFixTest extends LightQuickFixParameterizedTestCase {
|
||||
public void test() { doAllTests(); }
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected LightProjectDescriptor getProjectDescriptor() {
|
||||
return JAVA_8;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected LocalInspectionTool[] configureLocalInspectionTools() {
|
||||
return new LocalInspectionTool[]{
|
||||
new OptionalGetWithoutIsPresentInspection()
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return "/inspection/optionalGet";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user