Fixed KT-2072: do not suggest to use type inference in Introduce Variable in case if expression type depends on expected type.

This commit is contained in:
Alexander.Podkhalyuzin
2012-05-23 18:58:24 +04:00
parent ccb8c08684
commit 48533ca8d2
5 changed files with 119 additions and 9 deletions
@@ -21,10 +21,10 @@ import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.ui.popup.JBPopupAdapter;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.LightweightWindowEvent;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtilBase;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.ui.components.JBList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -85,7 +85,8 @@ public class JetRefactoringUtil {
return;
}
ArrayList<JetExpression> expressions = new ArrayList<JetExpression>();
while (element != null && !(element instanceof JetBlockExpression) && !(element instanceof JetNamedFunction)
while (element != null && !(element instanceof JetBlockExpression && !(element.getParent() instanceof JetFunctionLiteral)) &&
!(element instanceof JetNamedFunction)
&& !(element instanceof JetClassBody) && !(element instanceof JetSecondaryConstructor)) {
if (element instanceof JetExpression && !(element instanceof JetStatementExpression)) {
boolean addExpression = true;
@@ -132,7 +133,6 @@ public class JetRefactoringUtil {
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
Component rendererComponent = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
StringBuilder buffer = new StringBuilder();
JetExpression element = (JetExpression) value;
if (element.isValid()) {
setText(getExpressionShortText(element));
@@ -179,7 +179,7 @@ public class JetRefactoringUtil {
}
@Nullable
private static JetExpression findExpression(@NotNull Editor editor, @NotNull PsiFile file,
public static JetExpression findExpression(@NotNull Editor editor, @NotNull PsiFile file,
int startOffset, int endOffset) throws IntroduceRefactoringException{
PsiElement element = PsiTreeUtil.findElementOfClassAtRange(file, startOffset, endOffset, JetExpression.class);
if (element == null || element.getTextRange().getStartOffset() != startOffset ||
@@ -202,6 +202,66 @@ public class JetRefactoringUtil {
return (JetExpression) element;
}
@NotNull
public static PsiElement[] findStatements(@NotNull PsiFile file, int startOffset, int endOffset) {
PsiElement element1 = file.findElementAt(startOffset);
PsiElement element2 = file.findElementAt(endOffset - 1);
if (element1 instanceof PsiWhiteSpace) {
startOffset = element1.getTextRange().getEndOffset();
element1 = file.findElementAt(startOffset);
}
if (element2 instanceof PsiWhiteSpace) {
endOffset = element2.getTextRange().getStartOffset();
element2 = file.findElementAt(endOffset - 1);
}
if (element1 == null || element2 == null) return PsiElement.EMPTY_ARRAY;
PsiElement parent = PsiTreeUtil.findCommonParent(element1, element2);
if (parent == null) return PsiElement.EMPTY_ARRAY;
while (true) {
if (parent instanceof JetBlockExpression) break;
if (parent == null || parent instanceof JetFile) return PsiElement.EMPTY_ARRAY;
parent = parent.getParent();
}
if (!parent.equals(element1)) {
while (!parent.equals(element1.getParent())) {
element1 = element1.getParent();
}
}
if (startOffset != element1.getTextRange().getStartOffset()) return PsiElement.EMPTY_ARRAY;
if (!parent.equals(element2)) {
while (!parent.equals(element2.getParent())) {
element2 = element2.getParent();
}
}
if (endOffset != element2.getTextRange().getEndOffset()) return PsiElement.EMPTY_ARRAY;
PsiElement[] children = parent.getChildren();
ArrayList<PsiElement> array = new ArrayList<PsiElement>();
boolean flag = false;
for (PsiElement child : children) {
if (child.equals(element1)) {
flag = true;
}
if (flag && !(child instanceof PsiWhiteSpace)) {
array.add(child);
}
if (child.equals(element2)) {
break;
}
}
for (PsiElement element : array) {
if (!(element instanceof JetExpression || element instanceof PsiWhiteSpace || element instanceof PsiComment)) {
return PsiElement.EMPTY_ARRAY;
}
}
return PsiUtilCore.toPsiElementArray(array);
}
public static class IntroduceRefactoringException extends Exception {
private String myMessage;
@@ -16,6 +16,7 @@
package org.jetbrains.jet.plugin.refactoring.introduceVariable;
import com.google.common.base.Predicate;
import com.intellij.codeInsight.PsiEquivalenceUtil;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.application.ApplicationManager;
@@ -34,13 +35,24 @@ import com.intellij.refactoring.introduce.inplace.OccurrencesChooser;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.di.InjectorForMacros;
import org.jetbrains.jet.di.InjectorForTopDownAnalyzerForJvm;
import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowDataTraceFactory;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.ModuleDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.*;
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
import org.jetbrains.jet.lang.resolve.java.AnalyzerFacadeForJVM;
import org.jetbrains.jet.lang.resolve.java.CompilerDependencies;
import org.jetbrains.jet.lang.resolve.java.CompilerSpecialMode;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.NamespaceType;
import org.jetbrains.jet.lang.types.TypeUtils;
import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
import org.jetbrains.jet.lang.types.expressions.ExpressionTypingServices;
import org.jetbrains.jet.lang.types.lang.JetStandardLibrary;
import org.jetbrains.jet.lexer.JetTokens;
import org.jetbrains.jet.plugin.codeInsight.ReferenceToClassesShortening;
@@ -48,6 +60,7 @@ import org.jetbrains.jet.plugin.project.AnalyzeSingleFileUtil;
import org.jetbrains.jet.plugin.refactoring.*;
import org.jetbrains.jet.resolve.DescriptorRenderer;
import java.io.File;
import java.util.*;
/**
@@ -97,7 +110,6 @@ public class JetIntroduceVariableHandler extends JetIntroduceHandlerBase {
return;
} else if (expression.getParent() instanceof JetCallElement) {
if (expression instanceof JetFunctionLiteralExpression) {
noTypeInference = true;
needParentheses = true;
} else {
showErrorHint(project, editor, JetRefactoringBundle.message("cannot.refactor.no.expression"));
@@ -113,6 +125,23 @@ public class JetIntroduceVariableHandler extends JetIntroduceHandlerBase {
}
BindingContext bindingContext = AnalyzeSingleFileUtil.getContextForSingleFile((JetFile)expression.getContainingFile());
final JetType expressionType = bindingContext.get(BindingContext.EXPRESSION_TYPE, expression); //can be null or error type
JetScope scope = bindingContext.get(BindingContext.RESOLUTION_SCOPE, expression);
if (scope != null) {
DataFlowInfo dataFlowInfo = bindingContext.get(BindingContext.NON_DEFAULT_EXPRESSION_DATA_FLOW, expression);
if (dataFlowInfo == null) {
dataFlowInfo = DataFlowInfo.EMPTY;
}
ObservableBindingTrace bindingTrace = new ObservableBindingTrace(new BindingTraceContext());
InjectorForMacros injector = new InjectorForMacros(project);
JetType typeNoExpectedType = injector.getExpressionTypingServices().getType(scope, expression,
TypeUtils.NO_EXPECTED_TYPE, dataFlowInfo,
bindingTrace);
if (expressionType != null && typeNoExpectedType != null && !JetTypeChecker.INSTANCE.equalTypes(expressionType,
typeNoExpectedType)) {
noTypeInference = true;
}
}
if (expressionType instanceof NamespaceType) {
showErrorHint(project, editor, JetRefactoringBundle.message("cannot.refactor.namespace.expression"));
return;
@@ -3,7 +3,7 @@ fun foo(c : Collection<String>){
}
/*
fun foo(c : Collection<String>){
val function: () -> Boolean = {it; false}
val function = {it; false}
c.filter(function)
}
*/
@@ -0,0 +1,17 @@
class A {
fun foo() = 1
}
fun apply(x: (A) -> Int) = 2
fun test() {
apply<selection>{it.foo()}</selection>
}
/*
class A {
fun foo() = 1
}
fun apply(x: (A) -> Int) = 2
fun test() {
val function: (A) -> Int = {it.foo()}
apply(function)
}
*/
@@ -57,6 +57,10 @@ public class JetIntroduceVariableTest extends LightCodeInsightFixtureTestCase {
doTest();
}
public void testFunctionLiteralFromExpected() {
doTest();
}
public void testIfCondition() {
doTest();
}