From fa6abf812a4fb33a80843b4a4daab382078adebb Mon Sep 17 00:00:00 2001 From: peter Date: Sun, 2 May 2010 18:35:59 +0100 Subject: [PATCH] recognize OMI when instatiating objects as list literals --- plugins/groovy/groovypp/groovypp.iml | 1 + .../groovy/groovypp/src/META-INF/plugin.xml | 3 +- .../gpp/GppClosureParameterTypeProvider.java | 45 +++++++++--- .../gpp/GppExpectedTypesContributor.java | 69 +++++++++++++++++++ .../plugins/groovy/gpp/GppTypeConverter.java | 12 ++-- plugins/groovy/src/META-INF/plugin.xml | 1 + .../GroovyExpectedTypesContributor.java | 16 +++++ .../GroovyExpectedTypesProvider.java | 26 +++++-- .../groovy/lang/GppFunctionalTest.groovy | 14 ++++ 9 files changed, 166 insertions(+), 21 deletions(-) create mode 100644 plugins/groovy/groovypp/src/org/jetbrains/plugins/groovy/gpp/GppExpectedTypesContributor.java create mode 100644 plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/expectedTypes/GroovyExpectedTypesContributor.java diff --git a/plugins/groovy/groovypp/groovypp.iml b/plugins/groovy/groovypp/groovypp.iml index e5156fdc12ec..c80576200148 100644 --- a/plugins/groovy/groovypp/groovypp.iml +++ b/plugins/groovy/groovypp/groovypp.iml @@ -11,6 +11,7 @@ + diff --git a/plugins/groovy/groovypp/src/META-INF/plugin.xml b/plugins/groovy/groovypp/src/META-INF/plugin.xml index 70895af0ab7e..9358b1c52fc4 100644 --- a/plugins/groovy/groovypp/src/META-INF/plugin.xml +++ b/plugins/groovy/groovypp/src/META-INF/plugin.xml @@ -3,13 +3,14 @@ Groovy++ Support Adds code assistance for the Groovy++ features 0.1 - + JetBrains Inc. org.intellij.groovy + diff --git a/plugins/groovy/groovypp/src/org/jetbrains/plugins/groovy/gpp/GppClosureParameterTypeProvider.java b/plugins/groovy/groovypp/src/org/jetbrains/plugins/groovy/gpp/GppClosureParameterTypeProvider.java index 98cd311bf484..7a9548292da7 100644 --- a/plugins/groovy/groovypp/src/org/jetbrains/plugins/groovy/gpp/GppClosureParameterTypeProvider.java +++ b/plugins/groovy/groovypp/src/org/jetbrains/plugins/groovy/gpp/GppClosureParameterTypeProvider.java @@ -10,13 +10,14 @@ import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.psi.AbstractClosureParameterEnhancer; +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap; import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentLabel; import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; import org.jetbrains.plugins.groovy.lang.psi.expectedTypes.GroovyExpectedTypesProvider; -import org.jetbrains.plugins.groovy.lang.psi.expectedTypes.TypeConstraint; +import org.jetbrains.plugins.groovy.lang.psi.impl.GrTupleType; import org.jetbrains.plugins.groovy.lang.psi.impl.types.GrClosureSignatureUtil; import java.util.*; @@ -40,7 +41,37 @@ public class GppClosureParameterTypeProvider extends AbstractClosureParameterEnh return null; } - for (PsiType constraint : getExpectedTypes(closure)) { + final PsiElement parent = closure.getParent(); + if (parent instanceof GrListOrMap) { + final GrListOrMap list = (GrListOrMap)parent; + if (!list.isMap()) { + final PsiType listType = list.getType(); + final int argIndex = Arrays.asList(list.getInitializers()).indexOf(closure); + assert argIndex >= 0; + if (listType instanceof GrTupleType) { + for (PsiType type : GroovyExpectedTypesProvider.getDefaultExpectedTypes(list)) { + if (type instanceof PsiClassType) { + for (GroovyResolveResult resolveResult : GppTypeConverter + .getConstructorCandidates((PsiClassType)type, ((GrTupleType)listType).getComponentTypes(), closure)) { + final PsiElement method = resolveResult.getElement(); + if (method instanceof PsiMethod && ((PsiMethod)method).isConstructor()) { + final PsiType toCastTo = + resolveResult.getSubstitutor().substitute(((PsiMethod)method).getParameterList().getParameters()[argIndex].getType()); + final PsiType suggestion = getSingleMethodParameterType(toCastTo, index, closure); + if (suggestion != null) { + return suggestion; + } + } + + } + } + } + } + return null; + } + } + + for (PsiType constraint : GroovyExpectedTypesProvider.getDefaultExpectedTypes(closure)) { final PsiType suggestion = getSingleMethodParameterType(constraint, index, closure); if (suggestion != null) { return suggestion; @@ -49,14 +80,6 @@ public class GppClosureParameterTypeProvider extends AbstractClosureParameterEnh return null; } - private static Set getExpectedTypes(GrExpression element) { - final LinkedHashSet result = new LinkedHashSet(); - for (TypeConstraint constraint : GroovyExpectedTypesProvider.calculateTypeConstraints(element)) { - result.add(constraint.getDefaultType()); - } - return result; - } - @Nullable private static Pair getOverriddenMethod(GrClosableBlock closure) { final PsiElement parent = closure.getParent(); @@ -76,7 +99,7 @@ public class GppClosureParameterTypeProvider extends AbstractClosureParameterEnh final PsiElement map = parent.getParent(); if (map instanceof GrListOrMap && ((GrListOrMap)map).isMap()) { - for (PsiType expected : getExpectedTypes((GrExpression)map)) { + for (PsiType expected : GroovyExpectedTypesProvider.getDefaultExpectedTypes((GrExpression)map)) { if (expected instanceof PsiClassType) { final List> pairs = getMethodsToOverrideImplementInInheritor((PsiClassType)expected, false); final List> withName = diff --git a/plugins/groovy/groovypp/src/org/jetbrains/plugins/groovy/gpp/GppExpectedTypesContributor.java b/plugins/groovy/groovypp/src/org/jetbrains/plugins/groovy/gpp/GppExpectedTypesContributor.java new file mode 100644 index 000000000000..0c97a5c4beb6 --- /dev/null +++ b/plugins/groovy/groovypp/src/org/jetbrains/plugins/groovy/gpp/GppExpectedTypesContributor.java @@ -0,0 +1,69 @@ +package org.jetbrains.plugins.groovy.gpp; + +import com.intellij.psi.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; +import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; +import org.jetbrains.plugins.groovy.lang.psi.expectedTypes.GroovyExpectedTypesContributor; +import org.jetbrains.plugins.groovy.lang.psi.expectedTypes.GroovyExpectedTypesProvider; +import org.jetbrains.plugins.groovy.lang.psi.expectedTypes.SubtypeConstraint; +import org.jetbrains.plugins.groovy.lang.psi.expectedTypes.TypeConstraint; +import org.jetbrains.plugins.groovy.lang.psi.impl.GrTupleType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * @author peter + */ +public class GppExpectedTypesContributor extends GroovyExpectedTypesContributor { + @Override + public List calculateTypeConstraints(@NotNull GrExpression expression) { + if (!GppTypeConverter.hasTypedContext(expression)) { + return Collections.emptyList(); + } + + final PsiElement parent = expression.getParent(); + if (parent instanceof GrListOrMap) { + final GrListOrMap list = (GrListOrMap)parent; + if (!list.isMap()) { + return addExpectedConstructorParameters(expression, list); + } + else { + //todo expected property types + } + } + return Collections.emptyList(); + } + + private static List addExpectedConstructorParameters(GrExpression expression, GrListOrMap list) { + final PsiType listType = list.getType(); + if (!(listType instanceof GrTupleType)) { + return Collections.emptyList(); + } + + final PsiType[] argTypes = ((GrTupleType)listType).getComponentTypes(); + final int argIndex = Arrays.asList(list.getInitializers()).indexOf(expression); + assert argIndex >= 0; + + final ArrayList result = new ArrayList(); + for (PsiType type : GroovyExpectedTypesProvider.getDefaultExpectedTypes(expression)) { + if (type instanceof PsiClassType) { + for (GroovyResolveResult resolveResult : GppTypeConverter.getConstructorCandidates((PsiClassType)type, argTypes, expression)) { + final PsiElement method = resolveResult.getElement(); + if (method instanceof PsiMethod && ((PsiMethod)method).isConstructor()) { + final PsiParameter[] constructorParameters = ((PsiMethod)method).getParameterList().getParameters(); + if (constructorParameters.length > argIndex) { + final PsiType toCastTo = resolveResult.getSubstitutor().substitute(constructorParameters[argIndex].getType()); + result.add(SubtypeConstraint.create(toCastTo)); + } + } + } + } + } + return result; + } +} diff --git a/plugins/groovy/groovypp/src/org/jetbrains/plugins/groovy/gpp/GppTypeConverter.java b/plugins/groovy/groovypp/src/org/jetbrains/plugins/groovy/gpp/GppTypeConverter.java index 55d0f53079c6..ba7972af432b 100644 --- a/plugins/groovy/groovypp/src/org/jetbrains/plugins/groovy/gpp/GppTypeConverter.java +++ b/plugins/groovy/groovypp/src/org/jetbrains/plugins/groovy/gpp/GppTypeConverter.java @@ -105,19 +105,21 @@ public class GppTypeConverter extends GrTypeConverter { } private static boolean hasConstructor(PsiClassType lType, PsiType[] argTypes, GroovyPsiElement context) { - final PsiClassType.ClassResolveResult resolveResult = lType.resolveGenerics(); + return getConstructorCandidates(lType, argTypes, context).length == 1; + } + + public static GroovyResolveResult[] getConstructorCandidates(PsiClassType classType, PsiType[] argTypes, GroovyPsiElement context) { + final PsiClassType.ClassResolveResult resolveResult = classType.resolveGenerics(); final PsiClass psiClass = resolveResult.getElement(); final PsiSubstitutor substitutor = resolveResult.getSubstitutor(); if (psiClass == null) { - return false; + return GroovyResolveResult.EMPTY_ARRAY; } final GroovyResolveResult grResult = resolveResult instanceof GroovyResolveResult ? (GroovyResolveResult)resolveResult : new GroovyResolveResultImpl(psiClass, context, substitutor, true, true); - final GroovyResolveResult[] candidates = org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.getConstructorCandidates( - context, new GroovyResolveResult[]{grResult}, argTypes); - return candidates.length == 1; + return org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.getConstructorCandidates(context, new GroovyResolveResult[]{grResult}, argTypes); } } diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index 53fa86c3cf2c..7848c88e19bf 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -32,6 +32,7 @@ + diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/expectedTypes/GroovyExpectedTypesContributor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/expectedTypes/GroovyExpectedTypesContributor.java new file mode 100644 index 000000000000..5932d1e14357 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/expectedTypes/GroovyExpectedTypesContributor.java @@ -0,0 +1,16 @@ +package org.jetbrains.plugins.groovy.lang.psi.expectedTypes; + +import com.intellij.openapi.extensions.ExtensionPointName; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; + +import java.util.List; + +/** + * @author peter + */ +public abstract class GroovyExpectedTypesContributor { + public static final ExtensionPointName EP_NAME = ExtensionPointName.create("org.intellij.groovy.expectedTypesContributor"); + + public abstract List calculateTypeConstraints(@NotNull GrExpression expression); +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/expectedTypes/GroovyExpectedTypesProvider.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/expectedTypes/GroovyExpectedTypesProvider.java index 41d04fdc276a..8e6af987e71b 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/expectedTypes/GroovyExpectedTypesProvider.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/expectedTypes/GroovyExpectedTypesProvider.java @@ -38,9 +38,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; +import java.util.*; /** * @author ven @@ -52,7 +50,27 @@ public class GroovyExpectedTypesProvider { public static TypeConstraint[] calculateTypeConstraints(GrExpression expression) { MyCalculator calculator = new MyCalculator(expression); ((GroovyPsiElement)expression.getParent()).accept(calculator); - return calculator.getResult(); + final TypeConstraint[] result = calculator.getResult(); + + List custom = new ArrayList(); + for (GroovyExpectedTypesContributor contributor : GroovyExpectedTypesContributor.EP_NAME.getExtensions()) { + custom.addAll(contributor.calculateTypeConstraints(expression)); + } + + if (!custom.isEmpty()) { + custom.addAll(0, Arrays.asList(result)); + return custom.toArray(new TypeConstraint[custom.size()]); + } + + return result; + } + + public static Set getDefaultExpectedTypes(GrExpression element) { + final LinkedHashSet result = new LinkedHashSet(); + for (TypeConstraint constraint : calculateTypeConstraints(element)) { + result.add(constraint.getDefaultType()); + } + return result; } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GppFunctionalTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GppFunctionalTest.groovy index c639aa230e5a..3ed1eda224fc 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GppFunctionalTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GppFunctionalTest.groovy @@ -235,6 +235,20 @@ class Foo { @Typed Foo bar() { return [foo: { it.subs }] } +""" + myFixture.completeBasic() + assertSameElements myFixture.lookupElementStrings, "subSequence", "substring", "substring" + } + + public void testClosureInListInstantiation() throws Exception { + myFixture.configureByText "a.groovy", """ +class Foo { + def Foo(int a, Function1 f) {} +} + +@Typed Foo foo() { + [239, { s -> s.subs }] +} """ myFixture.completeBasic() assertSameElements myFixture.lookupElementStrings, "subSequence", "substring", "substring"