blob: 014bbdd0ce71c38bb537ba317a05e3f0b3e7309a [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.slicer;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInspection.dataFlow.DfaUtil;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiSubstitutorImpl;
import com.intellij.psi.impl.source.DummyHolder;
import com.intellij.psi.impl.source.resolve.DefaultParameterTypeInferencePolicy;
import com.intellij.psi.search.SearchScope;
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.tree.IElementType;
import com.intellij.psi.util.MethodSignatureUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.ArrayUtilRt;
import com.intellij.util.Processor;
import gnu.trove.THashMap;
import gnu.trove.THashSet;
import org.intellij.lang.annotations.Flow;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
/**
* @author cdr
*/
public class SliceUtil {
public static boolean processUsagesFlownDownTo(@NotNull PsiElement expression,
@NotNull Processor<SliceUsage> processor,
@NotNull SliceUsage parent,
@NotNull PsiSubstitutor parentSubstitutor,
int indexNesting,
@NotNull String syntheticField) {
assert indexNesting >= 0 : indexNesting;
expression = simplify(expression);
PsiElement original = expression;
if (expression instanceof PsiArrayAccessExpression) {
// now start tracking the array instead of element
expression = ((PsiArrayAccessExpression)expression).getArrayExpression();
indexNesting++;
}
PsiElement par = expression == null ? null : expression.getParent();
if (expression instanceof PsiExpressionList && par instanceof PsiMethodCallExpression) {
// expression list ends up here if we track varargs
PsiMethod method = ((PsiMethodCallExpression)par).resolveMethod();
if (method != null) {
int parametersCount = method.getParameterList().getParametersCount();
if (parametersCount != 0) {
// unfold varargs list into individual expressions
PsiExpression[] expressions = ((PsiExpressionList)expression).getExpressions();
if (indexNesting != 0) {
// should skip not-vararg arguments
for (int i = parametersCount-1; i < expressions.length; i++) {
PsiExpression arg = expressions[i];
if (!handToProcessor(arg, processor, parent, parentSubstitutor, indexNesting - 1, syntheticField)) return false;
}
}
return true;
}
}
}
boolean needToReportDeclaration = false;
if (expression instanceof PsiReferenceExpression) {
PsiElement element = SliceForwardUtil.complexify(expression);
if (element instanceof PsiExpression && PsiUtil.isOnAssignmentLeftHand((PsiExpression)element)) {
PsiExpression rightSide = ((PsiAssignmentExpression)element.getParent()).getRExpression();
return rightSide == null || handToProcessor(rightSide, processor, parent, parentSubstitutor, indexNesting, syntheticField);
}
PsiReferenceExpression ref = (PsiReferenceExpression)expression;
JavaResolveResult result = ref.advancedResolve(false);
parentSubstitutor = result.getSubstitutor().putAll(parentSubstitutor);
PsiElement resolved = result.getElement();
if (resolved instanceof PsiCompiledElement) {
resolved = resolved.getNavigationElement();
}
if (resolved instanceof PsiMethod && expression.getParent() instanceof PsiMethodCallExpression) {
return processUsagesFlownDownTo(expression.getParent(), processor, parent, parentSubstitutor, indexNesting, syntheticField);
}
if (!(resolved instanceof PsiVariable)) return true;
// check for container item modifications, like "array[i] = expression;"
addContainerReferences((PsiVariable)resolved, processor, parent, parentSubstitutor, indexNesting, syntheticField);
needToReportDeclaration = true;
expression = resolved;
}
if (expression instanceof PsiVariable) {
PsiVariable variable = (PsiVariable)expression;
Collection<PsiExpression> values = DfaUtil.getCachedVariableValues(variable, original);
if (values == null) {
SliceUsage stopUsage = createTooComplexDFAUsage(expression, parent, parentSubstitutor);
return processor.process(stopUsage);
}
final Set<PsiExpression> expressions = new THashSet<PsiExpression>(values);
PsiExpression initializer = variable.getInitializer();
if (initializer != null && expressions.isEmpty()) expressions.add(initializer);
boolean initializerReported = false;
for (PsiExpression exp : expressions) {
if (!handToProcessor(exp, processor, parent, parentSubstitutor, indexNesting, syntheticField)) return false;
if (exp == initializer) initializerReported = true;
}
if (!initializerReported && needToReportDeclaration) { // otherwise we'll reach var declaration anyway because it is the initializer' parent
// parameter or variable declaration can be far away from its usage (except for variable initializer) so report it separately
return handToProcessor(variable, processor, parent, parentSubstitutor, indexNesting, syntheticField);
}
if (variable instanceof PsiField) {
return processFieldUsages((PsiField)variable, parent, parentSubstitutor, processor);
}
else if (variable instanceof PsiParameter) {
return processParameterUsages((PsiParameter)variable, parent, parentSubstitutor, indexNesting, syntheticField, processor);
}
}
if (expression instanceof PsiMethodCallExpression) { // ctr call can't return value or be container get, so don't use PsiCall here
PsiMethod method = ((PsiMethodCallExpression)expression).resolveMethod();
Flow anno = isMethodFlowAnnotated(method);
if (anno != null) {
String target = anno.target();
if (target.equals(Flow.DEFAULT_TARGET)) target = Flow.RETURN_METHOD_TARGET;
if (target.equals(Flow.RETURN_METHOD_TARGET)) {
PsiExpression qualifier = ((PsiMethodCallExpression)expression).getMethodExpression().getQualifierExpression();
if (qualifier != null) {
int nesting = calcNewIndexNesting(indexNesting, anno);
String source = anno.source();
if (source.equals(Flow.DEFAULT_SOURCE)) source = Flow.THIS_SOURCE;
String synthetic = StringUtil.trimStart(StringUtil.trimStart(source, Flow.THIS_SOURCE),".");
return processUsagesFlownDownTo(qualifier, processor, parent, parentSubstitutor, nesting, synthetic);
}
}
}
return processMethodReturnValue((PsiMethodCallExpression)expression, processor, parent, parentSubstitutor);
}
if (expression instanceof PsiConditionalExpression) {
PsiConditionalExpression conditional = (PsiConditionalExpression)expression;
PsiExpression thenE = conditional.getThenExpression();
PsiExpression elseE = conditional.getElseExpression();
if (thenE != null && !handToProcessor(thenE, processor, parent, parentSubstitutor, indexNesting, syntheticField)) return false;
if (elseE != null && !handToProcessor(elseE, processor, parent, parentSubstitutor, indexNesting, syntheticField)) return false;
}
if (expression instanceof PsiAssignmentExpression) {
PsiAssignmentExpression assignment = (PsiAssignmentExpression)expression;
IElementType tokenType = assignment.getOperationTokenType();
PsiExpression rExpression = assignment.getRExpression();
if (tokenType == JavaTokenType.EQ && rExpression != null) {
return processUsagesFlownDownTo(rExpression, processor, parent, parentSubstitutor, indexNesting, syntheticField);
}
}
if (indexNesting != 0) {
// consider container creation
PsiElement initializer = expression instanceof PsiNewExpression ? ((PsiNewExpression)expression).getArrayInitializer() : expression;
if (initializer instanceof PsiArrayInitializerExpression) {
for (PsiExpression init : ((PsiArrayInitializerExpression)initializer).getInitializers()) {
if (!handToProcessor(init, processor, parent, parentSubstitutor, indexNesting - 1, syntheticField)) return false;
}
}
// check for constructor put arguments
if (expression instanceof PsiNewExpression &&
!processContainerPutArguments((PsiNewExpression)expression, processor, parent, parentSubstitutor, indexNesting, syntheticField)) {
return false;
}
}
return true;
}
private static Flow isMethodFlowAnnotated(PsiMethod method) {
if (method == null) return null;
return AnnotationUtil.findAnnotationInHierarchy(method, Flow.class);
}
private static Flow isParamFlowAnnotated(PsiMethod method, int paramIndex) {
PsiParameter[] parameters = method.getParameterList().getParameters();
if (parameters.length <= paramIndex) {
if (parameters.length != 0 && parameters[parameters.length-1].isVarArgs()) {
paramIndex = parameters.length-1;
}
else {
return null;
}
}
return AnnotationUtil.findAnnotationInHierarchy(parameters[paramIndex], Flow.class);
}
private static PsiElement simplify(@NotNull PsiElement expression) {
if (expression instanceof PsiParenthesizedExpression) {
return simplify(((PsiParenthesizedExpression)expression).getExpression());
}
if (expression instanceof PsiTypeCastExpression) {
return simplify(((PsiTypeCastExpression)expression).getOperand());
}
return expression;
}
private static boolean handToProcessor(@NotNull PsiElement expression,
@NotNull Processor<SliceUsage> processor,
@NotNull SliceUsage parent,
@NotNull PsiSubstitutor substitutor,
int indexNesting,
@NotNull String syntheticField) {
final PsiElement realExpression = expression.getParent() instanceof DummyHolder ? expression.getParent().getContext() : expression;
assert realExpression != null;
if (!(realExpression instanceof PsiCompiledElement)) {
SliceUsage usage = createSliceUsage(realExpression, parent, substitutor, indexNesting, syntheticField);
if (!processor.process(usage)) return false;
}
return true;
}
private static boolean processMethodReturnValue(@NotNull final PsiMethodCallExpression methodCallExpr,
@NotNull final Processor<SliceUsage> processor,
@NotNull final SliceUsage parent,
@NotNull final PsiSubstitutor parentSubstitutor) {
final JavaResolveResult resolved = methodCallExpr.resolveMethodGenerics();
PsiElement r = resolved.getElement();
if (r instanceof PsiCompiledElement) {
r = r.getNavigationElement();
}
if (!(r instanceof PsiMethod)) return true;
PsiMethod methodCalled = (PsiMethod)r;
PsiType returnType = methodCalled.getReturnType();
if (returnType == null) return true;
final PsiType parentType = parentSubstitutor.substitute(methodCallExpr.getType());
final PsiSubstitutor substitutor = resolved.getSubstitutor().putAll(parentSubstitutor);
Collection<PsiMethod> overrides = new THashSet<PsiMethod>(OverridingMethodsSearch.search(methodCalled, parent.getScope().toSearchScope(), true).findAll());
overrides.add(methodCalled);
final boolean[] result = {true};
for (PsiMethod override : overrides) {
if (!result[0]) break;
if (override instanceof PsiCompiledElement) {
override = (PsiMethod)override.getNavigationElement();
}
if (!parent.getScope().contains(override)) continue;
final PsiCodeBlock body = override.getBody();
if (body == null) continue;
final PsiSubstitutor s = methodCalled == override ? substitutor :
MethodSignatureUtil.getSuperMethodSignatureSubstitutor(methodCalled.getSignature(substitutor), override.getSignature(substitutor));
final PsiSubstitutor superSubstitutor = s == null ? parentSubstitutor : s;
body.accept(new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitAnonymousClass(PsiAnonymousClass aClass) {
// do not look for returns there
}
@Override
public void visitReturnStatement(final PsiReturnStatement statement) {
PsiExpression returnValue = statement.getReturnValue();
if (returnValue == null) return;
PsiType right = superSubstitutor.substitute(superSubstitutor.substitute(returnValue.getType()));
if (right == null || !TypeConversionUtil.isAssignable(parentType, right)) return;
if (!handToProcessor(returnValue, processor, parent, substitutor, parent.indexNesting, "")) {
stopWalking();
result[0] = false;
}
}
});
}
return result[0];
}
private static boolean processFieldUsages(@NotNull final PsiField field,
@NotNull final SliceUsage parent,
@NotNull final PsiSubstitutor parentSubstitutor,
@NotNull final Processor<SliceUsage> processor) {
if (field.hasInitializer()) {
PsiExpression initializer = field.getInitializer();
if (initializer != null && !(field instanceof PsiCompiledElement)) {
if (!handToProcessor(initializer, processor, parent, parentSubstitutor, parent.indexNesting, "")) return false;
}
}
SearchScope searchScope = parent.getScope().toSearchScope();
return ReferencesSearch.search(field, searchScope).forEach(new Processor<PsiReference>() {
@Override
public boolean process(final PsiReference reference) {
ProgressManager.checkCanceled();
PsiElement element = reference.getElement();
if (!(element instanceof PsiReferenceExpression)) return true;
if (element instanceof PsiCompiledElement) {
element = element.getNavigationElement();
if (!parent.getScope().contains(element)) return true;
}
final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)element;
PsiElement parentExpr = referenceExpression.getParent();
if (PsiUtil.isOnAssignmentLeftHand(referenceExpression)) {
PsiExpression rExpression = ((PsiAssignmentExpression)parentExpr).getRExpression();
PsiType rtype = rExpression.getType();
PsiType ftype = field.getType();
if (TypeConversionUtil.isAssignable(parentSubstitutor.substitute(ftype), parentSubstitutor.substitute(rtype))) {
return handToProcessor(rExpression, processor, parent, parentSubstitutor, parent.indexNesting, "");
}
}
if (parentExpr instanceof PsiPrefixExpression && ((PsiPrefixExpression)parentExpr).getOperand() == referenceExpression && ( ((PsiPrefixExpression)parentExpr).getOperationTokenType() == JavaTokenType.PLUSPLUS || ((PsiPrefixExpression)parentExpr).getOperationTokenType() == JavaTokenType.MINUSMINUS)) {
PsiPrefixExpression prefixExpression = (PsiPrefixExpression)parentExpr;
return handToProcessor(prefixExpression, processor, parent, parentSubstitutor, parent.indexNesting, "");
}
if (parentExpr instanceof PsiPostfixExpression && ((PsiPostfixExpression)parentExpr).getOperand() == referenceExpression && ( ((PsiPostfixExpression)parentExpr).getOperationTokenType() == JavaTokenType.PLUSPLUS || ((PsiPostfixExpression)parentExpr).getOperationTokenType() == JavaTokenType.MINUSMINUS)) {
PsiPostfixExpression postfixExpression = (PsiPostfixExpression)parentExpr;
return handToProcessor(postfixExpression, processor, parent, parentSubstitutor, parent.indexNesting, "");
}
return true;
}
});
}
@NotNull
public static SliceUsage createSliceUsage(@NotNull PsiElement element,
@NotNull SliceUsage parent,
@NotNull PsiSubstitutor substitutor,
int indexNesting,
@NotNull String syntheticField) {
return new SliceUsage(simplify(element), parent, substitutor,indexNesting, syntheticField);
}
@NotNull
public static SliceUsage createTooComplexDFAUsage(@NotNull PsiElement element, @NotNull SliceUsage parent, @NotNull PsiSubstitutor substitutor) {
return new SliceTooComplexDFAUsage(simplify(element), parent, substitutor);
}
private static boolean processParameterUsages(@NotNull final PsiParameter parameter,
@NotNull final SliceUsage parent,
@NotNull final PsiSubstitutor parentSubstitutor,
final int indexNesting,
@NotNull final String syntheticField,
@NotNull final Processor<SliceUsage> processor) {
PsiElement declarationScope = parameter.getDeclarationScope();
if (declarationScope instanceof PsiForeachStatement) {
PsiForeachStatement statement = (PsiForeachStatement)declarationScope;
PsiExpression iterated = statement.getIteratedValue();
if (statement.getIterationParameter() == parameter && iterated != null) {
if (!handToProcessor(iterated, processor, parent, parentSubstitutor, indexNesting + 1, syntheticField)) return false;
}
return true;
}
if (!(declarationScope instanceof PsiMethod)) return true;
final PsiMethod method = (PsiMethod)declarationScope;
final PsiType actualParameterType = parameter.getType();
final PsiParameter[] actualParameters = method.getParameterList().getParameters();
final int paramSeqNo = ArrayUtilRt.find(actualParameters, parameter);
assert paramSeqNo != -1;
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 superMethod : superMethods) {
if (!MethodReferencesSearch.search(superMethod, 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 refElement = reference.getElement();
PsiExpressionList argumentList;
JavaResolveResult result;
if (refElement instanceof PsiCall) {
// the case of enum constant decl
PsiCall call = (PsiCall)refElement;
argumentList = call.getArgumentList();
result = call.resolveMethodGenerics();
}
else {
PsiElement element = refElement.getParent();
if (element instanceof PsiCompiledElement) return true;
if (element instanceof PsiAnonymousClass) {
PsiAnonymousClass anon = (PsiAnonymousClass)element;
argumentList = anon.getArgumentList();
PsiElement callExp = element.getParent();
if (!(callExp instanceof PsiCallExpression)) return true;
result = ((PsiCall)callExp).resolveMethodGenerics();
}
else {
if (!(element instanceof PsiCall)) return true;
PsiCall call = (PsiCall)element;
argumentList = call.getArgumentList();
result = call.resolveMethodGenerics();
}
}
PsiSubstitutor substitutor = result.getSubstitutor();
PsiExpression[] expressions = argumentList.getExpressions();
if (paramSeqNo >= expressions.length) {
return true;
}
PsiElement passExpression;
PsiType actualExpressionType;
if (actualParameterType instanceof PsiEllipsisType) {
passExpression = argumentList;
actualExpressionType = expressions[paramSeqNo].getType();
}
else {
passExpression = expressions[paramSeqNo];
actualExpressionType = ((PsiExpression)passExpression).getType();
}
Project project = argumentList.getProject();
PsiElement element = result.getElement();
if (element instanceof PsiCompiledElement) {
element = element.getNavigationElement();
}
// for erased method calls for which we cannot determine target substitutor,
// rely on call argument types. I.e. new Pair(1,2) -> Pair<Integer, Integer>
if (element instanceof PsiTypeParameterListOwner && PsiUtil.isRawSubstitutor((PsiTypeParameterListOwner)element, substitutor)) {
PsiTypeParameter[] typeParameters = substitutor.getSubstitutionMap().keySet().toArray(new PsiTypeParameter[0]);
PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(project).getResolveHelper();
substitutor = resolveHelper.inferTypeArguments(typeParameters, actualParameters, expressions, parentSubstitutor, argumentList,
DefaultParameterTypeInferencePolicy.INSTANCE);
}
substitutor = removeRawMappingsLeftFromResolve(substitutor);
PsiSubstitutor combined = unify(substitutor, parentSubstitutor, project);
if (combined == null) return true;
//PsiType substituted = combined.substitute(passExpression.getType());
PsiType substituted = combined.substitute(actualExpressionType);
if (substituted instanceof PsiPrimitiveType) {
final PsiClassType boxedType = ((PsiPrimitiveType)substituted).getBoxedType(argumentList);
substituted = boxedType != null ? boxedType : substituted;
}
if (substituted == null) return true;
PsiType typeToCheck;
if (actualParameterType instanceof PsiEllipsisType) {
// there may be the case of passing the vararg argument to the other vararg method: foo(int... ints) { bar(ints); } bar(int... ints) {}
if (TypeConversionUtil.areTypesConvertible(substituted, actualParameterType)) {
return handToProcessor(expressions[paramSeqNo], processor, parent, combined, indexNesting, syntheticField);
}
typeToCheck = ((PsiEllipsisType)actualParameterType).getComponentType();
}
else {
typeToCheck = actualParameterType;
}
if (!TypeConversionUtil.areTypesConvertible(substituted, typeToCheck)) return true;
return handToProcessor(passExpression, processor, parent, combined, indexNesting, syntheticField);
}
})) {
return false;
}
}
return true;
}
private static void addContainerReferences(@NotNull PsiVariable variable,
@NotNull final Processor<SliceUsage> processor,
@NotNull final SliceUsage parent,
@NotNull final PsiSubstitutor parentSubstitutor,
final int indexNesting,
@NotNull final String syntheticField) {
if (indexNesting != 0) {
ReferencesSearch.search(variable).forEach(new Processor<PsiReference>() {
@Override
public boolean process(PsiReference reference) {
PsiElement element = reference.getElement();
if (element instanceof PsiExpression && !element.getManager().areElementsEquivalent(element, parent.getElement())) {
PsiExpression expression = (PsiExpression)element;
if (!addContainerItemModification(expression, processor, parent, parentSubstitutor, indexNesting, syntheticField)) return false;
}
return true;
}
});
}
}
private static boolean addContainerItemModification(@NotNull PsiExpression expression,
@NotNull Processor<SliceUsage> processor,
@NotNull SliceUsage parent,
@NotNull PsiSubstitutor parentSubstitutor,
int indexNesting,
@NotNull String syntheticField) {
PsiElement parentElement = expression.getParent();
if (parentElement instanceof PsiArrayAccessExpression &&
((PsiArrayAccessExpression)parentElement).getArrayExpression() == expression &&
PsiUtil.isAccessedForWriting((PsiExpression)parentElement)) {
if (PsiUtil.isOnAssignmentLeftHand((PsiExpression)parentElement)) {
PsiExpression rightSide = ((PsiAssignmentExpression)parentElement.getParent()).getRExpression();
return rightSide == null || handToProcessor(rightSide, processor, parent, parentSubstitutor, indexNesting -1, syntheticField);
}
}
PsiElement grand = parentElement == null ? null : parentElement.getParent();
if (grand instanceof PsiCallExpression) {
if (!processContainerPutArguments((PsiCallExpression)grand, processor, parent, parentSubstitutor, indexNesting, syntheticField)) return false;
}
return true;
}
private static boolean processContainerPutArguments(PsiCallExpression call,
Processor<SliceUsage> processor,
SliceUsage parent,
PsiSubstitutor parentSubstitutor,
int indexNesting,
@NotNull String syntheticField) {
assert indexNesting != 0;
JavaResolveResult result = call.resolveMethodGenerics();
PsiMethod method = (PsiMethod)result.getElement();
if (method != null) {
int parametersCount = method.getParameterList().getParametersCount();
Flow[] annotations = new Flow[parametersCount];
for (int i=0; i<parametersCount;i++) {
annotations[i] = isParamFlowAnnotated(method, i);
}
PsiExpression[] expressions = call.getArgumentList().getExpressions();
PsiParameter[] parameters = method.getParameterList().getParameters();
for (int i = 0; i < expressions.length; i++) {
PsiExpression argument = expressions[i];
Flow anno;
PsiParameter parameter;
if (i>=parameters.length) {
if (parameters.length != 0 && parameters[parameters.length-1].isVarArgs()) {
anno = annotations[parameters.length - 1];
parameter = parameters[parameters.length-1];
}
else {
break;
}
}
else {
anno = annotations[i];
parameter = parameters[i];
}
if (anno != null) {
String target = anno.target();
if (target.equals(Flow.DEFAULT_TARGET)) target = Flow.THIS_TARGET;
if (target.startsWith(Flow.THIS_TARGET)) {
String paramSynthetic = StringUtil.trimStart(StringUtil.trimStart(target, Flow.THIS_TARGET),".");
if (paramSynthetic.equals(syntheticField)) {
PsiSubstitutor substitutor = unify(result.getSubstitutor(), parentSubstitutor, argument.getProject());
int nesting = calcNewIndexNesting(indexNesting, anno);
if (!handToProcessor(argument, processor, parent, substitutor, nesting, paramSynthetic)) return false;
}
}
}
// check flow parameter to another param
for (int si=0; si<annotations.length; si++) {
if (si == i) continue;
Flow sourceAnno = annotations[si];
if (sourceAnno == null) continue;
if (sourceAnno.target().equals(parameter.getName())) {
int newNesting = calcNewIndexNesting(indexNesting, sourceAnno);
PsiExpression sourceArgument = expressions[si];
PsiSubstitutor substitutor = unify(result.getSubstitutor(), parentSubstitutor, argument.getProject());
if (!handToProcessor(sourceArgument, processor, parent, substitutor, newNesting, syntheticField)) return false;
}
}
}
}
return true;
}
private static int calcNewIndexNesting(int indexNesting, Flow anno) {
int nestingDelta = (anno.sourceIsContainer() ? 1 : 0) - (anno.targetIsContainer() ? 1 : 0);
return indexNesting + nestingDelta;
}
@NotNull
private static PsiSubstitutor removeRawMappingsLeftFromResolve(@NotNull PsiSubstitutor substitutor) {
Map<PsiTypeParameter, PsiType> map = null;
for (Map.Entry<PsiTypeParameter, PsiType> entry : substitutor.getSubstitutionMap().entrySet()) {
if (entry.getValue() == null) {
if (map == null) map = new THashMap<PsiTypeParameter, PsiType>();
map.put(entry.getKey(), entry.getValue());
}
}
if (map == null) return substitutor;
Map<PsiTypeParameter, PsiType> newMap = new THashMap<PsiTypeParameter, PsiType>(substitutor.getSubstitutionMap());
newMap.keySet().removeAll(map.keySet());
return PsiSubstitutorImpl.createSubstitutor(newMap);
}
@Nullable
private static PsiSubstitutor unify(@NotNull PsiSubstitutor substitutor, @NotNull PsiSubstitutor parentSubstitutor, @NotNull Project project) {
Map<PsiTypeParameter,PsiType> newMap = new THashMap<PsiTypeParameter, PsiType>(substitutor.getSubstitutionMap());
for (Map.Entry<PsiTypeParameter, PsiType> entry : substitutor.getSubstitutionMap().entrySet()) {
PsiTypeParameter typeParameter = entry.getKey();
PsiType type = entry.getValue();
PsiClass resolved = PsiUtil.resolveClassInType(type);
if (!parentSubstitutor.getSubstitutionMap().containsKey(typeParameter)) continue;
PsiType parentType = parentSubstitutor.substitute(parentSubstitutor.substitute(typeParameter));
if (resolved instanceof PsiTypeParameter) {
PsiTypeParameter res = (PsiTypeParameter)resolved;
newMap.put(res, parentType);
}
else if (!Comparing.equal(type, parentType)) {
return null; // cannot unify
}
}
return JavaPsiFacade.getElementFactory(project).createSubstitutor(newMap);
}
}