blob: 846e1fd5d0ee11d91818c41787df9f2c15ecc694 [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 com.intellij.refactoring.util.duplicates;
import com.intellij.codeInsight.PsiEquivalenceUtil;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.*;
import com.intellij.psi.controlFlow.*;
import com.intellij.psi.impl.source.PsiImmediateClassType;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.*;
import com.intellij.refactoring.extractMethod.InputVariables;
import com.intellij.refactoring.util.RefactoringChangeUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.IntArrayList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* @author dsl
*/
public class DuplicatesFinder {
private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.util.duplicates.DuplicatesFinder");
public static final Key<Pair<PsiVariable, PsiType>> PARAMETER = Key.create("PARAMETER");
private final PsiElement[] myPattern;
private InputVariables myParameters;
private final List<? extends PsiVariable> myOutputParameters;
private final List<PsiElement> myPatternAsList;
private boolean myMultipleExitPoints = false;
@Nullable private final ReturnValue myReturnValue;
public DuplicatesFinder(PsiElement[] pattern,
InputVariables parameters,
@Nullable ReturnValue returnValue,
@NotNull List<? extends PsiVariable> outputParameters
) {
myReturnValue = returnValue;
LOG.assertTrue(pattern.length > 0);
myPattern = pattern;
myPatternAsList = Arrays.asList(myPattern);
myParameters = parameters;
myOutputParameters = outputParameters;
final PsiElement codeFragment = ControlFlowUtil.findCodeFragment(pattern[0]);
try {
final ControlFlow controlFlow = ControlFlowFactory.getInstance(codeFragment.getProject()).getControlFlow(codeFragment, new LocalsControlFlowPolicy(codeFragment), false);
int startOffset;
int i = 0;
do {
startOffset = controlFlow.getStartOffset(pattern[i++]);
} while(startOffset < 0 && i < pattern.length);
int endOffset;
int j = pattern.length - 1;
do {
endOffset = controlFlow.getEndOffset(pattern[j--]);
} while(endOffset < 0 && j >= 0);
IntArrayList exitPoints = new IntArrayList();
final Collection<PsiStatement> exitStatements = ControlFlowUtil
.findExitPointsAndStatements(controlFlow, startOffset, endOffset, exitPoints, ControlFlowUtil.DEFAULT_EXIT_STATEMENTS_CLASSES);
myMultipleExitPoints = exitPoints.size() > 1;
if (myMultipleExitPoints) {
myParameters.removeParametersUsedInExitsOnly(codeFragment, exitStatements, controlFlow, startOffset, endOffset);
}
}
catch (AnalysisCanceledException e) {
}
}
public DuplicatesFinder(final PsiElement[] pattern,
final InputVariables psiParameters,
final List<? extends PsiVariable> psiVariables) {
this(pattern, psiParameters, null, psiVariables);
}
public List<Match> findDuplicates(PsiElement scope) {
annotatePattern();
final ArrayList<Match> result = new ArrayList<Match>();
findPatternOccurrences(result, scope);
deannotatePattern();
return result;
}
@Nullable
public Match isDuplicate(PsiElement element, boolean ignoreParameterTypesAndPostVariableUsages) {
annotatePattern();
Match match = isDuplicateFragment(element, ignoreParameterTypesAndPostVariableUsages);
deannotatePattern();
return match;
}
private void annotatePattern() {
for (final PsiElement patternComponent : myPattern) {
patternComponent.accept(new JavaRecursiveElementWalkingVisitor() {
@Override public void visitReferenceElement(PsiJavaCodeReferenceElement reference) {
final PsiElement element = reference.resolve();
if (element instanceof PsiVariable) {
final PsiVariable variable = (PsiVariable)element;
PsiType type = variable.getType();
myParameters.annotateWithParameter(reference);
if (myOutputParameters.contains(element)) {
reference.putUserData(PARAMETER, Pair.create(variable, type));
}
}
PsiElement qualifier = reference.getQualifier();
if (qualifier != null) {
qualifier.accept(this);
}
}
});
}
}
private void deannotatePattern() {
for (final PsiElement patternComponent : myPattern) {
patternComponent.accept(new JavaRecursiveElementWalkingVisitor() {
@Override public void visitReferenceElement(PsiJavaCodeReferenceElement reference) {
if (reference.getUserData(PARAMETER) != null) {
reference.putUserData(PARAMETER, null);
}
}
});
}
}
private void findPatternOccurrences(List<Match> array, PsiElement scope) {
PsiElement[] children = scope.getChildren();
for (PsiElement child : children) {
final Match match = isDuplicateFragment(child, false);
if (match != null) {
array.add(match);
continue;
}
findPatternOccurrences(array, child);
}
}
@Nullable
private Match isDuplicateFragment(PsiElement candidate, boolean ignoreParameterTypesAndPostVariableUsages) {
for (PsiElement pattern : myPattern) {
if (PsiTreeUtil.isAncestor(pattern, candidate, false)) return null;
}
PsiElement sibling = candidate;
ArrayList<PsiElement> candidates = new ArrayList<PsiElement>();
for (final PsiElement element : myPattern) {
if (sibling == null) return null;
if (!canBeEquivalent(element, sibling)) return null;
candidates.add(sibling);
sibling = PsiTreeUtil.skipSiblingsForward(sibling, PsiWhiteSpace.class, PsiComment.class);
}
LOG.assertTrue(myPattern.length == candidates.size());
if (myPattern.length == 1 && myPattern[0] instanceof PsiExpression) {
if (candidates.get(0) instanceof PsiExpression) {
final PsiExpression candidateExpression = (PsiExpression)candidates.get(0);
if (PsiUtil.isAccessedForWriting(candidateExpression)) return null;
final PsiType patternType = ((PsiExpression)myPattern[0]).getType();
final PsiType candidateType = candidateExpression.getType();
PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
final PsiMethod method = PsiTreeUtil.getParentOfType(myPattern[0], PsiMethod.class);
if (method != null) {
final PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(candidate.getProject()).getResolveHelper();
substitutor = resolveHelper.inferTypeArguments(method.getTypeParameters(), new PsiType[]{patternType},
new PsiType[]{candidateType}, PsiUtil.getLanguageLevel(method));
}
if (!canTypesBeEquivalent(substitutor.substitute(patternType), candidateType)) return null;
}
else {
return null;
}
}
final Match match = new Match(candidates.get(0), candidates.get(candidates.size() - 1), ignoreParameterTypesAndPostVariableUsages);
for (int i = 0; i < myPattern.length; i++) {
if (!matchPattern(myPattern[i], candidates.get(i), candidates, match)) return null;
}
if (!ignoreParameterTypesAndPostVariableUsages && checkPostVariableUsages(candidates, match)) return null;
return match;
}
private boolean checkPostVariableUsages(final ArrayList<PsiElement> candidates, final Match match) {
final PsiElement codeFragment = ControlFlowUtil.findCodeFragment(candidates.get(0));
try {
final ControlFlow controlFlow = ControlFlowFactory.getInstance(codeFragment.getProject()).getControlFlow(codeFragment, new LocalsControlFlowPolicy(codeFragment), false);
int startOffset;
int i = 0;
do {
startOffset = controlFlow.getStartOffset(candidates.get(i++));
} while(startOffset < 0 && i < candidates.size());
int endOffset;
int j = candidates.size() - 1;
do {
endOffset = controlFlow.getEndOffset(candidates.get(j--));
} while(endOffset < 0 && j >= 0);
final IntArrayList exitPoints = new IntArrayList();
ControlFlowUtil.findExitPointsAndStatements(controlFlow, startOffset, endOffset, exitPoints, ControlFlowUtil.DEFAULT_EXIT_STATEMENTS_CLASSES);
final PsiVariable[] outVariables = ControlFlowUtil.getOutputVariables(controlFlow, startOffset, endOffset, exitPoints.toArray());
if (outVariables.length > 0) {
if (outVariables.length == 1) {
ReturnValue returnValue = match.getReturnValue();
if (returnValue == null) {
returnValue = myReturnValue;
}
if (returnValue instanceof VariableReturnValue) {
final ReturnValue value = match.getOutputVariableValue(((VariableReturnValue)returnValue).getVariable());
if (value != null) {
if (value.isEquivalent(new VariableReturnValue(outVariables[0]))) return false;
if (value instanceof ExpressionReturnValue) {
final PsiExpression expression = ((ExpressionReturnValue)value).getExpression();
if (expression instanceof PsiReferenceExpression) {
final PsiElement variable = ((PsiReferenceExpression)expression).resolve();
return variable == null || !PsiEquivalenceUtil.areElementsEquivalent(variable, outVariables[0]);
}
}
}
}
}
return true;
}
}
catch (AnalysisCanceledException e) {
}
return false;
}
private static boolean canTypesBeEquivalent(PsiType type1, PsiType type2) {
if (type1 == null || type2 == null) return false;
if (!type2.isAssignableFrom(type1)) {
if (type1 instanceof PsiImmediateClassType && type2 instanceof PsiImmediateClassType) {
final PsiClass psiClass1 = ((PsiImmediateClassType)type1).resolve();
final PsiClass psiClass2 = ((PsiImmediateClassType)type2).resolve();
if (!(psiClass1 instanceof PsiAnonymousClass &&
psiClass2 instanceof PsiAnonymousClass &&
psiClass1.getManager().areElementsEquivalent(((PsiAnonymousClass)psiClass1).getBaseClassType().resolve(),
((PsiAnonymousClass)psiClass2).getBaseClassType().resolve()))) {
return false;
}
}
else {
return false;
}
}
return true;
}
private static boolean canBeEquivalent(final PsiElement pattern, PsiElement candidate) {
if (pattern instanceof PsiReturnStatement && candidate instanceof PsiExpressionStatement) return true;
if (pattern instanceof PsiReturnStatement && candidate instanceof PsiDeclarationStatement) return true;
if (pattern instanceof PsiThisExpression && candidate instanceof PsiReferenceExpression) return true;
final ASTNode node1 = pattern.getNode();
final ASTNode node2 = candidate.getNode();
if (node1 == null || node2 == null) return false;
return node1.getElementType() == node2.getElementType();
}
private boolean matchPattern(PsiElement pattern,
PsiElement candidate,
List<PsiElement> candidates,
Match match) {
if (pattern == null || candidate == null) return pattern == candidate;
if (pattern.getUserData(PARAMETER) != null) {
final Pair<PsiVariable, PsiType> parameter = pattern.getUserData(PARAMETER);
return match.putParameter(parameter, candidate);
}
if (!canBeEquivalent(pattern, candidate)) return false; // Q : is it correct to check implementation classes?
if (pattern instanceof PsiExpressionList && candidate instanceof PsiExpressionList) { //check varargs
final PsiExpression[] expressions = ((PsiExpressionList)pattern).getExpressions();
final PsiExpression[] childExpressions = ((PsiExpressionList)candidate).getExpressions();
if (expressions.length > 0 && expressions[expressions.length - 1] instanceof PsiReferenceExpression) {
final PsiElement resolved = ((PsiReferenceExpression)expressions[expressions.length - 1]).resolve();
if (resolved instanceof PsiParameter && ((PsiParameter)resolved).getType() instanceof PsiEllipsisType) {
for(int i = 0; i < expressions.length - 1; i++) {
final Pair<PsiVariable, PsiType> parameter = expressions[i].getUserData(PARAMETER);
if (parameter == null) {
if (!matchPattern(expressions[i], childExpressions[i], candidates, match)) {
return false;
}
} else if (!match.putParameter(parameter, childExpressions[i])) return false;
}
final Pair<PsiVariable, PsiType> param = expressions[expressions.length - 1].getUserData(PARAMETER);
if (param == null) return false;
for(int i = expressions.length - 1; i < childExpressions.length; i++) {
if (!match.putParameter(param, childExpressions[i])) return false;
}
return true;
}
}
}
if (pattern instanceof PsiAssignmentExpression) {
final PsiExpression lExpression = ((PsiAssignmentExpression)pattern).getLExpression();
if (lExpression.getType() instanceof PsiPrimitiveType &&
lExpression instanceof PsiReferenceExpression &&
((PsiReferenceExpression)lExpression).resolve() instanceof PsiParameter) {
return false;
}
} else if (pattern instanceof PsiPrefixExpression) {
if (checkParameterModification(((PsiPrefixExpression)pattern).getOperand(), ((PsiPrefixExpression)pattern).getOperationTokenType(),
((PsiPrefixExpression)candidate).getOperand())) return false;
} else if (pattern instanceof PsiPostfixExpression) {
if (checkParameterModification(((PsiPostfixExpression)pattern).getOperand(), ((PsiPostfixExpression)pattern).getOperationTokenType(),
((PsiPostfixExpression)candidate).getOperand())) return false;
}
if (pattern instanceof PsiJavaCodeReferenceElement) {
final PsiElement resolveResult1 = ((PsiJavaCodeReferenceElement)pattern).resolve();
final PsiElement resolveResult2 = ((PsiJavaCodeReferenceElement)candidate).resolve();
if (resolveResult1 instanceof PsiClass && resolveResult2 instanceof PsiClass) return true;
if (isUnder(resolveResult1, myPatternAsList) && isUnder(resolveResult2, candidates)) {
traverseParameter(resolveResult1, resolveResult2, match);
return match.putDeclarationCorrespondence(resolveResult1, resolveResult2);
}
final PsiElement qualifier2 = ((PsiJavaCodeReferenceElement)candidate).getQualifier();
if (!equivalentResolve(resolveResult1, resolveResult2, qualifier2)) {
return false;
}
PsiElement qualifier1 = ((PsiJavaCodeReferenceElement)pattern).getQualifier();
if (qualifier1 instanceof PsiReferenceExpression && qualifier2 instanceof PsiReferenceExpression &&
!match.areCorrespond(((PsiReferenceExpression)qualifier1).resolve(), ((PsiReferenceExpression)qualifier2).resolve())) {
return false;
}
if (qualifier1 == null && qualifier2 == null) {
final PsiClass patternClass = RefactoringChangeUtil.getThisClass(pattern);
final PsiClass candidateClass = RefactoringChangeUtil.getThisClass(candidate);
if (resolveResult1 == resolveResult2 &&
resolveResult1 instanceof PsiMember &&
!InheritanceUtil.isInheritorOrSelf(candidateClass, patternClass, true) &&
InheritanceUtil.isInheritorOrSelf(candidateClass, ((PsiMember)resolveResult1).getContainingClass(), true)) {
return false;
}
}
}
if (pattern instanceof PsiTypeCastExpression) {
final PsiTypeElement castTypeElement1 = ((PsiTypeCastExpression)pattern).getCastType();
final PsiTypeElement castTypeElement2 = ((PsiTypeCastExpression)candidate).getCastType();
if (castTypeElement1 != null && castTypeElement2 != null) {
final PsiType type1 = TypeConversionUtil.erasure(castTypeElement1.getType());
final PsiType type2 = TypeConversionUtil.erasure(castTypeElement2.getType());
if (!type1.equals(type2)) return false;
}
} else if (pattern instanceof PsiNewExpression) {
final PsiType type1 = ((PsiNewExpression)pattern).getType();
final PsiType type2 = ((PsiNewExpression)candidate).getType();
if (type1 == null || type2 == null) return false;
final PsiJavaCodeReferenceElement classReference1 = ((PsiNewExpression)pattern).getClassReference();
final PsiJavaCodeReferenceElement classReference2 = ((PsiNewExpression)candidate).getClassReference();
if (classReference1 != null && classReference2 != null) {
final PsiElement resolved1 = classReference1.resolve();
final PsiElement resolved2 = classReference2.resolve();
if (!pattern.getManager().areElementsEquivalent(resolved1, resolved2)) return false;
}
else {
if (!canTypesBeEquivalent(type1, type2)) return false;
}
} else if (pattern instanceof PsiClassObjectAccessExpression) {
final PsiTypeElement operand1 = ((PsiClassObjectAccessExpression)pattern).getOperand();
final PsiTypeElement operand2 = ((PsiClassObjectAccessExpression)candidate).getOperand();
return operand1.getType().equals(operand2.getType());
} else if (pattern instanceof PsiInstanceOfExpression) {
final PsiTypeElement operand1 = ((PsiInstanceOfExpression)pattern).getCheckType();
final PsiTypeElement operand2 = ((PsiInstanceOfExpression)candidate).getCheckType();
if (operand1 == null || operand2 == null) return false;
if (!operand1.getType().equals(operand2.getType())) return false;
} else if (pattern instanceof PsiReturnStatement) {
final PsiReturnStatement patternReturnStatement = (PsiReturnStatement)pattern;
return matchReturnStatement(patternReturnStatement, candidate, candidates, match);
} else if (pattern instanceof PsiContinueStatement) {
match.registerReturnValue(new ContinueReturnValue());
} else if (pattern instanceof PsiBreakStatement) {
match.registerReturnValue(new BreakReturnValue());
}else if (pattern instanceof PsiMethodCallExpression) {
final PsiMethod patternMethod = ((PsiMethodCallExpression)pattern).resolveMethod();
final PsiMethod candidateMethod = ((PsiMethodCallExpression)candidate).resolveMethod();
if (patternMethod != null && candidateMethod != null) {
if (!MethodSignatureUtil.areSignaturesEqual(patternMethod, candidateMethod)) return false;
}
} else if (pattern instanceof PsiReferenceExpression) {
final PsiReferenceExpression patternRefExpr = (PsiReferenceExpression)pattern;
final PsiReferenceExpression candidateRefExpr = (PsiReferenceExpression)candidate;
final PsiExpression patternQualifier = patternRefExpr.getQualifierExpression();
final PsiExpression candidateQualifier = candidateRefExpr.getQualifierExpression();
if (patternQualifier == null) {
PsiClass contextClass = PsiTreeUtil.getParentOfType(pattern, PsiClass.class);
if (candidateQualifier instanceof PsiReferenceExpression) {
final PsiElement resolved = ((PsiReferenceExpression)candidateQualifier).resolve();
if (resolved instanceof PsiClass && contextClass != null && InheritanceUtil.isInheritorOrSelf(contextClass, (PsiClass)resolved, true)) {
return true;
}
}
return contextClass != null && match.registerInstanceExpression(candidateQualifier, contextClass);
} else {
if (candidateQualifier == null) {
if (patternQualifier instanceof PsiThisExpression) {
final PsiJavaCodeReferenceElement qualifier = ((PsiThisExpression)patternQualifier).getQualifier();
if (candidate instanceof PsiReferenceExpression) {
PsiElement contextClass = qualifier == null ? PsiTreeUtil.getParentOfType(pattern, PsiClass.class) : qualifier.resolve();
return contextClass instanceof PsiClass && match.registerInstanceExpression(((PsiReferenceExpression)candidate).getQualifierExpression(),
(PsiClass)contextClass);
}
} else {
final PsiType type = patternQualifier.getType();
PsiClass contextClass = type instanceof PsiClassType ? ((PsiClassType)type).resolve() : null;
try {
final Pair<PsiVariable, PsiType> parameter = patternQualifier.getUserData(PARAMETER);
if (parameter != null) {
final PsiClass thisClass = RefactoringChangeUtil.getThisClass(parameter.first);
if (contextClass != null && InheritanceUtil.isInheritorOrSelf(thisClass, contextClass, true)) {
contextClass = thisClass;
}
final PsiClass thisCandidate = RefactoringChangeUtil.getThisClass(candidate);
if (thisCandidate != null && InheritanceUtil.isInheritorOrSelf(thisCandidate, contextClass, true)) {
contextClass = thisCandidate;
}
return contextClass != null && match.putParameter(parameter, RefactoringChangeUtil
.createThisExpression(patternQualifier.getManager(), contextClass));
} else if (patternQualifier instanceof PsiReferenceExpression) {
final PsiElement resolved = ((PsiReferenceExpression)patternQualifier).resolve();
if (resolved instanceof PsiClass) {
final PsiClass classContext = PsiTreeUtil.getParentOfType(candidate, PsiClass.class);
if (classContext != null && InheritanceUtil.isInheritorOrSelf(classContext, (PsiClass)resolved, true)) {
return true;
}
}
}
return false;
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
} else {
if (patternQualifier instanceof PsiThisExpression && candidateQualifier instanceof PsiThisExpression) {
final PsiJavaCodeReferenceElement thisPatternQualifier = ((PsiThisExpression)patternQualifier).getQualifier();
final PsiElement patternContextClass = thisPatternQualifier == null ? PsiTreeUtil.getParentOfType(patternQualifier, PsiClass.class) : thisPatternQualifier.resolve();
final PsiJavaCodeReferenceElement thisCandidateQualifier = ((PsiThisExpression)candidateQualifier).getQualifier();
final PsiElement candidateContextClass = thisCandidateQualifier == null ? PsiTreeUtil.getParentOfType(candidateQualifier, PsiClass.class) : thisCandidateQualifier.resolve();
return patternContextClass == candidateContextClass;
}
}
}
} else if (pattern instanceof PsiThisExpression) {
final PsiJavaCodeReferenceElement qualifier = ((PsiThisExpression)pattern).getQualifier();
final PsiElement contextClass = qualifier == null ? PsiTreeUtil.getParentOfType(pattern, PsiClass.class) : qualifier.resolve();
if (candidate instanceof PsiReferenceExpression) {
final PsiElement parent = candidate.getParent();
return parent instanceof PsiReferenceExpression && contextClass instanceof PsiClass && match.registerInstanceExpression(((PsiReferenceExpression)parent).getQualifierExpression(),
(PsiClass)contextClass);
} else if (candidate instanceof PsiThisExpression) {
final PsiJavaCodeReferenceElement candidateQualifier = ((PsiThisExpression)candidate).getQualifier();
final PsiElement candidateContextClass = candidateQualifier == null ? PsiTreeUtil.getParentOfType(candidate, PsiClass.class) : candidateQualifier.resolve();
return contextClass == candidateContextClass;
}
} else if (pattern instanceof PsiSuperExpression) {
final PsiJavaCodeReferenceElement qualifier = ((PsiSuperExpression)pattern).getQualifier();
final PsiElement contextClass = qualifier == null ? PsiTreeUtil.getParentOfType(pattern, PsiClass.class) : qualifier.resolve();
if (candidate instanceof PsiSuperExpression) {
final PsiJavaCodeReferenceElement candidateQualifier = ((PsiSuperExpression)candidate).getQualifier();
return contextClass == (candidateQualifier != null ? candidateQualifier.resolve() : PsiTreeUtil.getParentOfType(candidate, PsiClass.class));
}
}
PsiElement[] children1 = getFilteredChildren(pattern);
PsiElement[] children2 = getFilteredChildren(candidate);
if (children1.length != children2.length) return false;
for (int i = 0; i < children1.length; i++) {
PsiElement child1 = children1[i];
PsiElement child2 = children2[i];
if (!matchPattern(child1, child2, candidates, match)) return false;
}
if (children1.length == 0) {
if (pattern.getParent() instanceof PsiVariable && ((PsiVariable)pattern.getParent()).getNameIdentifier() == pattern) {
return match.putDeclarationCorrespondence(pattern.getParent(), candidate.getParent());
}
if (!pattern.textMatches(candidate)) return false;
}
return true;
}
private static boolean checkParameterModification(final PsiExpression expression,
final IElementType sign,
PsiExpression candidate) {
if (expression instanceof PsiReferenceExpression && ((PsiReferenceExpression)expression).resolve() instanceof PsiParameter &&
(sign.equals(JavaTokenType.MINUSMINUS)|| sign.equals(JavaTokenType.PLUSPLUS))) {
if (candidate instanceof PsiReferenceExpression && ((PsiReferenceExpression)candidate).resolve() instanceof PsiParameter) {
return false;
}
return true;
}
return false;
}
private static void traverseParameter(PsiElement pattern, PsiElement candidate, Match match) {
if (pattern == null || candidate == null) return;
if (pattern.getUserData(PARAMETER) != null) {
final Pair<PsiVariable, PsiType> parameter = pattern.getUserData(PARAMETER);
match.putParameter(parameter, candidate);
return;
}
PsiElement[] children1 = getFilteredChildren(pattern);
PsiElement[] children2 = getFilteredChildren(candidate);
if (children1.length != children2.length) return;
for (int i = 0; i < children1.length; i++) {
PsiElement child1 = children1[i];
PsiElement child2 = children2[i];
traverseParameter(child1, child2, match);
}
}
private boolean matchReturnStatement(final PsiReturnStatement patternReturnStatement,
PsiElement candidate,
List<PsiElement> candidates,
Match match) {
if (candidate instanceof PsiExpressionStatement) {
final PsiExpression expression = ((PsiExpressionStatement)candidate).getExpression();
if (expression instanceof PsiAssignmentExpression) {
final PsiExpression returnValue = patternReturnStatement.getReturnValue();
final PsiExpression rExpression = ((PsiAssignmentExpression)expression).getRExpression();
if (!matchPattern(returnValue, rExpression, candidates, match)) return false;
final PsiExpression lExpression = ((PsiAssignmentExpression)expression).getLExpression();
return match.registerReturnValue(new ExpressionReturnValue(lExpression));
}
else return false;
}
else if (candidate instanceof PsiDeclarationStatement) {
final PsiElement[] declaredElements = ((PsiDeclarationStatement)candidate).getDeclaredElements();
if (declaredElements.length != 1) return false;
if (!(declaredElements[0] instanceof PsiVariable)) return false;
final PsiVariable variable = (PsiVariable)declaredElements[0];
if (!matchPattern(patternReturnStatement.getReturnValue(), variable.getInitializer(), candidates, match)) return false;
return match.registerReturnValue(new VariableReturnValue(variable));
}
else if (candidate instanceof PsiReturnStatement) {
final PsiExpression returnValue = ((PsiReturnStatement)candidate).getReturnValue();
if (myMultipleExitPoints) {
return match.registerReturnValue(new ConditionalReturnStatementValue(returnValue));
}
else {
final PsiElement classOrLambda = PsiTreeUtil.getParentOfType(returnValue, PsiClass.class, PsiLambdaExpression.class);
final PsiElement commonParent = PsiTreeUtil.findCommonParent(match.getMatchStart(), match.getMatchEnd());
if (classOrLambda == null || !PsiTreeUtil.isAncestor(commonParent, classOrLambda, false)) {
if (returnValue != null && !match.registerReturnValue(ReturnStatementReturnValue.INSTANCE)) return false; //do not register return value for return; statement
}
return matchPattern(patternReturnStatement.getReturnValue(), returnValue, candidates, match);
}
}
else return false;
}
private static boolean equivalentResolve(final PsiElement resolveResult1, final PsiElement resolveResult2, PsiElement qualifier2) {
final boolean b = Comparing.equal(resolveResult1, resolveResult2);
if (b) return b;
if (resolveResult1 instanceof PsiMethod && resolveResult2 instanceof PsiMethod) {
final PsiMethod method1 = (PsiMethod)resolveResult1;
final PsiMethod method2 = (PsiMethod)resolveResult2;
if (ArrayUtil.find(method1.findSuperMethods(), method2) >= 0) return true;
if (ArrayUtil.find(method2.findSuperMethods(), method1) >= 0) return true;
if (method1.getName().equals(method2.getName())) {
PsiClass class2 = method2.getContainingClass();
if (qualifier2 instanceof PsiReferenceExpression) {
final PsiType type = ((PsiReferenceExpression)qualifier2).getType();
if (type instanceof PsiClassType){
final PsiClass resolvedClass = PsiUtil.resolveClassInType(type);
if (!(resolvedClass instanceof PsiTypeParameter)) {
class2 = resolvedClass;
}
}
}
if (class2 != null && PsiUtil.isAccessible(method1, class2, null)) {
final PsiMethod[] methods = class2.getAllMethods();
if (ArrayUtil.find(methods, method1) != -1) return true;
}
}
return false;
}
else {
return false;
}
}
private static boolean isUnder(PsiElement element, List<PsiElement> parents) {
if (element == null) return false;
for (final PsiElement parent : parents) {
if (PsiTreeUtil.isAncestor(parent, element, false)) return true;
}
return false;
}
private static PsiElement[] getFilteredChildren(PsiElement element1) {
PsiElement[] children1 = element1.getChildren();
ArrayList<PsiElement> array = new ArrayList<PsiElement>();
for (PsiElement child : children1) {
if (!(child instanceof PsiWhiteSpace) && !(child instanceof PsiComment)) {
if (child instanceof PsiBlockStatement) {
Collections.addAll(array, getFilteredChildren(child));
continue;
} else if (child instanceof PsiCodeBlock) {
final PsiStatement[] statements = ((PsiCodeBlock)child).getStatements();
if (statements.length == 1) {
array.add(statements[0]);
continue;
}
}
array.add(child);
}
}
return PsiUtilCore.toPsiElementArray(array);
}
}