blob: 49f9d40387d897586746e9c64f008e57ab10db6d [file] [log] [blame]
/*
* Copyright 2000-2014 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.resolve;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.*;
import com.intellij.psi.scope.JavaScopeProcessorEvent;
import com.intellij.psi.scope.NameHint;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.util.*;
import com.intellij.util.PairProcessor;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.GroovyLanguage;
import org.jetbrains.plugins.groovy.findUsages.LiteralConstructorReference;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase;
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.auxiliary.GrListOrMap;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation;
import org.jetbrains.plugins.groovy.lang.psi.api.signatures.GrSignature;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.*;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
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.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.path.GrCallExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrIndexProperty;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrAnonymousClassDefinition;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGdkMethod;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMember;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement;
import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner;
import org.jetbrains.plugins.groovy.lang.psi.impl.GrClosureType;
import org.jetbrains.plugins.groovy.lang.psi.impl.GrTraitType;
import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyResolveResultImpl;
import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrBindingVariable;
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightParameter;
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrScriptField;
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GroovyScriptClass;
import org.jetbrains.plugins.groovy.lang.psi.util.GdkMethodUtil;
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames;
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils;
import org.jetbrains.plugins.groovy.lang.resolve.processors.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author ven
*/
@SuppressWarnings({"StringBufferReplaceableByString"})
public class ResolveUtil {
private static final Logger LOG = Logger.getInstance(ResolveUtil.class);
public static final PsiScopeProcessor.Event DECLARATION_SCOPE_PASSED = new PsiScopeProcessor.Event() {};
private ResolveUtil() {
}
/**
*
* @param place - place to start tree walk up
* @param processor
* @param processNonCodeMethods - this parameter tells us if we need non code members
* @return
*/
public static boolean treeWalkUp(@NotNull final PsiElement place,
@NotNull final PsiScopeProcessor processor,
boolean processNonCodeMethods) {
return treeWalkUp(place, place, processor, processNonCodeMethods, ResolveState.initial());
}
/**
*
* @param place - place to start tree walk up
* @param processor
* @param processNonCodeMethods - this parameter tells us if we need non code members
* @param state
* @return
*/
public static boolean treeWalkUp(@NotNull final PsiElement place,
@NotNull final PsiElement originalPlace,
@NotNull final PsiScopeProcessor processor,
boolean processNonCodeMethods,
@NotNull final ResolveState state) {
try {
ClassHint hint = processor.getHint(ClassHint.KEY);
if (hint != null) {
return new DeclarationCacheKey(getNameHint(processor), hint, processNonCodeMethods, originalPlace).processCachedDeclarations(place, processor);
}
final PsiScopeProcessor nonCodeProcessor = processNonCodeMethods ? processor : null;
return doTreeWalkUp(place, originalPlace, processor, nonCodeProcessor, state);
}
catch (StackOverflowError e) {
LOG.error("StackOverflow", e, place.getContainingFile().getText());
throw e;
}
}
public static boolean doTreeWalkUp(@NotNull final PsiElement place,
@NotNull final PsiElement originalPlace,
@NotNull final PsiScopeProcessor processor,
@Nullable final PsiScopeProcessor nonCodeProcessor,
@NotNull final ResolveState state) {
final GrClosableBlock maxScope = nonCodeProcessor != null ? PsiTreeUtil.getParentOfType(place, GrClosableBlock.class, true, PsiFile.class) : null;
return PsiTreeUtil.treeWalkUp(place, maxScope, new PairProcessor<PsiElement, PsiElement>() {
@Override
public boolean process(PsiElement scope, PsiElement lastParent) {
ProgressManager.checkCanceled();
if (!doProcessDeclarations(originalPlace, lastParent, scope, substituteProcessor(processor, scope), nonCodeProcessor, state)) {
return false;
}
issueLevelChangeEvents(processor, scope);
return true;
}
});
}
static boolean doProcessDeclarations(@NotNull PsiElement place,
@Nullable PsiElement lastParent,
@NotNull PsiElement scope,
@NotNull PsiScopeProcessor plainProcessor,
@Nullable PsiScopeProcessor nonCodeProcessor,
@NotNull ResolveState state) {
if (scope instanceof GrClosableBlock && nonCodeProcessor != null) {
if (!((GrClosableBlock)scope).processClosureDeclarations(plainProcessor, nonCodeProcessor, state, lastParent, place)) return false;
}
else {
if (!scope.processDeclarations(plainProcessor, state, lastParent, place)) return false;
if (scope instanceof GrTypeDefinition || scope instanceof GrClosableBlock) {
if (!processStaticImports(plainProcessor, place.getContainingFile(), state, place)) return false;
}
}
if (nonCodeProcessor != null) {
if (!processScopeNonCodeMembers(place, lastParent, nonCodeProcessor, scope, state)) return false;
}
return true;
}
static void issueLevelChangeEvents(PsiScopeProcessor processor, PsiElement run) {
processor.handleEvent(JavaScopeProcessorEvent.CHANGE_LEVEL, null);
if (run instanceof GrClosableBlock && GrClosableBlock.OWNER_NAME.equals(getNameHint(processor)) ||
run instanceof PsiClass && !(run instanceof PsiAnonymousClass) ||
run instanceof GrMethod && run.getParent() instanceof GroovyFile) {
processor.handleEvent(DECLARATION_SCOPE_PASSED, run);
}
}
static PsiScopeProcessor substituteProcessor(PsiScopeProcessor processor, PsiElement scope) {
//hack for walking up in java code
//java's processDeclarations don't check names so we should do it manually
if (scope.getLanguage() != GroovyLanguage.INSTANCE && processor.getHint(NameHint.KEY) != null) {
return new JavaResolverProcessor(processor);
}
return processor;
}
private static boolean processScopeNonCodeMembers(@NotNull PsiElement place,
@Nullable PsiElement lastParent,
@NotNull PsiScopeProcessor processor,
@NotNull PsiElement scope,
@NotNull ResolveState state) {
//state = ResolveState.initial();
if (scope instanceof GrTypeDefinition) {
if (!processNonCodeMembers(createPsiType((GrTypeDefinition)scope), processor, place, state)) return false;
//@Category(CategoryType)
//class Scope {...}
PsiClassType categoryType = GdkMethodUtil.getCategoryType((PsiClass)scope);
if (categoryType != null) {
if (!processNonCodeMembers(categoryType, processor, place, state)) return false;
}
}
if (scope instanceof GroovyFileBase && ((GroovyFileBase)scope).isScript()) {
final PsiClass psiClass = ((GroovyFileBase)scope).getScriptClass();
if (psiClass != null) {
if (!processNonCodeMembers(createPsiType(psiClass), processor, place, state)) return false;
}
}
if (scope instanceof GrClosableBlock) {
ResolveState _state = state.put(ClassHint.RESOLVE_CONTEXT, scope);
PsiClass superClass = getLiteralSuperClass((GrClosableBlock)scope);
if (superClass != null && !superClass.processDeclarations(processor, _state, null, place)) return false;
if (!GdkMethodUtil.categoryIteration((GrClosableBlock)scope, processor, _state)) return false;
if (!processNonCodeMembers(GrClosureType.create(((GrClosableBlock)scope), false), processor, place, _state)) return false;
}
if (scope instanceof GrStatementOwner) {
if (!GdkMethodUtil.processMixinToMetaclass((GrStatementOwner)scope, processor, state, lastParent, place)) return false;
}
return true;
}
@NotNull
private static PsiClassType createPsiType(@NotNull PsiClass psiClass) {
PsiElementFactory factory = JavaPsiFacade.getElementFactory(psiClass.getProject());
return factory.createType(psiClass);
}
public static boolean processChildren(@NotNull PsiElement element,
@NotNull PsiScopeProcessor processor,
@NotNull ResolveState state,
@Nullable PsiElement lastParent,
@NotNull PsiElement place) {
if (!shouldProcessProperties(processor.getHint(ClassHint.KEY))) return true;
PsiElement run = lastParent == null ? element.getLastChild() : lastParent.getPrevSibling();
while (run != null) {
if (!run.processDeclarations(processor, state, null, place)) return false;
run = run.getPrevSibling();
}
return true;
}
@Nullable
public static String getNameHint(PsiScopeProcessor processor) {
NameHint nameHint = processor.getHint(NameHint.KEY);
if (nameHint == null) {
return null;
}
return nameHint.getName(ResolveState.initial());
}
public static boolean processElement(@NotNull PsiScopeProcessor processor,
@NotNull PsiNamedElement namedElement,
@NotNull ResolveState state) {
NameHint nameHint = processor.getHint(NameHint.KEY);
String name = nameHint == null ? null : nameHint.getName(state);
if (name == null || name.equals(namedElement.getName())) {
return processor.execute(namedElement, state);
}
return true;
}
public static boolean processAllDeclarations(@NotNull PsiType type,
@NotNull PsiScopeProcessor processor,
@NotNull ResolveState state,
@NotNull PsiElement place) {
return processAllDeclarationsSeparately(type, processor, processor, state, place);
}
public static boolean processAllDeclarationsSeparately(@NotNull PsiType type,
@NotNull PsiScopeProcessor processor,
@NotNull PsiScopeProcessor nonCodeProcessor,
@NotNull ResolveState state,
@NotNull PsiElement place) {
if (type instanceof PsiClassType) {
final PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)type).resolveGenerics();
final PsiClass psiClass = resolveResult.getElement();
final PsiSubstitutor substitutor = state.get(PsiSubstitutor.KEY);
state = state.put(PsiSubstitutor.KEY, substitutor.putAll(resolveResult.getSubstitutor()));
if (psiClass != null) {
if (!psiClass.processDeclarations(processor, state, null, place)) return false;
}
}
if (!processCategoryMembers(place, nonCodeProcessor, state)) return false;
if (!processNonCodeMembers(type, nonCodeProcessor, place, state)) return false;
return true;
}
public static boolean processNonCodeMembers(@NotNull PsiType type,
@NotNull PsiScopeProcessor processor,
@NotNull PsiElement place,
@NotNull ResolveState state) {
if (type instanceof PsiEllipsisType) {
type = ((PsiEllipsisType)type).toArrayType();
}
if (!NonCodeMembersContributor.runContributors(type, processor, place, state)) {
return false;
}
return true;
}
private static final Key<PsiType> COMPARABLE = Key.create(CommonClassNames.JAVA_LANG_COMPARABLE);
private static final Key<PsiType> SERIALIZABLE = Key.create(CommonClassNames.JAVA_IO_SERIALIZABLE);
private static final Key<PsiType> STRING = Key.create(CommonClassNames.JAVA_LANG_STRING);
private static void collectSuperTypes(PsiType type, Map<String, PsiType> visited, Project project) {
String qName = rawCanonicalText(type);
if (visited.put(qName, type) != null) {
return;
}
final PsiType[] superTypes = type.getSuperTypes();
for (PsiType superType : superTypes) {
collectSuperTypes(TypeConversionUtil.erasure(superType), visited, project);
}
if (type instanceof PsiArrayType && superTypes.length == 0) {
PsiType comparable = createTypeFromText(project, COMPARABLE, CommonClassNames.JAVA_LANG_COMPARABLE);
PsiType serializable = createTypeFromText(project, SERIALIZABLE, CommonClassNames.JAVA_IO_SERIALIZABLE);
collectSuperTypes(comparable, visited, project);
collectSuperTypes(serializable, visited, project);
}
if (GroovyCommonClassNames.GROOVY_LANG_GSTRING.equals(qName)) {
collectSuperTypes(createTypeFromText(project, STRING, CommonClassNames.JAVA_LANG_STRING), visited, project);
}
}
public static PsiType createTypeFromText(Project project, Key<PsiType> key, String text) {
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
PsiType type = project.getUserData(key);
if (type == null) {
type = factory.createTypeFromText(text, null);
project.putUserData(key, type);
}
return type;
}
public static Map<String, PsiType> getAllSuperTypes(@NotNull PsiType base, final Project project) {
final Map<String, Map<String, PsiType>> cache =
CachedValuesManager.getManager(project).getCachedValue(project, new CachedValueProvider<Map<String, Map<String, PsiType>>>() {
@Override
public Result<Map<String, Map<String, PsiType>>> compute() {
final Map<String, Map<String, PsiType>> result = new ConcurrentHashMap<String, Map<String, PsiType>>();
return Result.create(result, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT, ProjectRootManager.getInstance(project));
}
});
final PsiClass cls = PsiUtil.resolveClassInType(base);
String key;
if (cls instanceof PsiTypeParameter) {
final PsiClass superClass = cls.getSuperClass();
key = cls.getName() + (superClass == null ? CommonClassNames.JAVA_LANG_OBJECT : superClass.getName());
}
else if (base instanceof GrTraitType) {
key = ((GrTraitType)base).erasure().getCanonicalText();
}
else if (base instanceof PsiClassType) {
key = TypesUtil.getQualifiedName(base);
}
else {
key = base.getCanonicalText();
}
Map<String, PsiType> result = cache.get(key);
if (result == null) {
result = new HashMap<String, PsiType>();
collectSuperTypes(base, result, project);
cache.put(key, result);
}
return result;
}
@NotNull
private static String rawCanonicalText(@NotNull PsiType type) {
if (type instanceof PsiClassType) {
String qname = TypesUtil.getQualifiedName(type);
if (qname != null) {
return qname;
}
}
return TypeConversionUtil.erasure(type).getCanonicalText();
}
public static GroovyPsiElement resolveProperty(GroovyPsiElement place, String name) {
PropertyResolverProcessor processor = new PropertyResolverProcessor(name, place);
return resolveExistingElement(place, processor, GrVariable.class, GrReferenceExpression.class);
}
@Nullable
public static PsiClass resolveClass(GroovyPsiElement place, String name) {
ClassResolverProcessor processor = new ClassResolverProcessor(name, place);
return resolveExistingElement(place, processor, PsiClass.class);
}
@Nullable
public static <T> T resolveExistingElement(PsiElement place, ResolverProcessor processor, Class<? extends T>... classes) {
treeWalkUp(place, processor, true);
final GroovyResolveResult[] candidates = processor.getCandidates();
for (GroovyResolveResult candidate : candidates) {
final PsiElement element = candidate.getElement();
if (element == place) continue;
for (Class<? extends T> clazz : classes) {
if (clazz.isInstance(element)) //noinspection unchecked
return (T)element;
}
}
return null;
}
@NotNull
public static Pair<GrStatement, GrLabeledStatement> resolveLabelTargets(@Nullable String labelName,
@Nullable PsiElement element,
boolean isBreak) {
if (element == null) return new Pair<GrStatement, GrLabeledStatement>(null, null);
if (labelName == null) {
do {
element = element.getContext();
if (element == null || element instanceof GrClosableBlock || element instanceof GrMember || element instanceof GroovyFile) {
return new Pair<GrStatement, GrLabeledStatement>(null, null);
}
}
while (!(element instanceof GrLoopStatement) && !(isBreak && element instanceof GrSwitchStatement));
return new Pair<GrStatement, GrLabeledStatement>(((GrStatement)element), null);
}
GrStatement statement = null;
do {
PsiElement last = element;
element = element.getContext();
if (element == null || element instanceof GrMember || element instanceof GroovyFile) break;
if (element instanceof GrStatement && !(element instanceof GrClosableBlock)) {
statement = (GrStatement)element;
}
PsiElement sibling = element;
while (sibling != null) {
final GrLabeledStatement labelStatement = findLabelStatementIn(sibling, last, labelName);
if (labelStatement != null) {
return Pair.create(statement, labelStatement);
}
sibling = sibling.getPrevSibling();
}
if (element instanceof GrClosableBlock) break;
}
while (true);
return new Pair<GrStatement, GrLabeledStatement>(null, null);
}
private static boolean isApplicableLabelStatement(PsiElement element, String labelName) {
return ((element instanceof GrLabeledStatement && labelName.equals(((GrLabeledStatement)element).getName())));
}
@Nullable
private static GrLabeledStatement findLabelStatementIn(PsiElement element, PsiElement lastChild, String labelName) {
if (isApplicableLabelStatement(element, labelName)) {
return (GrLabeledStatement)element;
}
for (PsiElement child = element.getFirstChild(); child != null && child != lastChild; child = child.getNextSibling()) {
final GrLabeledStatement statement = findLabelStatementIn(child, child, labelName);
if (statement != null) return statement;
}
return null;
}
@Nullable
public static GrLabeledStatement resolveLabeledStatement(@Nullable String labelName, @Nullable PsiElement element, boolean isBreak) {
return resolveLabelTargets(labelName, element, isBreak).second;
}
@Nullable
public static GrStatement resolveLabelTargetStatement(@Nullable String labelName, @Nullable PsiElement element, boolean isBreak) {
return resolveLabelTargets(labelName, element, isBreak).first;
}
public static boolean processCategoryMembers(@NotNull PsiElement place,
@NotNull PsiScopeProcessor processor,
@NotNull ResolveState state) {
boolean inCodeBlock = true;
PsiElement run = place;
PsiElement lastParent = null;
while (run != null) {
ProgressManager.checkCanceled();
if (run instanceof GrMember) {
inCodeBlock = false;
}
if (run instanceof GrClosableBlock) {
if (inCodeBlock) {
if (!GdkMethodUtil.categoryIteration((GrClosableBlock)run, processor, state)) return false;
}
PsiClass superClass = getLiteralSuperClass((GrClosableBlock)run);
if (superClass != null && !GdkMethodUtil.processCategoryMethods(run, processor, state, superClass)) return false;
}
if (run instanceof GrStatementOwner) {
if (!GdkMethodUtil.processMixinToMetaclass((GrStatementOwner)run, processor, state, lastParent, place)) return false;
}
lastParent = run;
run = run.getContext();
}
return true;
}
@Nullable
private static PsiClass getLiteralSuperClass(GrClosableBlock closure) {
PsiClassType type;
if (closure.getParent() instanceof GrNamedArgument && closure.getParent().getParent() instanceof GrListOrMap) {
type = LiteralConstructorReference.getTargetConversionType((GrListOrMap)closure.getParent().getParent());
}
else {
type = LiteralConstructorReference.getTargetConversionType(closure);
}
return type != null ? type.resolve() : null;
}
public static PsiElement[] mapToElements(GroovyResolveResult[] candidates) {
PsiElement[] elements = new PsiElement[candidates.length];
for (int i = 0; i < elements.length; i++) {
elements[i] = candidates[i].getElement();
}
return elements;
}
public static GroovyResolveResult[] filterSameSignatureCandidates(Collection<GroovyResolveResult> candidates) {
GroovyResolveResult[] array = candidates.toArray(new GroovyResolveResult[candidates.size()]);
if (array.length == 1) return array;
List<GroovyResolveResult> result = new ArrayList<GroovyResolveResult>();
result.add(array[0]);
Outer:
for (int i = 1; i < array.length; i++) {
PsiElement currentElement = array[i].getElement();
if (currentElement instanceof PsiMethod) {
PsiMethod currentMethod = (PsiMethod)currentElement;
for (Iterator<GroovyResolveResult> iterator = result.iterator(); iterator.hasNext();) {
final GroovyResolveResult otherResolveResult = iterator.next();
PsiElement element = otherResolveResult.getElement();
if (element instanceof PsiMethod) {
PsiMethod method = (PsiMethod)element;
if (dominated(currentMethod, array[i].getSubstitutor(), method, otherResolveResult.getSubstitutor())) {
continue Outer;
}
else if (dominated(method, otherResolveResult.getSubstitutor(), currentMethod, array[i].getSubstitutor())) {
iterator.remove();
}
}
}
}
result.add(array[i]);
}
return result.toArray(new GroovyResolveResult[result.size()]);
}
public static boolean dominated(PsiMethod method1,
PsiSubstitutor substitutor1,
PsiMethod method2,
PsiSubstitutor substitutor2) { //method1 has more general parameter types then method2
if (!method1.getName().equals(method2.getName())) return false;
PsiParameter[] params1 = method1.getParameterList().getParameters();
PsiParameter[] params2 = method2.getParameterList().getParameters();
if (params1.length != params2.length) return false;
for (int i = 0; i < params2.length; i++) {
PsiType type1 = substitutor1.substitute(params1[i].getType());
PsiType type2 = substitutor2.substitute(params2[i].getType());
if (!type1.equals(type2)) return false;
}
if (method1 instanceof GrGdkMethod && method2 instanceof GrGdkMethod) {
PsiMethod static1 = ((GrGdkMethod)method1).getStaticMethod();
PsiMethod static2 = ((GrGdkMethod)method2).getStaticMethod();
PsiParameter p1 = static1.getParameterList().getParameters()[0];
PsiParameter p2 = static2.getParameterList().getParameters()[0];
PsiType t1 = substitutor1.substitute(p1.getType());
PsiType t2 = substitutor2.substitute(p2.getType());
if (!t1.equals(t2)) {
if (t1 instanceof PsiClassType) t1 = TypeConversionUtil.erasure(t1);
if (t2 instanceof PsiClassType) t2 = TypeConversionUtil.erasure(t2);
//method1 is more general than method2
return t1.isAssignableFrom(t2);
}
}
return true;
}
public static GroovyResolveResult[] getCallVariants(GroovyPsiElement place) {
final PsiElement parent = place.getParent();
GroovyResolveResult[] variants = GroovyResolveResult.EMPTY_ARRAY;
if (parent instanceof GrCallExpression) {
variants = ((GrCallExpression)parent).getCallVariants(place instanceof GrExpression ? (GrExpression)place : null);
}
else if (parent instanceof GrConstructorInvocation) {
final PsiClass clazz = ((GrConstructorInvocation)parent).getDelegatedClass();
if (clazz != null) {
final PsiMethod[] constructors = clazz.getConstructors();
variants = getConstructorResolveResult(constructors, place);
}
}
else if (parent instanceof GrAnonymousClassDefinition) {
final PsiElement element = ((GrAnonymousClassDefinition)parent).getBaseClassReferenceGroovy().resolve();
if (element instanceof PsiClass) {
final PsiMethod[] constructors = ((PsiClass)element).getConstructors();
variants = getConstructorResolveResult(constructors, place);
}
}
else if (place instanceof GrReferenceExpression) {
variants = ((GrReferenceExpression)place).getSameNameVariants();
}
return variants;
}
private static GroovyResolveResult[] getConstructorResolveResult(PsiMethod[] constructors, PsiElement place) {
GroovyResolveResult[] variants = new GroovyResolveResult[constructors.length];
for (int i = 0; i < constructors.length; i++) {
final boolean isAccessible = PsiUtil.isAccessible(constructors[i], place, null);
variants[i] = new GroovyResolveResultImpl(constructors[i], isAccessible);
}
return variants;
}
public static GroovyResolveResult[] getAllClassConstructors(@NotNull PsiClass psiClass,
@NotNull PsiSubstitutor substitutor,
@Nullable PsiType[] argTypes,
@NotNull PsiElement place) {
final MethodResolverProcessor processor = new MethodResolverProcessor(psiClass.getName(), place, true, null, argTypes, PsiType.EMPTY_ARRAY);
ResolveState state = ResolveState.initial().put(PsiSubstitutor.KEY, substitutor);
for (PsiMethod constructor : psiClass.getConstructors()) {
processor.execute(constructor, state);
}
final PsiClassType qualifierType = JavaPsiFacade.getElementFactory(psiClass.getProject()).createType(psiClass);
processNonCodeMembers(qualifierType, processor, place, state);
return processor.getCandidates();
}
public static boolean isKeyOfMap(GrReferenceExpression ref) {
if (!(ref.getParent() instanceof GrIndexProperty) && org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.isCall(ref)) return false;
if (ref.multiResolve(false).length > 0) return false;
return mayBeKeyOfMap(ref);
}
public static boolean mayBeKeyOfMap(GrReferenceExpression ref) {
final GrExpression qualifier = getSelfOrWithQualifier(ref);
if (qualifier == null) return false;
if (qualifier instanceof GrReferenceExpression && ((GrReferenceExpression)qualifier).resolve() instanceof PsiClass) return false;
return InheritanceUtil.isInheritor(qualifier.getType(), CommonClassNames.JAVA_UTIL_MAP);
}
@Nullable
public static GrExpression getSelfOrWithQualifier(GrReferenceExpression ref) {
final GrExpression qualifier = ref.getQualifierExpression();
if (qualifier != null) {
return qualifier;
}
PsiElement place = ref;
while (true) {
final GrClosableBlock closure = PsiTreeUtil.getParentOfType(place, GrClosableBlock.class, true, GrMember.class, GroovyFile.class);
if (closure == null) break;
place = closure;
PsiElement clParent = closure.getParent();
if (clParent instanceof GrArgumentList) clParent = clParent.getParent();
if (!(clParent instanceof GrMethodCall)) continue;
final GrExpression expression = ((GrMethodCall)clParent).getInvokedExpression();
if (expression instanceof GrReferenceExpression &&
GdkMethodUtil.isWithName(((GrReferenceExpression)expression).getReferenceName()) &&
((GrReferenceExpression)expression).resolve() instanceof GrGdkMethod) {
final GrExpression withQualifier = ((GrReferenceExpression)expression).getQualifierExpression();
if (withQualifier != null) {
return withQualifier;
}
}
}
return null;
}
@NotNull
public static GroovyResolveResult[] getMethodCandidates(@NotNull PsiType thisType,
@Nullable String methodName,
@NotNull PsiElement place,
@Nullable PsiType... argumentTypes) {
return getMethodCandidates(thisType, methodName, place, true, false, false, argumentTypes);
}
@NotNull
public static GroovyResolveResult[] getMethodCandidates(@NotNull PsiType thisType,
@Nullable String methodName,
@NotNull PsiElement place,
boolean resolveClosures,
boolean allVariants,
boolean byShape,
@Nullable PsiType... argumentTypes) {
if (methodName == null) return GroovyResolveResult.EMPTY_ARRAY;
MethodResolverProcessor processor =
new MethodResolverProcessor(methodName, place, false, thisType, argumentTypes, PsiType.EMPTY_ARRAY, allVariants, byShape);
final ResolveState state = ResolveState.initial().put(ClassHint.RESOLVE_CONTEXT, place);
processAllDeclarations(thisType, processor, state, place);
boolean hasApplicableMethods = processor.hasApplicableCandidates();
final GroovyResolveResult[] methodCandidates = processor.getCandidates();
if (hasApplicableMethods && methodCandidates.length == 1) return methodCandidates;
final GroovyResolveResult[] allPropertyCandidates;
if (resolveClosures) {
PropertyResolverProcessor propertyResolver = new PropertyResolverProcessor(methodName, place);
processAllDeclarations(thisType, propertyResolver, state, place);
allPropertyCandidates = propertyResolver.getCandidates();
}
else {
allPropertyCandidates = GroovyResolveResult.EMPTY_ARRAY;
}
List<GroovyResolveResult> propertyCandidates = new ArrayList<GroovyResolveResult>(allPropertyCandidates.length);
for (GroovyResolveResult candidate : allPropertyCandidates) {
final PsiElement resolved = candidate.getElement();
if (!(resolved instanceof GrField)) continue;
final PsiType type = ((GrField)resolved).getTypeGroovy();
if (isApplicableClosureType(type, argumentTypes, place)) {
propertyCandidates.add(candidate);
}
}
for (GroovyResolveResult candidate : propertyCandidates) {
final PsiElement element = candidate.getElement();
if (element instanceof GrField) {
final PsiClass containingClass = ((PsiField)element).getContainingClass();
if (containingClass != null && PsiTreeUtil.isContextAncestor(containingClass, place, true)) {
return new GroovyResolveResult[]{candidate};
}
}
}
List<GroovyResolveResult> allCandidates = new ArrayList<GroovyResolveResult>();
if (hasApplicableMethods) {
ContainerUtil.addAll(allCandidates, methodCandidates);
}
ContainerUtil.addAll(allCandidates, propertyCandidates);
//search for getters
for (String getterName : GroovyPropertyUtils.suggestGettersName(methodName)) {
AccessorResolverProcessor getterResolver =
new AccessorResolverProcessor(getterName, methodName, place, true, false, thisType, PsiType.EMPTY_ARRAY);
processAllDeclarations(thisType, getterResolver, state, place);
final GroovyResolveResult[] candidates = getterResolver.getCandidates(); //can be only one candidate
final List<GroovyResolveResult> applicable = new ArrayList<GroovyResolveResult>();
for (GroovyResolveResult candidate : candidates) {
PsiMethod method = (PsiMethod)candidate.getElement();
assert method != null;
final PsiType type = org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.getSmartReturnType(method);
if (isApplicableClosureType(type, argumentTypes, place)) {
applicable.add(candidate);
}
}
if (applicable.size() == 1) {
return applicable.toArray(new GroovyResolveResult[applicable.size()]);
}
ContainerUtil.addAll(allCandidates, applicable);
}
if (!allCandidates.isEmpty()) {
return allCandidates.toArray(new GroovyResolveResult[allCandidates.size()]);
}
else if (!hasApplicableMethods) {
return methodCandidates;
}
return GroovyResolveResult.EMPTY_ARRAY;
}
private static boolean isApplicableClosureType(@Nullable PsiType type, @Nullable PsiType[] argTypes, @NotNull PsiElement place) {
if (!(type instanceof GrClosureType)) return false;
if (argTypes == null) return true;
final GrSignature signature = ((GrClosureType)type).getSignature();
return GrClosureSignatureUtil.isSignatureApplicable(signature, argTypes, place);
}
@Nullable
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(org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.getSmartReturnType((PsiMethod)element), candidate.getSubstitutor(), candidate.getSpreadState(), expression);
}
final PsiType type;
if (element instanceof GrField) {
type = ((GrField)element).getTypeGroovy();
}
else if (element instanceof PsiMethod) {
type = org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.getSmartReturnType((PsiMethod)element);
}
else {
return null;
}
if (type instanceof GrClosureType) {
final GrSignature signature = ((GrClosureType)type).getSignature();
PsiType returnType = GrClosureSignatureUtil.getReturnType(signature, args, expression);
return TypesUtil.substituteBoxAndNormalizeType(returnType, candidate.getSubstitutor(), candidate.getSpreadState(), expression);
}
return null;
}
public static boolean isEnumConstant(PsiReference ref, String name, String qName) {
PsiElement resolved = ref.resolve();
if (!(resolved instanceof PsiEnumConstant)) return false;
if (!name.equals(((PsiEnumConstant)resolved).getName())) return false;
PsiClass aClass = ((PsiEnumConstant)resolved).getContainingClass();
if (aClass == null) return false;
return qName.equals(aClass.getQualifiedName());
}
public static boolean isScriptField(GrVariable var) {
PsiClass context = org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.getContextClass(var.getParent());
final GrModifierList modifierList = var.getModifierList();
return context instanceof GroovyScriptClass &&
modifierList != null &&
modifierList.findAnnotation(GroovyCommonClassNames.GROOVY_TRANSFORM_FIELD) != null;
}
@Nullable
public static PsiClass resolveAnnotation(PsiElement insideAnnotation) {
final GrAnnotation annotation = PsiTreeUtil.getParentOfType(insideAnnotation, GrAnnotation.class, false);
if (annotation == null) return null;
final GrCodeReferenceElement reference = annotation.getClassReference();
final GroovyResolveResult result = reference.advancedResolve();
final PsiElement element = result.getElement();
if (element instanceof PsiClass && ((PsiClass)element).isAnnotationType()) return (PsiClass)element;
return null;
}
@NotNull
public static String inferExpectedPackageName(PsiElement place) {
PsiFile file = place.getContainingFile();
PsiDirectory psiDirectory = file.getContainingDirectory();
if (psiDirectory != null && file instanceof GroovyFile) {
PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage(psiDirectory);
if (aPackage != null) {
return aPackage.getQualifiedName();
}
}
return "";
}
public static PsiNamedElement findDuplicate(@NotNull GrVariable variable) {
if (isScriptField(variable)) {
final String name = variable.getName();
int count = 0;
final GroovyScriptClass script = (GroovyScriptClass)((GroovyFile)variable.getContainingFile()).getScriptClass();
assert script != null;
for (GrScriptField field : GrScriptField.getScriptFields(script)) {
if (name.equals(field.getName())) count++;
}
return count > 1 ? GrScriptField.getScriptField(variable) : null;
}
else {
PsiNamedElement duplicate = resolveExistingElement(variable, new DuplicateVariablesProcessor(variable), GrVariable.class);
final PsiElement context1 = variable.getContext();
if (duplicate == null && variable instanceof GrParameter && context1 != null) {
final PsiElement context = context1.getContext();
if (context instanceof GrClosableBlock ||
context instanceof GrMethod && !(context.getParent() instanceof GroovyFile) ||
context instanceof GrTryCatchStatement) {
duplicate = resolveExistingElement(context.getParent(), new DuplicateVariablesProcessor(variable), GrVariable.class);
}
}
if (duplicate instanceof GrLightParameter && "args".equals(duplicate.getName())) {
return null;
}
else {
return duplicate;
}
}
}
public static boolean canBeClassOrPackage(final GrReferenceExpression ref) {
GrExpression qualifier = ref.getQualifier();
if (qualifier instanceof GrReferenceExpression) {
final PsiElement resolvedQualifier = ((GrReferenceExpression)qualifier).resolve();
return resolvedQualifier instanceof PsiClass || resolvedQualifier instanceof PsiPackage;
}
else {
return qualifier == null;
}
}
@NotNull
public static List<Pair<PsiParameter, PsiType>> collectExpectedParamsByArg(@NotNull PsiElement place,
@NotNull GroovyResolveResult[] variants,
@NotNull GrNamedArgument[] namedArguments,
@NotNull GrExpression[] expressionArguments,
@NotNull GrClosableBlock[] closureArguments,
@NotNull GrExpression arg) {
List<Pair<PsiParameter, PsiType>> expectedParams = ContainerUtil.newArrayList();
for (GroovyResolveResult variant : variants) {
final Map<GrExpression, Pair<PsiParameter, PsiType>> map = GrClosureSignatureUtil.mapArgumentsToParameters(
variant, place, true, true, namedArguments, expressionArguments, closureArguments
);
if (map != null) {
final Pair<PsiParameter, PsiType> pair = map.get(arg);
ContainerUtil.addIfNotNull(expectedParams, pair);
}
}
return expectedParams;
}
public static boolean shouldProcessClasses(ClassHint classHint) {
return classHint == null || classHint.shouldProcess(ClassHint.ResolveKind.CLASS);
}
public static boolean shouldProcessMethods(ClassHint classHint) {
return classHint == null || classHint.shouldProcess(ClassHint.ResolveKind.METHOD);
}
public static boolean shouldProcessProperties(ClassHint classHint) {
return classHint == null || classHint.shouldProcess(ClassHint.ResolveKind.PROPERTY);
}
public static boolean shouldProcessPackages(ClassHint classHint) {
return classHint == null || classHint.shouldProcess(ClassHint.ResolveKind.PACKAGE);
}
public static boolean processStaticImports(@NotNull PsiScopeProcessor resolver,
@NotNull PsiFile file,
@NotNull ResolveState state,
@NotNull PsiElement place) {
if (!shouldProcessMethods(resolver.getHint(ClassHint.KEY))) return true;
return file.processDeclarations(new GrDelegatingScopeProcessorWithHints(resolver, null, ClassHint.RESOLVE_KINDS_METHOD) {
@Override
public boolean execute(@NotNull PsiElement element, @NotNull ResolveState _state) {
if (_state.get(RESOLVE_CONTEXT) instanceof GrImportStatement) {
super.execute(element, _state);
}
return true;
}
}, state, null, place);
}
public static boolean isClassReference(@NotNull GrReferenceExpression ref) {
GrExpression qualifier = ref.getQualifier();
return "class".equals(ref.getReferenceName()) &&
qualifier instanceof GrReferenceExpression &&
((GrReferenceExpression)qualifier).resolve() instanceof PsiClass &&
!org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.isThisReference(qualifier);
}
private static class DuplicateVariablesProcessor extends PropertyResolverProcessor {
private boolean myBorderPassed;
private final boolean myHasVisibilityModifier;
public DuplicateVariablesProcessor(GrVariable variable) {
super(variable.getName(), variable);
myBorderPassed = false;
myHasVisibilityModifier = hasExplicitVisibilityModifiers(variable);
}
private static boolean hasExplicitVisibilityModifiers(GrVariable variable) {
final GrModifierList modifierList = variable.getModifierList();
return modifierList != null && modifierList.hasExplicitVisibilityModifiers();
}
@Override
public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) {
if (myBorderPassed) {
return false;
}
if (element instanceof GrVariable && hasExplicitVisibilityModifiers((GrVariable)element) != myHasVisibilityModifier) {
return true;
}
if (element instanceof GrBindingVariable) return true;
return super.execute(element, state);
}
@Override
public void handleEvent(@NotNull Event event, Object associated) {
if (event == DECLARATION_SCOPE_PASSED) {
myBorderPassed = true;
}
super.handleEvent(event, associated);
}
}
}