method refs: intention to convert method ref to lambda

This commit is contained in:
anna
2012-10-08 19:21:58 +02:00
parent 70abc42ce6
commit 9d574a96ff
32 changed files with 635 additions and 3 deletions
@@ -356,7 +356,7 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
PsiSubstitutor substitutor,
LanguageLevel languageLevel) {
if (signature == null) return PsiSubstitutor.EMPTY;
final PsiType[] types = method.getSignature(substitutor).getParameterTypes();
final PsiType[] types = method.getSignature(PsiUtil.isRawSubstitutor(method, substitutor) ? PsiSubstitutor.EMPTY : substitutor).getParameterTypes();
final PsiType[] rightTypes = signature.getParameterTypes();
if (types.length < rightTypes.length) {
return PsiUtil.resolveGenericsClassInType(rightTypes[0]).getSubstitutor();
@@ -364,9 +364,12 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
return PsiUtil.resolveGenericsClassInType(types[0]).getSubstitutor();
}
PsiSubstitutor psiSubstitutor = JavaPsiFacade.getInstance(getProject()).getResolveHelper().inferTypeArguments(
method.getTypeParameters(), types, rightTypes, languageLevel);
final PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(getProject()).getResolveHelper();
PsiSubstitutor psiSubstitutor = resolveHelper.inferTypeArguments(method.getTypeParameters(), types, rightTypes, languageLevel);
psiSubstitutor = psiSubstitutor.putAll(substitutor);
if (method.isConstructor()) {
psiSubstitutor = psiSubstitutor.putAll(resolveHelper.inferTypeArguments(method.getContainingClass().getTypeParameters(), types, rightTypes, languageLevel));
}
return LambdaUtil.inferFromReturnType(method.getTypeParameters(),
psiSubstitutor.substitute(method.getReturnType()),
@@ -214,6 +214,10 @@
<className>com.siyeh.ipp.types.ReplaceLambdaWithAnonymousIntention</className>
<categoryKey>intention.category.declaration</categoryKey>
</intentionAction>
<intentionAction>
<className>com.siyeh.ipp.types.ReplaceMethodRefWithLambdaIntention</className>
<categoryKey>intention.category.declaration</categoryKey>
</intentionAction>
<intentionAction>
<className>com.siyeh.ipp.types.InferLambdaParameterTypeIntention</className>
<categoryKey>intention.category.declaration</categoryKey>
@@ -163,6 +163,8 @@ replace.diamond.with.explicit.type.arguments.intention.name=Replace '<>' with ex
replace.diamond.with.explicit.type.arguments.intention.family.name=Replace Diamond with Explicit Type Arguments
replace.lambda.with.anonymous.intention.name=Replace lambda with anonymous class
replace.lambda.with.anonymous.intention.family.name=Replace lambda with anonymous class
replace.method.ref.with.lambda.intention.name=Replace method reference with lambda
replace.method.ref.with.lambda.intention.family.name=Replace method reference with lambda
infer.lambda.parameter.type.intention.family.name=Infer lambda parameter type
expand.one.line.lambda2.code.block.intention.family.name=Expand lambda expression body to code block
make.public.intention.name=Make 'public'
@@ -0,0 +1,178 @@
/*
* Copyright 2011 Bas Leijdekkers
*
* 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.siyeh.ipp.types;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.RedundantCastUtil;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.siyeh.ipp.base.Intention;
import com.siyeh.ipp.base.PsiElementPredicate;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
public class ReplaceMethodRefWithLambdaIntention extends Intention {
private static final Logger LOG = Logger.getInstance("#" + ReplaceMethodRefWithLambdaIntention.class.getName());
@NotNull
@Override
protected PsiElementPredicate getElementPredicate() {
return new MethodRefPredicate();
}
@Override
protected void processIntention(@NotNull PsiElement element) throws IncorrectOperationException {
final PsiMethodReferenceExpression referenceExpression = PsiTreeUtil.getParentOfType(element, PsiMethodReferenceExpression.class);
LOG.assertTrue(referenceExpression != null);
final PsiType functionalInterfaceType = referenceExpression.getFunctionalInterfaceType();
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(functionalInterfaceType);
final StringBuilder buf = new StringBuilder("(");
LOG.assertTrue(functionalInterfaceType != null);
buf.append(functionalInterfaceType.getCanonicalText()).append(")(");
LOG.assertTrue(interfaceMethod != null);
final PsiParameter[] parameters = interfaceMethod.getParameterList().getParameters();
final Map<PsiParameter, String> map = new HashMap<PsiParameter, String>();
final JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(element.getProject());
final String paramsString = StringUtil.join(parameters, new Function<PsiParameter, String>() {
@Override
public String fun(PsiParameter parameter) {
String parameterName = parameter.getName();
if (parameterName != null) {
final String baseName = codeStyleManager.variableNameToPropertyName(parameterName, VariableKind.PARAMETER);
parameterName = codeStyleManager.suggestUniqueVariableName(baseName, referenceExpression, true);
map.put(parameter, parameterName);
return parameterName;
}
return "";
}
}, ", ");
buf.append(paramsString);
buf.append(") -> ");
final JavaResolveResult resolveResult = referenceExpression.advancedResolve(false);
final PsiElement resolveElement = resolveResult.getElement();
if (resolveElement instanceof PsiMember) {
boolean needBraces = interfaceMethod.getReturnType() == PsiType.VOID && !(resolveElement instanceof PsiMethod && ((PsiMethod)resolveElement).getReturnType() == PsiType.VOID);
if (needBraces) {
buf.append("{");
}
final PsiElement qualifier = referenceExpression.getQualifier();
boolean isReceiver = false;
if (resolveElement instanceof PsiMethod) {
final PsiClass containingClass = ((PsiMember)resolveElement).getContainingClass();
LOG.assertTrue(containingClass != null);
isReceiver = parameters.length > 0 &&
LambdaUtil.isReceiverType(parameters[0].getType(), containingClass, PsiUtil.resolveGenericsClassInType(parameters[0].getType()).getSubstitutor());
if (isReceiver && ((PsiMethod)resolveElement).getParameterList().getParametersCount() != parameters.length - 1) {
isReceiver = false;
}
}
final PsiElement referenceNameElement = referenceExpression.getReferenceNameElement();
if (isReceiver){
buf.append(parameters[0].getName()).append(".");
} else if (qualifier != null &&
!(qualifier instanceof PsiThisExpression && ((PsiThisExpression)qualifier).getQualifier() == null) &&
!(referenceNameElement instanceof PsiKeyword)){
buf.append(qualifier.getText()).append(".");
}
//new or method name
buf.append(referenceExpression.getReferenceName());
if (referenceNameElement instanceof PsiKeyword) {
//class name
buf.append(" ");
buf.append(((PsiMember)resolveElement).getName());
final PsiSubstitutor substitutor = resolveResult.getSubstitutor();
PsiClass containingClass;
if (resolveElement instanceof PsiClass) {
containingClass = (PsiClass)resolveElement;
} else {
containingClass = ((PsiMember)resolveElement).getContainingClass();
}
LOG.assertTrue(containingClass != null);
if (containingClass.hasTypeParameters() && !PsiUtil.isRawSubstitutor(containingClass, substitutor)) {
buf.append("<").append(StringUtil.join(containingClass.getTypeParameters(), new Function<PsiTypeParameter, String>() {
@Override
public String fun(PsiTypeParameter parameter) {
final PsiType psiType = substitutor.substitute(parameter);
LOG.assertTrue(psiType != null);
return psiType.getCanonicalText();
}
}, ", ")).append(">");
}
}
//param list
buf.append("(");
boolean first = true;
for (int i = isReceiver ? 1 : 0; i < parameters.length; i++) {
PsiParameter parameter = parameters[i];
if (!first) {
buf.append(", ");
} else {
first = false;
}
buf.append(map.get(parameter));
}
buf.append(")");
if (needBraces) {
buf.append(";}");
}
}
final PsiTypeCastExpression typeCastExpression = (PsiTypeCastExpression)referenceExpression
.replace(JavaPsiFacade.getElementFactory(element.getProject()).createExpressionFromText(buf.toString(), referenceExpression));
if (RedundantCastUtil.isCastRedundant(typeCastExpression)) {
final PsiExpression operand = typeCastExpression.getOperand();
LOG.assertTrue(operand != null);
typeCastExpression.replace(operand);
}
}
private static class MethodRefPredicate implements PsiElementPredicate {
@Override
public boolean satisfiedBy(PsiElement element) {
final PsiMethodReferenceExpression methodReferenceExpression = PsiTreeUtil.getParentOfType(element, PsiMethodReferenceExpression.class);
if (methodReferenceExpression != null) {
final PsiType interfaceType = methodReferenceExpression.getFunctionalInterfaceType();
if (interfaceType != null &&
LambdaUtil.getFunctionalInterfaceMethod(interfaceType) != null &&
methodReferenceExpression.resolve() != null) {
return true;
}
}
return false;
}
}
}
@@ -0,0 +1,10 @@
class X {
interface I {
void _();
}
static void m() {}
{
I i = () -> m();
}
}
@@ -0,0 +1,10 @@
class X {
interface I {
void _();
}
static void m() {}
{
I i = <spot>X :: m</spot>;
}
}
@@ -0,0 +1,5 @@
<html>
<body>
This intention expands method reference to lambda expression
</body>
</html>
@@ -0,0 +1,19 @@
public class MyTest {
static void m(Integer i) { assertTrue(true); }
interface I1 {
void m(int x);
}
interface I2 {
void m(Integer x);
}
static void call(int i, I1 s) {}
static void call(int i, I2 s) {}
public static void main(String[] args) {
call(1, MyTest:<caret>:m);
}
}
@@ -0,0 +1,19 @@
public class MyTest {
static void m(Integer i) { assertTrue(true); }
interface I1 {
void m(int x);
}
interface I2 {
void m(Integer x);
}
static void call(int i, I1 s) {}
static void call(int i, I2 s) {}
public static void main(String[] args) {
call(1, (I2) (x) -> MyTest.m(x));
}
}
@@ -0,0 +1,7 @@
import java.util.*;
public class MyTest {
static {
Arrays.sort( new String[0], String.CASE_INSENSITIVE_ORDER:<caret>:compare );
}
}
@@ -0,0 +1,7 @@
import java.util.*;
public class MyTest {
static {
Arrays.sort( new String[0], (o1, o2) -> String.CASE_INSENSITIVE_ORDER.compare(o1, o2));
}
}
@@ -0,0 +1,16 @@
public class MyTest {
MyTest() {}
interface I {
MyTest m();
}
static void test(I i) {
i.m();
}
static {
I i = MyTest:<caret>:new;
}
}
@@ -0,0 +1,14 @@
public class MyTest {
interface I {
MyTest m();
}
static void test(I i) {
i.m();
}
static {
I i = MyTest:<caret>:new;
}
}
@@ -0,0 +1,14 @@
public class MyTest {
interface I {
MyTest m();
}
static void test(I i) {
i.m();
}
static {
I i = () -> new MyTest();
}
}
@@ -0,0 +1,16 @@
public class MyTest<X> {
MyTest(X x) {}
interface I<Z> {
MyTest<Z> m(Z z);
}
static <Y> void test(I<Y> s, Y arg) {
s.m(arg);
}
static {
I<String> s = MyTest<String>:<caret>:new;
}
}
@@ -0,0 +1,15 @@
public class MyTest {
interface I {
void m(Integer s);
}
static class Foo<X extends Number> {
Foo(X x) { }
}
static void m(I s) {}
static {
m(Foo:<caret>:new);
}
}
@@ -0,0 +1,17 @@
public class MyTest {
interface I {
void m(Integer s);
}
static class Foo<X extends Number> {
Foo(X x) { }
}
static void m(I s) {}
static {
m((s) -> {
new Foo<Integer>(s);
});
}
}
@@ -0,0 +1,16 @@
public class MyTest<X> {
MyTest(X x) {}
interface I<Z> {
MyTest<Z> m(Z z);
}
static <Y> void test(I<Y> s, Y arg) {
s.m(arg);
}
static {
I<String> s = (z) -> new MyTest<String>(z);
}
}
@@ -0,0 +1,13 @@
class MyTest {
class Inner {
Inner() {};
}
interface I {
Inner m(MyTest receiver);
}
static {
I i1 = MyTest.Inner:<caret>:new;
}
}
@@ -0,0 +1,13 @@
class MyTest {
class Inner {
Inner() {};
}
interface I {
Inner m(MyTest receiver);
}
static {
I i1 = (receiver) -> receiver.new Inner();
}
}
@@ -0,0 +1,15 @@
class MyTest {
static class Inner {
Inner(MyTest outer) {};
Inner() {};
}
interface I {
Inner m(MyTest receiver);
}
static {
I i1 = MyTest.Inner:<caret>:new;
}
}
@@ -0,0 +1,15 @@
class MyTest {
static class Inner {
Inner(MyTest outer) {};
Inner() {};
}
interface I {
Inner m(MyTest receiver);
}
static {
I i1 = (receiver) -> new Inner(receiver);
}
}
@@ -0,0 +1,16 @@
public class MyTest {
MyTest() {}
interface I {
MyTest m();
}
static void test(I i) {
i.m();
}
static {
I i = () -> new MyTest();
}
}
@@ -0,0 +1,16 @@
public class MyTest {
interface I {
void m(MyTest receiver);
}
void m() { }
{
I i = (I) MyTest::m;
s.m(this);
}
static {
I i = (I)MyTest:<caret>:m;
}
}
@@ -0,0 +1,16 @@
public class MyTest {
interface I {
void m(MyTest receiver);
}
void m() { }
{
I i = (I) MyTest::m;
s.m(this);
}
static {
I i = (I) (receiver) -> receiver.m();
}
}
@@ -0,0 +1,12 @@
public class MyTest {
interface I { void foo(int i); }
static void print(int i) {
System.out.println(i);
}
static {
I sam = MyTest:<caret>:print;
}
}
@@ -0,0 +1,12 @@
public class MyTest {
interface I { void foo(int i); }
static void print(int i) {
System.out.println(i);
}
static {
I sam = (i) -> MyTest.print(i);
}
}
@@ -0,0 +1,16 @@
public class MyTest {
interface I {
void meth(int i);
}
static class A {
void m(int i) {}
}
static class B extends A {
void m(int i1) {
I i = super:<caret>:m;
}
}
}
@@ -0,0 +1,16 @@
public class MyTest {
interface I {
void meth(int i);
}
static class A {
void m(int i) {}
}
static class B extends A {
void m(int i1) {
I i = (i2) -> super.m(i2);
}
}
}
@@ -0,0 +1,10 @@
class MyTest {
static interface SAM {
void m(Integer i);
}
void m(Integer i) {}
void m(Double d) {}
SAM s = this:<caret>:m;
}
@@ -0,0 +1,10 @@
class MyTest {
static interface SAM {
void m(Integer i);
}
void m(Integer i) {}
void m(Double d) {}
SAM s = (i) -> m(i);
}
@@ -0,0 +1,80 @@
/*
* Copyright 2000-2012 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.siyeh.ipp.types;
import com.siyeh.IntentionPowerPackBundle;
import com.siyeh.ipp.IPPTestCase;
public class ReplaceMethodReferenceWithLambdaIntentionTest extends IPPTestCase {
@Override
protected String getIntentionName() {
return IntentionPowerPackBundle.message("replace.method.ref.with.lambda.intention.name");
}
@Override
protected String getRelativePath() {
return "types/methodRefs2lambda";
}
public void testStaticMethodRef() throws Exception {
doTest();
}
public void testThisRefs() throws Exception {
doTest();
}
public void testSuperRefs() throws Exception {
doTest();
}
public void testExprRefs() throws Exception {
doTest();
}
public void testReceiver() throws Exception {
doTest();
}
public void testNewRefs() throws Exception {
doTest();
}
public void testNewRefsDefaultConstructor() throws Exception {
doTest();
}
public void testNewRefsInnerClass() throws Exception {
doTest();
}
public void testNewRefsStaticInnerClass() throws Exception {
doTest();
}
public void testNewRefsInference() throws Exception {
doTest();
}
public void testNewRefsInference1() throws Exception {
doTest();
}
public void testAmbiguity() throws Exception {
doTest();
}
}