blob: 2c2d8b2e12569cb8034e43895875a8f6239aa13a [file] [log] [blame]
/*
* Copyright 2000-2011 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.
*/
/**
* Created by IntelliJ IDEA.
* User: igork
* Date: Nov 25, 2002
* Time: 2:05:49 PM
* To change this template use Options | File Templates.
*/
package com.intellij.psi.scope.util;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicatorProvider;
import com.intellij.openapi.util.Comparing;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.JavaResolveUtil;
import com.intellij.psi.scope.JavaScopeProcessorEvent;
import com.intellij.psi.scope.MethodProcessorSetupFailedException;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.scope.processor.MethodsProcessor;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
public class PsiScopesUtil {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.scope.util.PsiScopesUtil");
private PsiScopesUtil() {
}
public static boolean treeWalkUp(@NotNull PsiScopeProcessor processor,
@NotNull PsiElement entrance,
@Nullable PsiElement maxScope) {
return treeWalkUp(processor, entrance, maxScope, ResolveState.initial());
}
public static boolean treeWalkUp(@NotNull final PsiScopeProcessor processor,
@NotNull final PsiElement entrance,
@Nullable final PsiElement maxScope,
@NotNull final ResolveState state) {
if (!entrance.isValid()) {
LOG.error(new PsiInvalidElementAccessException(entrance));
}
PsiElement prevParent = entrance;
PsiElement scope = entrance;
while (scope != null) {
ProgressIndicatorProvider.checkCanceled();
if (scope instanceof PsiClass) {
processor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, scope);
}
if (!scope.processDeclarations(processor, state, prevParent, entrance)) {
return false; // resolved
}
if (scope instanceof PsiModifierListOwner && !(scope instanceof PsiParameter/* important for not loading tree! */)) {
PsiModifierList modifierList = ((PsiModifierListOwner)scope).getModifierList();
if (modifierList != null && modifierList.hasModifierProperty(PsiModifier.STATIC)) {
processor.handleEvent(JavaScopeProcessorEvent.START_STATIC, null);
}
}
if (scope == maxScope) break;
prevParent = scope;
scope = prevParent.getContext();
processor.handleEvent(JavaScopeProcessorEvent.CHANGE_LEVEL, null);
}
return true;
}
public static boolean walkChildrenScopes(@NotNull PsiElement thisElement,
@NotNull PsiScopeProcessor processor,
@NotNull ResolveState state,
PsiElement lastParent,
PsiElement place) {
PsiElement child = null;
if (lastParent != null && lastParent.getParent() == thisElement) {
child = lastParent.getPrevSibling();
if (child == null) return true; // first element
}
if (child == null) {
child = thisElement.getLastChild();
}
while (child != null) {
if (!child.processDeclarations(processor, state, null, place)) return false;
child = child.getPrevSibling();
}
return true;
}
public static void processTypeDeclarations(PsiType type, PsiElement place, PsiScopeProcessor processor) {
if (type instanceof PsiArrayType) {
LanguageLevel languageLevel = PsiUtil.getLanguageLevel(place);
final PsiClass arrayClass = JavaPsiFacade.getInstance(place.getProject()).getElementFactory().getArrayClass(languageLevel);
final PsiTypeParameter[] arrayTypeParameters = arrayClass.getTypeParameters();
PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
if (arrayTypeParameters.length > 0) {
substitutor = substitutor.put(arrayTypeParameters[0], ((PsiArrayType)type).getComponentType());
}
arrayClass.processDeclarations(processor, ResolveState.initial().put(PsiSubstitutor.KEY, substitutor), arrayClass, place);
}
else if (type instanceof PsiIntersectionType) {
for (PsiType psiType : ((PsiIntersectionType)type).getConjuncts()) {
processTypeDeclarations(psiType, place, processor);
}
}
else if (type instanceof PsiDisjunctionType) {
final PsiType lub = ((PsiDisjunctionType)type).getLeastUpperBound();
processTypeDeclarations(lub, place, processor);
}
else {
final JavaResolveResult result = PsiUtil.resolveGenericsClassInType(type);
final PsiClass clazz = (PsiClass)result.getElement();
if (clazz != null) {
clazz.processDeclarations(processor, ResolveState.initial().put(PsiSubstitutor.KEY, result.getSubstitutor()), clazz, place);
}
}
}
public static boolean resolveAndWalk(@NotNull PsiScopeProcessor processor,
@NotNull PsiJavaCodeReferenceElement ref,
@Nullable PsiElement maxScope) {
return resolveAndWalk(processor, ref, maxScope, false);
}
public static boolean resolveAndWalk(@NotNull PsiScopeProcessor processor,
@NotNull PsiJavaCodeReferenceElement ref,
@Nullable PsiElement maxScope,
boolean incompleteCode) {
final PsiElement qualifier = ref.getQualifier();
final PsiElement classNameElement = ref.getReferenceNameElement();
if (classNameElement == null) return true;
if (qualifier != null) {
// Composite expression
PsiElement target = null;
PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
if (qualifier instanceof PsiExpression || qualifier instanceof PsiJavaCodeReferenceElement) {
PsiType type = null;
if (qualifier instanceof PsiExpression) {
type = ((PsiExpression)qualifier).getType();
if (type != null) {
assert type.isValid() : type.getClass() + "; " + qualifier;
}
processTypeDeclarations(type, ref, processor);
}
if (type == null && qualifier instanceof PsiJavaCodeReferenceElement) {
// In case of class qualifier
final PsiJavaCodeReferenceElement referenceElement = (PsiJavaCodeReferenceElement)qualifier;
final JavaResolveResult result = referenceElement.advancedResolve(incompleteCode);
target = result.getElement();
substitutor = result.getSubstitutor();
if (target instanceof PsiVariable) {
type = substitutor.substitute(((PsiVariable)target).getType());
if (type instanceof PsiClassType) {
final JavaResolveResult typeResult = ((PsiClassType)type).resolveGenerics();
target = typeResult.getElement();
substitutor = substitutor.putAll(typeResult.getSubstitutor());
}
else {
target = null;
}
}
else if (target instanceof PsiMethod) {
type = substitutor.substitute(((PsiMethod)target).getReturnType());
if (type instanceof PsiClassType) {
final JavaResolveResult typeResult = ((PsiClassType)type).resolveGenerics();
target = typeResult.getElement();
substitutor = substitutor.putAll(typeResult.getSubstitutor());
}
else {
target = null;
}
final PsiType[] types = referenceElement.getTypeParameters();
if (target instanceof PsiClass) {
substitutor = substitutor.putAll((PsiClass)target, types);
}
}
else if (target instanceof PsiClass) {
processor.handleEvent(JavaScopeProcessorEvent.START_STATIC, null);
}
}
}
if (target != null) {
return target.processDeclarations(processor, ResolveState.initial().put(PsiSubstitutor.KEY, substitutor), target, ref);
}
}
else {
// simple expression -> trying to resolve variable or method
return treeWalkUp(processor, ref, maxScope);
}
return true;
}
public static void setupAndRunProcessor(@NotNull MethodsProcessor processor,
@NotNull PsiCallExpression call,
boolean dummyImplicitConstructor)
throws MethodProcessorSetupFailedException {
if (call instanceof PsiMethodCallExpression) {
final PsiMethodCallExpression methodCall = (PsiMethodCallExpression)call;
final PsiJavaCodeReferenceElement ref = methodCall.getMethodExpression();
processor.setArgumentList(methodCall.getArgumentList());
processor.obtainTypeArguments(methodCall);
if (!ref.isQualified() || ref.getReferenceNameElement() instanceof PsiKeyword) {
final PsiElement referenceNameElement = ref.getReferenceNameElement();
if (referenceNameElement == null) return;
if (referenceNameElement instanceof PsiKeyword) {
final PsiKeyword keyword = (PsiKeyword)referenceNameElement;
if (keyword.getTokenType() == JavaTokenType.THIS_KEYWORD) {
final PsiClass aClass = JavaResolveUtil.getContextClass(methodCall);
if (aClass == null) {
throw new MethodProcessorSetupFailedException("Can't resolve class for this expression");
}
processor.setIsConstructor(true);
processor.setAccessClass(aClass);
aClass.processDeclarations(processor, ResolveState.initial(), null, call);
if (dummyImplicitConstructor) {
processDummyConstructor(processor, aClass);
}
}
else if (keyword.getTokenType() == JavaTokenType.SUPER_KEYWORD) {
PsiClass aClass = JavaResolveUtil.getContextClass(methodCall);
if (aClass == null) {
throw new MethodProcessorSetupFailedException("Can't resolve class for super expression");
}
final PsiClass superClass = aClass.getSuperClass();
if (superClass != null) {
PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
PsiClass runSuper = superClass;
List<PsiSubstitutor> contextSubstitutors = new ArrayList<PsiSubstitutor>();
do {
if (runSuper != null) {
PsiSubstitutor superSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(runSuper, aClass, PsiSubstitutor.EMPTY);
contextSubstitutors.add(superSubstitutor);
}
if (aClass.hasModifierProperty(PsiModifier.STATIC)) break;
aClass = JavaResolveUtil.getContextClass(aClass);
if (aClass != null) runSuper = aClass.getSuperClass();
}
while (aClass != null);
//apply substitutors in 'outer classes down to inner classes' order because inner class subst take precedence
for (int i = contextSubstitutors.size() - 1; i >= 0; i--) {
PsiSubstitutor contextSubstitutor = contextSubstitutors.get(i);
substitutor = substitutor.putAll(contextSubstitutor);
}
processor.setIsConstructor(true);
processor.setAccessClass(null);
final PsiMethod[] constructors = superClass.getConstructors();
ResolveState state = ResolveState.initial().put(PsiSubstitutor.KEY, substitutor);
for (PsiMethod constructor : constructors) {
if (!processor.execute(constructor, state)) return;
}
if (dummyImplicitConstructor) processDummyConstructor(processor, superClass);
}
}
else {
LOG.error("Unknown name element " + referenceNameElement + " in reference " + ref.getText() + "(" + ref + ")");
}
}
else if (referenceNameElement instanceof PsiIdentifier) {
processor.setIsConstructor(false);
processor.setName(referenceNameElement.getText());
processor.setAccessClass(null);
resolveAndWalk(processor, ref, null);
}
else {
LOG.error("Unknown name element " + referenceNameElement + " in reference " + ref.getText() + "(" + ref + ")");
}
}
else {
// Complex expression
final PsiElement referenceName = methodCall.getMethodExpression().getReferenceNameElement();
final PsiManager manager = call.getManager();
final PsiElement qualifier = ref.getQualifier();
if (referenceName == null) {
// e.g. "manager.(beginTransaction)"
throw new MethodProcessorSetupFailedException("Can't resolve method name for this expression");
}
if (referenceName instanceof PsiIdentifier && qualifier instanceof PsiExpression) {
PsiType type = ((PsiExpression)qualifier).getType();
if (type != null && qualifier instanceof PsiReferenceExpression) {
final PsiElement resolve = ((PsiReferenceExpression)qualifier).resolve();
if (resolve instanceof PsiVariable && ((PsiVariable)resolve).hasModifierProperty(PsiModifier.FINAL) && ((PsiVariable)resolve).hasInitializer()) {
final PsiExpression initializer = ((PsiVariable)resolve).getInitializer();
if (initializer instanceof PsiNewExpression) {
final PsiAnonymousClass anonymousClass = ((PsiNewExpression)initializer).getAnonymousClass();
if (anonymousClass != null && type.equals(anonymousClass.getBaseClassType())) {
final PsiMethod[] refMethods = anonymousClass.findMethodsByName(methodCall.getMethodExpression().getReferenceName(), false);
if (refMethods.length > 0) {
final PsiClass baseClass = PsiUtil.resolveClassInType(type);
if (baseClass != null && !hasCovariantOverriding(baseClass, refMethods)) {
type = initializer.getType();
}
}
}
}
}
}
if (type == null) {
if (qualifier instanceof PsiJavaCodeReferenceElement) {
final JavaResolveResult result = ((PsiJavaCodeReferenceElement)qualifier).advancedResolve(false);
if (result.getElement() instanceof PsiClass) {
processor.handleEvent(JavaScopeProcessorEvent.START_STATIC, null);
processQualifierResult(result, processor, methodCall);
}
}
else {
throw new MethodProcessorSetupFailedException("Cant determine qualifier type!");
}
}
else if (type instanceof PsiIntersectionType) {
final PsiType[] conjuncts = ((PsiIntersectionType)type).getConjuncts();
for (PsiType conjunct : conjuncts) {
if (!processQualifierType(conjunct, processor, manager, methodCall)) break;
}
}
else if (type instanceof PsiDisjunctionType) {
processQualifierType(((PsiDisjunctionType)type).getLeastUpperBound(), processor, manager, methodCall);
}
else {
processQualifierType(type, processor, manager, methodCall);
}
}
else {
LOG.error("ref: " + ref + " (" + ref.getClass() + ")," +
" ref.getReferenceNameElement()=" + ref.getReferenceNameElement() +
"; methodCall.getMethodExpression().getReferenceNameElement()=" + methodCall.getMethodExpression().getReferenceNameElement() +
"; qualifier="+qualifier);
}
}
}
else {
LOG.assertTrue(call instanceof PsiNewExpression);
PsiNewExpression newExpr = (PsiNewExpression)call;
PsiJavaCodeReferenceElement classRef = newExpr.getClassOrAnonymousClassReference();
if (classRef == null) {
throw new MethodProcessorSetupFailedException("Cant get reference to class in new expression");
}
final JavaResolveResult result = classRef.advancedResolve(false);
PsiClass aClass = (PsiClass)result.getElement();
if (aClass == null) {
throw new MethodProcessorSetupFailedException("Cant resolve class in new expression");
}
processor.setIsConstructor(true);
processor.setAccessClass(aClass);
processor.setArgumentList(newExpr.getArgumentList());
processor.obtainTypeArguments(newExpr);
aClass.processDeclarations(processor, ResolveState.initial().put(PsiSubstitutor.KEY, result.getSubstitutor()), null, call);
if (dummyImplicitConstructor) {
processDummyConstructor(processor, aClass);
}
}
}
private static boolean hasCovariantOverriding(PsiClass baseClass, PsiMethod[] refMethods) {
for (PsiMethod method : refMethods) {
final PsiType methodReturnType = method.getReturnType();
for (PsiMethod superMethod : method.findSuperMethods(baseClass)) {
if (!Comparing.equal(methodReturnType, superMethod.getReturnType())) {
return true;
}
}
}
return false;
}
private static boolean processQualifierType(@NotNull final PsiType type,
final MethodsProcessor processor,
PsiManager manager,
PsiMethodCallExpression call) throws MethodProcessorSetupFailedException {
LOG.assertTrue(type.isValid());
if (type instanceof PsiClassType) {
JavaResolveResult qualifierResult = ((PsiClassType)type).resolveGenerics();
return processQualifierResult(qualifierResult, processor, call);
}
if (type instanceof PsiArrayType) {
LanguageLevel languageLevel = PsiUtil.getLanguageLevel(call);
PsiElementFactory factory = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory();
JavaResolveResult qualifierResult =
factory.getArrayClassType(((PsiArrayType)type).getComponentType(), languageLevel).resolveGenerics();
return processQualifierResult(qualifierResult, processor, call);
}
if (type instanceof PsiIntersectionType) {
for (PsiType conjunct : ((PsiIntersectionType)type).getConjuncts()) {
if (!processQualifierType(conjunct, processor, manager, call)) return false;
}
}
return true;
}
private static boolean processQualifierResult(@NotNull JavaResolveResult qualifierResult,
@NotNull MethodsProcessor processor,
@NotNull PsiMethodCallExpression methodCall) throws MethodProcessorSetupFailedException {
PsiElement resolve = qualifierResult.getElement();
if (resolve == null) {
throw new MethodProcessorSetupFailedException("Cant determine qualifier class!");
}
if (resolve instanceof PsiTypeParameter) {
processor.setAccessClass((PsiClass)resolve);
}
else if (resolve instanceof PsiClass) {
PsiExpression qualifier = methodCall.getMethodExpression().getQualifierExpression();
if (!(qualifier instanceof PsiSuperExpression)) {
processor.setAccessClass((PsiClass)PsiUtil.getAccessObjectClass(qualifier).getElement());
}
else if (((PsiSuperExpression)qualifier).getQualifier() != null && PsiUtil.isLanguageLevel8OrHigher(qualifier) &&
CommonClassNames.JAVA_LANG_CLONEABLE.equals(((PsiClass)resolve).getQualifiedName()) && ((PsiClass)resolve).isInterface()) {
processor.setAccessClass((PsiClass)resolve);
}
}
processor.setIsConstructor(false);
processor.setName(methodCall.getMethodExpression().getReferenceName());
ResolveState state = ResolveState.initial().put(PsiSubstitutor.KEY, qualifierResult.getSubstitutor());
return resolve.processDeclarations(processor, state, methodCall, methodCall);
}
private static void processDummyConstructor(MethodsProcessor processor, PsiClass aClass) {
if (aClass instanceof PsiAnonymousClass) return;
try {
PsiMethod[] constructors = aClass.getConstructors();
if (constructors.length != 0) {
return;
}
final PsiElementFactory factory = JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory();
final PsiMethod dummyConstructor = factory.createConstructor();
PsiIdentifier nameIdentifier = aClass.getNameIdentifier();
if (nameIdentifier != null) {
dummyConstructor.getNameIdentifier().replace(nameIdentifier);
}
processor.forceAddResult(dummyConstructor);
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
}