create method from method reference (IDEA-112556)

This commit is contained in:
Anna Kozlova
2014-04-02 13:52:31 +02:00
parent 702932a6fd
commit cbe0d6d860
12 changed files with 286 additions and 28 deletions
@@ -179,6 +179,7 @@ public abstract class QuickFixFactory {
int minUsagesNumberToShowDialog);
@NotNull public abstract IntentionAction createCreateMethodFromUsageFix(@NotNull PsiMethodCallExpression call);
@NotNull public abstract IntentionAction createCreateMethodFromUsageFix(PsiMethodReferenceExpression methodReferenceExpression);
@NotNull public abstract IntentionAction createCreateAbstractMethodFromUsageFix(@NotNull PsiMethodCallExpression call);
@@ -1328,9 +1328,12 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
}
if (description != null) {
myHolder.add(HighlightInfo.newHighlightInfo(HighlightInfoType.WRONG_REF)
.descriptionAndTooltip(description)
.range(expression.getReferenceNameElement()).create());
final PsiElement referenceNameElement = expression.getReferenceNameElement();
final HighlightInfo highlightInfo =
HighlightInfo.newHighlightInfo(HighlightInfoType.WRONG_REF).descriptionAndTooltip(description).range(referenceNameElement).create();
myHolder.add(highlightInfo);
final TextRange fixRange = HighlightMethodUtil.getFixRange(referenceNameElement);
QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QuickFixFactory.getInstance().createCreateMethodFromUsageFix(expression));
}
}
}
@@ -0,0 +1,156 @@
/*
* Copyright 2000-2014 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.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.ExpectedTypeInfo;
import com.intellij.codeInsight.ExpectedTypeInfoImpl;
import com.intellij.codeInsight.TailType;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
public class CreateMethodFromMethodReferenceFix extends CreateFromUsageBaseFix {
private static final Logger LOG = Logger.getInstance("#" + CreateMethodFromMethodReferenceFix.class.getName());
private final SmartPsiElementPointer myMethodReferenceExpression;
public CreateMethodFromMethodReferenceFix(@NotNull PsiMethodReferenceExpression methodRef) {
myMethodReferenceExpression = SmartPointerManager.getInstance(methodRef.getProject()).createSmartPsiElementPointer(methodRef);
}
@Override
protected boolean isAvailableImpl(int offset) {
final PsiMethodReferenceExpression call = getMethodReference();
if (call == null || !call.isValid()) return false;
final PsiType functionalInterfaceType = call.getFunctionalInterfaceType();
if (functionalInterfaceType == null ||
LambdaUtil.getFunctionalInterfaceMethod(functionalInterfaceType) == null){
return false;
}
final String name = call.getReferenceName();
if (name == null) return false;
if (call.isConstructor() && name.equals("new") || PsiNameHelper.getInstance(call.getProject()).isIdentifier(name)) {
setText(call.isConstructor() ? QuickFixBundle.message("create.constructor.from.new.text") : QuickFixBundle.message("create.method.from.usage.text", name));
return true;
}
return false;
}
@Override
protected PsiElement getElement() {
final PsiMethodReferenceExpression call = getMethodReference();
if (call == null || !call.getManager().isInProject(call)) return null;
return call;
}
@Override
@NotNull
protected List<PsiClass> getTargetClasses(PsiElement element) {
List<PsiClass> targets = super.getTargetClasses(element);
PsiMethodReferenceExpression call = getMethodReference();
if (call == null) return Collections.emptyList();
return targets;
}
@Override
protected void invokeImpl(final PsiClass targetClass) {
if (targetClass == null) return;
PsiMethodReferenceExpression expression = getMethodReference();
if (expression == null) return;
if (isValidElement(expression)) return;
PsiClass parentClass = PsiTreeUtil.getParentOfType(expression, PsiClass.class);
PsiMember enclosingContext = PsiTreeUtil.getParentOfType(expression, PsiMethod.class, PsiField.class, PsiClassInitializer.class);
String methodName = expression.getReferenceName();
LOG.assertTrue(methodName != null);
final Project project = targetClass.getProject();
JVMElementFactory elementFactory = JVMElementFactories.getFactory(targetClass.getLanguage(), project);
if (elementFactory == null) elementFactory = JavaPsiFacade.getElementFactory(project);
PsiMethod method = expression.isConstructor() ? (PsiMethod)targetClass.add(elementFactory.createConstructor())
: CreateMethodFromUsageFix.createMethod(targetClass, parentClass, enclosingContext, methodName);
if (method == null) {
return;
}
if (!expression.isConstructor()) {
setupVisibility(parentClass, targetClass, method.getModifierList());
}
expression = getMethodReference();
LOG.assertTrue(expression.isValid());
if (!expression.isConstructor() && shouldCreateStaticMember(expression, targetClass)) {
PsiUtil.setModifierProperty(method, PsiModifier.STATIC, true);
}
final PsiElement context = PsiTreeUtil.getParentOfType(expression, PsiClass.class, PsiMethod.class);
final PsiType functionalInterfaceType = expression.getFunctionalInterfaceType();
final PsiClassType.ClassResolveResult classResolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(classResolveResult);
LOG.assertTrue(interfaceMethod != null);
final PsiType interfaceReturnType = LambdaUtil.getFunctionalInterfaceReturnType(functionalInterfaceType);
LOG.assertTrue(interfaceReturnType != null);
final ExpectedTypeInfo[] expectedTypes = {new ExpectedTypeInfoImpl(interfaceReturnType, ExpectedTypeInfo.TYPE_OR_SUBTYPE, interfaceReturnType, TailType.NONE, null, ExpectedTypeInfoImpl.NULL)};
CreateMethodFromUsageFix.doCreate(targetClass, method, false,
ContainerUtil.map2List(interfaceMethod.getParameterList().getParameters(), new Function<PsiParameter, Pair<PsiExpression, PsiType>>() {
@Override
public Pair<PsiExpression, PsiType> fun(PsiParameter parameter) {
return Pair.create(null, parameter.getType());
}
}),
LambdaUtil.getSubstitutor(interfaceMethod, classResolveResult),
expectedTypes, context);
}
@Override
protected boolean isValidElement(PsiElement element) {
return false;
}
@Override
@NotNull
public String getFamilyName() {
return QuickFixBundle.message("create.method.from.usage.family");
}
@Nullable
protected PsiMethodReferenceExpression getMethodReference() {
return (PsiMethodReferenceExpression)myMethodReferenceExpression.getElement();
}
}
@@ -66,7 +66,7 @@ public class CreateMethodFromUsageFix extends CreateFromUsageBaseFix {
PsiReferenceExpression ref = call.getMethodExpression();
String name = ref.getReferenceName();
if (name == null || !JavaPsiFacade.getInstance(ref.getProject()).getNameHelper().isIdentifier(name)) return false;
if (name == null || !PsiNameHelper.getInstance(ref.getProject()).isIdentifier(name)) return false;
if (hasErrorsInArgumentList(call)) return false;
setText(getDisplayString(name));
return true;
@@ -133,7 +133,6 @@ public class CreateMethodFromUsageFix extends CreateFromUsageBaseFix {
if (targetClass == null) return;
PsiMethodCallExpression expression = getMethodCall();
if (expression == null) return;
final Project project = expression.getProject();
PsiReferenceExpression ref = expression.getMethodExpression();
if (isValidElement(expression)) return;
@@ -144,30 +143,11 @@ public class CreateMethodFromUsageFix extends CreateFromUsageBaseFix {
String methodName = ref.getReferenceName();
LOG.assertTrue(methodName != null);
JVMElementFactory factory = JVMElementFactories.getFactory(targetClass.getLanguage(), project);
if (factory == null) {
PsiMethod method = createMethod(targetClass, parentClass, enclosingContext, methodName);
if (method == null) {
return;
}
PsiMethod method = factory.createMethod(methodName, PsiType.VOID);
if (targetClass.equals(parentClass)) {
method = (PsiMethod)targetClass.addAfter(method, enclosingContext);
}
else {
PsiElement anchor = enclosingContext;
while (anchor != null && anchor.getParent() != null && !anchor.getParent().equals(targetClass)) {
anchor = anchor.getParent();
}
if (anchor != null && anchor.getParent() == null) anchor = null;
if (anchor != null) {
method = (PsiMethod)targetClass.addAfter(method, anchor);
}
else {
method = (PsiMethod)targetClass.add(method);
}
}
if (enclosingContext instanceof PsiMethod && methodName.equals(enclosingContext.getName()) &&
PsiTreeUtil.isAncestor(targetClass, parentClass, true) && !ref.isQualified()) {
RefactoringChangeUtil.qualifyReference(ref, method, null);
@@ -201,6 +181,36 @@ public class CreateMethodFromUsageFix extends CreateFromUsageBaseFix {
context);
}
protected static PsiMethod createMethod(PsiClass targetClass,
PsiClass parentClass,
PsiMember enclosingContext,
String methodName) {
JVMElementFactory factory = JVMElementFactories.getFactory(targetClass.getLanguage(), targetClass.getProject());
if (factory == null) {
return null;
}
PsiMethod method = factory.createMethod(methodName, PsiType.VOID);
if (targetClass.equals(parentClass)) {
method = (PsiMethod)targetClass.addAfter(method, enclosingContext);
}
else {
PsiElement anchor = enclosingContext;
while (anchor != null && anchor.getParent() != null && !anchor.getParent().equals(targetClass)) {
anchor = anchor.getParent();
}
if (anchor != null && anchor.getParent() == null) anchor = null;
if (anchor != null) {
method = (PsiMethod)targetClass.addAfter(method, anchor);
}
else {
method = (PsiMethod)targetClass.add(method);
}
}
return method;
}
public static void doCreate(PsiClass targetClass, PsiMethod method, List<Pair<PsiExpression, PsiType>> arguments, PsiSubstitutor substitutor,
ExpectedTypeInfo[] expectedTypes, @Nullable PsiElement context) {
doCreate(targetClass, method, shouldBeAbstractImpl(targetClass), arguments, substitutor, expectedTypes, context);
@@ -227,8 +237,11 @@ public class CreateMethodFromUsageFix extends CreateFromUsageBaseFix {
TemplateBuilderImpl builder = new TemplateBuilderImpl(method);
CreateFromUsageUtils.setupMethodParameters(method, builder, context, substitutor, arguments);
new GuessTypeParameters(JavaPsiFacade.getInstance(project).getElementFactory())
.setupTypeElement(method.getReturnTypeElement(), expectedTypes, substitutor, builder, context, targetClass);
final PsiTypeElement returnTypeElement = method.getReturnTypeElement();
if (returnTypeElement != null) {
new GuessTypeParameters(JavaPsiFacade.getInstance(project).getElementFactory())
.setupTypeElement(returnTypeElement, expectedTypes, substitutor, builder, context, targetClass);
}
PsiCodeBlock body = method.getBody();
builder.setEndVariableAfter(shouldBeAbstract || body == null ? method : body.getLBrace());
method = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(method);
@@ -467,6 +467,12 @@ public class QuickFixFactoryImpl extends QuickFixFactory {
return new CreateMethodFromUsageFix(call);
}
@NotNull
@Override
public IntentionAction createCreateMethodFromUsageFix(PsiMethodReferenceExpression methodReferenceExpression) {
return new CreateMethodFromMethodReferenceFix(methodReferenceExpression);
}
@NotNull
@Override
public IntentionAction createCreateAbstractMethodFromUsageFix(@NotNull PsiMethodCallExpression call) {
@@ -0,0 +1,13 @@
// "Create Constructor" "true"
class FooBar {
FooBar(int i) {
}
{
Runnable r = FooBar::new;
}
public FooBar() {
}
}
@@ -0,0 +1,10 @@
// "Create Method 'fooBar'" "true"
class FooBar {
{
Runnable r = FooBar::fooBar;
}
private static void fooBar() {
}
}
@@ -0,0 +1,10 @@
// "Create Method 'fooBar'" "true"
class FooBar {
{
Runnable r = this::fooBar;
}
private void fooBar() {
}
}
@@ -0,0 +1,9 @@
// "Create Constructor" "true"
class FooBar {
FooBar(int i) {
}
{
Runnable r = FooBar::ne<caret>w;
}
}
@@ -0,0 +1,6 @@
// "Create Method 'fooBar'" "true"
class FooBar {
{
Runnable r = FooBar::foo<caret>Bar;
}
}
@@ -0,0 +1,6 @@
// "Create Method 'fooBar'" "true"
class FooBar {
{
Runnable r = this::foo<caret>Bar;
}
}
@@ -0,0 +1,25 @@
/*
* Copyright 2000-2014 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.codeInsight.daemon.quickFix;
public class CreateMethodFromMethodReferenceFixTest extends LightQuickFixParameterizedTestCase {
public void test() throws Exception { doAllTests(); }
@Override
protected String getBasePath() {
return "/codeInsight/daemonCodeAnalyzer/quickFix/createMethodFromMethodRef";
}
}