From 48c825df060590078cd5cdf71eb6aa69d0a1ee05 Mon Sep 17 00:00:00 2001 From: Jack Zhou Date: Fri, 19 Apr 2013 01:08:57 -0400 Subject: [PATCH] Create from usage: Added guessing types for a declaration without an explicit type. --- .../quickfix/CreateMethodFromUsageFix.java | 62 ++++++++++++++----- 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/CreateMethodFromUsageFix.java b/idea/src/org/jetbrains/jet/plugin/quickfix/CreateMethodFromUsageFix.java index 76250dcaa12..5b64a243216 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/CreateMethodFromUsageFix.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/CreateMethodFromUsageFix.java @@ -39,7 +39,10 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiReference; +import com.intellij.psi.search.SearchScope; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.refactoring.psi.SearchUtils; import com.intellij.ui.components.JBList; import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; @@ -58,6 +61,7 @@ import org.jetbrains.jet.lang.resolve.DescriptorUtils; import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.types.*; +import org.jetbrains.jet.lang.types.checker.JetTypeChecker; import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; import org.jetbrains.jet.plugin.JetBundle; import org.jetbrains.jet.plugin.codeInsight.ReferenceToClassesShortening; @@ -65,6 +69,7 @@ import org.jetbrains.jet.plugin.presentation.JetLightClassListCellRenderer; import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache; import org.jetbrains.jet.plugin.refactoring.JetNameSuggester; import org.jetbrains.jet.plugin.refactoring.JetNameValidator; +import org.jetbrains.jet.plugin.references.JetSimpleNameReference; import org.jetbrains.jet.renderer.DescriptorRenderer; import javax.swing.*; @@ -449,7 +454,7 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase { @Override public void invoke(@NotNull final Project project, Editor editor, PsiFile file) throws IncorrectOperationException { - assert file != null && file instanceof JetFile; + assert file != null && file instanceof JetFile; // TODO: change some assertions to notifications currentFile = (JetFile) file; currentFileEditor = editor; @@ -824,7 +829,7 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase { public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { assert file instanceof JetFile; JetFile jetFile = (JetFile) file; - currentFileContext = AnalyzerFacadeWithCache.analyzeFileWithCache(jetFile).getBindingContext(); // TODO: can't be called from EDT? + currentFileContext = AnalyzerFacadeWithCache.analyzeFileWithCache(jetFile).getBindingContext(); JetType[] possibleOwnerTypes = ownerType.getPossibleTypes(currentFileContext); if (possibleOwnerTypes.length == 0) return false; @@ -930,11 +935,17 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase { @NotNull private static JetType[] guessTypeForExpression(@NotNull JetExpression expr, @NotNull BindingContext context) { - JetType actualType = context.get(BindingContext.EXPRESSION_TYPE, expr); + JetType type = context.get(BindingContext.EXPRESSION_TYPE, expr); + JetNamedDeclaration declaration = null; // if we know the actual type of the expression - if (actualType != null) { - return new JetType[] {actualType}; + if (type != null) { + return new JetType[] {type}; + } + + // expression has an expected type + else if ((type = context.get(BindingContext.EXPECTED_EXPRESSION_TYPE, expr)) != null) { + return new JetType[] {type}; } // expression itself is a type assertion @@ -956,7 +967,7 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase { if (typeRef != null) { // and has a specified type return new JetType[] {context.get(BindingContext.TYPE, typeRef)}; } - // otherwise fall through and guess + declaration = entry; // otherwise fall through and guess } // expression is a parameter (e.g. declared in a for-loop) @@ -966,25 +977,44 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase { if (typeRef != null) { // and has a specified type return new JetType[] {context.get(BindingContext.TYPE, typeRef)}; } - // otherwise fall through and guess + declaration = parameter; // otherwise fall through and guess } // the expression is the RHS of a variable assignment with a specified type else if (expr.getParent() instanceof JetVariableDeclaration) { JetVariableDeclaration variable = (JetVariableDeclaration) expr.getParent(); - JetTypeReference typetypeRefEf = variable.getTypeRef(); - if (typetypeRefEf != null) { // and has a specified type - return new JetType[] {context.get(BindingContext.TYPE, typetypeRefEf)}; + JetTypeReference typeRef = variable.getTypeRef(); + if (typeRef != null) { // and has a specified type + return new JetType[] {context.get(BindingContext.TYPE, typeRef)}; } - // otherwise fall through and guess + declaration = variable; // otherwise fall through and guess, based on LHS } - // TODO: need to guess based on usages of expression or expected type info - - // TODO: usages of variable - // TODO: nested in a trivial expression (e.g. parentheses or if-else) - return new JetType[0]; + + // guess based on declaration + SearchScope scope = expr.getContainingFile().getUseScope(); + Set expectedTypes = new HashSet(); + if (declaration != null) { + for (PsiReference ref : SearchUtils.findAllReferences(declaration, scope)) { + if (ref instanceof JetSimpleNameReference) { + JetSimpleNameReference simpleNameRef = (JetSimpleNameReference) ref; + JetType expectedType = context.get(BindingContext.EXPECTED_EXPRESSION_TYPE, simpleNameRef.getExpression()); + if (expectedType != null) { + expectedTypes.add(expectedType); + } + } + } + } + if (expectedTypes.isEmpty()) { + return new JetType[0]; + } + type = TypeUtils.intersect(JetTypeChecker.INSTANCE, expectedTypes); + if (type != null) { + return new JetType[] {type}; + } else { // intersection doesn't exist; let user make an imperfect choice + return expectedTypes.toArray(new JetType[expectedTypes.size()]); + } } private static boolean isUnit(@NotNull JetType type) {