From d6dd392ee99907cfce9285c167bde99c2261ba82 Mon Sep 17 00:00:00 2001 From: "Maxim.Medvedev" Date: Fri, 18 May 2012 14:34:56 +0400 Subject: [PATCH] IDEA-63669 Groovy: [ .. ].property is the same as [ .. ]*.property --- .../findUsages/GDKSuperMethodSearcher.java | 3 +- .../lang/psi/api/GroovyResolveResult.java | 13 +- .../groovy/lang/psi/api/SpreadState.java | 55 +++++ .../groovy/lang/psi/impl/GrSpreadType.java | 78 +++++++ .../psi/impl/GroovyResolveResultImpl.java | 36 ++-- .../GrConstructorInvocationImpl.java | 2 +- .../CompleteReferenceExpression.java | 12 +- .../GrReferenceExpressionImpl.java | 191 ++++++++++-------- .../expressions/GrReferenceResolveUtil.java | 106 +++++----- .../statements/expressions/TypesUtil.java | 51 ++++- .../DefaultCallExpressionTypeCalculator.java | 65 +++--- .../expressions/path/GrDGMTypeCalculator.java | 49 +---- .../expressions/path/GrIndexPropertyImpl.java | 2 +- .../enumConstant/GrEnumConstantImpl.java | 4 +- .../types/GrCodeReferenceElementImpl.java | 2 +- .../groovy/lang/psi/util/GdkMethodUtil.java | 12 +- .../plugins/groovy/lang/psi/util/PsiUtil.java | 2 +- .../groovy/lang/resolve/ResolveUtil.java | 54 ++--- .../processors/AccessorResolverProcessor.java | 4 +- .../processors/MethodResolverProcessor.java | 7 +- .../resolve/processors/ResolverProcessor.java | 4 +- .../lang/resolve/ResolveMethodTest.groovy | 2 +- .../lang/resolve/TypeInferenceTest.groovy | 51 +++++ 23 files changed, 518 insertions(+), 287 deletions(-) create mode 100644 plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/SpreadState.java create mode 100644 plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrSpreadType.java diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GDKSuperMethodSearcher.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GDKSuperMethodSearcher.java index 3e604a744937..3f4182719b1d 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GDKSuperMethodSearcher.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GDKSuperMethodSearcher.java @@ -61,7 +61,8 @@ public class GDKSuperMethodSearcher implements QueryExecutor SPREAD_STATE = Key.create("Spread state"); + + private final PsiType containerType; + @Nullable private final SpreadState innerState; + + public SpreadState(PsiType type, @Nullable SpreadState state) { + containerType = type; + innerState = state; + } + + public PsiType getContainerType() { + return containerType; + } + + @Nullable + public SpreadState getInnerState() { + return innerState; + } + + public static SpreadState create(PsiType type, @Nullable SpreadState state) { + return new SpreadState(type, state); + } + + public static PsiType apply(PsiType item, @Nullable SpreadState state, Project project) { + if (state == null) return item; + return apply(TypesUtil.createSimilarCollection(state.getContainerType(), project, item), state.getInnerState(), project); + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrSpreadType.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrSpreadType.java new file mode 100644 index 000000000000..99b1556901de --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrSpreadType.java @@ -0,0 +1,78 @@ +/* + * 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 org.jetbrains.plugins.groovy.lang.psi.impl; + +import com.intellij.openapi.project.Project; +import com.intellij.pom.java.LanguageLevel; +import com.intellij.psi.JavaPsiFacade; +import com.intellij.psi.PsiClassType; +import com.intellij.psi.PsiType; +import com.intellij.psi.search.GlobalSearchScope; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; + +/** + * @author Max Medvedev + */ +public class GrSpreadType extends GrLiteralClassType { + + private PsiType myType; + + public GrSpreadType(PsiType original, PsiType containerType, GlobalSearchScope scope) { + this(original, containerType, LanguageLevel.JDK_1_5, scope, JavaPsiFacade.getInstance(scope.getProject())); + } + + public GrSpreadType(PsiType original, PsiType containerType, LanguageLevel languageLevel, GlobalSearchScope scope, JavaPsiFacade facade) { + super(languageLevel, scope, facade); + + final Project project = facade.getProject(); + myType = TypesUtil.createSimilarCollection(containerType, project, original); + } + + @NotNull + @Override + protected String getJavaClassName() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @NotNull + @Override + public String getClassName() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @NotNull + @Override + public PsiType[] getParameters() { + return new PsiType[0]; //To change body of implemented methods use File | Settings | File Templates. + } + + @NotNull + @Override + public PsiClassType setLanguageLevel(@NotNull LanguageLevel languageLevel) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public String getInternalCanonicalText() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public boolean isValid() { + return false; //To change body of implemented methods use File | Settings | File Templates. + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyResolveResultImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyResolveResultImpl.java index e8b06b9c8eee..0f4521128023 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyResolveResultImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyResolveResultImpl.java @@ -16,10 +16,12 @@ package org.jetbrains.plugins.groovy.lang.psi.impl; import com.intellij.psi.*; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.psi.GrClassSubstitutor; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; +import org.jetbrains.plugins.groovy.lang.psi.api.SpreadState; /** * @author ven @@ -31,28 +33,30 @@ public class GroovyResolveResultImpl implements GroovyResolveResult { private final PsiSubstitutor mySubstitutor; private final boolean myIsInvokedOnProperty; - private GroovyPsiElement myCurrentFileResolveContext; + private final GroovyPsiElement myCurrentFileResolveContext; + private final SpreadState mySpreadState; - public GroovyResolveResultImpl(PsiElement element, boolean isAccessible) { - this(element, null, PsiSubstitutor.EMPTY, isAccessible, true); + public GroovyResolveResultImpl(@NotNull PsiElement element, boolean isAccessible) { + this(element, null, null, PsiSubstitutor.EMPTY, isAccessible, true, false); } - public GroovyResolveResultImpl(PsiElement element, - @Nullable GroovyPsiElement context, - PsiSubstitutor substitutor, + public GroovyResolveResultImpl(@NotNull PsiElement element, + @Nullable GroovyPsiElement resolveContext, + @Nullable SpreadState spreadState, + @NotNull PsiSubstitutor substitutor, boolean isAccessible, boolean staticsOK) { - this(element, context, substitutor, isAccessible, staticsOK, false); + this(element, resolveContext, spreadState, substitutor, isAccessible, staticsOK, false); } public GroovyResolveResultImpl(PsiClassType.ClassResolveResult classResolveResult) { - this(classResolveResult.getElement(), null, classResolveResult.getSubstitutor(), classResolveResult.isAccessible(), - classResolveResult.isStaticsScopeCorrect()); + this(classResolveResult.getElement(), null, null, classResolveResult.getSubstitutor(), classResolveResult.isAccessible(), classResolveResult.isStaticsScopeCorrect(), false); } - public GroovyResolveResultImpl(PsiElement element, - GroovyPsiElement resolveContext, - PsiSubstitutor substitutor, + public GroovyResolveResultImpl(@NotNull PsiElement element, + @Nullable GroovyPsiElement resolveContext, + @Nullable SpreadState spreadState, + @NotNull PsiSubstitutor substitutor, boolean isAccessible, boolean staticsOK, boolean isInvokedOnProperty) { @@ -62,8 +66,10 @@ public class GroovyResolveResultImpl implements GroovyResolveResult { mySubstitutor = substitutor; myIsStaticsOK = staticsOK; myIsInvokedOnProperty = isInvokedOnProperty; + mySpreadState = spreadState; } + @NotNull public PsiSubstitutor getSubstitutor() { return mySubstitutor; } @@ -108,6 +114,7 @@ public class GroovyResolveResultImpl implements GroovyResolveResult { return result; } + @Nullable public GroovyPsiElement getCurrentFileResolveContext() { return myCurrentFileResolveContext; } @@ -116,6 +123,11 @@ public class GroovyResolveResultImpl implements GroovyResolveResult { return myIsInvokedOnProperty; } + @Override + public SpreadState getSpreadState() { + return mySpreadState; + } + @Override public String toString() { return "GroovyResolveResultImpl{" + diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrConstructorInvocationImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrConstructorInvocationImpl.java index b1eb3ea33932..e6c630bef390 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrConstructorInvocationImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrConstructorInvocationImpl.java @@ -95,7 +95,7 @@ public class GrConstructorInvocationImpl extends GrCallImpl implements GrConstru } public GroovyResolveResult[] multiResolveClass() { - return new GroovyResolveResult[]{new GroovyResolveResultImpl(getDelegatedClass(), this, PsiSubstitutor.EMPTY, true, true)}; + return new GroovyResolveResult[]{new GroovyResolveResultImpl(getDelegatedClass(), this, null, PsiSubstitutor.EMPTY, true, true)}; } public PsiMethod resolveMethod() { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/CompleteReferenceExpression.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/CompleteReferenceExpression.java index 080bc3d9e4cc..75e809c5b795 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/CompleteReferenceExpression.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/CompleteReferenceExpression.java @@ -35,6 +35,7 @@ import org.jetbrains.plugins.groovy.lang.completion.GroovyCompletionUtil; import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; +import org.jetbrains.plugins.groovy.lang.psi.api.SpreadState; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; @@ -117,7 +118,7 @@ public class CompleteReferenceExpression { getVariantsFromQualifierForSpreadOperator(refExpr, processor, qualifier); } } - ResolveUtil.processCategoryMembers(refExpr, processor); + ResolveUtil.processCategoryMembers(refExpr, processor, ResolveState.initial()); } private static void getVariantsFromQualifierForSpreadOperator(GrReferenceExpression refExpr, @@ -206,8 +207,8 @@ public class CompleteReferenceExpression { final GrPropertyForCompletion field = new GrPropertyForCompletion(method, name, type); if (resolveResult != null) { - return new GroovyResolveResultImpl(field, resolveResult.getCurrentFileResolveContext(), resolveResult.getSubstitutor(), - resolveResult.isAccessible(), resolveResult.isStaticsOK()); + return new GroovyResolveResultImpl(field, resolveResult.getCurrentFileResolveContext(), resolveResult.getSpreadState(), + resolveResult.getSubstitutor(), resolveResult.isAccessible(), resolveResult.isStaticsOK(), false); } else { return new GroovyResolveResultImpl(field, true); @@ -377,12 +378,13 @@ public class CompleteReferenceExpression { boolean isAccessible = isAccessible(namedElement); final GroovyPsiElement resolveContext = state.get(RESOLVE_CONTEXT); + final SpreadState spreadState = state.get(SpreadState.SPREAD_STATE); boolean isStaticsOK = isStaticsOK(namedElement, resolveContext, myParameters.getInvocationCount() <= 1); PsiSubstitutor substitutor = state.get(PsiSubstitutor.KEY); if (substitutor == null) substitutor = PsiSubstitutor.EMPTY; - consume(new GroovyResolveResultImpl(namedElement, resolveContext, substitutor, isAccessible, isStaticsOK)); + consume(new GroovyResolveResultImpl(namedElement, resolveContext, spreadState, substitutor, isAccessible, isStaticsOK)); return true; } @@ -407,7 +409,7 @@ public class CompleteReferenceExpression { element = ((GrReflectedMethod)element).getBaseMethod(); if (!myProcessedMethodWithOptionalParams.add((GrMethod)element)) return; - result = new GroovyResolveResultImpl(element, result.getCurrentFileResolveContext(), + result = new GroovyResolveResultImpl(element, result.getCurrentFileResolveContext(), result.getSpreadState(), result.getSubstitutor(), result.isAccessible(), result.isStaticsOK(), result.isInvokedOnProperty()); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java index 8cdc0970c303..6c54eeebb8d3 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java @@ -42,6 +42,7 @@ import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; +import org.jetbrains.plugins.groovy.lang.psi.api.SpreadState; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; @@ -64,7 +65,8 @@ import java.util.Arrays; import java.util.EnumSet; import java.util.List; -import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.*; +import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mAT; +import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mMEMBER_POINTER; /** * @author ilyas @@ -121,7 +123,8 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl accessorResults = new ArrayList(); for (String accessorName : accessorNames) { - AccessorResolverProcessor accessorResolver = new AccessorResolverProcessor(accessorName, name, this, !isLValue, false, getThisType(), getTypeArguments()); + AccessorResolverProcessor accessorResolver = + new AccessorResolverProcessor(accessorName, name, this, !isLValue, false, getThisType(), getTypeArguments()); GrReferenceResolveUtil.resolveImpl(accessorResolver, this); final GroovyResolveResult[] candidates = accessorResolver.getCandidates(); @@ -198,7 +202,6 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl>() { - @Override - public Result> compute() { - return Result.create(doResolveByShape(false, null), PsiModificationTracker.MODIFICATION_COUNT); - } - }); + return CachedValuesManager.getManager(getProject()) + .getCachedValue(this, new CachedValueProvider>() { + @Override + public Result> compute() { + return Result.create(doResolveByShape(false, null), PsiModificationTracker.MODIFICATION_COUNT); + } + }); } private Pair doResolveByShape(boolean allVariants, @Nullable GrExpression upToArgument) { @@ -329,7 +334,10 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl() { @Override public Result compute() { - GroovyResolveResult[] value = RecursionManager.doPreventingRecursion(GrReferenceExpressionImpl.this, true, new Computable() { - @Override - public GroovyResolveResult[] compute() { - return doPolyResolve(false, false); - } - }); + GroovyResolveResult[] value = + RecursionManager.doPreventingRecursion(GrReferenceExpressionImpl.this, true, new Computable() { + @Override + public GroovyResolveResult[] compute() { + return doPolyResolve(false, false); + } + }); if (value == null) { value = GroovyResolveResult.EMPTY_ARRAY; } @@ -459,8 +468,6 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl 1) { PsiParameter last = parameters[parameters.length - 1]; - return TypesUtil.substituteBoxAndNormalizeType(last.getType(), candidate.getSubstitutor(), this); + return TypesUtil.substituteBoxAndNormalizeType(last.getType(), candidate.getSubstitutor(), candidate.getSpreadState(), this); } } return null; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/enumConstant/GrEnumConstantImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/enumConstant/GrEnumConstantImpl.java index be8c8d08977b..afd5148ac5c0 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/enumConstant/GrEnumConstantImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/enumConstant/GrEnumConstantImpl.java @@ -102,8 +102,7 @@ public class GrEnumConstantImpl extends GrFieldImpl implements GrEnumConstant { } public GroovyResolveResult[] multiResolveClass() { - final PsiClass psiClass = getContainingClass(); - GroovyResolveResult result = new GroovyResolveResultImpl(psiClass, this, PsiSubstitutor.EMPTY, true, true); + GroovyResolveResult result = new GroovyResolveResultImpl(getContainingClass(), this, null, PsiSubstitutor.EMPTY, true, true); return new GroovyResolveResult[]{result}; } @@ -135,7 +134,6 @@ public class GrEnumConstantImpl extends GrFieldImpl implements GrEnumConstant { public GrExpression[] getExpressionArguments() { final GrArgumentList argumentList = getArgumentList(); return argumentList == null ? GrExpression.EMPTY_ARRAY : argumentList.getExpressionArguments(); - } @NotNull diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/types/GrCodeReferenceElementImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/types/GrCodeReferenceElementImpl.java index 239e24f42bc7..0aed67b8c24e 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/types/GrCodeReferenceElementImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/types/GrCodeReferenceElementImpl.java @@ -376,7 +376,7 @@ public class GrCodeReferenceElementImpl extends GrReferenceElementImplp ''', "$JAVA_UTIL_MAP<$JAVA_LANG_STRING,$JAVA_LANG_STRING>") } + void testSpread1() { + myFixture.addClass('''\ +class A { + String getString() {return "a";} +}''') + doTest('''\ +[new A()].string +''', "$JAVA_UTIL_ARRAY_LIST<$JAVA_LANG_STRING>") + } + + void testSpread2() { + + myFixture.addClass('''\ +class A { + String getString() {return "a";} +}''') + doTest('''\ +class Cat { + static getFoo(String b) {2} +} +use(Cat) { + [new A()].string.foo +} +''', "$JAVA_UTIL_ARRAY_LIST<$JAVA_LANG_INTEGER>") + } + + void testSpread3() { + myFixture.addClass('''\ +class A { + String getString() {return "a";} +}''') + doTest('''\ +[[new A()]].string +''', "$JAVA_UTIL_ARRAY_LIST<$JAVA_UTIL_ARRAY_LIST<$JAVA_LANG_STRING>>") + } + + void testSpread4() { + myFixture.addClass('''\ +class A { + String getString() {return "a";} +}''') + doTest('''\ +class Cat { + static getFoo(String b) {2} +} + +use(Cat){ + [[new A()]].string.foo +} +''', "$JAVA_UTIL_ARRAY_LIST<$JAVA_UTIL_ARRAY_LIST<$JAVA_LANG_INTEGER>>") + } private void doTest(String text, String type) { def file = myFixture.configureByText('_.groovy', text)