blob: 254498e5674d441134e1c5e577eaf21e346afbfe [file] [log] [blame]
/*
* Copyright 2000-2013 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 com.intellij.slicer;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.*;
import com.intellij.psi.search.searches.MethodReferencesSearch;
import com.intellij.psi.search.searches.OverridingMethodsSearch;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.MethodSignatureUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ArrayUtilRt;
import com.intellij.util.Processor;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
/**
* @author cdr
*/
public class SliceForwardUtil {
public static boolean processUsagesFlownFromThe(@NotNull PsiElement element, @NotNull final Processor<SliceUsage> processor, @NotNull final SliceUsage parent) {
Pair<PsiElement, PsiSubstitutor> pair = getAssignmentTarget(element, parent);
if (pair != null) {
PsiElement target = pair.getFirst();
final PsiSubstitutor substitutor = pair.getSecond();
if (target instanceof PsiParameter) {
PsiParameter parameter = (PsiParameter)target;
PsiElement declarationScope = parameter.getDeclarationScope();
if (declarationScope instanceof PsiMethod) {
final PsiMethod method = (PsiMethod)declarationScope;
final int parameterIndex = method.getParameterList().getParameterIndex(parameter);
Processor<PsiMethod> myProcessor = new Processor<PsiMethod>() {
@Override
public boolean process(PsiMethod override) {
if (!parent.getScope().contains(override)) return true;
final PsiSubstitutor superSubstitutor = method == override
? substitutor
: MethodSignatureUtil.getSuperMethodSignatureSubstitutor(method.getSignature(substitutor),
override.getSignature(substitutor));
PsiParameter[] parameters = override.getParameterList().getParameters();
if (parameters.length <= parameterIndex) return true;
PsiParameter actualParam = parameters[parameterIndex];
SliceUsage usage = SliceUtil.createSliceUsage(actualParam, parent, superSubstitutor,parent.indexNesting, "");
return processor.process(usage);
}
};
if (!myProcessor.process(method)) return false;
return OverridingMethodsSearch.search(method, parent.getScope().toSearchScope(), true).forEach(myProcessor);
}
}
SliceUsage usage = SliceUtil.createSliceUsage(target, parent, parent.getSubstitutor(),parent.indexNesting, "");
return processor.process(usage);
}
if (element instanceof PsiReferenceExpression) {
PsiReferenceExpression ref = (PsiReferenceExpression)element;
PsiElement resolved = ref.resolve();
if (!(resolved instanceof PsiVariable)) return true;
final PsiVariable variable = (PsiVariable)resolved;
return processAssignedFrom(variable, ref, parent, processor);
}
if (element instanceof PsiVariable) {
return processAssignedFrom(element, element, parent, processor);
}
if (element instanceof PsiMethod) {
return processAssignedFrom(element, element, parent, processor);
}
return true;
}
private static boolean processAssignedFrom(final PsiElement from,
final PsiElement context,
final SliceUsage parent,
@NotNull final Processor<SliceUsage> processor) {
if (from instanceof PsiLocalVariable) {
return searchReferencesAndProcessAssignmentTarget(from, context, parent, processor);
}
if (from instanceof PsiParameter) {
PsiParameter parameter = (PsiParameter)from;
PsiElement scope = parameter.getDeclarationScope();
Collection<PsiParameter> parametersToAnalyze = new THashSet<PsiParameter>();
if (scope instanceof PsiMethod) {
final PsiMethod method = (PsiMethod)scope;
int index = method.getParameterList().getParameterIndex(parameter);
Collection<PsiMethod> superMethods = new THashSet<PsiMethod>(Arrays.asList(method.findDeepestSuperMethods()));
superMethods.add(method);
for (Iterator<PsiMethod> iterator = superMethods.iterator(); iterator.hasNext(); ) {
ProgressManager.checkCanceled();
PsiMethod superMethod = iterator.next();
if (!parent.params.scope.contains(superMethod)) {
iterator.remove();
}
}
final THashSet<PsiMethod> implementors = new THashSet<PsiMethod>(superMethods);
for (PsiMethod superMethod : superMethods) {
ProgressManager.checkCanceled();
if (!OverridingMethodsSearch.search(superMethod, parent.getScope().toSearchScope(), true).forEach(new Processor<PsiMethod>() {
@Override
public boolean process(PsiMethod sub) {
ProgressManager.checkCanceled();
implementors.add(sub);
return true;
}
})) return false;
}
for (PsiMethod implementor : implementors) {
ProgressManager.checkCanceled();
if (!parent.params.scope.contains(implementor)) continue;
if (implementor instanceof PsiCompiledElement) implementor = (PsiMethod)implementor.getNavigationElement();
PsiParameter[] parameters = implementor.getParameterList().getParameters();
if (index != -1 && index < parameters.length) {
parametersToAnalyze.add(parameters[index]);
}
}
}
else {
parametersToAnalyze.add(parameter);
}
for (final PsiParameter psiParameter : parametersToAnalyze) {
ProgressManager.checkCanceled();
if (!searchReferencesAndProcessAssignmentTarget(psiParameter, null, parent, processor)) return false;
}
return true;
}
if (from instanceof PsiField) {
return searchReferencesAndProcessAssignmentTarget(from, null, parent, processor);
}
if (from instanceof PsiMethod) {
PsiMethod method = (PsiMethod)from;
Collection<PsiMethod> superMethods = new THashSet<PsiMethod>(Arrays.asList(method.findDeepestSuperMethods()));
superMethods.add(method);
final Set<PsiReference> processed = new THashSet<PsiReference>(); //usages of super method and overridden method can overlap
for (final PsiMethod containingMethod : superMethods) {
if (!MethodReferencesSearch.search(containingMethod, parent.getScope().toSearchScope(), true).forEach(new Processor<PsiReference>() {
@Override
public boolean process(final PsiReference reference) {
ProgressManager.checkCanceled();
synchronized (processed) {
if (!processed.add(reference)) return true;
}
PsiElement element = reference.getElement().getParent();
return processAssignmentTarget(element, parent, processor);
}
})) {
return false;
}
}
}
return true;
}
private static boolean searchReferencesAndProcessAssignmentTarget(@NotNull PsiElement element, @Nullable final PsiElement context, final SliceUsage parent,
final Processor<SliceUsage> processor) {
return ReferencesSearch.search(element).forEach(new Processor<PsiReference>() {
@Override
public boolean process(PsiReference reference) {
PsiElement element = reference.getElement();
if (context != null && element.getTextOffset() < context.getTextOffset()) return true;
return processAssignmentTarget(element, parent, processor);
}
});
}
private static boolean processAssignmentTarget(PsiElement element, final SliceUsage parent, final Processor<SliceUsage> processor) {
if (!parent.params.scope.contains(element)) return true;
if (element instanceof PsiCompiledElement) element = element.getNavigationElement();
Pair<PsiElement, PsiSubstitutor> pair = getAssignmentTarget(element, parent);
if (pair != null) {
SliceUsage usage = SliceUtil.createSliceUsage(element, parent, pair.getSecond(),parent.indexNesting, "");
return processor.process(usage);
}
if (parent.params.showInstanceDereferences && isDereferenced(element)) {
SliceUsage usage = new SliceDereferenceUsage(element.getParent(), parent, parent.getSubstitutor());
return processor.process(usage);
}
return true;
}
private static boolean isDereferenced(PsiElement element) {
if (!(element instanceof PsiReferenceExpression)) return false;
PsiElement parent = element.getParent();
if (!(parent instanceof PsiReferenceExpression)) return false;
return ((PsiReferenceExpression)parent).getQualifierExpression() == element;
}
private static Pair<PsiElement,PsiSubstitutor> getAssignmentTarget(PsiElement element, SliceUsage parentUsage) {
element = complexify(element);
PsiElement target = null;
PsiSubstitutor substitutor = parentUsage.getSubstitutor();
//assignment
PsiElement parent = element.getParent();
if (parent instanceof PsiAssignmentExpression) {
PsiAssignmentExpression assignment = (PsiAssignmentExpression)parent;
if (element.equals(assignment.getRExpression())) {
PsiElement left = assignment.getLExpression();
if (left instanceof PsiReferenceExpression) {
JavaResolveResult result = ((PsiReferenceExpression)left).advancedResolve(false);
target = result.getElement();
substitutor = result.getSubstitutor();
}
}
}
else if (parent instanceof PsiVariable) {
PsiVariable variable = (PsiVariable)parent;
PsiElement initializer = variable.getInitializer();
if (element.equals(initializer)) {
target = variable;
}
}
//method call
else if (parent instanceof PsiExpressionList && parent.getParent() instanceof PsiCallExpression) {
PsiExpression[] expressions = ((PsiExpressionList)parent).getExpressions();
int index = ArrayUtilRt.find(expressions, element);
PsiCallExpression methodCall = (PsiCallExpression)parent.getParent();
JavaResolveResult result = methodCall.resolveMethodGenerics();
PsiMethod method = (PsiMethod)result.getElement();
if (index != -1 && method != null) {
PsiParameter[] parameters = method.getParameterList().getParameters();
if (index < parameters.length) {
target = parameters[index];
substitutor = result.getSubstitutor();
}
}
}
else if (parent instanceof PsiReturnStatement) {
PsiReturnStatement statement = (PsiReturnStatement)parent;
if (element.equals(statement.getReturnValue())) {
target = PsiTreeUtil.getParentOfType(statement, PsiMethod.class);
}
}
return target == null ? null : Pair.create(target, substitutor);
}
@NotNull
public static PsiElement complexify(@NotNull PsiElement element) {
PsiElement parent = element.getParent();
if (parent instanceof PsiParenthesizedExpression && element.equals(((PsiParenthesizedExpression)parent).getExpression())) {
return complexify(parent);
}
if (parent instanceof PsiTypeCastExpression && element.equals(((PsiTypeCastExpression)parent).getOperand())) {
return complexify(parent);
}
return element;
}
}