IDEA-63669 Groovy: [ .. ].property is the same as [ .. ]*.property

This commit is contained in:
Maxim.Medvedev
2012-05-18 14:40:10 +04:00
parent bcc709fe57
commit d6dd392ee9
23 changed files with 518 additions and 287 deletions
@@ -61,7 +61,8 @@ public class GDKSuperMethodSearcher implements QueryExecutor<MethodSignatureBack
final String name = method.getName();
final MethodResolverProcessor processor = new MethodResolverProcessor(name, ((GrMethod)method), false, null, null, PsiType.EMPTY_ARRAY);
ResolveUtil.processNonCodeMembers(JavaPsiFacade.getElementFactory(project).createType(psiClass), processor, (GrMethod)method);
ResolveUtil.processNonCodeMembers(JavaPsiFacade.getElementFactory(project).createType(psiClass), processor, (GrMethod)method,
ResolveState.initial());
final GroovyResolveResult[] candidates = processor.getCandidates();
@@ -15,9 +15,10 @@
*/
package org.jetbrains.plugins.groovy.lang.psi.api;
import com.intellij.psi.ResolveResult;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiSubstitutor;
import com.intellij.psi.ResolveResult;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
@@ -34,10 +35,14 @@ public interface GroovyResolveResult extends ResolveResult {
@Nullable
GroovyPsiElement getCurrentFileResolveContext();
@NotNull
PsiSubstitutor getSubstitutor();
boolean isInvokedOnProperty();
@Nullable
SpreadState getSpreadState();
GroovyResolveResult EMPTY_RESULT = new GroovyResolveResult() {
public boolean isAccessible() {
return false;
@@ -51,6 +56,7 @@ public interface GroovyResolveResult extends ResolveResult {
return true;
}
@NotNull
public PsiSubstitutor getSubstitutor() {
return PsiSubstitutor.EMPTY;
}
@@ -68,5 +74,10 @@ public interface GroovyResolveResult extends ResolveResult {
public boolean isInvokedOnProperty() {
return false;
}
@Override
public SpreadState getSpreadState() {
return null;
}
};
}
@@ -0,0 +1,55 @@
/*
* 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.api;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.psi.PsiType;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
/**
* @author Max Medvedev
*/
public final class SpreadState {
public static final Key<SpreadState> 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);
}
}
@@ -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.
}
}
@@ -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{" +
@@ -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() {
@@ -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());
}
@@ -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<GrExpressi
if (!InheritanceUtil.isInheritor(containingClass, CommonClassNames.JAVA_UTIL_MAP)) continue;
final String name = containingClass.getQualifiedName();
if (name != null && name.startsWith("java.")) continue;
if (containingClass.getLanguage() != GroovyFileType.GROOVY_LANGUAGE && !InheritanceUtil.isInheritor(containingClass, GroovyCommonClassNames.DEFAULT_BASE_CLASS_NAME)) {
if (containingClass.getLanguage() != GroovyFileType.GROOVY_LANGUAGE &&
!InheritanceUtil.isInheritor(containingClass, GroovyCommonClassNames.DEFAULT_BASE_CLASS_NAME)) {
continue;
}
}
@@ -174,7 +177,8 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl<GrExpressi
String[] accessorNames = isLValue ? GroovyPropertyUtils.suggestSettersName(name) : GroovyPropertyUtils.suggestGettersName(name);
List<GroovyResolveResult> accessorResults = new ArrayList<GroovyResolveResult>();
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<GrExpressi
}
public GroovyResolveResult[] getCallVariants(GrExpression upToArgument) {
return resolveMethodOrProperty(true, upToArgument, true);
}
@@ -221,13 +224,12 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl<GrExpressi
}
}
}
}
}
/**
* priority: inside class C: local variable, c.method, c.property, c.getter
* in other places: local variable, c.method, c.getter, c.property
* in other places: local variable, c.method, c.getter, c.property
*/
private GroovyResolveResult[] resolveMethodOrProperty(boolean allVariants, @Nullable GrExpression upToArgument, boolean genericsMatter) {
final String name = getReferenceName();
@@ -260,7 +262,8 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl<GrExpressi
for (GroovyResolveResult result : shapeResults.second) {
final ResolveState state = ResolveState.initial().
put(PsiSubstitutor.KEY, result.getSubstitutor()).
put(ResolverProcessor.RESOLVE_CONTEXT, result.getCurrentFileResolveContext());
put(ResolverProcessor.RESOLVE_CONTEXT, result.getCurrentFileResolveContext()).
put(SpreadState.SPREAD_STATE, result.getSpreadState());
methodResolver.execute(result.getElement(), state);
}
@@ -286,7 +289,8 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl<GrExpressi
//search for getters
for (String getterName : GroovyPropertyUtils.suggestGettersName(name)) {
AccessorResolverProcessor getterResolver = new AccessorResolverProcessor(getterName, name, this, true, genericsMatter, getThisType(), getTypeArguments());
AccessorResolverProcessor getterResolver =
new AccessorResolverProcessor(getterName, name, this, true, genericsMatter, getThisType(), getTypeArguments());
GrReferenceResolveUtil.resolveImpl(getterResolver, this);
final GroovyResolveResult[] candidates = getterResolver.getCandidates(); //can be only one candidate
if (!allVariants && candidates.length == 1) {
@@ -308,12 +312,13 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl<GrExpressi
assert upToArgument == null;
return CachedValuesManager.getManager(getProject()).getCachedValue(this, new CachedValueProvider<Pair<Boolean, GroovyResolveResult[]>>() {
@Override
public Result<Pair<Boolean, GroovyResolveResult[]>> compute() {
return Result.create(doResolveByShape(false, null), PsiModificationTracker.MODIFICATION_COUNT);
}
});
return CachedValuesManager.getManager(getProject())
.getCachedValue(this, new CachedValueProvider<Pair<Boolean, GroovyResolveResult[]>>() {
@Override
public Result<Pair<Boolean, GroovyResolveResult[]>> compute() {
return Result.create(doResolveByShape(false, null), PsiModificationTracker.MODIFICATION_COUNT);
}
});
}
private Pair<Boolean, GroovyResolveResult[]> doResolveByShape(boolean allVariants, @Nullable GrExpression upToArgument) {
@@ -329,7 +334,10 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl<GrExpressi
return Pair.create(shapeProcessor.hasApplicableCandidates(), candidates);
}
private MethodResolverProcessor createMethodProcessor(boolean allVariants, String name, final boolean byShape, @Nullable GrExpression upToArgument) {
private MethodResolverProcessor createMethodProcessor(boolean allVariants,
String name,
final boolean byShape,
@Nullable GrExpression upToArgument) {
final PsiType[] argTypes = PsiUtil.getArgumentTypes(this, false, upToArgument);
if (byShape && argTypes != null) {
for (int i = 0; i < argTypes.length; i++) {
@@ -436,12 +444,13 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl<GrExpressi
return CachedValuesManager.getManager(getProject()).getCachedValue(this, new CachedValueProvider<GroovyResolveResult[]>() {
@Override
public Result<GroovyResolveResult[]> compute() {
GroovyResolveResult[] value = RecursionManager.doPreventingRecursion(GrReferenceExpressionImpl.this, true, new Computable<GroovyResolveResult[]>() {
@Override
public GroovyResolveResult[] compute() {
return doPolyResolve(false, false);
}
});
GroovyResolveResult[] value =
RecursionManager.doPreventingRecursion(GrReferenceExpressionImpl.this, true, new Computable<GroovyResolveResult[]>() {
@Override
public GroovyResolveResult[] compute() {
return doPolyResolve(false, false);
}
});
if (value == null) {
value = GroovyResolveResult.EMPTY_ARRAY;
}
@@ -459,8 +468,6 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl<GrExpressi
private static final OurTypesCalculator TYPES_CALCULATOR = new OurTypesCalculator();
public PsiType getNominalType() {
IElementType dotType = getDotTokenType();
final GroovyResolveResult resolveResult = advancedResolve();
PsiElement resolved = resolveResult.getElement();
@@ -471,71 +478,93 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl<GrExpressi
}
}
IElementType dotType = getDotTokenType();
if (dotType == mMEMBER_POINTER) {
return GrClosureType.create(multiResolve(false), this);
}
PsiType result = null;
JavaPsiFacade facade = JavaPsiFacade.getInstance(getProject());
if (resolved == null && !"class".equals(getReferenceName())) {
resolved = getReference().resolve();
}
if (resolved instanceof PsiClass) {
if (getParent() instanceof GrReferenceExpression) {
result = facade.getElementFactory().createType((PsiClass) resolved);
} else {
result = TypesUtil
.createJavaLangClassType(facade.getElementFactory().createType((PsiClass)resolved), getProject(), getResolveScope());
}
} else if (resolved instanceof GrVariable) {
result = ((GrVariable) resolved).getDeclaredType();
} else if (resolved instanceof PsiVariable) {
result = ((PsiVariable) resolved).getType();
} else
if (resolved instanceof PsiMethod) {
PsiMethod method = (PsiMethod) resolved;
if (PropertyUtil.isSimplePropertySetter(method) && !method.getName().equals(getReferenceName())) {
result = method.getParameterList().getParameters()[0].getType();
} else {
PsiClass containingClass = method.getContainingClass();
if (containingClass != null && CommonClassNames.JAVA_LANG_OBJECT.equals(containingClass.getQualifiedName()) &&
"getClass".equals(method.getName())) {
result = TypesUtil.createJavaLangClassType(getQualifierType(), getProject(), getResolveScope());
} else {
result = PsiUtil.getSmartReturnType(method);
}
PsiType result = getNominalTypeInner(resolved);
if (result == null) return null;
result = TypesUtil.substituteBoxAndNormalizeType(result, resolveResult.getSubstitutor(), resolveResult.getSpreadState(), this);
return result;
}
@Nullable
private PsiType getNominalTypeInner(PsiElement resolved) {
if (resolved == null && !"class".equals(getReferenceName())) {
resolved = resolve();
}
if (resolved instanceof PsiClass) {
final PsiElementFactory factory = JavaPsiFacade.getInstance(getProject()).getElementFactory();
if (getParent() instanceof GrReferenceExpression) {
return factory.createType((PsiClass)resolved);
}
} else if (resolved instanceof GrReferenceExpression) {
else {
return TypesUtil.createJavaLangClassType(factory.createType((PsiClass)resolved), getProject(), getResolveScope());
}
}
if (resolved instanceof GrVariable) {
return ((GrVariable)resolved).getDeclaredType();
}
if (resolved instanceof PsiVariable) {
return ((PsiVariable)resolved).getType();
}
if (resolved instanceof PsiMethod) {
PsiMethod method = (PsiMethod)resolved;
if (PropertyUtil.isSimplePropertySetter(method) && !method.getName().equals(getReferenceName())) {
return method.getParameterList().getParameters()[0].getType();
}
//'class' property with explicit generic
PsiClass containingClass = method.getContainingClass();
if (containingClass != null &&
CommonClassNames.JAVA_LANG_OBJECT.equals(containingClass.getQualifiedName()) &&
"getClass".equals(method.getName())) {
return TypesUtil.createJavaLangClassType(getQualifierType(), getProject(), getResolveScope());
}
return PsiUtil.getSmartReturnType(method);
}
if (resolved instanceof GrReferenceExpression) {
PsiElement parent = resolved.getParent();
if (parent instanceof GrAssignmentExpression) {
GrAssignmentExpression assignment = (GrAssignmentExpression) parent;
GrAssignmentExpression assignment = (GrAssignmentExpression)parent;
if (resolved.equals(assignment.getLValue())) {
GrExpression rValue = assignment.getRValue();
if (rValue != null) {
PsiType rType = rValue.getType();
if (rType != null) result = rType;
if (rType != null) {
return rType;
}
}
}
}
}
else if (resolved == null) {
GrExpression qualifier = getQualifierExpression();
if (resolved == null) {
if ("class".equals(getReferenceName())) {
result = TypesUtil.createJavaLangClassType(getQualifierType(), getProject(), getResolveScope());
return TypesUtil.createJavaLangClassType(getQualifierType(), getProject(), getResolveScope());
}
else {
if (qualifier != null) {
PsiType qType = qualifier.getType();
if (qType instanceof PsiClassType) {
PsiClassType.ClassResolveResult qResult = ((PsiClassType)qType).resolveGenerics();
PsiClass clazz = qResult.getElement();
if (clazz != null) {
PsiClass mapClass = facade.findClass(CommonClassNames.JAVA_UTIL_MAP, getResolveScope());
if (mapClass != null && mapClass.getTypeParameters().length == 2) {
PsiSubstitutor substitutor = TypeConversionUtil.getClassSubstitutor(mapClass, clazz, qResult.getSubstitutor());
if (substitutor != null) {
result = TypeConversionUtil.erasure(substitutor.substitute(mapClass.getTypeParameters()[1]));
}
//map access
GrExpression qualifier = getQualifierExpression();
if (qualifier != null) {
PsiType qType = qualifier.getType();
if (qType instanceof PsiClassType) {
PsiClassType.ClassResolveResult qResult = ((PsiClassType)qType).resolveGenerics();
PsiClass clazz = qResult.getElement();
if (clazz != null) {
PsiClass mapClass = JavaPsiFacade.getInstance(getProject()).findClass(CommonClassNames.JAVA_UTIL_MAP, getResolveScope());
if (mapClass != null && mapClass.getTypeParameters().length == 2) {
PsiSubstitutor substitutor = TypeConversionUtil.getClassSubstitutor(mapClass, clazz, qResult.getSubstitutor());
if (substitutor != null) {
return TypeConversionUtil.erasure(substitutor.substitute(mapClass.getTypeParameters()[1]));
}
}
}
@@ -543,14 +572,7 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl<GrExpressi
}
}
if (result != null) {
result = TypesUtil.substituteBoxAndNormalizeType(result, resolveResult.getSubstitutor(), this);
}
if (dotType != mSPREAD_DOT) {
return result;
} else {
return ResolveUtil.getListTypeForSpreadOperator(this, result);
}
return null;
}
@Nullable
@@ -588,7 +610,7 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl<GrExpressi
if (!resolved.isValid()) {
throw new AssertionError("Invalid target of a valid reference");
}
return ((GrVariable) resolved).getTypeGroovy();
return ((GrVariable)resolved).getTypeGroovy();
}
}
@@ -598,7 +620,7 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl<GrExpressi
if (nominal == null) return inferred;
if (!TypeConversionUtil.isAssignable(nominal, inferred, false)) {
final PsiElement resolved = refExpr.resolve();
if (resolved instanceof GrVariable && ((GrVariable) resolved).getTypeElementGroovy() != null) {
if (resolved instanceof GrVariable && ((GrVariable)resolved).getTypeElementGroovy() != null) {
return nominal; //see GRVY-487
}
}
@@ -650,7 +672,8 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl<GrExpressi
return results;
default:
return GroovyResolveResult.EMPTY_ARRAY;
} }
}
}
private PsiType getThisType() {
GrExpression qualifier = getQualifierExpression();
@@ -755,13 +778,13 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl<GrExpressi
public GroovyResolveResult advancedResolve() {
ResolveResult[] results = ResolveCache.getInstance(getProject()).resolveWithCaching(this, POLY_RESOLVER, true, false);
return results.length == 1 ? (GroovyResolveResult) results[0] : GroovyResolveResult.EMPTY_RESULT;
return results.length == 1 ? (GroovyResolveResult)results[0] : GroovyResolveResult.EMPTY_RESULT;
}
@NotNull
public GroovyResolveResult[] multiResolve(boolean incomplete) { //incomplete means we do not take arguments into consideration
final ResolveResult[] results = ResolveCache.getInstance(getProject()).resolveWithCaching(this, POLY_RESOLVER, true, incomplete);
return results.length == 0 ? GroovyResolveResult.EMPTY_ARRAY : (GroovyResolveResult[])results;
return results.length == 0 ? GroovyResolveResult.EMPTY_ARRAY : (GroovyResolveResult[])results;
}
@Override
@@ -18,9 +18,13 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions;
import com.intellij.psi.*;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.util.TypeConversionUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
import org.jetbrains.plugins.groovy.lang.psi.api.SpreadState;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrThisReferenceExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
@@ -39,10 +43,11 @@ import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mSPREAD_D
* @author Medvedev Max
*/
public class GrReferenceResolveUtil {
private GrReferenceResolveUtil() {
}
public static boolean resolveImpl(ResolverProcessor processor, GrReferenceExpression place) {
static boolean resolveImpl(ResolverProcessor processor, GrReferenceExpression place) {
GrExpression qualifier = place.getQualifier();
if (qualifier == null) {
if (processor instanceof MethodResolverProcessor || processor instanceof CompletionProcessor) {
@@ -62,7 +67,14 @@ public class GrReferenceResolveUtil {
}
else {
if (place.getDotTokenType() == mSPREAD_DOT) {
if (!processQualifierForSpreadDot(processor, qualifier, place)) return false;
final PsiType qtype = qualifier.getType();
final PsiType componentType = getComponentTypeForSpreadDot(qtype, place);
if (componentType != null) {
final ResolveState state = ResolveState.initial()
.put(ResolverProcessor.RESOLVE_CONTEXT, qualifier)
.put(SpreadState.SPREAD_STATE, SpreadState.create(qtype, null));
if (!processQualifierType(processor, componentType, state, place)) return false;
}
}
else {
if (!processQualifier(processor, qualifier, place)) return false;
@@ -88,10 +100,10 @@ public class GrReferenceResolveUtil {
return true;
}
public static boolean processIfJavaLangClass(ResolverProcessor processor,
private static boolean processIfJavaLangClass(ResolverProcessor processor,
PsiType type,
GroovyPsiElement resolveContext,
GroovyPsiElement place) {
GrReferenceExpression place) {
if (!(type instanceof PsiClassType)) return true;
final PsiClass psiClass = ((PsiClassType)type).resolve();
@@ -100,70 +112,60 @@ public class GrReferenceResolveUtil {
final PsiType[] params = ((PsiClassType)type).getParameters();
if (params.length != 1) return true;
if (!processClassQualifierType(processor, params[0], resolveContext, place)) return false;
if (!processQualifierType(processor, params[0], ResolveState.initial().put(ResolverProcessor.RESOLVE_CONTEXT, resolveContext), place)) return false;
return true;
}
public static boolean processQualifierForSpreadDot(ResolverProcessor processor, GrExpression qualifier, GroovyPsiElement place) {
PsiType qualifierType = qualifier.getType();
@Nullable
private static PsiType getComponentTypeForSpreadDot(@Nullable PsiType qualifierType, @NotNull GroovyPsiElement place) {
if (qualifierType instanceof PsiArrayType) {
if (!processClassQualifierType(processor, ((PsiArrayType)qualifierType).getComponentType(), qualifier, place)) return false;
return true;
return ((PsiArrayType)qualifierType).getComponentType();
}
//process for collections
if (!(qualifierType instanceof PsiClassType)) return true;
if (!(qualifierType instanceof PsiClassType)) return null;
PsiClassType.ClassResolveResult result = ((PsiClassType)qualifierType).resolveGenerics();
PsiClass clazz = result.getElement();
if (clazz == null) return true;
if (clazz == null) return null;
PsiClass collection = GroovyPsiManager.getInstance(place.getProject())
.findClassWithCache(CommonClassNames.JAVA_UTIL_COLLECTION, place.getResolveScope());
if (collection == null || collection.getTypeParameters().length != 1) return true;
PsiClass collection = GroovyPsiManager.getInstance(place.getProject()).findClassWithCache(CommonClassNames.JAVA_UTIL_COLLECTION,
place.getResolveScope());
if (collection == null || collection.getTypeParameters().length != 1) return null;
PsiSubstitutor substitutor = TypeConversionUtil.getClassSubstitutor(collection, clazz, result.getSubstitutor());
if (substitutor == null) return true;
if (substitutor == null) return null;
PsiType componentType = substitutor.substitute(collection.getTypeParameters()[0]);
if (componentType == null) return true;
if (!processClassQualifierType(processor, componentType, qualifier, place)) return false;
return true;
return substitutor.substitute(collection.getTypeParameters()[0]);
}
public static boolean processQualifier(PsiScopeProcessor processor, GrExpression qualifier, GroovyPsiElement place) {
public static boolean processQualifier(PsiScopeProcessor processor, GrExpression qualifier, GrReferenceExpression place) {
PsiType qualifierType = qualifier.getType();
ResolveState state = ResolveState.initial().put(ResolverProcessor.RESOLVE_CONTEXT, qualifier);
if (qualifierType == null) {
if (qualifier instanceof GrReferenceExpression) {
PsiElement resolved = ((GrReferenceExpression)qualifier).resolve();
if (resolved instanceof PsiPackage) {
final ResolveState state = ResolveState.initial().put(ResolverProcessor.RESOLVE_CONTEXT, qualifier);
if (!resolved.processDeclarations(processor, state, null, place)) return false;
}
else {
qualifierType = TypesUtil.getJavaLangObject(place);
if (!processClassQualifierType(processor, qualifierType, qualifier, place)) return false;
if (!processQualifierType(processor, qualifierType, state, place)) return false;
}
}
}
else {
if (qualifierType instanceof PsiIntersectionType) {
for (PsiType conjunct : ((PsiIntersectionType)qualifierType).getConjuncts()) {
if (!processClassQualifierType(processor, conjunct, qualifier, place)) return false;
if (!processQualifierType(processor, conjunct, state, place)) return false;
}
}
else {
if (!processClassQualifierType(processor, qualifierType, qualifier, place)) return false;
if (!processQualifierType(processor, qualifierType, state, place)) return false;
if (qualifier instanceof GrReferenceExpression) {
PsiElement resolved = ((GrReferenceExpression)qualifier).resolve();
if (resolved instanceof PsiClass) { //omitted .class
PsiClass javaLangClass = PsiUtil.getJavaLangClass(resolved, place.getResolveScope());
if (javaLangClass != null) {
ResolveState state = ResolveState.initial();
PsiTypeParameter[] typeParameters = javaLangClass.getTypeParameters();
PsiSubstitutor substitutor = state.get(PsiSubstitutor.KEY);
if (substitutor == null) substitutor = PsiSubstitutor.EMPTY;
@@ -172,8 +174,7 @@ public class GrReferenceResolveUtil {
state = state.put(PsiSubstitutor.KEY, substitutor);
}
if (!javaLangClass.processDeclarations(processor, state, null, place)) return false;
PsiType javaLangClassType =
JavaPsiFacade.getInstance(place.getProject()).getElementFactory().createType(javaLangClass, substitutor);
PsiType javaLangClassType = TypesUtil.createJavaLangClassType(qualifierType, place.getProject(), place.getResolveScope());
if (!ResolveUtil.processNonCodeMembers(javaLangClassType, processor, place, state)) return false;
}
}
@@ -183,39 +184,42 @@ public class GrReferenceResolveUtil {
return true;
}
public static boolean processClassQualifierType(PsiScopeProcessor processor,
PsiType qualifierType,
GroovyPsiElement resolveContext,
GroovyPsiElement place) {
final ResolveState state;
if (qualifierType instanceof PsiDisjunctionType) {
qualifierType = ((PsiDisjunctionType)qualifierType).getLeastUpperBound();
private static boolean processQualifierType(final PsiScopeProcessor processor,
final PsiType originalQualifierType,
final ResolveState state,
final GrReferenceExpression place) {
PsiType qualifierType = originalQualifierType instanceof PsiDisjunctionType
? ((PsiDisjunctionType)originalQualifierType).getLeastUpperBound()
: originalQualifierType;
if (qualifierType instanceof PsiIntersectionType) {
for (PsiType conjunct : ((PsiIntersectionType)qualifierType).getConjuncts()) {
if (!processQualifierType(processor, conjunct, state, place)) return false;
}
return true;
}
if (qualifierType instanceof PsiClassType) {
PsiClassType.ClassResolveResult qualifierResult = ((PsiClassType)qualifierType).resolveGenerics();
PsiClass qualifierClass = qualifierResult.getElement();
state = ResolveState.initial().put(PsiSubstitutor.KEY, qualifierResult.getSubstitutor())
.put(ResolverProcessor.RESOLVE_CONTEXT, resolveContext);
if (qualifierClass != null) {
if (!qualifierClass.processDeclarations(processor, state, null, place)) return false;
if (!qualifierClass.processDeclarations(processor, state.put(PsiSubstitutor.KEY, qualifierResult.getSubstitutor()), null, place)) return false;
}
}
else if (qualifierType instanceof PsiArrayType) {
final GrTypeDefinition arrayClass = GroovyPsiManager.getInstance(place.getProject()).getArrayClass();
state = ResolveState.initial();
if (!arrayClass.processDeclarations(processor, state, null, place)) return false;
}
else if (qualifierType instanceof PsiIntersectionType) {
for (PsiType conjunct : ((PsiIntersectionType)qualifierType).getConjuncts()) {
if (!processClassQualifierType(processor, conjunct, resolveContext, place)) return false;
if (!(place.getParent() instanceof GrMethodCall)) {
final PsiType componentType = getComponentTypeForSpreadDot(qualifierType, place);
if (componentType != null) {
final SpreadState spreadState = state.get(SpreadState.SPREAD_STATE);
processQualifierType(processor, componentType, state.put(SpreadState.SPREAD_STATE, SpreadState.create(qualifierType, spreadState)), place);
}
return true;
}
else {
state = ResolveState.initial();
}
if (!ResolveUtil.processCategoryMembers(place, processor)) return false;
if (!ResolveUtil.processCategoryMembers(place, processor, state)) return false;
if (!ResolveUtil.processNonCodeMembers(qualifierType, processor, place, state)) return false;
return true;
}
@@ -35,6 +35,7 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.GrTypeConverter;
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.signatures.GrClosureSignature;
import org.jetbrains.plugins.groovy.lang.psi.api.signatures.GrSignature;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrBinaryExpression;
@@ -688,14 +689,16 @@ public class TypesUtil {
@Nullable
public static PsiType substituteBoxAndNormalizeType(@Nullable PsiType type,
@NotNull PsiSubstitutor substitutor,
@NotNull GrExpression expression) {
@Nullable SpreadState state, @NotNull GrExpression expression) {
if (type == null) return null;
GlobalSearchScope resolveScope = expression.getResolveScope();
PsiManager manager = expression.getManager();
type = substitutor.substitute(type);
type = boxPrimitiveType(type, manager, resolveScope);
if (type == null) return null;
return PsiImplUtil.normalizeWildcardTypeByPosition(type, expression);
type = PsiImplUtil.normalizeWildcardTypeByPosition(type, expression);
type = SpreadState.apply(type, state, expression.getProject());
return type;
}
@Nullable
@@ -705,4 +708,48 @@ public class TypesUtil {
if (containerType instanceof PsiArrayType) return ((PsiArrayType)containerType).getComponentType();
return PsiUtil.extractIterableTypeParameter(containerType, false);
}
@Nullable
public static PsiClassType createSimilarCollection(PsiType collection, Project project, PsiType... itemType) {
if (InheritanceUtil.isInheritor(collection, "java.util.SortedSet")) {
return createCollection(project, "java.util.SortedSet", itemType);
}
if (InheritanceUtil.isInheritor(collection, "java.util.LinkedHashSet")) {
return createCollection(project, "java.util.LinkedHashSet", itemType);
}
if (InheritanceUtil.isInheritor(collection, "java.util.Set")) {
return createCollection(project, "java.util.HashSet", itemType);
}
if (InheritanceUtil.isInheritor(collection, "java.util.LinkedList")) {
return createCollection(project, "java.util.LInkedList", itemType);
}
if (InheritanceUtil.isInheritor(collection, "java.util.Stack")) {
return createCollection(project, "java.util.Stack", itemType);
}
if (InheritanceUtil.isInheritor(collection, "java.util.Vector")) {
return createCollection(project, "java.util.Vector", itemType);
}
if (InheritanceUtil.isInheritor(collection, "java.util.List")) {
return createCollection(project, "java.util.ArrayList", itemType);
}
if (InheritanceUtil.isInheritor(collection, "java.util.Queue")) {
return createCollection(project, "java.util.LinkedList", itemType);
}
return createCollection(project, "java.util.ArrayList", itemType);
}
@Nullable
private static PsiClassType createCollection(Project project, String collectionName, PsiType... item) {
PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
PsiClass collection =
JavaPsiFacade.getInstance(project).findClass(collectionName, GlobalSearchScope.allScope(project));
if (collection == null) return null;
PsiTypeParameter[] parameters = collection.getTypeParameters();
if (parameters.length != 1) return null;
return factory.createType(collection, item);
}
}
@@ -21,7 +21,6 @@ import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.hash.HashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
@@ -54,48 +53,56 @@ public class DefaultCallExpressionTypeCalculator extends GrCallExpressionTypeCal
PsiManager manager = callExpression.getManager();
PsiType result = null;
for (GroovyResolveResult resolveResult : resolveResults) {
PsiElement resolved = resolveResult.getElement();
PsiType returnType = null;
if (resolved instanceof PsiMethod) {
PsiMethod method = (PsiMethod) resolved;
if (resolveResult.isInvokedOnProperty()) {
final PsiType propertyType = PsiUtil.getSmartReturnType(method);
returnType = extractReturnTypeFromType(propertyType, true, callExpression);
} else {
returnType = getClosureMethodsReturnType(callExpression, refExpr, method);
if (returnType == null) {
returnType = PsiUtil.getSmartReturnType(method);
}
}
} else if (resolved instanceof GrVariable) {
PsiType refType = refExpr.getType();
final PsiType type = refType == null ? ((GrVariable) resolved).getTypeGroovy() : refType;
returnType = extractReturnTypeFromType(type, false, callExpression);
}
PsiType returnType = calculateReturnTypeInner(callExpression, refExpr, resolveResult);
if (returnType == null) return null;
if (!(returnType instanceof GrLiteralClassType)) {
returnType = TypesUtil.substituteBoxAndNormalizeType(returnType, resolveResult.getSubstitutor(), callExpression);
returnType = TypesUtil.substituteBoxAndNormalizeType(returnType, resolveResult.getSubstitutor(), resolveResult.getSpreadState(), callExpression);
LOG.assertTrue(returnType != null);
}
if (result == null || returnType.isAssignableFrom(result)) result = returnType;
else if (!result.isAssignableFrom(returnType))
if (result == null || returnType.isAssignableFrom(result)) {
result = returnType;
}
else if (!result.isAssignableFrom(returnType)) {
result = TypesUtil.getLeastUpperBound(result, returnType, manager);
}
}
if (result == null) return null;
if (refExpr.getDotTokenType() != GroovyTokenTypes.mSPREAD_DOT) {
return result;
} else {
return ResolveUtil.getListTypeForSpreadOperator(refExpr, result);
}
return result;
}
else {
return extractReturnTypeFromType(invoked.getType(), false, callExpression);
}
}
@Nullable
private static PsiType calculateReturnTypeInner(GrMethodCall callExpression,
GrReferenceExpression refExpr,
GroovyResolveResult resolveResult) {
PsiElement resolved = resolveResult.getElement();
PsiType returnType = null;
if (resolved instanceof PsiMethod) {
PsiMethod method = (PsiMethod)resolved;
if (resolveResult.isInvokedOnProperty()) {
final PsiType propertyType = PsiUtil.getSmartReturnType(method);
returnType = extractReturnTypeFromType(propertyType, true, callExpression);
}
else {
returnType = getClosureMethodsReturnType(callExpression, refExpr, method);
if (returnType == null) {
returnType = PsiUtil.getSmartReturnType(method);
}
}
}
else if (resolved instanceof GrVariable) {
PsiType refType = refExpr.getType();
final PsiType type = refType == null ? ((GrVariable)resolved).getTypeGroovy() : refType;
returnType = extractReturnTypeFromType(type, false, callExpression);
}
return returnType;
}
@Nullable
private static PsiType extractReturnTypeFromType(PsiType type, boolean returnTypeIfFail, GrMethodCall callExpression) {
PsiType returnType = returnTypeIfFail ? type: null;
@@ -15,10 +15,7 @@
*/
package org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.path;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.containers.hash.HashSet;
import org.jetbrains.annotations.NotNull;
@@ -28,6 +25,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethod
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGdkMethod;
import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
import org.jetbrains.plugins.groovy.lang.psi.typeEnhancers.GrCallExpressionTypeCalculator;
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames;
@@ -82,7 +80,7 @@ public class GrDGMTypeCalculator extends GrCallExpressionTypeCalculator {
itemType = iitype;
}
}
return createSimilarCollection(type, callExpression.getProject(), itemType);
return TypesUtil.createSimilarCollection(type, callExpression.getProject(), itemType);
}
}
}
@@ -100,49 +98,6 @@ public class GrDGMTypeCalculator extends GrCallExpressionTypeCalculator {
}
}
@Nullable
private static PsiType createSimilarCollection(PsiType collection, Project project, PsiType... itemType) {
if (InheritanceUtil.isInheritor(collection, "java.util.SortedSet")) {
return createCollection(project, "java.util.SortedSet", itemType);
}
if (InheritanceUtil.isInheritor(collection, "java.util.LinkedHashSet")) {
return createCollection(project, "java.util.LinkedHashSet", itemType);
}
if (InheritanceUtil.isInheritor(collection, "java.util.Set")) {
return createCollection(project, "java.util.HashSet", itemType);
}
if (InheritanceUtil.isInheritor(collection, "java.util.LinkedList")) {
return createCollection(project, "java.util.LInkedList", itemType);
}
if (InheritanceUtil.isInheritor(collection, "java.util.Stack")) {
return createCollection(project, "java.util.Stack", itemType);
}
if (InheritanceUtil.isInheritor(collection, "java.util.Vector")) {
return createCollection(project, "java.util.Vector", itemType);
}
if (InheritanceUtil.isInheritor(collection, "java.util.List")) {
return createCollection(project, "java.util.ArrayList", itemType);
}
if (InheritanceUtil.isInheritor(collection, "java.util.Queue")) {
return createCollection(project, "java.util.LinkedList", itemType);
}
return createCollection(project, "java.util.ArrayList", itemType);
}
@Nullable
private static PsiType createCollection(Project project, String collectionName, PsiType... item) {
PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
PsiClass collection =
JavaPsiFacade.getInstance(project).findClass(collectionName, GlobalSearchScope.allScope(project));
if (collection == null) return null;
PsiTypeParameter[] parameters = collection.getTypeParameters();
if (parameters.length != 1) return null;
return factory.createType(collection, item);
}
private static boolean isSimilarCollectionReturner(PsiMethod resolved) {
PsiParameter[] params = resolved.getParameterList().getParameters();
if (params.length == 0) return false;
@@ -270,7 +270,7 @@ public class GrIndexPropertyImpl extends GrExpressionImpl implements GrIndexProp
PsiParameter[] parameters = ((PsiMethod)element).getParameterList().getParameters();
if (parameters.length > 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;
@@ -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
@@ -376,7 +376,7 @@ public class GrCodeReferenceElementImpl extends GrReferenceElementImpl<GrCodeRef
final PsiSubstitutor substitutor = result.getSubstitutor();
final PsiSubstitutor newSubstitutor = substitutor.putAll((PsiClass) element, args);
GroovyPsiElement context = result.getCurrentFileResolveContext();
GroovyResolveResultImpl newResult = new GroovyResolveResultImpl(element, context, newSubstitutor, result.isAccessible(), result.isStaticsOK());
GroovyResolveResultImpl newResult = new GroovyResolveResultImpl(element, context, null, newSubstitutor, result.isAccessible(), result.isStaticsOK());
results[i] = newResult;
if (context instanceof GrImportStatement) {
imported.add(newResult);
@@ -62,7 +62,7 @@ public class GdkMethodUtil {
private GdkMethodUtil() {
}
public static boolean categoryIteration(GrClosableBlock place, final PsiScopeProcessor processor) {
public static boolean categoryIteration(GrClosableBlock place, final PsiScopeProcessor processor, ResolveState state) {
final ClassHint classHint = processor.getHint(ClassHint.KEY);
if (classHint != null && !classHint.shouldProcess(ClassHint.ResolveKind.METHOD)) return true;
@@ -79,11 +79,12 @@ public class GdkMethodUtil {
if (!(call.resolveMethod() instanceof GrGdkMethod)) return true;
state = state.put(ResolverProcessor.RESOLVE_CONTEXT, call);
for (GrExpression arg : args) {
if (arg instanceof GrReferenceExpression) {
final PsiElement resolved = ((GrReferenceExpression)arg).resolve();
if (resolved instanceof PsiClass) {
if (!processCategoryMethods(place, processor, call, (PsiClass)resolved)) return false;
if (!processCategoryMethods(place, processor, state, (PsiClass)resolved)) return false;
}
}
}
@@ -100,7 +101,7 @@ public class GdkMethodUtil {
*/
public static boolean processCategoryMethods(final GroovyPsiElement place,
final PsiScopeProcessor processor,
@Nullable GroovyPsiElement resolveContext,
@NotNull ResolveState state,
@NotNull PsiClass categoryClass) {
final DelegatingScopeProcessor delegate = new DelegatingScopeProcessor(processor) {
@Override
@@ -114,18 +115,17 @@ public class GdkMethodUtil {
return true;
}
};
final ResolveState state = ResolveState.initial().put(ResolverProcessor.RESOLVE_CONTEXT, resolveContext);
return categoryClass.processDeclarations(delegate, state, null, place);
}
public static boolean withIteration(GrClosableBlock block, final PsiScopeProcessor processor, GroovyPsiElement place) {
public static boolean withIteration(GrClosableBlock block, final PsiScopeProcessor processor) {
GrMethodCall call = checkMethodCall(block, WITH);
if (call == null) return true;
final GrExpression invoked = call.getInvokedExpression();
LOG.assertTrue(invoked instanceof GrReferenceExpression);
final GrExpression qualifier = ((GrReferenceExpression)invoked).getQualifier();
if (qualifier == null) return true;
if (!GrReferenceResolveUtil.processQualifier(processor, qualifier, place)) return false;
if (!GrReferenceResolveUtil.processQualifier(processor, qualifier, (GrReferenceExpression)invoked)) return false;
return true;
}
@@ -937,7 +937,7 @@ public class PsiUtil {
final GroovyResolveResult grResult = resolveResult instanceof GroovyResolveResult
? (GroovyResolveResult)resolveResult
: new GroovyResolveResultImpl(psiClass, context, substitutor, true, true);
: new GroovyResolveResultImpl(psiClass, context, null, substitutor, true, true);
return getConstructorCandidates(context, new GroovyResolveResult[]{grResult}, argTypes);
}
@@ -101,20 +101,20 @@ public class ResolveUtil {
}
if (doProcessNonCodeMembers) {
if (run instanceof GrTypeDefinition) {
if (!processNonCodeMembers(factory.createType(((GrTypeDefinition)run)), processor, place)) return false;
if (!processNonCodeMembers(factory.createType(((GrTypeDefinition)run)), processor, place, ResolveState.initial())) return false;
}
else if ((run instanceof GroovyFileBase) && ((GroovyFileBase)run).isScript()) {
final PsiClass psiClass = ((GroovyFileBase)run).getScriptClass();
if (psiClass != null) {
if (!processNonCodeMembers(factory.createType(psiClass), processor, place)) return false;
if (!processNonCodeMembers(factory.createType(psiClass), processor, place, ResolveState.initial())) return false;
}
}
else if (run instanceof GrClosableBlock) {
PsiClass superClass = getLiteralSuperClass((GrClosableBlock)run);
if (superClass != null && !superClass.processDeclarations(processor, ResolveState.initial(), null, place)) return false;
if (!GdkMethodUtil.categoryIteration((GrClosableBlock)run, processor)) return false;
if (!GdkMethodUtil.withIteration((GrClosableBlock)run, processor, place)) return false;
if (!GdkMethodUtil.categoryIteration((GrClosableBlock)run, processor, ResolveState.initial())) return false;
if (!GdkMethodUtil.withIteration((GrClosableBlock)run, processor)) return false;
}
}
}
@@ -128,12 +128,12 @@ public class ResolveUtil {
public static boolean processChildren(PsiElement element,
PsiScopeProcessor processor,
ResolveState substitutor,
ResolveState state,
PsiElement lastParent,
PsiElement place) {
PsiElement run = lastParent == null ? element.getLastChild() : lastParent.getPrevSibling();
while (run != null) {
if (!run.processDeclarations(processor, substitutor, null, place)) return false;
if (!run.processDeclarations(processor, state, null, place)) return false;
run = run.getPrevSibling();
}
@@ -175,16 +175,10 @@ public class ResolveUtil {
}
}
if (!processNonCodeMembers(type, processor, place, state)) return false;
if (!processCategoryMembers(place, processor)) return false;
if (!processCategoryMembers(place, processor, state)) return false;
return true;
}
public static boolean processNonCodeMembers(PsiType type,
PsiScopeProcessor processor,
GroovyPsiElement place) {
return processNonCodeMembers(type, processor, place, ResolveState.initial());
}
public static boolean processNonCodeMembers(PsiType type,
PsiScopeProcessor processor,
GroovyPsiElement place,
@@ -277,21 +271,6 @@ public class ResolveUtil {
return result;
}
@Nullable
public static PsiType getListTypeForSpreadOperator(GrReferenceExpression refExpr, PsiType componentType) {
PsiClass clazz = JavaPsiFacade.getInstance(refExpr.getManager().getProject())
.findClass(CommonClassNames.JAVA_UTIL_LIST, refExpr.getResolveScope());
if (clazz != null) {
PsiTypeParameter[] typeParameters = clazz.getTypeParameters();
if (typeParameters.length == 1) {
PsiSubstitutor substitutor = PsiSubstitutor.EMPTY.put(typeParameters[0], componentType);
return JavaPsiFacade.getInstance(refExpr.getProject()).getElementFactory().createType(clazz, substitutor);
}
}
return null;
}
public static GroovyPsiElement resolveProperty(GroovyPsiElement place, String name) {
PropertyResolverProcessor processor = new PropertyResolverProcessor(name, place);
return resolveExistingElement(place, processor, GrVariable.class, GrReferenceExpression.class);
@@ -384,9 +363,9 @@ public class ResolveUtil {
return resolveLabelTargets(labelName, element, isBreak).first;
}
public static boolean processCategoryMembers(GroovyPsiElement place, PsiScopeProcessor processor) {
public static boolean processCategoryMembers(GroovyPsiElement place, PsiScopeProcessor processor, ResolveState state) {
boolean gpp = GppTypeConverter.hasTypedContext(place);
if (gpp && !processUseAnnotation(place, processor)) return false;
if (gpp && !processUseAnnotation(place, processor, state)) return false;
boolean inCodeBlock = true;
PsiElement run = place;
@@ -395,14 +374,15 @@ public class ResolveUtil {
inCodeBlock = false;
}
if (run instanceof GrClosableBlock) {
if (inCodeBlock && !GdkMethodUtil.categoryIteration((GrClosableBlock)run, processor)) return false;
if (inCodeBlock && !GdkMethodUtil.categoryIteration((GrClosableBlock)run, processor, state)) return false;
PsiClass superClass = getLiteralSuperClass((GrClosableBlock)run);
if (superClass != null && !GdkMethodUtil.processCategoryMethods(((GrClosableBlock)run), processor, null, superClass)) return false;
if (superClass != null && !GdkMethodUtil.processCategoryMethods(((GrClosableBlock)run), processor, state, superClass)) return false;
}
if (gpp && run instanceof GrTypeDefinition) {
final GrTypeDefinition typeDefinition = (GrTypeDefinition)run;
if (!GdkMethodUtil.processCategoryMethods(typeDefinition, processor, typeDefinition, typeDefinition)) return false;
state = state.put(ResolverProcessor.RESOLVE_CONTEXT, typeDefinition);
if (!GdkMethodUtil.processCategoryMethods(typeDefinition, processor, state, typeDefinition)) return false;
}
run = run.getContext();
}
@@ -410,13 +390,13 @@ public class ResolveUtil {
return true;
}
private static boolean processUseAnnotation(GroovyPsiElement place, PsiScopeProcessor processor) {
private static boolean processUseAnnotation(GroovyPsiElement place, PsiScopeProcessor processor, ResolveState state) {
PsiAnnotation use = AnnotatedContextFilter.findContextAnnotation(place, GroovyCommonClassNames.GROOVY_LANG_USE);
if (use != null) {
for (PsiElement element : PsiElementCategory.asList(use.findDeclaredAttributeValue("value"))) {
if (element instanceof GrReferenceExpression) {
PsiElement resolve = ((GrReferenceExpression)element).resolve();
if (resolve instanceof PsiClass && !GdkMethodUtil.processCategoryMethods(place, processor, null, (PsiClass)resolve)) {
if (resolve instanceof PsiClass && !GdkMethodUtil.processCategoryMethods(place, processor, state, (PsiClass)resolve)) {
return false;
}
}
@@ -687,7 +667,7 @@ public class ResolveUtil {
public static PsiType extractReturnTypeFromCandidate(GroovyResolveResult candidate, GrExpression expression, @Nullable PsiType[] args) {
final PsiElement element = candidate.getElement();
if (element instanceof PsiMethod && !candidate.isInvokedOnProperty()) {
return TypesUtil.substituteBoxAndNormalizeType(getSmartReturnType((PsiMethod)element), candidate.getSubstitutor(), expression);
return TypesUtil.substituteBoxAndNormalizeType(getSmartReturnType((PsiMethod)element), candidate.getSubstitutor(), candidate.getSpreadState(), expression);
}
final PsiType type;
@@ -703,7 +683,7 @@ public class ResolveUtil {
if (type instanceof GrClosureType) {
final GrSignature signature = ((GrClosureType)type).getSignature();
PsiType returnType = GrClosureSignatureUtil.getReturnType(signature, args, expression);
return TypesUtil.substituteBoxAndNormalizeType(returnType, candidate.getSubstitutor(), expression);
return TypesUtil.substituteBoxAndNormalizeType(returnType, candidate.getSubstitutor(), candidate.getSpreadState(), expression);
}
return null;
}
@@ -20,6 +20,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
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.typedef.members.GrGdkMethod;
import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyResolveResultImpl;
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils;
@@ -78,9 +79,10 @@ public class AccessorResolverProcessor extends MethodResolverProcessor {
}
boolean isAccessible = isAccessible(method);
final GroovyPsiElement resolveContext = state.get(RESOLVE_CONTEXT);
final SpreadState spreadState = state.get(SpreadState.SPREAD_STATE);
boolean isStaticsOK = isStaticsOK(method, resolveContext, true);
final GroovyResolveResultImpl candidate =
new GroovyResolveResultImpl(method, resolveContext, substitutor, isAccessible, isStaticsOK, true);
new GroovyResolveResultImpl(method, resolveContext, spreadState, substitutor, isAccessible, isStaticsOK, true);
if (isAccessible && isStaticsOK) {
addCandidate(candidate);
return method instanceof GrGdkMethod; //don't stop searching if we found only gdk method
@@ -27,6 +27,7 @@ import org.jetbrains.plugins.groovy.gpp.GppClosureParameterTypeProvider;
import org.jetbrains.plugins.groovy.gpp.GppTypeConverter;
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.signatures.GrClosureSignature;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGdkMethod;
import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyResolveResultImpl;
@@ -100,12 +101,14 @@ public class MethodResolverProcessor extends ResolverProcessor {
}
boolean isAccessible = isAccessible(method);
GroovyPsiElement resolveContext = state.get(RESOLVE_CONTEXT);
final SpreadState spreadState = state.get(SpreadState.SPREAD_STATE);
boolean isStaticsOK = isStaticsOK(method, resolveContext, true);
if (!myAllVariants && isStaticsOK && isAccessible &&
PsiUtil.isApplicable(myArgumentTypes, method, substitutor, (GroovyPsiElement)myPlace, myByShape)) {
addCandidate(new GroovyResolveResultImpl(method, resolveContext, substitutor, isAccessible, isStaticsOK));
addCandidate(new GroovyResolveResultImpl(method, resolveContext, spreadState, substitutor, isAccessible, isStaticsOK));
} else {
myInapplicableCandidates.add(new GroovyResolveResultImpl(method, resolveContext, substitutor, isAccessible, isStaticsOK));
myInapplicableCandidates.add(new GroovyResolveResultImpl(method, resolveContext, spreadState, substitutor, isAccessible, isStaticsOK));
}
return true;
@@ -26,6 +26,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
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.expressions.GrReferenceExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrAccessorMethod;
@@ -97,8 +98,9 @@ public class ResolverProcessor implements PsiScopeProcessor, NameHint, ClassHint
boolean isAccessible = isAccessible(namedElement);
final GroovyPsiElement resolveContext = state.get(RESOLVE_CONTEXT);
final SpreadState spreadState = state.get(SpreadState.SPREAD_STATE);
boolean isStaticsOK = isStaticsOK(namedElement, resolveContext, true);
addCandidate(new GroovyResolveResultImpl(namedElement, resolveContext, substitutor, isAccessible, isStaticsOK));
addCandidate(new GroovyResolveResultImpl(namedElement, resolveContext, spreadState, substitutor, isAccessible, isStaticsOK));
return !isAccessible || !isStaticsOK;
}
@@ -180,7 +180,7 @@ public class ResolveMethodTest extends GroovyResolveTestCase {
assertTrue(type instanceof PsiClassType);
PsiClass clazz = ((PsiClassType) type).resolve();
assertNotNull(clazz);
assertEquals("java.util.List", clazz.getQualifiedName());
assertEquals("java.util.ArrayList", clazz.getQualifiedName());
}
@@ -426,6 +426,57 @@ print ma<caret>p
''', "$JAVA_UTIL_MAP<$JAVA_LANG_STRING,$JAVA_LANG_STRING>")
}
void testSpread1() {
myFixture.addClass('''\
class A {
String getString() {return "a";}
}''')
doTest('''\
[new A()].stri<caret>ng
''', "$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.fo<caret>o
}
''', "$JAVA_UTIL_ARRAY_LIST<$JAVA_LANG_INTEGER>")
}
void testSpread3() {
myFixture.addClass('''\
class A {
String getString() {return "a";}
}''')
doTest('''\
[[new A()]].stri<caret>ng
''', "$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.fo<caret>o
}
''', "$JAVA_UTIL_ARRAY_LIST<$JAVA_UTIL_ARRAY_LIST<$JAVA_LANG_INTEGER>>")
}
private void doTest(String text, String type) {
def file = myFixture.configureByText('_.groovy', text)