| /* |
| * 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.typeCook.deductive.builder; |
| |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.fileTypes.StdFileTypes; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.psi.*; |
| import com.intellij.psi.search.GlobalSearchScope; |
| import com.intellij.psi.search.PsiSearchHelper; |
| import com.intellij.psi.search.SearchScope; |
| import com.intellij.psi.search.searches.OverridingMethodsSearch; |
| import com.intellij.psi.search.searches.ReferencesSearch; |
| import com.intellij.psi.util.InheritanceUtil; |
| import com.intellij.psi.util.PsiTreeUtil; |
| import com.intellij.psi.util.PsiUtil; |
| import com.intellij.psi.util.TypeConversionUtil; |
| import com.intellij.refactoring.typeCook.Settings; |
| import com.intellij.refactoring.typeCook.Util; |
| import com.intellij.refactoring.typeCook.deductive.PsiTypeVariableFactory; |
| import com.intellij.refactoring.typeCook.deductive.util.VictimCollector; |
| import com.intellij.util.containers.HashMap; |
| |
| import java.util.Arrays; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * Created by IntelliJ IDEA. |
| * User: db |
| * Date: 27.06.2003 |
| * Time: 22:48:08 |
| * To change this template use Options | File Templates. |
| */ |
| public class SystemBuilder { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.typeCook.deductive.builder.SystemBuilder"); |
| |
| private final PsiManager myManager; |
| private final Map<PsiElement, Boolean> myMethodCache; |
| private final Map<PsiParameter, PsiParameter> myParameters; |
| private final Map<PsiMethod, PsiMethod> myMethods; |
| private final Map<PsiElement, PsiType> myTypes; |
| private final Set<PsiAnchor> myVisitedConstructions; |
| private final Settings mySettings; |
| private final PsiTypeVariableFactory myTypeVariableFactory; |
| private final Project myProject; |
| |
| public SystemBuilder(final Project project, final Settings settings) { |
| myProject = project; |
| myManager = PsiManager.getInstance(myProject); |
| mySettings = settings; |
| myMethodCache = new HashMap<PsiElement, Boolean>(); |
| myParameters = new HashMap<PsiParameter, PsiParameter>(); |
| myMethods = new HashMap<PsiMethod, PsiMethod>(); |
| myTypes = new HashMap<PsiElement, PsiType>(); |
| myVisitedConstructions = new HashSet<PsiAnchor>(); |
| myTypeVariableFactory = new PsiTypeVariableFactory(); |
| } |
| |
| private Set<PsiElement> collect(final PsiElement[] scopes) { |
| return new VictimCollector(scopes, mySettings).getVictims(); |
| } |
| |
| private boolean verifyMethod(final PsiElement element, final Set<PsiElement> victims, final PsiSearchHelper helper) { |
| PsiMethod method; |
| PsiParameter parameter = null; |
| int index = 0; |
| |
| if (element instanceof PsiMethod) { |
| method = (PsiMethod)element; |
| } |
| else if (element instanceof PsiParameter) { |
| parameter = (PsiParameter)element; |
| method = (PsiMethod)parameter.getDeclarationScope(); |
| index = method.getParameterList().getParameterIndex(parameter); |
| } |
| else { |
| LOG.error("Parameter or method expected, but found " + (element == null ? "null" : element.getClass().getName())); |
| return false; |
| } |
| |
| final PsiMethod superMethod = method.findDeepestSuperMethod(); |
| PsiMethod keyMethod; |
| PsiParameter keyParameter = null; |
| |
| if (superMethod != null) { |
| Boolean good = myMethodCache.get(superMethod); |
| |
| if (good != null && !good.booleanValue()) { |
| return false; |
| } |
| |
| final PsiElement e = parameter == null ? superMethod : superMethod.getParameterList().getParameters()[index]; |
| |
| if (!victims.contains(e)) { |
| myMethodCache.put(superMethod, Boolean.FALSE); |
| return false; |
| } |
| |
| keyMethod = superMethod; |
| |
| myMethods.put(method, keyMethod); |
| |
| if (parameter != null) { |
| keyParameter = (PsiParameter)e; |
| } |
| } |
| else { |
| Boolean good = myMethodCache.get(method); |
| |
| if (good != null && good.booleanValue()) { |
| if (myMethods.get(method) == null) { |
| myMethods.put(method, method); |
| } |
| |
| if (parameter != null && myParameters.get(parameter) == null) { |
| myParameters.put(parameter, parameter); |
| } |
| |
| return true; |
| } |
| |
| keyMethod = method; |
| keyParameter = parameter; |
| } |
| |
| final PsiMethod[] overriders = OverridingMethodsSearch.search(keyMethod, true).toArray(PsiMethod.EMPTY_ARRAY); |
| |
| for (final PsiMethod overrider : overriders) { |
| final PsiElement e = parameter != null ? overrider.getParameterList().getParameters()[index] : overrider; |
| |
| if (!victims.contains(e)) { |
| myMethodCache.put(keyMethod, Boolean.FALSE); |
| return false; |
| } |
| } |
| |
| for (final PsiMethod overrider : overriders) { |
| final PsiElement e = parameter != null ? overrider.getParameterList().getParameters()[index] : overrider; |
| |
| myMethods.put(overrider, keyMethod); |
| |
| if (parameter != null) { |
| myParameters.put((PsiParameter)e, keyParameter); |
| } |
| } |
| |
| myMethods.put(method, keyMethod); |
| |
| if (parameter != null) { |
| myParameters.put(parameter, keyParameter); |
| } |
| |
| myMethodCache.put(keyMethod, Boolean.TRUE); |
| |
| return true; |
| } |
| |
| private void setType(final PsiElement e, final PsiType t) { |
| myTypes.put(e, t); |
| } |
| |
| private PsiType defineType(final PsiElement e) { |
| PsiType t = myTypes.get(e); |
| |
| if (t != null) { |
| return t; |
| } |
| |
| t = Util.getType(e); |
| |
| final PsiType parameterizedType = Util.createParameterizedType(t, myTypeVariableFactory, e); |
| |
| myTypes.put(e, parameterizedType); |
| |
| return parameterizedType; |
| } |
| |
| private PsiType getType(final PsiElement e) { |
| final PsiType t = myTypes.get(e); |
| |
| if (t != null) { |
| return t; |
| } |
| |
| return Util.banalize(Util.getType(e)); |
| } |
| |
| private boolean isCooked(final PsiElement element) { |
| return myTypes.get(element) != null; |
| } |
| |
| public PsiType inferTypeForMethodTypeParameter(final PsiTypeParameter typeParameter, |
| final PsiParameter[] parameters, |
| PsiExpression[] arguments, |
| PsiSubstitutor partialSubstitutor, |
| PsiElement parent, |
| ReductionSystem system) { |
| PsiType substitution = PsiType.NULL; |
| PsiResolveHelper helper = JavaPsiFacade.getInstance(typeParameter.getProject()).getResolveHelper(); |
| if (parameters.length > 0) { |
| for (int j = 0; j < arguments.length; j++) { |
| PsiExpression argument = arguments[j]; |
| final PsiParameter parameter = parameters[Math.min(j, parameters.length - 1)]; |
| if (j >= parameters.length && !parameter.isVarArgs()) break; |
| PsiType parameterType = parameter.getType(); |
| PsiType argumentType = evaluateType(argument, system); |
| |
| if (parameterType instanceof PsiEllipsisType) { |
| parameterType = ((PsiEllipsisType)parameterType).getComponentType(); |
| if (arguments.length == parameters.length && |
| argumentType instanceof PsiArrayType && |
| !(((PsiArrayType)argumentType).getComponentType() instanceof PsiPrimitiveType)) { |
| argumentType = ((PsiArrayType)argumentType).getComponentType(); |
| } |
| } |
| final PsiType currentSubstitution = |
| helper.getSubstitutionForTypeParameter(typeParameter, parameterType, argumentType, true, PsiUtil.getLanguageLevel(parent)); |
| if (currentSubstitution == null) { |
| substitution = null; |
| break; |
| } |
| else if (currentSubstitution instanceof PsiWildcardType) { |
| if (substitution instanceof PsiWildcardType) return PsiType.NULL; |
| } |
| else if (PsiType.NULL.equals(currentSubstitution)) continue; |
| |
| if (PsiType.NULL.equals(substitution)) { |
| substitution = currentSubstitution; |
| continue; |
| } |
| if (!substitution.equals(currentSubstitution)) { |
| if (substitution instanceof PsiTypeVariable || currentSubstitution instanceof PsiTypeVariable) { |
| substitution = GenericsUtil.getLeastUpperBound(substitution, currentSubstitution, typeParameter.getManager()); |
| if (substitution == null) break; |
| } |
| } |
| } |
| } |
| |
| if (PsiType.NULL.equals(substitution)) { |
| substitution = inferMethodTypeParameterFromParent(typeParameter, partialSubstitutor, parent, system); |
| } |
| return substitution; |
| } |
| |
| private PsiType inferMethodTypeParameterFromParent(final PsiTypeParameter typeParameter, |
| PsiSubstitutor substitutor, |
| PsiElement parent, |
| ReductionSystem system) { |
| PsiTypeParameterListOwner owner = typeParameter.getOwner(); |
| PsiType substitution = PsiType.NULL; |
| if (owner instanceof PsiMethod) { |
| if (parent instanceof PsiMethodCallExpression) { |
| PsiMethodCallExpression methodCall = (PsiMethodCallExpression)parent; |
| substitution = inferMethodTypeParameterFromParent(methodCall.getParent(), methodCall, typeParameter, substitutor, system); |
| } |
| } |
| return substitution; |
| } |
| |
| private PsiType inferMethodTypeParameterFromParent(PsiElement parent, |
| PsiMethodCallExpression methodCall, |
| final PsiTypeParameter typeParameter, |
| PsiSubstitutor substitutor, |
| ReductionSystem system) { |
| PsiType type = null; |
| |
| if (parent instanceof PsiVariable && methodCall.equals(((PsiVariable)parent).getInitializer())) { |
| type = getType(parent); |
| } |
| else if (parent instanceof PsiAssignmentExpression && methodCall.equals(((PsiAssignmentExpression)parent).getRExpression())) { |
| type = evaluateType(((PsiAssignmentExpression)parent).getLExpression(), system); |
| } |
| else if (parent instanceof PsiTypeCastExpression && methodCall.equals(((PsiTypeCastExpression)parent).getOperand())) { |
| type = evaluateType((PsiExpression)parent, system); |
| } |
| else if (parent instanceof PsiReturnStatement) { |
| PsiMethod method = PsiTreeUtil.getParentOfType(parent, PsiMethod.class); |
| if (method != null) { |
| type = getType(method); |
| } |
| } |
| |
| if (type == null) { |
| type = PsiType.getJavaLangObject(methodCall.getManager(), methodCall.getResolveScope()); |
| } |
| |
| PsiType returnType = ((PsiMethod)typeParameter.getOwner()).getReturnType(); |
| PsiType guess = JavaPsiFacade.getInstance(parent.getProject()).getResolveHelper() |
| .getSubstitutionForTypeParameter(typeParameter, returnType, type, false, PsiUtil.getLanguageLevel(parent)); |
| |
| if (PsiType.NULL.equals(guess)) { |
| PsiType superType = substitutor.substitute(typeParameter.getSuperTypes()[0]); |
| return superType == null ? PsiType.getJavaLangObject(methodCall.getManager(), methodCall.getResolveScope()) : superType; |
| } |
| |
| //The following code is the result of deep thought, do not shit it out before discussing with [ven] |
| if (returnType instanceof PsiClassType && typeParameter.equals(((PsiClassType)returnType).resolve())) { |
| PsiClassType[] extendsTypes = typeParameter.getExtendsListTypes(); |
| PsiSubstitutor newSubstitutor = substitutor.put(typeParameter, guess); |
| for (PsiClassType t : extendsTypes) { |
| PsiType extendsType = newSubstitutor.substitute(t); |
| if (!extendsType.isAssignableFrom(guess)) { |
| if (guess.isAssignableFrom(extendsType)) { |
| guess = extendsType; |
| newSubstitutor = substitutor.put(typeParameter, guess); |
| } |
| else { |
| break; |
| } |
| } |
| } |
| } |
| |
| return guess; |
| } |
| |
| PsiType evaluateType(final PsiExpression expr, final ReductionSystem system) { |
| if (expr instanceof PsiArrayAccessExpression && !mySettings.preserveRawArrays()) { |
| final PsiType at = evaluateType(((PsiArrayAccessExpression)expr).getArrayExpression(), system); |
| |
| if (at instanceof PsiArrayType) { |
| return ((PsiArrayType)at).getComponentType(); |
| } |
| } |
| else if (expr instanceof PsiAssignmentExpression) { |
| return evaluateType(((PsiAssignmentExpression)expr).getLExpression(), system); |
| } |
| else if (expr instanceof PsiCallExpression) { |
| final PsiCallExpression call = (PsiCallExpression)expr; |
| final PsiMethod method = call.resolveMethod(); |
| |
| if (method != null) { |
| final PsiClass aClass = method.getContainingClass(); |
| final PsiTypeParameter[] methodTypeParameters = method.getTypeParameters(); |
| final PsiParameter[] parameters = method.getParameterList().getParameters(); |
| final PsiExpression[] arguments = call.getArgumentList().getExpressions(); |
| final PsiExpression qualifier = |
| expr instanceof PsiMethodCallExpression ? ((PsiMethodCallExpression)expr).getMethodExpression().getQualifierExpression() : null; |
| |
| final Set<PsiTypeParameter> typeParameters = new HashSet<PsiTypeParameter>(Arrays.asList(methodTypeParameters)); |
| |
| PsiSubstitutor qualifierSubstitutor = PsiSubstitutor.EMPTY; |
| PsiSubstitutor supertypeSubstitutor = PsiSubstitutor.EMPTY; |
| |
| PsiType aType; |
| |
| if (method.isConstructor()) { |
| if (expr instanceof PsiNewExpression) { |
| aType = isCooked(expr) ? getType(expr) : expr.getType(); |
| qualifierSubstitutor = Util.resolveType(aType).getSubstitutor(); |
| } |
| else { |
| LOG.assertTrue(expr instanceof PsiMethodCallExpression); //either this(); or super(); |
| final PsiReferenceExpression methodExpression = ((PsiMethodCallExpression)expr).getMethodExpression(); |
| if (PsiKeyword.THIS.equals(methodExpression.getText())) { |
| aType = JavaPsiFacade.getInstance(myManager.getProject()).getElementFactory().createType(aClass); |
| } |
| else { |
| LOG.assertTrue(PsiKeyword.SUPER.equals(methodExpression.getText())); |
| PsiClass placeClass = PsiTreeUtil.getParentOfType(expr, PsiClass.class); |
| qualifierSubstitutor = TypeConversionUtil.getClassSubstitutor(aClass, placeClass, PsiSubstitutor.EMPTY); |
| aType = JavaPsiFacade.getInstance(myManager.getProject()).getElementFactory().createType(aClass, qualifierSubstitutor); |
| } |
| } |
| } |
| else { |
| aType = getType(method); |
| } |
| |
| if (qualifier != null) { |
| final PsiType qualifierType = evaluateType(qualifier, system); |
| final PsiClassType.ClassResolveResult result = Util.resolveType(qualifierType); |
| |
| if (result.getElement() != null) { |
| final PsiClass qualifierClass = result.getElement(); |
| |
| qualifierSubstitutor = TypeConversionUtil.getClassSubstitutor(aClass, qualifierClass, result.getSubstitutor()); |
| |
| if (qualifierSubstitutor != null) { |
| aType = qualifierSubstitutor.substitute(aType); |
| } |
| } |
| } |
| |
| final Map<PsiTypeParameter, PsiType> mapping = new HashMap<PsiTypeParameter, PsiType>(); |
| |
| for (int i = 0; i < Math.min(parameters.length, arguments.length); i++) { |
| final PsiType argumentType = evaluateType(arguments[i], system); |
| |
| PsiType parmType; |
| |
| if (isCooked(parameters[i])) { |
| parmType = getType(parameters[i]); |
| system.addSubtypeConstraint(argumentType, parmType); |
| } |
| else { |
| parmType = parameters[i].getType(); |
| if (qualifierSubstitutor != null) { |
| parmType = qualifierSubstitutor.substitute(parmType); |
| } |
| |
| if (!Util.bindsTypeVariables(parmType) && !Util.bindsTypeParameters(parmType, typeParameters)) { |
| parmType = Util.banalize(parmType); |
| } |
| |
| final PsiType theType = new Object() { |
| PsiType introduceAdditionalTypeVariables(final PsiType type, final PsiSubstitutor qualifier, final PsiSubstitutor supertype) { |
| final int level = type.getArrayDimensions(); |
| final PsiClassType.ClassResolveResult result = Util.resolveType(type); |
| final PsiClass aClass = result.getElement(); |
| |
| if (aClass != null) { |
| if (aClass instanceof PsiTypeParameter) { |
| final PsiTypeParameter tp = (PsiTypeParameter)aClass; |
| final PsiClassType[] extypes = tp.getExtendsListTypes(); |
| |
| PsiType pv = mapping.get(tp); |
| |
| if (pv == null) { |
| pv = myTypeVariableFactory.create(); |
| mapping.put(tp, pv); |
| } |
| |
| for (final PsiClassType ext : extypes) { |
| final PsiType extype = qualifier.substitute(new Object() { |
| public PsiType substitute(final PsiType ext) { |
| final PsiClassType.ClassResolveResult result = Util.resolveType(ext); |
| final PsiClass aClass = result.getElement(); |
| |
| if (aClass != null) { |
| if (aClass instanceof PsiTypeParameter) { |
| final PsiType type = mapping.get(aClass); |
| |
| if (type != null) { |
| return type; |
| } |
| |
| return ext; |
| } |
| |
| final PsiSubstitutor aSubst = result.getSubstitutor(); |
| PsiSubstitutor theSubst = PsiSubstitutor.EMPTY; |
| |
| for (final PsiTypeParameter parm : aSubst.getSubstitutionMap().keySet()) { |
| PsiType type = aSubst.substitute(parm); |
| |
| if (type != null) { |
| if (type instanceof PsiWildcardType) { |
| final PsiWildcardType wildcard = (PsiWildcardType)type; |
| final PsiType bound = wildcard.getBound(); |
| if (bound != null) { |
| final PsiManager manager = parm.getManager(); |
| type = wildcard.isExtends() |
| ? PsiWildcardType.createExtends(manager, substitute(bound)) |
| : PsiWildcardType.createSuper(manager, substitute(bound)); |
| } |
| } |
| else { |
| type = substitute(type); |
| } |
| } |
| |
| theSubst = theSubst.put(parm, type); |
| } |
| |
| return JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory() |
| .createType(aClass, theSubst); |
| } |
| |
| return ext; |
| } |
| }.substitute(ext)); |
| system.addSubtypeConstraint(pv, extype); |
| } |
| |
| return Util.createArrayType(pv, level); |
| } |
| |
| final Map<PsiTypeParameter, PsiType> substitutionMap = result.getSubstitutor().getSubstitutionMap(); |
| |
| PsiSubstitutor theSubst = PsiSubstitutor.EMPTY; |
| |
| for (final PsiTypeParameter p : substitutionMap.keySet()) { |
| final PsiType pType = substitutionMap.get(p); |
| |
| if (pType instanceof PsiWildcardType) { |
| final PsiWildcardType wildcard = (PsiWildcardType)pType; |
| final PsiType theBound = wildcard.getBound(); |
| |
| if (theBound != null) { |
| final PsiType bound = qualifier.substitute(supertype.substitute(theBound)); |
| |
| if (Util.bindsTypeVariables(bound)) { |
| final PsiType var = myTypeVariableFactory.create(); |
| |
| if (wildcard.isExtends()) { |
| system.addSubtypeConstraint(var, bound); |
| } |
| else { |
| system.addSubtypeConstraint(bound, var); |
| } |
| |
| theSubst = theSubst.put(p, var); |
| } |
| else if (Util.bindsTypeParameters(bound, typeParameters)) { |
| final PsiType var = myTypeVariableFactory.create(); |
| PsiSubstitutor subst = PsiSubstitutor.EMPTY; |
| |
| for (final PsiTypeParameter aTypeParm : methodTypeParameters) { |
| PsiType parmVar = mapping.get(aTypeParm); |
| |
| if (parmVar == null) { |
| parmVar = myTypeVariableFactory.create(); |
| mapping.put(aTypeParm, parmVar); |
| } |
| |
| subst = subst.put(aTypeParm, parmVar); |
| } |
| |
| final PsiType bnd = subst.substitute(bound); |
| |
| if (wildcard.isExtends()) { |
| system.addSubtypeConstraint(bnd, var); |
| } |
| else { |
| system.addSubtypeConstraint(var, bnd); |
| } |
| |
| theSubst = theSubst.put(p, var); |
| } |
| else { |
| theSubst = theSubst.put(p, pType); |
| } |
| } |
| else { |
| theSubst = theSubst.put(p, pType); |
| } |
| } |
| else if (pType != null) { |
| theSubst = theSubst.put(p, introduceAdditionalTypeVariables(pType, qualifier, supertype)); |
| } |
| } |
| |
| return Util.createArrayType(JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory().createType(aClass, theSubst), level); |
| } |
| |
| return Util.createArrayType(type, level); |
| } |
| }.introduceAdditionalTypeVariables(parmType, qualifierSubstitutor, supertypeSubstitutor); |
| |
| system.addSubtypeConstraint(argumentType, theType); |
| } |
| } |
| |
| PsiSubstitutor theSubst = PsiSubstitutor.EMPTY; |
| |
| for (final PsiTypeParameter parm : mapping.keySet()) { |
| final PsiType type = mapping.get(parm); |
| |
| theSubst = theSubst.put(parm, type); |
| } |
| |
| for (PsiTypeParameter typeParam : methodTypeParameters) { |
| final PsiType inferred = inferTypeForMethodTypeParameter(typeParam, parameters, arguments, theSubst, expr, system); |
| theSubst = theSubst.put(typeParam, inferred); |
| } |
| |
| return theSubst.substitute(aType); |
| } |
| } |
| else if (expr instanceof PsiParenthesizedExpression) { |
| return evaluateType(((PsiParenthesizedExpression)expr).getExpression(), system); |
| } |
| else if (expr instanceof PsiConditionalExpression) { |
| return evaluateType(((PsiConditionalExpression)expr).getThenExpression(), system); |
| } |
| else if (expr instanceof PsiReferenceExpression) { |
| final PsiReferenceExpression ref = (PsiReferenceExpression)expr; |
| final PsiExpression qualifier = ref.getQualifierExpression(); |
| |
| if (qualifier == null) { |
| return getType(ref.resolve()); |
| } |
| else { |
| final PsiType qualifierType = evaluateType(qualifier, system); |
| final PsiElement element = ref.resolve(); |
| |
| final PsiClassType.ClassResolveResult result = Util.resolveType(qualifierType); |
| |
| if (result.getElement() != null) { |
| final PsiClass aClass = result.getElement(); |
| final PsiSubstitutor aSubst = result.getSubstitutor(); |
| |
| if (element instanceof PsiField) { |
| final PsiField field = (PsiField)element; |
| final PsiType fieldType = getType(field); |
| final PsiClass superClass = field.getContainingClass(); |
| |
| PsiType aType = fieldType; |
| |
| if (!aClass.equals(superClass) && field.isPhysical()) { |
| aType = TypeConversionUtil.getSuperClassSubstitutor(superClass, aClass, PsiSubstitutor.EMPTY).substitute(aType); |
| } |
| |
| return aSubst.substitute(aType); |
| } |
| } |
| else if (element != null) { |
| return getType(element); |
| } |
| } |
| } |
| |
| return getType(expr); |
| } |
| |
| |
| private void addUsage(final ReductionSystem system, final PsiElement element) { |
| |
| if (element instanceof PsiVariable) { |
| final PsiExpression initializer = ((PsiVariable)element).getInitializer(); |
| |
| if (initializer != null) { |
| final PsiExpression core = PsiUtil.deparenthesizeExpression(initializer); |
| |
| if (core instanceof PsiArrayInitializerExpression) { |
| final PsiExpression[] inits = ((PsiArrayInitializerExpression)core).getInitializers(); |
| final PsiType type = getType(element); |
| |
| for (PsiExpression init : inits) { |
| system.addSubtypeConstraint(evaluateType(init, system).createArrayType(), type); |
| } |
| } |
| else if (core instanceof PsiNewExpression) { |
| final PsiArrayInitializerExpression init = ((PsiNewExpression)core).getArrayInitializer(); |
| |
| if (init != null) { |
| final PsiExpression[] inits = init.getInitializers(); |
| final PsiType type = getType(element); |
| |
| for (PsiExpression init1 : inits) { |
| system.addSubtypeConstraint(evaluateType(init1, system).createArrayType(), type); |
| } |
| } |
| |
| system.addSubtypeConstraint(evaluateType(core, system), getType(element)); |
| } |
| else { |
| system.addSubtypeConstraint(evaluateType(core, system), getType(element)); |
| } |
| } |
| |
| if (element instanceof PsiParameter) { |
| PsiParameter parameter = (PsiParameter)element; |
| final PsiElement declarationScope = parameter.getDeclarationScope(); |
| if (declarationScope instanceof PsiMethod) { |
| final PsiMethod method = (PsiMethod)declarationScope; |
| final PsiSearchHelper helper = PsiSearchHelper.SERVICE.getInstance(myManager.getProject()); |
| SearchScope scope = getScope(helper, method); |
| |
| for (PsiReference ref : ReferencesSearch.search(method, scope, true)) { |
| final PsiElement elt = ref.getElement(); |
| |
| if (elt != null) { |
| final PsiCallExpression call = PsiTreeUtil.getParentOfType(elt, PsiCallExpression.class); |
| |
| if (call != null) { |
| PsiExpressionList argList = call.getArgumentList(); |
| if (argList != null) { |
| PsiExpression[] args = argList.getExpressions(); |
| int index = method.getParameterList().getParameterIndex(parameter); |
| if (index < args.length) { |
| system.addSubtypeConstraint(evaluateType(args[index], system), myTypes.get(element)); |
| } |
| } |
| } |
| } |
| } |
| } else if (declarationScope instanceof PsiForeachStatement) { |
| addForEachConstraint(system, (PsiForeachStatement)declarationScope); |
| } |
| } |
| return; |
| } |
| else if (element instanceof PsiMethod) { |
| final PsiType reType = getType(element); |
| |
| element.accept(new JavaRecursiveElementWalkingVisitor() { |
| @Override public void visitReturnStatement(final PsiReturnStatement statement) { |
| super.visitReturnStatement(statement); |
| |
| final PsiExpression retExpr = statement.getReturnValue(); |
| |
| if (retExpr != null) { |
| system.addSubtypeConstraint(evaluateType(retExpr, system), reType); |
| } |
| } |
| }); |
| |
| return; |
| } |
| |
| final PsiElement root = PsiTreeUtil.getParentOfType(element, PsiStatement.class, PsiField.class); |
| |
| if (root != null) { |
| final PsiAnchor anchor = PsiAnchor.create(root); |
| |
| if (!myVisitedConstructions.contains(anchor)) { |
| root.accept(new JavaRecursiveElementWalkingVisitor() { |
| @Override public void visitAssignmentExpression(final PsiAssignmentExpression expression) { |
| super.visitAssignmentExpression(expression); |
| |
| system |
| .addSubtypeConstraint(evaluateType(expression.getRExpression(), system), evaluateType(expression.getLExpression(), system)); |
| } |
| |
| @Override public void visitConditionalExpression(final PsiConditionalExpression expression) { |
| super.visitConditionalExpression(expression); |
| |
| system.addSubtypeConstraint(evaluateType(expression.getThenExpression(), system), |
| evaluateType(expression.getElseExpression(), system)); |
| system.addSubtypeConstraint(evaluateType(expression.getElseExpression(), system), |
| evaluateType(expression.getThenExpression(), system)); |
| } |
| |
| @Override public void visitCallExpression(final PsiCallExpression expression) { |
| super.visitCallExpression(expression); |
| evaluateType(expression, system); |
| } |
| |
| @Override public void visitReturnStatement(final PsiReturnStatement statement) { |
| super.visitReturnStatement(statement); |
| |
| final PsiMethod method = PsiTreeUtil.getParentOfType(statement, PsiMethod.class); |
| |
| if (method != null) { |
| system.addSubtypeConstraint(evaluateType(statement.getReturnValue(), system), getType(method)); |
| } |
| } |
| |
| @Override public void visitTypeCastExpression(final PsiTypeCastExpression expression) { |
| super.visitTypeCastExpression(expression); |
| |
| final PsiType operandType = evaluateType(expression.getOperand(), system); |
| final PsiType castType = evaluateType(expression, system); |
| if (operandType == null || castType == null) return; |
| |
| if (Util.bindsTypeVariables(operandType)) { |
| system.addCast(expression, operandType); |
| } |
| |
| if (operandType.getDeepComponentType() instanceof PsiTypeVariable || |
| castType.getDeepComponentType() instanceof PsiTypeVariable) { |
| system.addSubtypeConstraint(operandType, castType); |
| } |
| else { |
| final PsiClassType.ClassResolveResult operandResult = Util.resolveType(operandType); |
| final PsiClassType.ClassResolveResult castResult = Util.resolveType(castType); |
| |
| final PsiClass operandClass = operandResult.getElement(); |
| final PsiClass castClass = castResult.getElement(); |
| |
| if (operandClass != null && castClass != null) { |
| if (InheritanceUtil.isInheritorOrSelf(operandClass, castClass, true)) { |
| system.addSubtypeConstraint(operandType, castType); |
| } |
| } |
| } |
| } |
| |
| @Override public void visitVariable(final PsiVariable variable) { |
| super.visitVariable(variable); |
| |
| final PsiExpression init = variable.getInitializer(); |
| |
| if (init != null) { |
| system.addSubtypeConstraint(evaluateType(init, system), getType(variable)); |
| } |
| } |
| |
| @Override public void visitNewExpression(final PsiNewExpression expression) { |
| super.visitNewExpression(expression); |
| |
| final PsiArrayInitializerExpression init = expression.getArrayInitializer(); |
| |
| if (init != null) { |
| final PsiExpression[] inits = init.getInitializers(); |
| final PsiType type = getType(expression); |
| |
| for (PsiExpression init1 : inits) { |
| system.addSubtypeConstraint(evaluateType(init1, system).createArrayType(), type); |
| } |
| } |
| } |
| |
| @Override public void visitReferenceExpression(final PsiReferenceExpression expression) { |
| final PsiExpression qualifierExpression = expression.getQualifierExpression(); |
| |
| if (qualifierExpression != null) { |
| qualifierExpression.accept(this); |
| } |
| } |
| }); |
| |
| myVisitedConstructions.add(anchor); |
| } |
| } |
| } |
| |
| private static SearchScope getScope(final PsiSearchHelper helper, final PsiElement element) { |
| SearchScope scope = helper.getUseScope(element); |
| if (scope instanceof GlobalSearchScope) { |
| scope = |
| GlobalSearchScope.getScopeRestrictedByFileTypes((GlobalSearchScope)scope, StdFileTypes.JAVA, StdFileTypes.JSP, StdFileTypes.JSPX); |
| } |
| return scope; |
| } |
| |
| PsiType replaceWildCards(final PsiType type, final ReductionSystem system, final PsiSubstitutor definedSubst) { |
| if (type instanceof PsiWildcardType) { |
| final PsiWildcardType wildcard = (PsiWildcardType)type; |
| final PsiType var = myTypeVariableFactory.create(); |
| final PsiType bound = wildcard.getBound(); |
| |
| if (bound != null) { |
| if (wildcard.isExtends()) { |
| system.addSubtypeConstraint(Util.banalize(definedSubst.substitute(replaceWildCards(bound, system, definedSubst))), var); |
| } |
| else { |
| system.addSubtypeConstraint(var, Util.banalize(definedSubst.substitute(replaceWildCards(bound, system, definedSubst)))); |
| } |
| } |
| |
| return var; |
| } |
| else if (type instanceof PsiClassType) { |
| final PsiClassType.ClassResolveResult result = Util.resolveType(type); |
| final PsiClass aClass = result.getElement(); |
| final PsiSubstitutor aSubst = result.getSubstitutor(); |
| |
| if (aClass != null) { |
| PsiSubstitutor theSubst = PsiSubstitutor.EMPTY; |
| |
| for (final PsiTypeParameter p : aSubst.getSubstitutionMap().keySet()) { |
| theSubst = theSubst.put(p, replaceWildCards(aSubst.substitute(p), system, definedSubst)); |
| } |
| |
| return JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory().createType(aClass, theSubst); |
| } |
| } |
| |
| return type; |
| } |
| |
| private void addBoundConstraintsImpl(final PsiType defined, final PsiType type, final ReductionSystem system) { |
| final PsiClassType.ClassResolveResult resultDefined = Util.resolveType(defined); |
| final PsiClassType.ClassResolveResult resultType = Util.resolveType(type); |
| final PsiClass definedClass = resultDefined.getElement(); |
| |
| if (definedClass == null || !definedClass.equals(resultType.getElement())) { |
| return; |
| } |
| |
| final PsiSubstitutor definedSubst = resultDefined.getSubstitutor(); |
| final PsiSubstitutor typeSubst = resultType.getSubstitutor(); |
| |
| for (final PsiTypeParameter parameter : definedSubst.getSubstitutionMap().keySet()) { |
| final PsiClassType[] extendsList = parameter.getExtendsList().getReferencedTypes(); |
| final PsiType definedType = definedSubst.substitute(parameter); |
| |
| if (definedType instanceof PsiTypeVariable) { |
| for (PsiType extendsType : extendsList) { |
| extendsType = replaceWildCards(extendsType, system, definedSubst); |
| |
| system.addSubtypeConstraint(definedType, Util.banalize(definedSubst.substitute(extendsType))); |
| } |
| } |
| else { |
| addBoundConstraintsImpl(definedType, typeSubst.substitute(parameter), system); |
| } |
| } |
| } |
| |
| |
| private void addBoundConstraints(final ReductionSystem system, final PsiType definedType, final PsiElement element) { |
| final PsiType elemenType = Util.getType(element); |
| |
| if (elemenType != null) { |
| addBoundConstraintsImpl(definedType, elemenType, system); |
| |
| if (mySettings.cookObjects() && elemenType.getCanonicalText().equals(CommonClassNames.JAVA_LANG_OBJECT)) { |
| system.addSubtypeConstraint(definedType, elemenType); |
| } |
| } |
| } |
| |
| public ReductionSystem build(final PsiElement... scopes) { |
| return build(collect(scopes)); |
| } |
| |
| public ReductionSystem build(final Set<PsiElement> victims) { |
| final PsiSearchHelper helper = PsiSearchHelper.SERVICE.getInstance(myManager.getProject()); |
| |
| ReductionSystem system = new ReductionSystem(myProject, victims, myTypes, myTypeVariableFactory, mySettings); |
| |
| for (final PsiElement element : victims) { |
| if (element instanceof PsiParameter && ((PsiParameter)element).getDeclarationScope() instanceof PsiMethod) { |
| if (!verifyMethod(element, victims, helper)) { |
| continue; |
| } |
| } |
| else if (element instanceof PsiMethod) { |
| if (!verifyMethod(element, victims, helper)) { |
| continue; |
| } |
| } |
| } |
| |
| for (final PsiElement element : victims) { |
| PsiType definedType; |
| if (element instanceof PsiParameter && ((PsiParameter)element).getDeclarationScope() instanceof PsiMethod) { |
| final PsiParameter p = myParameters.get(element); |
| |
| if (p != null) { |
| setType(element, definedType = defineType(p)); |
| } |
| else { |
| continue; |
| } |
| } |
| else if (element instanceof PsiMethod) { |
| final PsiMethod m = myMethods.get(element); |
| |
| if (m != null) { |
| system.addSubtypeConstraint(defineType(element), definedType = defineType(m)); |
| } |
| else { |
| continue; |
| } |
| } |
| else { |
| definedType = defineType(element); |
| } |
| |
| addBoundConstraints(system, definedType, element); |
| } |
| |
| for (final PsiElement element : victims) { |
| if (element instanceof PsiParameter) { |
| final PsiElement scope = ((PsiParameter)element).getDeclarationScope(); |
| if (scope instanceof PsiMethod) { |
| final PsiParameter p = myParameters.get(element); |
| |
| if (p == null) continue; |
| } |
| /*else if (scope instanceof PsiForeachStatement) { |
| addForEachConstraint(system, (PsiForeachStatement)scope); |
| }*/ |
| else if (element instanceof PsiMethod) { |
| final PsiMethod m = myMethods.get(element); |
| |
| if (m == null) continue; |
| } |
| } |
| else if (element instanceof PsiMethod) { |
| final PsiMethod m = myMethods.get(element); |
| |
| if (m == null) continue; |
| } |
| |
| addUsage(system, element); |
| |
| if (!(element instanceof PsiExpression)) { |
| |
| for (PsiReference ref : ReferencesSearch.search(element, getScope(helper, element), true)) { |
| final PsiElement elt = ref.getElement(); |
| |
| if (elt != null) { |
| addUsage(system, elt); |
| } |
| } |
| } |
| } |
| |
| return system; |
| } |
| |
| private void addForEachConstraint(final ReductionSystem system, final PsiForeachStatement statement) { |
| final PsiType paramType = getType(statement.getIterationParameter()); |
| final PsiExpression value = statement.getIteratedValue(); |
| if (value != null) { |
| final PsiType type = evaluateType(value, system); |
| if (type instanceof PsiClassType) { |
| final PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)type).resolveGenerics(); |
| final PsiClass clazz = resolveResult.getElement(); |
| if (clazz != null) { |
| final PsiClass iterableClass = |
| JavaPsiFacade.getInstance(clazz.getProject()).findClass("java.lang.Iterable", clazz.getResolveScope()); |
| if (iterableClass != null) { |
| final PsiTypeParameter[] typeParameters = iterableClass.getTypeParameters(); |
| if (typeParameters.length == 1) { |
| final PsiSubstitutor substitutor = |
| TypeConversionUtil.getClassSubstitutor(iterableClass, clazz, resolveResult.getSubstitutor()); |
| if (substitutor != null) { |
| final PsiType componentType = substitutor.substitute(typeParameters[0]); |
| system.addSubtypeConstraint(componentType, paramType); |
| } |
| } |
| } |
| } |
| } |
| else if (type instanceof PsiArrayType) { |
| system.addSubtypeConstraint(((PsiArrayType)type).getComponentType(), paramType); |
| } |
| } |
| } |
| } |