StaticPseudoFunctionalStyleMethodInspection: more clear options, validation of obtained result, part of problems with type parameters are fixed

This commit is contained in:
Dmitry Batkovich
2015-07-01 14:51:30 +03:00
parent e80642a796
commit 507388b7a5
20 changed files with 1152 additions and 406 deletions
@@ -25,6 +25,7 @@ import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.refactoring.util.RefactoringChangeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class AddTypeArgumentsFix extends MethodArgumentFix {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.quickfix.AddTypeArgumentsFix");
@@ -51,57 +52,7 @@ public class AddTypeArgumentsFix extends MethodArgumentFix {
@Override
protected PsiExpression getModifiedArgument(final PsiExpression expression, final PsiType toType) throws IncorrectOperationException {
if (!PsiUtil.isLanguageLevel5OrHigher(expression)) return null;
if (expression instanceof PsiMethodCallExpression) {
final PsiMethodCallExpression methodCall = (PsiMethodCallExpression)expression;
final PsiReferenceParameterList list = methodCall.getMethodExpression().getParameterList();
if (list == null || list.getTypeArguments().length > 0) return null;
final JavaResolveResult resolveResult = methodCall.resolveMethodGenerics();
final PsiElement element = resolveResult.getElement();
if (element instanceof PsiMethod) {
final PsiMethod method = (PsiMethod)element;
final PsiType returnType = method.getReturnType();
if (returnType == null) return null;
final PsiTypeParameter[] typeParameters = method.getTypeParameters();
if (typeParameters.length > 0) {
PsiType[] mappings = PsiType.createArray(typeParameters.length);
PsiResolveHelper helper = JavaPsiFacade.getInstance(expression.getProject()).getResolveHelper();
LanguageLevel level = PsiUtil.getLanguageLevel(expression);
for (int i = 0; i < typeParameters.length; i++) {
PsiTypeParameter typeParameter = typeParameters[i];
final PsiType substitution = helper.getSubstitutionForTypeParameter(typeParameter, returnType, toType, false, level);
if (substitution == null || PsiType.NULL.equals(substitution)) return null;
mappings[i] = GenericsUtil.eliminateWildcards(substitution, false);
}
final PsiElementFactory factory = JavaPsiFacade.getInstance(expression.getProject()).getElementFactory();
PsiMethodCallExpression copy = (PsiMethodCallExpression)expression.copy();
final PsiReferenceExpression methodExpression = copy.getMethodExpression();
final PsiReferenceParameterList parameterList = methodExpression.getParameterList();
LOG.assertTrue(parameterList != null);
for (PsiType mapping : mappings) {
parameterList.add(factory.createTypeElement(mapping));
}
if (methodExpression.getQualifierExpression() == null) {
final PsiExpression qualifierExpression;
final PsiClass containingClass = method.getContainingClass();
LOG.assertTrue(containingClass != null);
if (method.hasModifierProperty(PsiModifier.STATIC)) {
qualifierExpression = factory.createReferenceExpression(containingClass);
} else {
qualifierExpression = RefactoringChangeUtil.createThisExpression(method.getManager(), null);
}
methodExpression.setQualifierExpression(qualifierExpression);
}
return (PsiExpression)JavaCodeStyleManager.getInstance(copy.getProject()).shortenClassReferences(copy);
}
}
}
return null;
return addTypeArguments(expression, toType);
}
@Override
@@ -110,5 +61,59 @@ public class AddTypeArgumentsFix extends MethodArgumentFix {
}
}
@Nullable
public static PsiExpression addTypeArguments(PsiExpression expression, PsiType toType) {
if (!PsiUtil.isLanguageLevel5OrHigher(expression)) return null;
if (expression instanceof PsiMethodCallExpression) {
final PsiMethodCallExpression methodCall = (PsiMethodCallExpression)expression;
final PsiReferenceParameterList list = methodCall.getMethodExpression().getParameterList();
if (list == null || list.getTypeArguments().length > 0) return null;
final JavaResolveResult resolveResult = methodCall.resolveMethodGenerics();
final PsiElement element = resolveResult.getElement();
if (element instanceof PsiMethod) {
final PsiMethod method = (PsiMethod)element;
final PsiType returnType = method.getReturnType();
if (returnType == null) return null;
final PsiTypeParameter[] typeParameters = method.getTypeParameters();
if (typeParameters.length > 0) {
PsiType[] mappings = PsiType.createArray(typeParameters.length);
PsiResolveHelper helper = JavaPsiFacade.getInstance(expression.getProject()).getResolveHelper();
LanguageLevel level = PsiUtil.getLanguageLevel(expression);
for (int i = 0; i < typeParameters.length; i++) {
PsiTypeParameter typeParameter = typeParameters[i];
final PsiType substitution = helper.getSubstitutionForTypeParameter(typeParameter, returnType, toType, false, level);
if (substitution == null || PsiType.NULL.equals(substitution)) return null;
mappings[i] = GenericsUtil.eliminateWildcards(substitution, false);
}
final PsiElementFactory factory = JavaPsiFacade.getInstance(expression.getProject()).getElementFactory();
PsiMethodCallExpression copy = (PsiMethodCallExpression)expression.copy();
final PsiReferenceExpression methodExpression = copy.getMethodExpression();
final PsiReferenceParameterList parameterList = methodExpression.getParameterList();
LOG.assertTrue(parameterList != null);
for (PsiType mapping : mappings) {
parameterList.add(factory.createTypeElement(mapping));
}
if (methodExpression.getQualifierExpression() == null) {
final PsiExpression qualifierExpression;
final PsiClass containingClass = method.getContainingClass();
LOG.assertTrue(containingClass != null);
if (method.hasModifierProperty(PsiModifier.STATIC)) {
qualifierExpression = factory.createReferenceExpression(containingClass);
} else {
qualifierExpression = RefactoringChangeUtil.createThisExpression(method.getManager(), null);
}
methodExpression.setQualifierExpression(qualifierExpression);
}
return (PsiExpression)JavaCodeStyleManager.getInstance(copy.getProject()).shortenClassReferences(copy);
}
}
}
return null;
}
public static ArgumentFixerActionFactory REGISTRAR = new AddTypeArgumentsFix.MyFixerActionFactory();
}
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.codeInspection.java18StreamApi.AddMethodsDialog">
<grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="4" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="500" height="400"/>
@@ -8,53 +8,103 @@
<properties/>
<border type="none"/>
<children>
<component id="5c4b9" class="com.intellij.ui.components.JBLabel">
<grid id="8e5de" layout-manager="GridLayoutManager" row-count="3" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="4" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Fully qualified class name:"/>
</properties>
</component>
<vspacer id="c8137">
<constraints>
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<component id="c43e4" class="com.intellij.ui.components.JBLabel">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="4" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Method name:"/>
</properties>
</component>
<component id="f01fe" class="com.intellij.ui.components.JBLabel">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Stream API replacement:"/>
</properties>
</component>
<component id="1f7b6" class="com.intellij.openapi.ui.ComboBox" binding="myPatternsCombo">
<constraints>
<grid row="2" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="1" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<component id="7bc3f" class="com.intellij.refactoring.ui.ClassNameReferenceEditor" binding="myClassNameEditor" custom-create="true" default-binding="true">
<border type="none"/>
<children>
<component id="5c4b9" class="com.intellij.ui.components.JBLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="4" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Fully qualified class name:"/>
</properties>
</component>
<component id="c43e4" class="com.intellij.ui.components.JBLabel">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="4" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Method name:"/>
</properties>
</component>
<component id="f01fe" class="com.intellij.ui.components.JBLabel">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Stream API replacement:"/>
</properties>
</component>
<component id="1f7b6" class="com.intellij.openapi.ui.ComboBox" binding="myTemplatesCombo">
<constraints>
<grid row="2" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<component id="f7881" class="com.intellij.openapi.ui.ComboBox" binding="myMethodNameCombo">
<constraints>
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="0" fill="1" indent="0" use-parent-layout="false">
<minimum-size width="200" height="-1"/>
</grid>
</constraints>
<properties/>
</component>
<component id="9c747" class="com.intellij.refactoring.ui.ClassNameReferenceEditor" binding="myClassNameEditor" custom-create="true">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
</children>
</grid>
<grid id="6dced" binding="myExamplePanel" layout-manager="GridLayoutManager" row-count="4" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="7" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<component id="f7881" class="com.intellij.openapi.ui.ComboBox" binding="myMethodNameCombo">
<constraints>
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<border type="none"/>
<children>
<component id="8c2dc" class="com.intellij.codeInsight.intention.impl.config.ActionUsagePanel" binding="myBeforeActionPanel">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
<minimum-size width="-1" height="50"/>
</grid>
</constraints>
<properties/>
</component>
<component id="e9aff" class="com.intellij.codeInsight.intention.impl.config.ActionUsagePanel" binding="myAfterActionPanel">
<constraints>
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
<minimum-size width="-1" height="50"/>
</grid>
</constraints>
<properties/>
</component>
<component id="cc542" class="com.intellij.ui.TitledSeparator">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Before quick fix:"/>
</properties>
</component>
<component id="a9a06" class="com.intellij.ui.TitledSeparator">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<opaque value="false"/>
<text value="After quick fix:"/>
</properties>
</component>
</children>
</grid>
</children>
</grid>
</form>
@@ -16,27 +16,36 @@
package com.intellij.codeInspection.java18StreamApi;
import com.intellij.codeInsight.intention.impl.config.ActionUsagePanel;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.event.DocumentAdapter;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiModifier;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.refactoring.ui.ClassNameReferenceEditor;
import com.intellij.ui.CollectionComboBoxModel;
import com.intellij.ui.ColoredListCellRenderer;
import com.intellij.ui.ListCellRendererWrapper;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.*;
import java.util.List;
/**
* @author Dmitry Batkovich
@@ -46,36 +55,94 @@ public class AddMethodsDialog extends DialogWrapper {
@NotNull private final Project myProject;
private JPanel myPanel;
private ComboBox myPatternsCombo;
private ComboBox myTemplatesCombo;
private ClassNameReferenceEditor myClassNameEditor;
private ComboBox myMethodNameCombo;
private ActionUsagePanel myBeforeActionPanel;
private ActionUsagePanel myAfterActionPanel;
private JPanel myExamplePanel;
@SuppressWarnings("unchecked")
protected AddMethodsDialog(@NotNull final Project project, @NotNull final Component parent, boolean canBeParent) {
super(parent, canBeParent);
myProject = project;
final DefaultComboBoxModel model = new DefaultComboBoxModel();
myPatternsCombo.setModel(model);
for (String methodName : StreamApiConstants.STREAM_STREAM_API_METHODS.getValue()) {
model.addElement(methodName);
}
model.addElement(StreamApiConstants.FAKE_FIND_MATCHED);
myPatternsCombo.setRenderer(new ColoredListCellRenderer<String>() {
myTemplatesCombo.setEnabled(false);
myTemplatesCombo.setRenderer(new ColoredListCellRenderer<PseudoLambdaReplaceTemplate>() {
@Override
protected void customizeCellRenderer(JList list, String methodName, int index, boolean selected, boolean hasFocus) {
protected void customizeCellRenderer(JList list,
PseudoLambdaReplaceTemplate template,
int index,
boolean selected,
boolean hasFocus) {
if (template == null) {
return;
}
append("stream.");
if (StreamApiConstants.STREAM_STREAM_API_METHODS.getValue().contains(methodName)) {
append(methodName + "()", SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
final String streamApiMethodName = template.getStreamApiMethodName();
if (StreamApiConstants.STREAM_STREAM_API_METHODS.getValue().contains(streamApiMethodName)) {
append(streamApiMethodName + "()", SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
}
else {
LOG.assertTrue(StreamApiConstants.FAKE_FIND_MATCHED.equals(methodName));
LOG.assertTrue(StreamApiConstants.FAKE_FIND_MATCHED.equals(streamApiMethodName));
append(String.format(StreamApiConstants.FAKE_FIND_MATCHED_PATTERN, "condition"), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
append(" or ");
append(String.format(StreamApiConstants.FAKE_FIND_MATCHED_WITH_DEFAULT_PATTERN, "condition", "defaultValue"), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
append(".orElseGet(() -> defaultValue)", SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
}
}
});
myTemplatesCombo.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
final PseudoLambdaReplaceTemplate template = (PseudoLambdaReplaceTemplate)e.getItem();
final Collection<PsiMethod> methods = (Collection<PsiMethod>)myMethodNameCombo.getSelectedItem();
if (methods == null) {
return;
}
for (PsiMethod method : methods) {
if (template.validate(method) != null) {
showTemplateExample(template, method);
break;
}
}
}
});
myMethodNameCombo.setModel(new DefaultComboBoxModel());
myMethodNameCombo.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (!myExamplePanel.isEnabled()) {
myExamplePanel.setEnabled(true);
}
final Collection<PseudoLambdaReplaceTemplate> suitableTemplates = new LinkedHashSet<PseudoLambdaReplaceTemplate>();
final Collection<PsiMethod> methods = (Collection<PsiMethod>) e.getItem();
for (PseudoLambdaReplaceTemplate template : PseudoLambdaReplaceTemplate.getAllTemplates()) {
for (PsiMethod method : methods) {
if (template.validate(method) != null) {
if (suitableTemplates.isEmpty()) {
showTemplateExample(template, method);
}
suitableTemplates.add(template);
}
}
}
if (!myTemplatesCombo.isEnabled()) {
myTemplatesCombo.setEnabled(true);
}
LOG.assertTrue(!suitableTemplates.isEmpty());
final List<PseudoLambdaReplaceTemplate> templatesAsList = new ArrayList<PseudoLambdaReplaceTemplate>(suitableTemplates);
myTemplatesCombo.setModel(new CollectionComboBoxModel(templatesAsList));
myTemplatesCombo.setSelectedItem(templatesAsList.get(0));
}
});
myMethodNameCombo.setRenderer(new ListCellRendererWrapper<Collection<PsiMethod>>() {
@Override
public void customize(JList list, Collection<PsiMethod> methods, int index, boolean selected, boolean hasFocus) {
if (methods != null) {
LOG.assertTrue(!methods.isEmpty());
setText(ContainerUtil.getFirstItem(methods).getName());
}
}
});
myClassNameEditor.addDocumentListener(new DocumentAdapter() {
@Override
public void documentChanged(DocumentEvent e) {
@@ -87,34 +154,79 @@ public class AddMethodsDialog extends DialogWrapper {
myMethodNameCombo.setEnabled(false);
}
else {
for (String name : ContainerUtil.newTreeSet(ContainerUtil.mapNotNull(aClass.getMethods(), new Function<PsiMethod, String>() {
final List<PseudoLambdaReplaceTemplate> possibleTemplates = PseudoLambdaReplaceTemplate.getAllTemplates();
final MultiMap<String, PsiMethod> nameToMethod = new MultiMap<String, PsiMethod>();
for (PsiMethod m : ContainerUtil.filter(aClass.getMethods(), new Condition<PsiMethod>() {
@Override
public String fun(PsiMethod method) {
public boolean value(PsiMethod method) {
if (method.isConstructor() ||
!method.hasModifierProperty(PsiModifier.STATIC) ||
method.hasModifierProperty(PsiModifier.PRIVATE)) {
return null;
return false;
}
return method.getName();
boolean templateFound = false;
for (PseudoLambdaReplaceTemplate template : possibleTemplates) {
if (template.validate(method) != null) {
templateFound = true;
}
}
if (!templateFound) {
return false;
}
return true;
}
}))) {
comboBoxModel.addElement(name);
})) {
nameToMethod.putValue(m.getName(), m);
}
for (Map.Entry<String, Collection<PsiMethod>> entry : nameToMethod.entrySet()) {
comboBoxModel.addElement(entry.getValue());
}
myMethodNameCombo.setEnabled(true);
}
}
});
myExamplePanel.setEnabled(false);
init();
}
private void showTemplateExample(final PseudoLambdaReplaceTemplate template, final PsiMethod method) {
final PsiClass aClass = method.getContainingClass();
LOG.assertTrue(aClass != null);
final String fqn = aClass.getQualifiedName();
LOG.assertTrue(fqn != null);
final String parameters =
StringUtil.join(ContainerUtil.map(method.getParameterList().getParameters(), new Function<PsiParameter, String>() {
@Override
public String fun(PsiParameter parameter) {
return parameter.getName();
}
}), ", ");
final String expressionText = fqn + "." + method.getName() + "(" + parameters + ")";
final PsiExpression psiExpression = JavaPsiFacade.getElementFactory(method.getProject())
.createExpressionFromText(expressionText, null);
LOG.assertTrue(psiExpression instanceof PsiMethodCallExpression);
final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)psiExpression;
template.convertToStream(methodCallExpression, method, false);
myBeforeActionPanel.reset("void example() {\n <spot>" + methodCallExpression.getText() + "</spot>;\n}", StdFileTypes.JAVA);
myAfterActionPanel.reset("void example() {\n <spot>" + template.convertToStream(methodCallExpression, method, true).getText() + "</spot>\n}", StdFileTypes.JAVA);
}
@Override
protected void dispose() {
Disposer.dispose(myBeforeActionPanel);
Disposer.dispose(myAfterActionPanel);
super.dispose();
}
private void createUIComponents() {
myClassNameEditor = new ClassNameReferenceEditor(myProject, null);
}
public StaticPseudoFunctionalStyleMethodOptions.PipelineElement getSelectedElement() {
return new StaticPseudoFunctionalStyleMethodOptions.PipelineElement(myClassNameEditor.getText(),
(String)myMethodNameCombo.getSelectedItem(),
(String)myPatternsCombo.getSelectedItem());
ContainerUtil.getFirstItem((Collection < PsiMethod >)myMethodNameCombo.getSelectedItem()).getName(),
(PseudoLambdaReplaceTemplate)myTemplatesCombo.getSelectedItem());
}
@Nullable
@@ -0,0 +1,629 @@
/*
* Copyright 2000-2015 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.java18StreamApi;
import com.intellij.codeInsight.daemon.impl.quickfix.AddTypeArgumentsFix;
import com.intellij.codeInspection.AnonymousCanBeLambdaInspection;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.impl.PsiDiamondTypeUtil;
import com.intellij.psi.impl.PsiSubstitutorImpl;
import com.intellij.psi.impl.source.PsiClassReferenceType;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.*;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.hash.HashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* @author Dmitry Batkovich
*/
class PseudoLambdaReplaceTemplate {
private final static Logger LOG = Logger.getInstance(PseudoLambdaReplaceTemplate.class);
public enum LambdaRole {
PREDICATE,
FUNCTION
}
public static class ValidationInfo {
private final int myLambdaPosition;
private final int myIterablePosition;
private final int myDefaultValuePosition;
public ValidationInfo(int lambdaPosition, int iterablePosition, int defaultValuePosition) {
myLambdaPosition = lambdaPosition;
myIterablePosition = iterablePosition;
myDefaultValuePosition = defaultValuePosition;
}
public int getLambdaPosition() {
return myLambdaPosition;
}
public int getIterablePosition() {
return myIterablePosition;
}
public int getDefaultValuePosition() {
return myDefaultValuePosition;
}
}
static final PseudoLambdaReplaceTemplate MAP =
new PseudoLambdaReplaceTemplate(StreamApiConstants.MAP, LambdaRole.FUNCTION, false);
static final PseudoLambdaReplaceTemplate FILTER =
new PseudoLambdaReplaceTemplate(StreamApiConstants.FILTER, LambdaRole.PREDICATE, false);
static final PseudoLambdaReplaceTemplate FIND =
new PseudoLambdaReplaceTemplate(StreamApiConstants.FAKE_FIND_MATCHED, LambdaRole.PREDICATE, true);
static final PseudoLambdaReplaceTemplate ALL_MATCH =
new PseudoLambdaReplaceTemplate(StreamApiConstants.ALL_MATCH, LambdaRole.PREDICATE, false);
static final PseudoLambdaReplaceTemplate ANY_MATCH =
new PseudoLambdaReplaceTemplate(StreamApiConstants.ANY_MATCH, LambdaRole.PREDICATE, false);
private final String myStreamApiMethodName;
private final LambdaRole myLambdaRole;
private final boolean myAcceptDefaultValue;
public PseudoLambdaReplaceTemplate(String method,
LambdaRole type,
boolean acceptDefaultValue) {
myStreamApiMethodName = method;
myLambdaRole = type;
myAcceptDefaultValue = acceptDefaultValue;
}
public static List<PseudoLambdaReplaceTemplate> getAllTemplates() {
return ContainerUtil.newArrayList(MAP, FILTER, FIND, ALL_MATCH, ANY_MATCH);
}
public ValidationInfo validate(final PsiMethod method) {
final PsiParameter[] parameters = method.getParameterList().getParameters();
final PsiType[] parameterTypes =
ContainerUtil.map2Array(Arrays.asList(parameters), new PsiType[parameters.length], new Function<PsiParameter, PsiType>() {
@Override
public PsiType fun(PsiParameter parameter) {
return parameter.getType();
}
});
final PsiType returnType = method.getReturnType();
if (returnType instanceof PsiClassType) {
final PsiClass resolvedReturnTypeClass = ((PsiClassType)returnType).resolve();
if (!InheritanceUtil.isInheritor(resolvedReturnTypeClass, CommonClassNames.JAVA_LANG_ITERABLE)) {
return null;
}
} else if (!(returnType instanceof PsiArrayType)) {
return null;
}
return validate(parameterTypes, returnType, method);
}
@Nullable
public ValidationInfo validate(final PsiMethodCallExpression expression) {
final PsiType[] argumentTypes = expression.getArgumentList().getExpressionTypes();
final PsiType methodReturnType = expression.getType();
if (methodReturnType == null) {
return null;
}
final PsiMethod method = expression.resolveMethod();
if (method == null) {
return null;
}
final PsiParameter[] expectedParameters = method.getParameterList().getParameters();
if (argumentTypes.length != expectedParameters.length) {
return null;
}
return validate(argumentTypes, methodReturnType, expression);
}
public String getStreamApiMethodName() {
return myStreamApiMethodName;
}
public LambdaRole getLambdaRole() {
return myLambdaRole;
}
public boolean isAcceptDefaultValue() {
return myAcceptDefaultValue;
}
private ValidationInfo validate(final PsiType[] arguments, final PsiType methodReturnType, final PsiElement context) {
int lambdaPosition = -1;
int defaultValuePosition = -1;
int iterablePosition = -1;
if (!myAcceptDefaultValue) {
if (arguments.length != 2) {
return null;
}
} else {
if (arguments.length != 2 && arguments.length != 3) {
return null;
}
}
for (int i = 0; i < arguments.length; i++) {
PsiType type = arguments[i];
if (isFunction(type, methodReturnType, context)) {
if (lambdaPosition == -1) {
lambdaPosition = i;
continue;
}
else {
return null;
}
}
if (isIterableOrArray(type)) {
if (iterablePosition == -1) {
iterablePosition = i;
continue;
}
else {
return null;
}
}
if (myAcceptDefaultValue && methodReturnType.isAssignableFrom(type)) {
if (defaultValuePosition == -1) {
defaultValuePosition = i;
}
else {
return null;
}
}
}
if (lambdaPosition == -1 || iterablePosition == -1) {
return null;
}
if (myAcceptDefaultValue) {
if (defaultValuePosition == -1 && arguments.length == 3) {
return null;
}
}
return new ValidationInfo(lambdaPosition, iterablePosition, defaultValuePosition);
}
private boolean isFunction(PsiType type, PsiType baseMethodReturnType, PsiElement context) {
if (type instanceof PsiMethodReferenceType) {
final PsiMethodReferenceExpression expression = ((PsiMethodReferenceType)type).getExpression();
final PsiMethod resolvedMethod = (PsiMethod)expression.resolve();
if (resolvedMethod == null) {
return false;
}
final PsiParameter[] parameters = resolvedMethod.getParameterList().getParameters();
if ((parameters.length != 1 && myLambdaRole == LambdaRole.FUNCTION) || (parameters.length != 0 && myLambdaRole == LambdaRole.PREDICATE)) {
return false;
}
final PsiType returnType = LambdaUtil.getFunctionalInterfaceReturnType(expression);
return isSuitableLambdaRole(returnType, baseMethodReturnType, context);
} else if (type instanceof PsiLambdaExpressionType) {
final PsiLambdaExpression expression = ((PsiLambdaExpressionType)type).getExpression();
final PsiType psiType = LambdaUtil.getFunctionalInterfaceReturnType(expression.getFunctionalInterfaceType());
return isSuitableLambdaRole(psiType, baseMethodReturnType, context);
} else if (isSuitableFunctionalType(type, baseMethodReturnType, context)) {
return true;
}
return isJavaLangClassType(type) && myLambdaRole == LambdaRole.PREDICATE;
}
private boolean isSuitableLambdaRole(final PsiType lambdaReturnType,
final PsiType baseMethodReturnType,
final PsiElement context) {
if (lambdaReturnType == null) {
return false;
}
if (myLambdaRole == LambdaRole.PREDICATE) {
final PsiClassType boxedBoolean = PsiType.BOOLEAN.getBoxedType(context);
if (!(PsiType.BOOLEAN.equals(lambdaReturnType) || (boxedBoolean != null && boxedBoolean.equals(lambdaReturnType)))) {
return false;
}
}
else {
LOG.assertTrue(myLambdaRole == LambdaRole.FUNCTION);
if (baseMethodReturnType instanceof PsiClassType) {
final PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)baseMethodReturnType).resolveGenerics();
final Map<PsiTypeParameter, PsiType> substitutionMap = resolveResult.getSubstitutor().getSubstitutionMap();
if (substitutionMap.size() != 1) {
return false;
}
final PsiType iterableParametrizedType = ContainerUtil.getFirstItem(substitutionMap.values());
if (!lambdaReturnType.equals(iterableParametrizedType)) {
return false;
}
}
else if (baseMethodReturnType instanceof PsiArrayType) {
if (!lambdaReturnType.equals(((PsiArrayType)baseMethodReturnType).getComponentType())) {
return false;
}
}
}
return true;
}
private static boolean isJavaLangClassType(PsiType type) {
if (type instanceof PsiClassType) {
final PsiClass resolvedClass = ((PsiClassType)type).resolve();
if (resolvedClass != null && CommonClassNames.JAVA_LANG_CLASS.equals(resolvedClass.getQualifiedName())) {
return true;
}
}
return false;
}
private boolean isSuitableFunctionalType(final PsiType type, final PsiType baseMethodReturnType, final PsiElement context) {
if (type instanceof PsiClassType) {
PsiClass targetClass = ((PsiClassType)type).resolve();
if (targetClass != null) {
if (targetClass instanceof PsiAnonymousClass) {
targetClass = ((PsiAnonymousClass)targetClass).getBaseClassType().resolve();
if (targetClass == null) {
return false;
}
}
if (!LambdaUtil.isFunctionalClass(targetClass)) {
return false;
}
PsiMethod method = LambdaUtil.getFunctionalInterfaceMethod(targetClass);
final PsiMethod[] methods = ((PsiClassType)type).resolve().findMethodsByName(method.getName(), false);
if (methods.length != 1) {
return false;
}
method = methods[0];
if (method == null) {
return false;
}
final PsiType psiType;
if (type instanceof PsiClassReferenceType) {
final PsiJavaCodeReferenceElement reference = ((PsiClassReferenceType)type).getReference();
final PsiClass resolvedClass = ((PsiClassReferenceType)type).resolve();
final PsiTypeParameter[] typeParameters = resolvedClass.getTypeParameters();
final PsiType[] substitutedTypeParameters = reference.getTypeParameters();
LOG.assertTrue(typeParameters.length == substitutedTypeParameters.length);
final Map<PsiTypeParameter, PsiType> substitutionMap = new HashMap<PsiTypeParameter, PsiType>();
for (int i = 0; i < typeParameters.length; i++) {
PsiTypeParameter parameter = typeParameters[i];
PsiType t = substitutedTypeParameters[i];
substitutionMap.put(parameter, t);
}
PsiSubstitutor substitutor = PsiSubstitutorImpl.createSubstitutor(substitutionMap);
psiType = substitutor.substitute(method.getReturnType());
} else {
psiType = method.getReturnType();
}
return isSuitableLambdaRole(psiType, baseMethodReturnType, context);
}
return false;
} else {
return false;
}
}
@NotNull
public PsiExpression convertToStream(final PsiMethodCallExpression expression, PsiMethod method, boolean force) {
LOG.assertTrue(expression != null);
if (method == null) {
method = expression.resolveMethod();
if (method == null) {
return expression;
}
}
final ValidationInfo validationInfo = force ? validate(method) : validate(expression);
if (validationInfo == null) {
return expression;
}
final Project project = expression.getProject();
int lambdaIndex = validationInfo.getLambdaPosition();
final PsiExpression[] expressions = expression.getArgumentList().getExpressions();
PsiExpression lambdaExpression = expressions[lambdaIndex];
if (!force) {
lambdaExpression = convertClassTypeExpression(lambdaExpression);
lambdaExpression = convertToJavaLambda(lambdaExpression, myStreamApiMethodName);
}
LOG.assertTrue(lambdaExpression != null);
final PsiExpression iterableExpression = expressions[validationInfo.getIterablePosition()];
final String pipelineHead = createPipelineHeadText(iterableExpression, lambdaExpression, force);
final String lambdaExpressionText;
final String elementText;
if (!StreamApiConstants.FAKE_FIND_MATCHED.equals(myStreamApiMethodName)) {
elementText = myStreamApiMethodName;
lambdaExpressionText = lambdaExpression.getText();
}
else {
elementText = validationInfo.getDefaultValuePosition() != -1
? String.format(StreamApiConstants.FAKE_FIND_MATCHED_WITH_DEFAULT_PATTERN, lambdaExpression.getText(),
expressions[validationInfo.getDefaultValuePosition()].getText())
: String.format(StreamApiConstants.FAKE_FIND_MATCHED_PATTERN, lambdaExpression.getText());
lambdaExpressionText = null;
}
final String pipelineTail =
StreamApiConstants.STREAM_STREAM_API_METHODS.getValue().contains(myStreamApiMethodName)
? findSuitableTailMethodForCollection(method)
: null;
final PsiElement replaced =
expression.replace(createPipelineExpression(pipelineHead, elementText, lambdaExpressionText, pipelineTail, project));
JavaCodeStyleManager.getInstance(project).shortenClassReferences(replaced.getParent());
return (PsiExpression)replaced;
}
private static PsiExpression createPipelineExpression(String pipelineHead,
String elementText,
String lambdaExpression,
String pipelineTail,
Project project) {
final StringBuilder sb = new StringBuilder();
sb.append(pipelineHead).append(".").append(elementText);
if (lambdaExpression != null) {
sb.append("(").append(lambdaExpression).append(")");
}
if (pipelineTail != null) {
sb.append(".").append(pipelineTail);
}
return JavaPsiFacade.getElementFactory(project).createExpressionFromText(sb.toString(), null);
}
private static void convertNewExpression(PsiMethod containingMethod, PsiNewExpression newExpression, PsiClass expectedReturnClass) {
final String expectedReturnQName = expectedReturnClass.getQualifiedName();
LOG.assertTrue(expectedReturnQName != null);
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(newExpression.getProject());
PsiAnonymousClass anonymousClass = PsiTreeUtil.findChildOfType(newExpression, PsiAnonymousClass.class);
LOG.assertTrue(anonymousClass != null);
PsiJavaCodeReferenceElement referenceElement = PsiTreeUtil.findChildOfType(anonymousClass, PsiJavaCodeReferenceElement.class);
LOG.assertTrue(referenceElement != null);
final PsiReferenceParameterList parameterList = PsiTreeUtil.findChildOfType(referenceElement, PsiReferenceParameterList.class);
final PsiJavaCodeReferenceElement newCodeReferenceElement = factory.createReferenceFromText(expectedReturnClass.getQualifiedName()
+
(parameterList == null
? ""
: parameterList.getText()), null);
referenceElement.replace(newCodeReferenceElement);
final List<PsiMethod> methods = ContainerUtil.filter(anonymousClass.getMethods(), new Condition<PsiMethod>() {
@Override
public boolean value(PsiMethod method) {
return !"equals".equals(method.getName());
}
});
LOG.assertTrue(methods.size() == 1, methods);
final PsiMethod method = methods.get(0);
method.setName(expectedReturnClass.getMethods()[0].getName());
final PsiTypeElement element = containingMethod.getReturnTypeElement();
if (element != null) {
final PsiReferenceParameterList genericParameter = PsiTreeUtil.findChildOfType(element, PsiReferenceParameterList.class);
element.replace(
factory.createTypeElementFromText(expectedReturnQName + (genericParameter == null ? "" : genericParameter.getText()), null));
}
}
@Nullable
private static String findSuitableTailMethodForCollection(PsiMethod lambdaHandler) {
final PsiType type = lambdaHandler.getReturnType();
if (type instanceof PsiArrayType) {
final PsiType arrayComponentType = ((PsiArrayType)type).getComponentType();
return "toArray(" + arrayComponentType.getCanonicalText() + "[]::new)";
}
else if (type instanceof PsiClassType) {
final PsiClass resolvedClass = ((PsiClassType)type).resolve();
if (resolvedClass == null) {
return null;
}
final String qName = resolvedClass.getQualifiedName();
if (qName == null) {
return null;
}
if (qName.equals(CommonClassNames.JAVA_UTIL_LIST)
|| qName.equals(CommonClassNames.JAVA_UTIL_COLLECTION)
|| qName.equals(CommonClassNames.JAVA_LANG_ITERABLE)) {
return "collect(" + StreamApiConstants.JAVA_UTIL_STREAM_COLLECTORS + ".toList())";
}
else if (qName.equals(CommonClassNames.JAVA_UTIL_SET)) {
return "collect(" + StreamApiConstants.JAVA_UTIL_STREAM_COLLECTORS + ".toSet())";
}
else if (qName.equals(CommonClassNames.JAVA_UTIL_ITERATOR)) {
return "iterator()";
}
}
return null;
}
private static PsiExpression convertToJavaLambda(PsiExpression expression, String streamApiMethodName) {
if (streamApiMethodName.equals(StreamApiConstants.FAKE_FIND_MATCHED)) {
streamApiMethodName = StreamApiConstants.FILTER;
}
if (expression instanceof PsiMethodReferenceExpression) {
return expression;
}
if (expression instanceof PsiLambdaExpression) {
return expression;
}
if (expression instanceof PsiMethodCallExpression) {
final PsiMethod method = ((PsiMethodCallExpression)expression).resolveMethod();
if (method == null) {
return null;
}
final PsiType type = method.getReturnType();
if (!(type instanceof PsiClassType)) {
return null;
}
final PsiClass lambdaClass = ((PsiClassType)type).resolve();
if (lambdaClass == null) {
return null;
}
final String methodName = lambdaClass.getMethods()[0].getName();
if (tryConvertPseudoLambdaToStreamApi(method, resolveStreamApiLambdaClass(expression.getProject(), streamApiMethodName))) {
return expression;
}
else {
return JavaPsiFacade.getElementFactory(expression.getProject())
.createExpressionFromText(expression.getText() + "::" + methodName, null);
}
}
return AnonymousCanBeLambdaInspection.replacePsiElementWithLambda(expression, true);
}
@NotNull
private static PsiClass resolveStreamApiLambdaClass(Project project, String streamApiMethodName) {
final PsiClass javaUtilStream = JavaPsiFacade.getInstance(project)
.findClass(StreamApiConstants.JAVA_UTIL_STREAM_STREAM, GlobalSearchScope.notScope(GlobalSearchScope.projectScope(project)));
LOG.assertTrue(javaUtilStream != null);
final PsiMethod[] methods = javaUtilStream.findMethodsByName(streamApiMethodName, false);
LOG.assertTrue(methods.length == 1);
final PsiMethod method = methods[0];
final PsiParameter[] parameters = method.getParameterList().getParameters();
LOG.assertTrue(parameters.length == 1);
final PsiType type = parameters[0].getType();
LOG.assertTrue(type instanceof PsiClassType);
final PsiClass resolved = ((PsiClassType)type).resolve();
LOG.assertTrue(resolved != null);
return resolved;
}
private static boolean tryConvertPseudoLambdaToStreamApi(final @NotNull PsiMethod method, final @NotNull PsiClass expectedReturnClass) {
final PsiType currentReturnType = method.getReturnType();
if (!(currentReturnType instanceof PsiClassType)) {
LOG.error("pseudo-lambda return type must be class " + currentReturnType);
return true;
}
final PsiClass resolvedCurrentReturnType = ((PsiClassType)currentReturnType).resolve();
if (expectedReturnClass.getManager().areElementsEquivalent(expectedReturnClass, resolvedCurrentReturnType)) {
return true;
}
final PsiCodeBlock body = method.getBody();
Collection<PsiReturnStatement> returnStatements = PsiTreeUtil.findChildrenOfType(body, PsiReturnStatement.class);
returnStatements = ContainerUtil.filter(returnStatements, new Condition<PsiReturnStatement>() {
@Override
public boolean value(PsiReturnStatement statement) {
return PsiTreeUtil.getParentOfType(statement, PsiMethod.class) == method;
}
});
if (returnStatements.size() != 1) {
return false;
}
final PsiReturnStatement returnStatement = ContainerUtil.getFirstItem(returnStatements);
assert returnStatement != null;
final PsiExpression returnValue = returnStatement.getReturnValue();
if (returnValue instanceof PsiNewExpression) {
convertNewExpression(method, (PsiNewExpression)returnValue, expectedReturnClass);
return true;
}
else {
return false;
}
}
@NotNull
private static PsiExpression convertClassTypeExpression(PsiExpression expression) {
final PsiType type = expression.getType();
if (type instanceof PsiClassType) {
final PsiClass resolvedClass = ((PsiClassType)type).resolve();
if (resolvedClass != null && CommonClassNames.JAVA_LANG_CLASS.equals(resolvedClass.getQualifiedName())) {
return JavaPsiFacade.getElementFactory(expression.getProject())
.createExpressionFromText("(" + expression.getText() + ")::isInstance", null);
}
}
return expression;
}
private static String createPipelineHeadText(PsiExpression collectionExpression, PsiExpression lambdaExpression, boolean force) {
if (collectionExpression instanceof PsiNewExpression) {
final PsiDiamondType.DiamondInferenceResult diamondResolveResult =
PsiDiamondTypeImpl.resolveInferredTypesNoCheck((PsiNewExpression)collectionExpression, collectionExpression);
if (!diamondResolveResult.getInferredTypes().isEmpty()) {
collectionExpression = PsiDiamondTypeUtil.expandTopLevelDiamondsInside(collectionExpression);
}
}
else if (collectionExpression instanceof PsiMethodCallExpression) {
final PsiType currentType = collectionExpression.getType();
if (currentType == null) {
return null;
}
final PsiExpression copiedExpression = (PsiExpression) collectionExpression.copy();
final PsiType newType = copiedExpression.getType();
if (!currentType.equals(newType)) {
collectionExpression = AddTypeArgumentsFix.addTypeArguments(copiedExpression, currentType);
}
}
final PsiType type = collectionExpression.getType();
if (type instanceof PsiClassType) {
final PsiClass resolved = ((PsiClassType)type).resolve();
LOG.assertTrue(resolved != null && resolved.getQualifiedName() != null, type);
return collectionExpression.getText() + ".stream()";
}
else if (type instanceof PsiArrayType) {
return CommonClassNames.JAVA_UTIL_ARRAYS + ".stream(" + collectionExpression.getText() + ")";
} else if (force) {
return collectionExpression.getText() + ".stream()";
}
throw new AssertionError("type: " + type + " is unexpected for expression: " + collectionExpression.getText());
}
private static boolean isIterableOrArray(final PsiType type) {
if (type instanceof PsiClassType) {
final PsiClass resolvedClass = ((PsiClassType)type).resolve();
if (resolvedClass != null) {
return InheritanceUtil.isInheritor(resolvedClass, CommonClassNames.JAVA_LANG_ITERABLE);
}
}
else if (type instanceof PsiArrayType) {
return true;
}
return false;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PseudoLambdaReplaceTemplate template = (PseudoLambdaReplaceTemplate)o;
if (myAcceptDefaultValue != template.myAcceptDefaultValue) return false;
if (!myStreamApiMethodName.equals(template.myStreamApiMethodName)) return false;
if (myLambdaRole != template.myLambdaRole) return false;
return true;
}
@Override
public int hashCode() {
int result = myStreamApiMethodName.hashCode();
result = 31 * result + myLambdaRole.hashCode();
result = 31 * result + (myAcceptDefaultValue ? 1 : 0);
return result;
}
@Override
public String toString() {
return "PseudoLambdaReplaceTemplate{" +
"myStreamApiMethodName='" + myStreamApiMethodName + '\'' +
'}';
}
}
@@ -18,16 +18,12 @@ package com.intellij.codeInspection.java18StreamApi;
import com.intellij.codeInspection.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jdom.Element;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
@@ -35,7 +31,6 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.Collection;
import java.util.List;
/**
* @author Dmitry Batkovich
@@ -102,10 +97,9 @@ public class StaticPseudoFunctionalStyleMethodInspection extends BaseJavaBatchLo
if (suitableHandler == null) {
return;
}
final int lambdaIndex = validateMethodParameters(methodCallExpression, method, suitableHandler.getStreamApiMethodName() == StreamApiConstants.FAKE_FIND_MATCHED);
if (lambdaIndex != -1) {
holder.registerProblem(methodCallExpression.getMethodExpression(), "",
new ReplacePseudoLambdaWithLambda(lambdaIndex, methodCallExpression, method, suitableHandler));
final PseudoLambdaReplaceTemplate.ValidationInfo validationInfo = suitableHandler.getTemplate().validate(methodCallExpression);
if (validationInfo != null) {
holder.registerProblem(methodCallExpression.getMethodExpression(), "Pseudo functional style code", new ReplacePseudoLambdaWithLambda(suitableHandler));
}
}
}
@@ -113,17 +107,10 @@ public class StaticPseudoFunctionalStyleMethodInspection extends BaseJavaBatchLo
}
public static class ReplacePseudoLambdaWithLambda implements LocalQuickFix {
private final int myLambdaIndex;
private final SmartPsiElementPointer<PsiMethod> myMethodPointer;
private final StaticPseudoFunctionalStyleMethodOptions.PipelineElement mySuitableHandler;
private final StaticPseudoFunctionalStyleMethodOptions.PipelineElement myHandler;
private ReplacePseudoLambdaWithLambda(int lambdaIndex,
@NotNull PsiMethodCallExpression expression,
@NotNull PsiMethod method,
@NotNull StaticPseudoFunctionalStyleMethodOptions.PipelineElement suitableHandler) {
myLambdaIndex = lambdaIndex;
myMethodPointer = SmartPointerManager.getInstance(expression.getProject()).createSmartPsiElementPointer(method);
mySuitableHandler = suitableHandler;
private ReplacePseudoLambdaWithLambda(StaticPseudoFunctionalStyleMethodOptions.PipelineElement handler) {
myHandler = handler;
}
@Nls
@@ -143,253 +130,7 @@ public class StaticPseudoFunctionalStyleMethodInspection extends BaseJavaBatchLo
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
PsiMethodCallExpression expression = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PsiMethodCallExpression.class);
LOG.assertTrue(expression != null);
final PsiExpression[] expressions = expression.getArgumentList().getExpressions();
PsiExpression lambdaExpression = expressions[myLambdaIndex];
lambdaExpression = convertClassTypeExpression(lambdaExpression);
lambdaExpression = convertToJavaLambda(lambdaExpression, mySuitableHandler.getStreamApiMethodName());
LOG.assertTrue(lambdaExpression != null);
final PsiExpression collectionExpression = expressions[(1 + myLambdaIndex) % 2];
final String pipelineHead = createPipelineHeadText(collectionExpression);
final String lambdaExpressionText;
final String elementText;
if (!StreamApiConstants.FAKE_FIND_MATCHED.equals(mySuitableHandler.getStreamApiMethodName())) {
elementText = mySuitableHandler.getStreamApiMethodName();
lambdaExpressionText = lambdaExpression.getText();
}
else {
elementText = expressions.length == 3
? String.format(StreamApiConstants.FAKE_FIND_MATCHED_WITH_DEFAULT_PATTERN, lambdaExpression.getText(), expressions[2].getText())
: String.format(StreamApiConstants.FAKE_FIND_MATCHED_PATTERN, lambdaExpression.getText());
lambdaExpressionText = null;
}
final String pipelineTail =
StreamApiConstants.STREAM_STREAM_API_METHODS.getValue().contains(mySuitableHandler.getStreamApiMethodName())
? findSuitableTailMethodForCollection(myMethodPointer.getElement())
: null;
final PsiElement replaced =
expression.replace(createPipelineExpression(pipelineHead, elementText, lambdaExpressionText, pipelineTail, project));
JavaCodeStyleManager.getInstance(project).shortenClassReferences(replaced.getParent());
myHandler.getTemplate().convertToStream(expression, null, false);
}
@NotNull
private static PsiExpression convertClassTypeExpression(PsiExpression expression) {
final PsiType type = expression.getType();
if (type instanceof PsiClassType) {
final PsiClass resolvedClass = ((PsiClassType)type).resolve();
if (resolvedClass != null && CommonClassNames.JAVA_LANG_CLASS.equals(resolvedClass.getQualifiedName())) {
return JavaPsiFacade.getElementFactory(expression.getProject())
.createExpressionFromText("(" + expression.getText() + ")::isInstance", null);
}
}
return expression;
}
private static String createPipelineHeadText(PsiExpression collectionExpression) {
final PsiType type = collectionExpression.getType();
if (type instanceof PsiClassType) {
final PsiClass resolved = ((PsiClassType)type).resolve();
LOG.assertTrue(resolved != null && resolved.getQualifiedName() != null, type);
return collectionExpression.getText() + ".stream()";
}
else if (type instanceof PsiArrayType) {
return CommonClassNames.JAVA_UTIL_ARRAYS + ".stream(" + collectionExpression.getText() + ")";
}
throw new AssertionError("type: " + type + " is unexpected");
}
private static PsiExpression createPipelineExpression(String pipelineHead,
String elementText,
String lambdaExpression,
String pipelineTail,
Project project) {
final StringBuilder sb = new StringBuilder();
sb.append(pipelineHead).append(".").append(elementText);
if (lambdaExpression != null) {
sb.append("(").append(lambdaExpression).append(")");
}
if (pipelineTail != null) {
sb.append(".").append(pipelineTail);
}
return JavaPsiFacade.getElementFactory(project).createExpressionFromText(sb.toString(), null);
}
}
private static int validateMethodParameters(final PsiMethodCallExpression methodCallExpression, final PsiMethod method, boolean canThirdParameterExist) {
final PsiType[] argumentTypes = methodCallExpression.getArgumentList().getExpressionTypes();
final PsiParameter[] expectedParameters = method.getParameterList().getParameters();
if (argumentTypes.length != expectedParameters.length) {
return -1;
}
if (expectedParameters.length == 2 || (canThirdParameterExist && expectedParameters.length == 3)) {
final int collectionOrArrayIndex = findCollectionOrArrayPlacement(expectedParameters);
if (collectionOrArrayIndex == -1) {
return -1;
}
return (1 + collectionOrArrayIndex) % 2;
}
return -1;
}
private static int findCollectionOrArrayPlacement(final PsiParameter[] parameters) {
for (int i = 0, length = parameters.length; i < length; i++) {
PsiParameter parameter = parameters[i];
final PsiType type = parameter.getType();
if (type instanceof PsiClassType || type instanceof PsiArrayType) {
return i;
}
}
return -1;
}
private static PsiExpression convertToJavaLambda(PsiExpression expression, String streamApiMethodName) {
if (streamApiMethodName.equals(StreamApiConstants.FAKE_FIND_MATCHED)) {
streamApiMethodName = StreamApiConstants.FILTER;
}
if (expression instanceof PsiMethodReferenceExpression) {
return expression;
}
if (expression instanceof PsiLambdaExpression) {
return expression;
}
if (expression instanceof PsiMethodCallExpression) {
final PsiMethod method = ((PsiMethodCallExpression)expression).resolveMethod();
if (method == null) {
return null;
}
final PsiType type = method.getReturnType();
if (!(type instanceof PsiClassType)) {
return null;
}
final PsiClass lambdaClass = ((PsiClassType)type).resolve();
if (lambdaClass == null) {
return null;
}
final String methodName = lambdaClass.getMethods()[0].getName();
if (tryConvertPseudoLambdaToStreamApi(method, resolveStreamApiLambdaClass(expression.getProject(), streamApiMethodName))) {
return expression;
}
else {
return JavaPsiFacade.getElementFactory(expression.getProject())
.createExpressionFromText(expression.getText() + "::" + methodName, null);
}
}
return AnonymousCanBeLambdaInspection.replacePsiElementWithLambda(expression, true);
}
@NotNull
private static PsiClass resolveStreamApiLambdaClass(Project project, String streamApiMethodName) {
final PsiClass javaUtilStream = JavaPsiFacade.getInstance(project)
.findClass(StreamApiConstants.JAVA_UTIL_STREAM_STREAM, GlobalSearchScope.notScope(GlobalSearchScope.projectScope(project)));
LOG.assertTrue(javaUtilStream != null);
final PsiMethod[] methods = javaUtilStream.findMethodsByName(streamApiMethodName, false);
LOG.assertTrue(methods.length == 1);
final PsiMethod method = methods[0];
final PsiParameter[] parameters = method.getParameterList().getParameters();
LOG.assertTrue(parameters.length == 1);
final PsiType type = parameters[0].getType();
LOG.assertTrue(type instanceof PsiClassType);
final PsiClass resolved = ((PsiClassType)type).resolve();
LOG.assertTrue(resolved != null);
return resolved;
}
private static boolean tryConvertPseudoLambdaToStreamApi(final @NotNull PsiMethod method, final @NotNull PsiClass expectedReturnClass) {
final PsiType currentReturnType = method.getReturnType();
if (!(currentReturnType instanceof PsiClassType)) {
LOG.error("pseudo-lambda return type must be class " + currentReturnType);
return true;
}
final PsiClass resolvedCurrentReturnType = ((PsiClassType)currentReturnType).resolve();
if (expectedReturnClass.getManager().areElementsEquivalent(expectedReturnClass, resolvedCurrentReturnType)) {
return true;
}
final PsiCodeBlock body = method.getBody();
Collection<PsiReturnStatement> returnStatements = PsiTreeUtil.findChildrenOfType(body, PsiReturnStatement.class);
returnStatements = ContainerUtil.filter(returnStatements, new Condition<PsiReturnStatement>() {
@Override
public boolean value(PsiReturnStatement statement) {
return PsiTreeUtil.getParentOfType(statement, PsiMethod.class) == method;
}
});
if (returnStatements.size() != 1) {
return false;
}
final PsiReturnStatement returnStatement = ContainerUtil.getFirstItem(returnStatements);
assert returnStatement != null;
final PsiExpression returnValue = returnStatement.getReturnValue();
if (returnValue instanceof PsiNewExpression) {
convertNewExpression(method, (PsiNewExpression)returnValue, expectedReturnClass);
return true;
}
else {
return false;
}
}
private static void convertNewExpression(PsiMethod containingMethod, PsiNewExpression newExpression, PsiClass expectedReturnClass) {
final String expectedReturnQName = expectedReturnClass.getQualifiedName();
LOG.assertTrue(expectedReturnQName != null);
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(newExpression.getProject());
PsiAnonymousClass anonymousClass = PsiTreeUtil.findChildOfType(newExpression, PsiAnonymousClass.class);
LOG.assertTrue(anonymousClass != null);
PsiJavaCodeReferenceElement referenceElement = PsiTreeUtil.findChildOfType(anonymousClass, PsiJavaCodeReferenceElement.class);
LOG.assertTrue(referenceElement != null);
final PsiReferenceParameterList parameterList = PsiTreeUtil.findChildOfType(referenceElement, PsiReferenceParameterList.class);
final PsiJavaCodeReferenceElement newCodeReferenceElement = factory.createReferenceFromText(expectedReturnClass.getQualifiedName()
+
(parameterList == null
? ""
: parameterList.getText()), null);
referenceElement.replace(newCodeReferenceElement);
final List<PsiMethod> methods = ContainerUtil.filter(anonymousClass.getMethods(), new Condition<PsiMethod>() {
@Override
public boolean value(PsiMethod method) {
return !"equals".equals(method.getName());
}
});
LOG.assertTrue(methods.size() == 1, methods);
final PsiMethod method = methods.get(0);
method.setName(expectedReturnClass.getMethods()[0].getName());
final PsiTypeElement element = containingMethod.getReturnTypeElement();
if (element != null) {
final PsiReferenceParameterList genericParameter = PsiTreeUtil.findChildOfType(element, PsiReferenceParameterList.class);
element.replace(factory.createTypeElementFromText(expectedReturnQName + (genericParameter == null ? "" : genericParameter.getText()), null));
}
}
@Nullable
private static String findSuitableTailMethodForCollection(PsiMethod lambdaHandler) {
final PsiType type = lambdaHandler.getReturnType();
if (type instanceof PsiArrayType) {
return "toArray(String[]::new)";
}
else if (type instanceof PsiClassType) {
final PsiClass resolvedClass = ((PsiClassType)type).resolve();
if (resolvedClass == null) {
return null;
}
final String qName = resolvedClass.getQualifiedName();
if (qName == null) {
return null;
}
if (qName.equals(CommonClassNames.JAVA_UTIL_LIST)
|| qName.equals(CommonClassNames.JAVA_UTIL_COLLECTION)
|| qName.equals(CommonClassNames.JAVA_LANG_ITERABLE)) {
return "collect(" + StreamApiConstants.JAVA_UTIL_STREAM_COLLECTORS + ".toList())";
}
else if (qName.equals(CommonClassNames.JAVA_UTIL_SET)) {
return "collect(" + StreamApiConstants.JAVA_UTIL_STREAM_COLLECTORS + ".toSet())";
}
else if (qName.equals(CommonClassNames.JAVA_UTIL_ITERATOR)) {
return "iterator()";
}
}
return null;
}
}
@@ -16,11 +16,9 @@
package com.intellij.codeInspection.java18StreamApi;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.ui.*;
import com.intellij.ui.components.JBList;
import com.intellij.util.containers.ContainerUtil;
@@ -32,7 +30,6 @@ import javax.swing.*;
import javax.swing.event.ListDataListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
@@ -43,6 +40,8 @@ public class StaticPseudoFunctionalStyleMethodOptions {
private static final String FQN_ATTR = "classFqn";
private static final String METHOD_ATTR = "method";
private static final String STREAM_API_METHOD_ATTR = "streamApiMethod";
private static final String LAMBDA_ROLE_ATTR = "lambdaRole";
private static final String ACCEPTS_DEFAULT_ATTR = "acceptsDefault";
private static final String DELETE_ATTR = "toDelete";
private final List<PipelineElement> myElements;
@@ -54,11 +53,11 @@ public class StaticPseudoFunctionalStyleMethodOptions {
private static void restoreDefault(final List<PipelineElement> elements) {
elements.clear();
final String guavaIterables = "com.google.common.collect.Iterables";
elements.add(new PipelineElement(guavaIterables, "transform", StreamApiConstants.MAP));
elements.add(new PipelineElement(guavaIterables, "filter", StreamApiConstants.FILTER));
elements.add(new PipelineElement(guavaIterables, "find", StreamApiConstants.FAKE_FIND_MATCHED));
elements.add(new PipelineElement(guavaIterables, "all", StreamApiConstants.ALL_MATCH));
elements.add(new PipelineElement(guavaIterables, "any", StreamApiConstants.ANY_MATCH));
elements.add(new PipelineElement(guavaIterables, "transform", PseudoLambdaReplaceTemplate.MAP));
elements.add(new PipelineElement(guavaIterables, "filter", PseudoLambdaReplaceTemplate.FILTER));
elements.add(new PipelineElement(guavaIterables, "find", PseudoLambdaReplaceTemplate.FIND));
elements.add(new PipelineElement(guavaIterables, "all", PseudoLambdaReplaceTemplate.ALL_MATCH));
elements.add(new PipelineElement(guavaIterables, "any", PseudoLambdaReplaceTemplate.ANY_MATCH));
}
@NotNull
@@ -77,8 +76,11 @@ public class StaticPseudoFunctionalStyleMethodOptions {
final String fqn = element.getAttributeValue(FQN_ATTR);
final String method = element.getAttributeValue(METHOD_ATTR);
final String streamApiMethod = element.getAttributeValue(STREAM_API_METHOD_ATTR);
final PseudoLambdaReplaceTemplate.LambdaRole lambdaRole =
PseudoLambdaReplaceTemplate.LambdaRole.valueOf(element.getAttributeValue(LAMBDA_ROLE_ATTR));
final boolean acceptsDefault = Boolean.valueOf(element.getAttributeValue(ACCEPTS_DEFAULT_ATTR));
final boolean toDelete = element.getAttribute(DELETE_ATTR) != null;
final PipelineElement pipelineElement = new PipelineElement(fqn, method, streamApiMethod);
final PipelineElement pipelineElement = new PipelineElement(fqn, method, new PseudoLambdaReplaceTemplate(streamApiMethod, lambdaRole, acceptsDefault));
if (toDelete) {
myElements.remove(pipelineElement);
}
@@ -107,10 +109,13 @@ public class StaticPseudoFunctionalStyleMethodOptions {
}
public Element createXmlElement(PipelineElement element) {
final PseudoLambdaReplaceTemplate template = element.getTemplate();
return new Element(PIPELINE_ELEMENT_NAME)
.setAttribute(FQN_ATTR, element.getHandlerClass())
.setAttribute(METHOD_ATTR, element.getMethodName())
.setAttribute(STREAM_API_METHOD_ATTR, element.getStreamApiMethodName());
.setAttribute(STREAM_API_METHOD_ATTR, template.getStreamApiMethodName())
.setAttribute(LAMBDA_ROLE_ATTR, template.getLambdaRole().toString())
.setAttribute(ACCEPTS_DEFAULT_ATTR, String.valueOf(template.isAcceptDefaultValue()));
}
public JComponent createPanel() {
@@ -137,6 +142,9 @@ public class StaticPseudoFunctionalStyleMethodOptions {
if (currentProject == null) {
return;
}
if (DumbService.isDumb(currentProject)) {
return;
}
final AddMethodsDialog dlg = new AddMethodsDialog(currentProject, list, false);
if (dlg.showAndGet()) {
final PipelineElement newElement = dlg.getSelectedElement();
@@ -147,6 +155,7 @@ public class StaticPseudoFunctionalStyleMethodOptions {
UIUtil.invokeLaterIfNeeded(new Runnable() {
@Override
public void run() {
list.revalidate();
list.updateUI();
}
});
@@ -156,19 +165,28 @@ public class StaticPseudoFunctionalStyleMethodOptions {
@Override
public void run(AnActionButton button) {
myElements.remove(list.getSelectedIndex());
UIUtil.invokeLaterIfNeeded(new Runnable() {
@Override
public void run() {
list.revalidate();
list.updateUI();
}
});
}
}).createPanel();
}
public static class PipelineElement {
public static class PipelineElement {
private final String myHandlerClass;
private final String myMethodName;
private final String myStreamApiMethod;
private final PseudoLambdaReplaceTemplate myTemplate;
public PipelineElement(@NotNull String handlerClass, @NotNull String methodName, @NotNull String streamApiMethod) {
public PipelineElement(@NotNull String handlerClass,
@NotNull String methodName,
@NotNull PseudoLambdaReplaceTemplate template) {
myHandlerClass = handlerClass;
myMethodName = methodName;
myStreamApiMethod = streamApiMethod;
myTemplate = template;
}
public String getHandlerClass() {
@@ -179,8 +197,8 @@ public class StaticPseudoFunctionalStyleMethodOptions {
return myMethodName;
}
public String getStreamApiMethodName() {
return myStreamApiMethod;
public PseudoLambdaReplaceTemplate getTemplate() {
return myTemplate;
}
@Override
@@ -192,7 +210,7 @@ public class StaticPseudoFunctionalStyleMethodOptions {
if (!myHandlerClass.equals(element.myHandlerClass)) return false;
if (!myMethodName.equals(element.myMethodName)) return false;
if (!myStreamApiMethod.equals(element.myStreamApiMethod)) return false;
if (!myTemplate.equals(element.myTemplate)) return false;
return true;
}
@@ -201,7 +219,7 @@ public class StaticPseudoFunctionalStyleMethodOptions {
public int hashCode() {
int result = myHandlerClass.hashCode();
result = 31 * result + myMethodName.hashCode();
result = 31 * result + myStreamApiMethod.hashCode();
result = 31 * result + myTemplate.hashCode();
return result;
}
}
@@ -0,0 +1,13 @@
import com.google.common.collect.Iterables;
import com.google.common.base.Predicate;
import java.lang.Iterable;
import java.util.HashMap;
import java.util.Collections;
import java.util.ArrayList;
class c {
void m() {
Iterable<HashMap> l = Iterables.fil<caret>ter(new ArrayList<>(), op -> false);
}
}
@@ -0,0 +1,14 @@
import com.google.common.collect.Iterables;
import com.google.common.base.Predicate;
import java.lang.Iterable;
import java.util.HashMap;
import java.util.Collections;
import java.util.ArrayList;
import java.util.stream.Collectors;
class c {
void m() {
Iterable<HashMap> l = new ArrayList<HashMap>().stream().filter(op -> false).collect(Collectors.toList());
}
}
@@ -6,7 +6,7 @@ import java.util.Collections;
class c {
void m() {
Collections.emptyList().stream().allMatch(getPredicate(100)::apply);
Collections.<String>emptyList().stream().allMatch(getPredicate(100)::apply);
}
public Predicate<String> getPredicate(final int param) {
@@ -0,0 +1,13 @@
import com.google.common.collect.Iterables;
import com.google.common.base.Predicate;
import java.lang.Iterable;
import java.util.HashMap;
import java.util.Collections;
import java.util.ArrayList;
class c {
void m() {
Iterable<HashMap> l = Iterables.fil<caret>ter(Collections.emptyList(), op -> false);
}
}
@@ -0,0 +1,14 @@
import com.google.common.collect.Iterables;
import com.google.common.base.Predicate;
import java.lang.Iterable;
import java.util.HashMap;
import java.util.Collections;
import java.util.ArrayList;
import java.util.stream.Collectors;
class c {
void m() {
Iterable<HashMap> l = Collections.<HashMap>emptyList().stream().filter(op -> false).collect(Collectors.toList());
}
}
@@ -0,0 +1,19 @@
import com.google.common.collect.Iterables;
import com.google.common.base.Function;
import java.lang.String;
import java.util.Collections;
class c {
Iterable<String> transform = Iterables.tra<caret>nsform(Collections.emptyList(), new Function<String, String>() {
@Override
public String apply(String input) {
Collectors c;
ArrayList l;
System.out.println(input);
//do something
int i = 1;
return input;
}
});
}
@@ -0,0 +1,17 @@
import com.google.common.collect.Iterables;
import com.google.common.base.Function;
import java.lang.String;
import java.util.Collections;
import java.util.stream.Collectors;
class c {
Iterable<String> transform = Collections.<String>emptyList().stream().map(input -> {
Collectors c;
ArrayList l;
System.out.println(input);
//do something
int i = 1;
return input;
}).collect(Collectors.toList());
}
@@ -0,0 +1,17 @@
import com.google.common.collect.Iterables;
import com.google.common.base.Function;
import java.lang.String;
import java.util.Collections;
class c {
void m() {
Iterables.transf<caret>orm(new ArrayList<>(), getFunction())
}
Function<String, String> getFunction() {
return new Function<String, String>() {
}
}
}
@@ -0,0 +1,20 @@
import com.google.common.collect.Iterables;
import com.google.common.base.Function;
import java.lang.String;
import java.util.ArrayList;
import java.util.Collections;
import java.util.stream.Collectors;
class c {
void m() {
Collections.emptyList().stream().map(input -> {
Collectors c;
ArrayList l;
System.out.println(input);
//do something
int i = 1;
return input;
}).collect(Collectors.toList())
}
}
@@ -0,0 +1,21 @@
import com.google.common.collect.Iterables;
import com.google.common.base.Function;
import java.lang.String;
import java.util.Collections;
class c {
void m() {
Iterables.transf<caret>orm(Collections.emptyList(), new Function<String, String> () {
@Override
public String apply(String input) {
java.util.stream.Collectors c;
java.util.ArrayList l;
System.out.println(input);
//do something
int i = 1;
return input;
}
})
}
}
@@ -0,0 +1,20 @@
import com.google.common.collect.Iterables;
import com.google.common.base.Function;
import java.lang.String;
import java.util.ArrayList;
import java.util.Collections;
import java.util.stream.Collectors;
class c {
void m() {
Collections.emptyList().stream().map(input -> {
Collectors c;
ArrayList l;
System.out.println(input);
//do something
int i = 1;
return input;
}).collect(Collectors.toList())
}
}
@@ -28,6 +28,7 @@ import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.testFramework.IdeaTestUtil;
import com.intellij.testFramework.builders.JavaModuleFixtureBuilder;
import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase;
import com.intellij.util.PathUtil;
import com.intellij.util.Processor;
import java.io.File;
@@ -91,6 +92,22 @@ public class StaticPseudoFunctionalStyleMethodTest extends JavaCodeInsightFixtur
doTest();
}
public void testDiamondResolve() {
doTest();
}
public void _testTransformLambda() {
doTest();
}
public void _testTransformMethodCalling() {
doTest();
}
public void testTransformMethodRef() {
doTest();
}
public void _testReplaceWithMethodReference() {
doTest();
}
@@ -109,7 +126,7 @@ public class StaticPseudoFunctionalStyleMethodTest extends JavaCodeInsightFixtur
}
}
}
assertTrue(isQuickFixFound);
assertTrue("Quick fix isn't found", isQuickFixFound);
myFixture.checkResultByFile(getTestName(true) + "/test_after.java");
}
}
@@ -1,5 +1,11 @@
<html>
<body>
Inspection detects usages of pseudo-functional code if Java Stream API is available.
<br/>
Consider cases when monad behaviour is emulated by static method call:
<br/>
<code>
List&lt;String&gt; transformedIterable = Iterables.transform(someList, someTransformFunction);
</code>
</body>
</html>
+1 -1
View File
@@ -727,7 +727,7 @@
displayName="Block marker comment"/>
<localInspection groupPath="Java" language="JAVA" shortName="StaticPseudoFunctionalStyleMethod"
groupBundle="messages.InspectionsBundle"
groupName="Code style issues" enabledByDefault="false" level="WARNING"
groupName="Java language level migration aids" enabledByDefault="false" level="WARNING"
implementationClass="com.intellij.codeInspection.java18StreamApi.StaticPseudoFunctionalStyleMethodInspection"
displayName="Pseudo functional expression using static class"/>
<localInspection groupPath="Java" language="JAVA" shortName="StringTokenizerDelimiter"