mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-05 01:50:56 +07:00
Quick-fix to insert .filter(Objects::nonNull) (IDEA-176699)
This commit is contained in:
@@ -265,6 +265,7 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
|
||||
if (qualifier == null || expression == null) return fixes;
|
||||
|
||||
try {
|
||||
ContainerUtil.addIfNotNull(fixes, createAddStreamFilterNotNullFix(qualifier));
|
||||
if (isVolatileFieldReference(qualifier)) {
|
||||
ContainerUtil.addIfNotNull(fixes, createIntroduceVariableFix(qualifier));
|
||||
}
|
||||
@@ -318,6 +319,10 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected LocalQuickFix createAddStreamFilterNotNullFix(PsiElement reference) {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void addSurroundWithIfFix(PsiExpression qualifier, List<LocalQuickFix> fixes, boolean onTheFly) {
|
||||
}
|
||||
|
||||
@@ -336,7 +341,7 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
|
||||
for (PsiElement element : visitor.getProblems(NullabilityProblem.callNPE)) {
|
||||
if (reportedAnchors.add(element)) {
|
||||
if (element instanceof PsiMethodReferenceExpression) {
|
||||
holder.registerProblem(element, InspectionsBundle.message("dataflow.message.npe.methodref.invocation"));
|
||||
holder.registerProblem(element, InspectionsBundle.message("dataflow.message.npe.methodref.invocation"), createAddStreamFilterNotNullFix(element));
|
||||
}
|
||||
else {
|
||||
reportCallMayProduceNpe(holder, (PsiMethodCallExpression)element, holder.isOnTheFly());
|
||||
@@ -712,7 +717,9 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
|
||||
if (!reportedAnchors.add(expr)) continue;
|
||||
|
||||
if (expr.getParent() instanceof PsiMethodReferenceExpression) {
|
||||
holder.registerProblem(expr.getParent(), InspectionsBundle.message("dataflow.message.passing.nullable.argument.methodref"));
|
||||
PsiMethodReferenceExpression methodRef = (PsiMethodReferenceExpression)expr.getParent();
|
||||
holder.registerProblem(methodRef, InspectionsBundle.message("dataflow.message.passing.nullable.argument.methodref"),
|
||||
createAddStreamFilterNotNullFix(methodRef));
|
||||
}
|
||||
else {
|
||||
final String text = isNullLiteralExpression(expr)
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.codeInspection;
|
||||
|
||||
import com.intellij.codeInsight.intention.HighPriorityAction;
|
||||
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.InheritanceUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.siyeh.ig.psiutils.StreamApiUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.intellij.util.ObjectUtils.tryCast;
|
||||
|
||||
public class StreamFilterNotNullFix implements LocalQuickFix, HighPriorityAction {
|
||||
@Override
|
||||
@NotNull
|
||||
public String getFamilyName() {
|
||||
return InspectionsBundle.message("inspection.data.flow.filter.notnull.quickfix");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
PsiFunctionalExpression function = findFunction(descriptor.getStartElement());
|
||||
if (function == null) return;
|
||||
PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(function, PsiMethodCallExpression.class);
|
||||
if (call == null) return;
|
||||
PsiExpression qualifier = call.getMethodExpression().getQualifierExpression();
|
||||
if (qualifier == null) return;
|
||||
String name = suggestVariableName(function, qualifier);
|
||||
// We create first lambda, then convert to method reference as user code style might be set to prefer lambdas
|
||||
PsiExpression replacement = JavaPsiFacade.getElementFactory(project)
|
||||
.createExpressionFromText(qualifier.getText() + ".filter(" + name + "->" + name + "!=null)", qualifier);
|
||||
PsiMethodCallExpression result = (PsiMethodCallExpression)qualifier.replace(replacement);
|
||||
LambdaCanBeMethodReferenceInspection.replaceAllLambdasWithMethodReferences(result.getArgumentList());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String suggestVariableName(@NotNull PsiFunctionalExpression function, @NotNull PsiExpression qualifier) {
|
||||
String name = null;
|
||||
if (function instanceof PsiLambdaExpression) {
|
||||
PsiParameter parameter = ArrayUtil.getFirstElement(((PsiLambdaExpression)function).getParameterList().getParameters());
|
||||
if (parameter != null) {
|
||||
name = parameter.getName();
|
||||
}
|
||||
}
|
||||
PsiType type = StreamApiUtil.getStreamElementType(qualifier.getType());
|
||||
JavaCodeStyleManager javaCodeStyleManager = JavaCodeStyleManager.getInstance(function.getProject());
|
||||
SuggestedNameInfo info = javaCodeStyleManager.suggestVariableName(VariableKind.PARAMETER, name, null, type, true);
|
||||
name = ArrayUtil.getFirstElement(info.names);
|
||||
return javaCodeStyleManager.suggestUniqueVariableName(name == null ? "obj" : name, qualifier, false);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiFunctionalExpression findFunction(PsiElement reference) {
|
||||
if (reference instanceof PsiFunctionalExpression) {
|
||||
return (PsiFunctionalExpression)reference;
|
||||
}
|
||||
if (reference instanceof PsiIdentifier) {
|
||||
// in "str.trim()" go from "trim" to "str"
|
||||
reference = reference.getParent();
|
||||
if (reference instanceof PsiReferenceExpression) {
|
||||
reference = PsiUtil.skipParenthesizedExprDown(((PsiReferenceExpression)reference).getQualifierExpression());
|
||||
}
|
||||
}
|
||||
if (reference instanceof PsiReferenceExpression) {
|
||||
PsiParameter parameter = tryCast(((PsiReferenceExpression)reference).resolve(), PsiParameter.class);
|
||||
if (parameter == null) return null;
|
||||
PsiParameterList parameterList = tryCast(parameter.getParent(), PsiParameterList.class);
|
||||
if (parameterList == null || parameterList.getParametersCount() != 1) return null;
|
||||
return tryCast(parameterList.getParent(), PsiLambdaExpression.class);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static StreamFilterNotNullFix makeFix(PsiElement reference) {
|
||||
PsiFunctionalExpression fn = findFunction(reference);
|
||||
if (fn == null) return null;
|
||||
PsiExpressionList args = tryCast(PsiUtil.skipParenthesizedExprUp(fn.getParent()), PsiExpressionList.class);
|
||||
if (args == null || args.getExpressions().length != 1) return null;
|
||||
PsiMethodCallExpression call = tryCast(args.getParent(), PsiMethodCallExpression.class);
|
||||
if (call == null) return null;
|
||||
PsiExpression qualifier = call.getMethodExpression().getQualifierExpression();
|
||||
if (qualifier == null || !InheritanceUtil.isInheritor(qualifier.getType(), CommonClassNames.JAVA_UTIL_STREAM_STREAM)) return null;
|
||||
return new StreamFilterNotNullFix();
|
||||
}
|
||||
}
|
||||
@@ -66,6 +66,11 @@ public class DataFlowInspection extends DataFlowInspectionBase {
|
||||
return new ReplaceWithTrivialLambdaFix(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LocalQuickFix createAddStreamFilterNotNullFix(PsiElement reference) {
|
||||
return StreamFilterNotNullFix.makeFix(reference);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LocalQuickFix createIntroduceVariableFix(final PsiExpression expression) {
|
||||
return new IntroduceVariableFix(true);
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// "Insert 'filter(Objects::nonNull)' step" "true"
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
class MyClass {
|
||||
@Nullable String convert(String s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
String accept(@NotNull String s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
void test(List<String> list, @Nullable String s1) {
|
||||
list.stream().map(this::convert).filter(Objects::nonNull).map(s -> accept(s)).forEach(System.out::println);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// "Insert 'filter(Objects::nonNull)' step" "true"
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
class MyClass {
|
||||
@Nullable String convert(String s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
void test(List<String> list, @Nullable String s1) {
|
||||
list.stream().map(this::convert).filter(Objects::nonNull).map(s -> s.trim()).forEach(System.out::println);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// "Insert 'filter(Objects::nonNull)' step" "true"
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
class MyClass {
|
||||
@Nullable String convert(String s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
String accept(@NotNull String s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
void test(List<String> list, @Nullable String s1) {
|
||||
list.stream().map(this::convert).filter(Objects::nonNull).map(this::accept).forEach(System.out::println);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// "Insert 'filter(Objects::nonNull)' step" "true"
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
class MyClass {
|
||||
@Nullable String convert(String s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
void test(List<String> list, @Nullable String s1) {
|
||||
list.stream().map(this::convert).filter(Objects::nonNull).map(String::trim).forEach(System.out::println);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
// "Insert 'filter(Objects::nonNull)' step" "true"
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
class MyClass {
|
||||
@Nullable String convert(String s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
String accept(@NotNull String s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
String execute(Supplier<String> op) {
|
||||
return op.get();
|
||||
}
|
||||
|
||||
void test(List<String> list, @Nullable String s1) {
|
||||
list.stream().map(this::convert).filter(Objects::nonNull).map(s -> execute(() -> accept(s))).forEach(System.out::println);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// "Insert 'filter(Objects::nonNull)' step" "true"
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
class MyClass {
|
||||
@Nullable String convert(String s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
String accept(@NotNull String s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
void test(List<String> list, @Nullable String s1) {
|
||||
list.stream().map(this::convert).map(s -> accept(s<caret>)).forEach(System.out::println);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// "Insert 'filter(Objects::nonNull)' step" "false"
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
class MyClass {
|
||||
@Nullable String convert(String s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
String accept(@NotNull String s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
void test(List<String> list, @Nullable String s1) {
|
||||
list.stream().map(this::convert).map(s -> accept(s1<caret>)).forEach(System.out::println);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// "Insert 'filter(Objects::nonNull)' step" "true"
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
class MyClass {
|
||||
@Nullable String convert(String s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
void test(List<String> list, @Nullable String s1) {
|
||||
list.stream().map(this::convert).map(s -> s.t<caret>rim()).forEach(System.out::println);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// "Insert 'filter(Objects::nonNull)' step" "true"
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
class MyClass {
|
||||
@Nullable String convert(String s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
String accept(@NotNull String s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
void test(List<String> list, @Nullable String s1) {
|
||||
list.stream().map(this::convert).map(this:<caret>:accept).forEach(System.out::println);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// "Insert 'filter(Objects::nonNull)' step" "true"
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
class MyClass {
|
||||
@Nullable String convert(String s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
void test(List<String> list, @Nullable String s1) {
|
||||
list.stream().map(this::convert).map(String:<caret>:trim).forEach(System.out::println);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
// "Insert 'filter(Objects::nonNull)' step" "true"
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
class MyClass {
|
||||
@Nullable String convert(String s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
String accept(@NotNull String s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
String execute(Supplier<String> op) {
|
||||
return op.get();
|
||||
}
|
||||
|
||||
void test(List<String> list, @Nullable String s1) {
|
||||
list.stream().map(this::convert).map(s -> execute(() -> accept(s<caret>))).forEach(System.out::println);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// "Insert 'filter(Objects::nonNull)' step" "false"
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
class MyClass {
|
||||
interface MyFunction {
|
||||
String apply(@Nullable String s);
|
||||
}
|
||||
|
||||
void doSmth(MyFunction fn) {}
|
||||
|
||||
void test(List<String> list, @Nullable String s1) {
|
||||
this.doSmth(s -> s.t<caret>rim());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.intellij.java.codeInsight.daemon.quickFix;
|
||||
|
||||
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
|
||||
import com.intellij.codeInspection.LocalInspectionTool;
|
||||
import com.intellij.codeInspection.dataFlow.DataFlowInspection;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.testFramework.IdeaTestUtil;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.testFramework.PsiTestUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class StreamFilterNotNullFixTest extends LightQuickFixParameterizedTestCase {
|
||||
private static final LightProjectDescriptor DESCRIPTOR = new LightProjectDescriptor() {
|
||||
@Nullable
|
||||
@Override
|
||||
public Sdk getSdk() {
|
||||
return PsiTestUtil.addJdkAnnotations(IdeaTestUtil.getMockJdk18());
|
||||
}
|
||||
};
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected LocalInspectionTool[] configureLocalInspectionTools() {
|
||||
return new LocalInspectionTool[]{new DataFlowInspection()};
|
||||
}
|
||||
|
||||
public void test() throws Exception {
|
||||
doAllTests();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected LightProjectDescriptor getProjectDescriptor() {
|
||||
return DESCRIPTOR;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return "/codeInsight/daemonCodeAnalyzer/quickFix/streamFilterNotNull";
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,7 @@ inspection.data.flow.true.asserts.option=Don't report assertions with condition
|
||||
inspection.data.flow.redundant.instanceof.quickfix=Replace with != null
|
||||
inspection.data.flow.simplify.boolean.expression.quickfix=Simplify boolean expression
|
||||
inspection.data.flow.simplify.to.assignment.quickfix.name=Simplify to normal assignment
|
||||
inspection.data.flow.filter.notnull.quickfix=Insert 'filter(Objects::nonNull)' step
|
||||
configure.annotations.option=Configure annotations
|
||||
configure.checker.option.button=Configure Assert/Check Methods
|
||||
configure.checker.option.main.dialog.title=Assert/Check Method Configuration
|
||||
|
||||
Reference in New Issue
Block a user