emptyIterator() used; misc warning fixes
[idea/community.git] / java / java-impl / src / com / intellij / refactoring / typeMigration / TypeMigrationLabeler.java
1 // Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.refactoring.typeMigration;
3
4 import com.intellij.codeInsight.generation.GenerateMembersUtil;
5 import com.intellij.codeInsight.generation.GetterSetterPrototypeProvider;
6 import com.intellij.lang.java.JavaLanguage;
7 import com.intellij.openapi.application.ApplicationManager;
8 import com.intellij.openapi.diagnostic.Logger;
9 import com.intellij.openapi.project.Project;
10 import com.intellij.openapi.ui.Messages;
11 import com.intellij.openapi.util.Comparing;
12 import com.intellij.openapi.util.Pair;
13 import com.intellij.openapi.util.TextRange;
14 import com.intellij.openapi.util.text.StringUtil;
15 import com.intellij.psi.*;
16 import com.intellij.psi.impl.source.PsiImmediateClassType;
17 import com.intellij.psi.javadoc.PsiDocTagValue;
18 import com.intellij.psi.search.PsiSearchScopeUtil;
19 import com.intellij.psi.search.SearchScope;
20 import com.intellij.psi.search.searches.OverridingMethodsSearch;
21 import com.intellij.psi.search.searches.ReferencesSearch;
22 import com.intellij.psi.util.PsiTreeUtil;
23 import com.intellij.psi.util.PsiTypesUtil;
24 import com.intellij.psi.util.PsiUtil;
25 import com.intellij.psi.util.TypeConversionUtil;
26 import com.intellij.refactoring.rename.RenameProcessor;
27 import com.intellij.refactoring.typeMigration.usageInfo.OverriddenUsageInfo;
28 import com.intellij.refactoring.typeMigration.usageInfo.OverriderUsageInfo;
29 import com.intellij.refactoring.typeMigration.usageInfo.TypeMigrationUsageInfo;
30 import com.intellij.usageView.UsageInfo;
31 import com.intellij.util.*;
32 import com.intellij.util.concurrency.Semaphore;
33 import com.intellij.util.containers.ContainerUtil;
34 import com.intellij.util.containers.MultiMap;
35 import com.intellij.util.graph.DFSTBuilder;
36 import com.intellij.util.graph.GraphGenerator;
37 import com.intellij.util.graph.InboundSemiGraph;
38 import gnu.trove.THashMap;
39 import org.jetbrains.annotations.NotNull;
40 import org.jetbrains.annotations.Nullable;
41 import org.jetbrains.annotations.TestOnly;
42
43 import javax.swing.*;
44 import java.util.*;
45
46 /**
47  * @author db
48  */
49 public class TypeMigrationLabeler {
50   private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.typeMigration.TypeMigrationLabeler");
51   private boolean myShowWarning = true;
52   private volatile MigrateException myException;
53   private final Semaphore myDialogSemaphore = new Semaphore();
54   private final Project myProject;
55
56   public TypeMigrationRules getRules() {
57     return myRules;
58   }
59
60   private final TypeMigrationRules myRules;
61   private final Function<PsiElement, PsiType> myMigrationRootTypeFunction;
62   @Nullable private final Set<PsiElement> myAllowedRoots;
63   private TypeEvaluator myTypeEvaluator;
64   private final LinkedHashMap<PsiElement, Object> myConversions;
65   private final Map<Pair<SmartPsiElementPointer<PsiExpression>, PsiType>, TypeMigrationUsageInfo> myFailedConversions;
66   private LinkedList<Pair<TypeMigrationUsageInfo, PsiType>> myMigrationRoots;
67   private final LinkedHashMap<TypeMigrationUsageInfo, PsiType> myNewExpressionTypeChange;
68   private final LinkedHashMap<TypeMigrationUsageInfo, PsiClassType> myClassTypeArgumentsChange;
69
70   private TypeMigrationUsageInfo[] myMigratedUsages;
71
72   private TypeMigrationUsageInfo myCurrentRoot;
73   private final Map<TypeMigrationUsageInfo, HashSet<Pair<TypeMigrationUsageInfo, PsiType>>> myRootsTree =
74     new HashMap<>();
75   private final Map<Pair<TypeMigrationUsageInfo, TypeMigrationUsageInfo>, Set<PsiElement>> myRootUsagesTree = new HashMap<>();
76   private final Set<TypeMigrationUsageInfo> myProcessedRoots = new HashSet<>();
77   private final Set<PsiTypeParameter> myDisappearedTypeParameters = new HashSet<>();
78
79   public TypeMigrationLabeler(TypeMigrationRules rules, PsiType rootType, Project project) {
80     this(rules, Functions.constant(rootType), null, project);
81   }
82
83   public TypeMigrationLabeler(TypeMigrationRules rules,
84                               Function<PsiElement, PsiType> migrationRootTypeFunction,
85                               @Nullable("any root accepted if null") PsiElement[] allowedRoots,
86                               Project project) {
87     myRules = rules;
88     myMigrationRootTypeFunction = migrationRootTypeFunction;
89     myAllowedRoots = allowedRoots == null ? null : ContainerUtil.set(allowedRoots);
90
91     myConversions = new LinkedHashMap<>();
92     myFailedConversions = new LinkedHashMap<>();
93     myNewExpressionTypeChange = new LinkedHashMap<>();
94     myClassTypeArgumentsChange = new LinkedHashMap<>();
95     myProject = project;
96   }
97
98   public boolean hasFailedConversions() {
99     return !myFailedConversions.isEmpty();
100   }
101
102   public Function<PsiElement, PsiType> getMigrationRootTypeFunction() {
103     return myMigrationRootTypeFunction;
104   }
105
106   public String[] getFailedConversionsReport() {
107     final String[] report = new String[myFailedConversions.size()];
108     int j = 0;
109
110     for (final Pair<SmartPsiElementPointer<PsiExpression>, PsiType> p : myFailedConversions.keySet()) {
111       final PsiExpression element = p.getFirst().getElement();
112       LOG.assertTrue(element != null);
113       final PsiType type = element.getType();
114       report[j++] = "Cannot convert type of expression <b>" + StringUtil.escapeXmlEntities(element.getText()) + "</b>" +
115                     (type != null
116                      ? " from <b>" + StringUtil.escapeXmlEntities(type.getCanonicalText()) + "</b>" +
117                        " to <b>" + StringUtil.escapeXmlEntities(p.getSecond().getCanonicalText()) + "</b>"
118                      : "")
119                     + "<br>";
120     }
121
122     return report;
123   }
124
125   public UsageInfo[] getFailedUsages(final TypeMigrationUsageInfo root) {
126     return map2Usages(ContainerUtil.mapNotNull(myFailedConversions.entrySet(),
127                                                entry -> entry.getValue().equals(root) ? entry.getKey() : null));
128   }
129
130   public UsageInfo[] getFailedUsages() {
131     return map2Usages(myFailedConversions.keySet());
132   }
133
134   @NotNull
135   private static UsageInfo[] map2Usages(Collection<? extends Pair<SmartPsiElementPointer<PsiExpression>, PsiType>> usages) {
136     return ContainerUtil
137       .map2Array(usages, new UsageInfo[usages.size()], pair -> {
138         final PsiExpression expr = pair.getFirst().getElement();
139         LOG.assertTrue(expr != null);
140         return new UsageInfo(expr) {
141           @Override
142           @Nullable
143           public String getTooltipText() {
144             final PsiType type = expr.isValid() ? expr.getType() : null;
145             if (type == null) return null;
146             return "Cannot convert type of the expression from " +
147                    type.getCanonicalText() + " to " + pair.getSecond().getCanonicalText();
148           }
149         };
150       });
151   }
152
153   public TypeMigrationUsageInfo[] getMigratedUsages() {
154     final LinkedList<Pair<TypeMigrationUsageInfo, PsiType>> declarations = getTypeEvaluator().getMigratedDeclarations();
155     final TypeMigrationUsageInfo[] usages = new TypeMigrationUsageInfo[declarations.size() + myConversions.size() + myNewExpressionTypeChange.size() + myClassTypeArgumentsChange.size()];
156
157     int j = 0;
158
159     for (final PsiElement element : myConversions.keySet()) {
160       final Object conv = myConversions.get(element);
161       usages[j++] = new TypeMigrationUsageInfo(element) {
162         @Override
163         public String getTooltipText() {
164           if (conv instanceof String) {   //todo
165             final String conversion = (String)conv;
166             return "Replaced with " + conversion.replaceAll("\\$", element.getText());
167           }
168           else {
169             return "Replaced with " + conv.toString();
170           }
171         }
172
173         @Override
174         public boolean isExcluded() {
175           if (conv instanceof TypeConversionDescriptorBase) return ((TypeConversionDescriptorBase)conv).getRoot().isExcluded();
176           return super.isExcluded();
177         }
178
179         @Override
180         public TypeMigrationUsageInfo getOwnerRoot() {
181           return conv instanceof TypeConversionDescriptorBase ? ((TypeConversionDescriptorBase)conv).getRoot() : null;
182         }
183       };
184     }
185
186     for (final Pair<TypeMigrationUsageInfo, PsiType> p : declarations) {
187       final TypeMigrationUsageInfo element = p.getFirst();
188       usages[j++] = element;
189     }
190
191     for (TypeMigrationUsageInfo info : myClassTypeArgumentsChange.keySet()) {
192       usages[j++] = info;
193     }
194
195     for (final TypeMigrationUsageInfo expr : myNewExpressionTypeChange.keySet()) {
196       usages[j++] = expr;
197     }
198     return sortMigratedUsages(usages);
199   }
200
201   private TypeMigrationUsageInfo[] sortMigratedUsages(TypeMigrationUsageInfo[] infos) {
202     final DFSTBuilder<TypeMigrationUsageInfo> builder = new DFSTBuilder<>(GraphGenerator.generate(
203       new InboundSemiGraph<TypeMigrationUsageInfo>() {
204         @NotNull
205         @Override
206         public Collection<TypeMigrationUsageInfo> getNodes() {
207           final Set<TypeMigrationUsageInfo> infos = new HashSet<>();
208           for (Map.Entry<TypeMigrationUsageInfo, HashSet<Pair<TypeMigrationUsageInfo, PsiType>>> entry : myRootsTree.entrySet()) {
209             infos.add(entry.getKey());
210             infos.addAll(ContainerUtil.map(entry.getValue(), pair -> pair.getFirst()));
211           }
212           return infos;
213         }
214
215         @NotNull
216         @Override
217         public Iterator<TypeMigrationUsageInfo> getIn(TypeMigrationUsageInfo n) {
218           final HashSet<Pair<TypeMigrationUsageInfo, PsiType>> rawNodes = myRootsTree.get(n);
219           if (rawNodes == null) {
220             return Collections.emptyIterator();
221           }
222           final List<TypeMigrationUsageInfo> in =
223             ContainerUtil.map(rawNodes, pair -> pair.getFirst());
224           return in.iterator();
225         }
226       }));
227     final Comparator<TypeMigrationUsageInfo> cmp = builder.comparator();
228
229     Arrays.sort(infos, (info1, info2) -> {
230       final TypeMigrationUsageInfo i1 = info1.getOwnerRoot();
231       final TypeMigrationUsageInfo i2 = info2.getOwnerRoot();
232       if (i1 == null && i2 == null) {
233         return 0;
234       }
235       if (i1 == null) {
236         return 1;
237       }
238       if (i2 == null) {
239         return -1;
240       }
241       final PsiElement element1 = info1.getElement();
242       final PsiElement element2 = info2.getElement();
243       LOG.assertTrue(element1 != null && element2 != null);
244       if (element1.equals(element2)) {
245         return 0;
246       }
247       final TextRange range1 = element1.getTextRange();
248       final TextRange range2 = element2.getTextRange();
249       if (range1.contains(range2)) {
250         return 1;
251       }
252       if (range2.contains(range1)) {
253         return -1;
254       }
255
256       final int res = cmp.compare(i1, i2);
257       if (res != 0) {
258         return res;
259       }
260       return Integer.compare(range2.getStartOffset(), range1.getStartOffset());
261     });
262
263     return infos;
264   }
265
266   MigrationProducer createMigratorFor(UsageInfo[] usages) {
267     final Map<UsageInfo, Object> conversions = new HashMap<>();
268     for (UsageInfo usage : usages) {
269       final Object conversion = getConversion(usage.getElement());
270       if (conversion != null) {
271         conversions.put(usage, conversion);
272       }
273     }
274     return new MigrationProducer(conversions);
275   }
276
277   @Nullable
278   public <T> T getSettings(Class<T> aClass) {
279     return myRules.getConversionSettings(aClass);
280   }
281
282   class MigrationProducer {
283     private final Map<UsageInfo, Object> myRemainConversions;
284     private final MultiMap<PsiTypeElement, TypeMigrationUsageInfo> myVariableMigration = new MultiMap<PsiTypeElement, TypeMigrationUsageInfo>() {
285       @NotNull
286       @Override
287       protected Map<PsiTypeElement, Collection<TypeMigrationUsageInfo>> createMap() {
288         return new THashMap<>();
289       }
290     };
291
292     private MigrationProducer(Map<UsageInfo, Object> conversions) {
293       myRemainConversions = conversions;
294     }
295
296     public void change(@NotNull final TypeMigrationUsageInfo usageInfo,
297                        @NotNull Consumer<? super PsiNewExpression> consumer) {
298       final PsiElement element = usageInfo.getElement();
299       if (element == null) return;
300       final Project project = element.getProject();
301       if (element instanceof PsiExpression) {
302         final PsiExpression expression = (PsiExpression)element;
303         if (element instanceof PsiNewExpression) {
304           for (Map.Entry<TypeMigrationUsageInfo, PsiType> info : myNewExpressionTypeChange.entrySet()) {
305             final PsiElement expressionToReplace = info.getKey().getElement();
306             if (expression.equals(expressionToReplace)) {
307               final PsiNewExpression newExpression =
308                 TypeMigrationReplacementUtil.replaceNewExpressionType(project, (PsiNewExpression)expressionToReplace, info);
309               if (newExpression != null) {
310                 consumer.consume(newExpression);
311               }
312             }
313           }
314         }
315         final Object conversion = myRemainConversions.get(usageInfo);
316         if (conversion != null) {
317           myRemainConversions.remove(usageInfo);
318           TypeMigrationReplacementUtil.replaceExpression(expression, project, conversion, myTypeEvaluator);
319         }
320       } else if (element instanceof PsiReferenceParameterList) {
321         for (Map.Entry<TypeMigrationUsageInfo, PsiClassType> entry : myClassTypeArgumentsChange.entrySet()) {
322           if (element.equals(entry.getKey().getElement())) { //todo check null
323             final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
324             try {
325               element.getParent().replace(factory.createReferenceElementByType(entry.getValue()));
326             }
327             catch (IncorrectOperationException e) {
328               LOG.error(e);
329             }
330           }
331         }
332       }
333       else if ((element instanceof PsiField || element instanceof PsiLocalVariable) &&
334                isMultiVariableDeclaration((PsiVariable)element)) {
335         final PsiTypeElement typeElement = ((PsiVariable)element).getTypeElement();
336         myVariableMigration.putValue(typeElement, usageInfo);
337       }
338       else {
339         TypeMigrationReplacementUtil.migrateMemberOrVariableType(element, project, getTypeEvaluator().getType(usageInfo));
340         if (usageInfo instanceof OverriddenUsageInfo) {
341           final String migrationName = ((OverriddenUsageInfo)usageInfo).getMigrateMethodName();
342           if (migrationName != null) {
343             ApplicationManager.getApplication().invokeLater(() -> {
344               if (element.isValid()) {
345                 new RenameProcessor(project, element, migrationName, false, false).run();
346               }
347             });
348           }
349         }
350       }
351     }
352
353     public void flush() {
354       for (Map.Entry<PsiTypeElement, Collection<TypeMigrationUsageInfo>> entry : myVariableMigration.entrySet()) {
355         final PsiTypeElement typeElement = entry.getKey();
356         if (!typeElement.isValid()) continue;
357         final Collection<TypeMigrationUsageInfo> migrations = entry.getValue();
358         if (migrations.size() != 1) {
359           MultiMap<PsiType, PsiVariable> variablesByMigrationType = new MultiMap<>();
360           for (TypeMigrationUsageInfo migration : migrations) {
361             final PsiElement var = migration.getElement();
362             if (!(var instanceof PsiLocalVariable || var instanceof PsiField)) {
363               continue;
364             }
365             final PsiType type = getTypeEvaluator().getType(migration);
366             variablesByMigrationType.putValue(type, (PsiVariable)var);
367           }
368           if (variablesByMigrationType.size() == 1) {
369             final Map.Entry<PsiType, Collection<PsiVariable>> migrationTypeAndVariables =
370               ContainerUtil.getFirstItem(variablesByMigrationType.entrySet());
371             LOG.assertTrue(migrationTypeAndVariables != null);
372             final PsiVariable[] variables = PsiTreeUtil.getChildrenOfType(typeElement.getParent().getParent(), PsiVariable.class);
373             if (variables != null && variables.length == migrationTypeAndVariables.getValue().size()) {
374               try {
375                 PsiType migrationType = migrationTypeAndVariables.getKey();
376                 final Project project = variables[0].getProject();
377                 migrationType = TypeMigrationReplacementUtil.revalidateType(migrationType, project);
378                 typeElement.replace(JavaPsiFacade.getElementFactory(project).createTypeElement(migrationType));
379               }
380               catch (IncorrectOperationException e) {
381                 LOG.error(e);
382               }
383               continue;
384             }
385           }
386         }
387         for (TypeMigrationUsageInfo info : entry.getValue()) migrateMultiDeclarationVariable(info);
388       }
389     }
390
391     private void migrateMultiDeclarationVariable(TypeMigrationUsageInfo varUsageInfo) {
392       final PsiElement var = varUsageInfo.getElement();
393       if (!(var instanceof PsiLocalVariable || var instanceof PsiField)) return;
394       ((PsiVariable) var).normalizeDeclaration();
395       TypeMigrationReplacementUtil.migrateMemberOrVariableType(var, var.getProject(), getTypeEvaluator().getType(varUsageInfo));
396     }
397
398     Object getConversion(UsageInfo info) {
399       return myRemainConversions.remove(info);
400     }
401
402     private boolean isMultiVariableDeclaration(PsiVariable variable) {
403       final PsiElement parent = variable.getParent();
404       LOG.assertTrue(parent != null);
405       final PsiVariable[] variables = PsiTreeUtil.getChildrenOfType(parent, PsiVariable.class);
406       LOG.assertTrue(variables != null);
407       return variables.length != 1;
408     }
409   }
410
411   void postProcessNewExpression(@NotNull PsiNewExpression expression) {
412     TypeMigrationReplacementUtil.tryToReplaceWithDiamond(expression, null);
413   }
414
415   @Nullable
416   Object getConversion(PsiElement element) {
417     return myConversions.get(element);
418   }
419
420   public TypeMigrationUsageInfo[] getMigratedUsages(boolean autoMigrate, final PsiElement... roots) {
421     if (myMigratedUsages == null) {
422       myShowWarning = autoMigrate;
423       migrate(autoMigrate, roots);
424       myMigratedUsages = getMigratedUsages();
425     }
426     return myMigratedUsages;
427   }
428
429   @Nullable
430   public Set<PsiElement> getTypeUsages(final TypeMigrationUsageInfo element, final TypeMigrationUsageInfo currentRoot) {
431     return myRootUsagesTree.get(Pair.create(element, currentRoot));
432   }
433
434   void convertExpression(final PsiExpression expr, final PsiType toType, final PsiType fromType, final boolean isCovariantPosition) {
435     final TypeConversionDescriptorBase conversion = myRules.findConversion(fromType, toType, expr instanceof PsiMethodCallExpression ? ((PsiMethodCallExpression)expr).resolveMethod() : null, expr,
436                                                      isCovariantPosition, this);
437
438     if (conversion == null) {
439       markFailedConversion(Pair.create(fromType, toType), expr);
440     }
441     else {
442       setConversionMapping(expr, conversion);
443     }
444   }
445
446   public void migrateExpressionType(final PsiExpression expr, final PsiType migrationType, final PsiElement place, boolean alreadyProcessed, final boolean isCovariant) {
447     PsiType originalType = expr.getType();
448
449     if (originalType == null || originalType.equals(migrationType)) return;
450
451     if (originalType.equals(PsiType.NULL)) {
452       if (migrationType instanceof PsiPrimitiveType) {
453         markFailedConversion(Pair.create(originalType, migrationType), expr);
454         return;
455       }
456       if (place instanceof PsiVariable) {
457         PsiType type = ((PsiVariable)place).getType();
458         if (((PsiVariable)place).getInitializer() == expr && myRules.shouldConvertNull(type, migrationType, expr)) {
459           convertExpression(expr, migrationType, type, isCovariant);
460         }
461       }
462       return;
463     }
464
465     if (expr instanceof PsiConditionalExpression) {
466       final PsiConditionalExpression condExpr = (PsiConditionalExpression)expr;
467       for (PsiExpression e : ContainerUtil.newArrayList(condExpr.getThenExpression(), condExpr.getElseExpression())) {
468         if (e != null) {
469           migrateExpressionType(e, migrationType, place, alreadyProcessed, false);
470         }
471       }
472       getTypeEvaluator().setType(new TypeMigrationUsageInfo(expr), migrationType);
473       return;
474     } else if (expr instanceof PsiClassObjectAccessExpression) {
475       if (!TypeConversionUtil.isAssignable(migrationType, expr.getType())) {
476         markFailedConversion(Pair.create(expr.getType(), migrationType), expr);
477         return;
478       }
479     } else if (expr instanceof PsiArrayInitializerExpression && migrationType instanceof PsiArrayType) {
480       final PsiExpression[] initializers = ((PsiArrayInitializerExpression)expr).getInitializers();
481       for (PsiExpression initializer : initializers) {
482         migrateExpressionType(initializer, ((PsiArrayType)migrationType).getComponentType(), expr, alreadyProcessed, true);
483       }
484       getTypeEvaluator().setType(new TypeMigrationUsageInfo(expr), migrationType);
485       return;
486     } else if (expr instanceof PsiArrayAccessExpression) {
487       migrateExpressionType(((PsiArrayAccessExpression)expr).getArrayExpression(), migrationType.createArrayType(), place, alreadyProcessed, isCovariant);
488       return;
489     }
490     else if (expr instanceof PsiReferenceExpression) {
491       final PsiElement resolved = ((PsiReferenceExpression)expr).resolve();
492       if (resolved != null) {
493         if (!addMigrationRoot(resolved, migrationType, place, alreadyProcessed, !isCovariant)) {
494           convertExpression(expr, migrationType, getTypeEvaluator().evaluateType(expr), isCovariant);
495         }
496       }
497       return;
498     }
499     else if (expr instanceof PsiMethodCallExpression) {
500       final PsiMethod resolved = ((PsiMethodCallExpression)expr).resolveMethod();
501       if (resolved != null) {
502         if (!addMigrationRoot(resolved, migrationType, place, alreadyProcessed, !isCovariant)) {
503           convertExpression(expr, migrationType, getTypeEvaluator().evaluateType(expr), isCovariant);
504         }
505       }
506       return;
507     }
508     else if (expr instanceof PsiNewExpression) {
509       if (originalType.getArrayDimensions() == migrationType.getArrayDimensions()) {
510         if (migrationType.getArrayDimensions() > 0) {
511           final PsiType elementType = ((PsiArrayType)migrationType).getComponentType();
512
513           final PsiArrayInitializerExpression arrayInitializer = ((PsiNewExpression)expr).getArrayInitializer();
514
515           if (arrayInitializer != null) {
516             final PsiExpression[] initializers = arrayInitializer.getInitializers();
517             for (int i = initializers.length - 1; i >= 0; i--) {
518               migrateExpressionType(initializers[i], elementType, place, alreadyProcessed, true);
519             }
520           }
521
522           if (isGenericsArrayType(elementType)){
523             markFailedConversion(Pair.create(originalType, migrationType), expr);
524             return;
525           }
526
527           final TypeMigrationUsageInfo usageInfo = new TypeMigrationUsageInfo(expr);
528           usageInfo.setOwnerRoot(myCurrentRoot);
529           myNewExpressionTypeChange.put(usageInfo, migrationType);
530           getTypeEvaluator().setType(new TypeMigrationUsageInfo(expr), migrationType);
531           return;
532         } else {
533           if (migrationType instanceof PsiClassType && originalType instanceof PsiClassType && ((PsiClassType)migrationType).rawType().isAssignableFrom(((PsiClassType)originalType).rawType())) {
534             final PsiClass originalClass = PsiUtil.resolveClassInType(originalType);
535             if (originalClass instanceof PsiAnonymousClass) {
536               originalType = ((PsiAnonymousClass)originalClass).getBaseClassType();
537             }
538             final PsiType type = TypeEvaluator.substituteType(migrationType, originalType, true, ((PsiClassType)originalType).resolveGenerics().getElement(),
539                                                               JavaPsiFacade.getElementFactory(expr.getProject()).createType(((PsiClassType)originalType).resolve(), PsiSubstitutor.EMPTY));
540             if (type != null){
541               final TypeMigrationUsageInfo usageInfo = new TypeMigrationUsageInfo(expr);
542               usageInfo.setOwnerRoot(myCurrentRoot);
543               myNewExpressionTypeChange.put(usageInfo, type);
544               getTypeEvaluator().setType(new TypeMigrationUsageInfo(expr), type);
545               return;
546             }
547           }
548         }
549       }
550
551     }
552     else if (expr instanceof PsiLambdaExpression) {
553       //TODO conversion of lambda expression now works incorrectly [Dmitry Batkovich]
554       return;
555     }
556
557     convertExpression(expr, migrationType, originalType, isCovariant);
558   }
559
560   private static boolean isGenericsArrayType(final PsiType elementType) {
561     if (elementType instanceof PsiClassType && ((PsiClassType)elementType).hasParameters()) {
562       return true;
563     } else if (elementType instanceof PsiArrayType) {
564       final PsiType componentType = ((PsiArrayType)elementType).getComponentType();
565       return isGenericsArrayType(componentType);
566     }
567     return false;
568   }
569
570   boolean addMigrationRoot(PsiElement element, PsiType type, final PsiElement place, boolean alreadyProcessed, final boolean isContraVariantPosition) {
571     return addMigrationRoot(element, type, place, alreadyProcessed, isContraVariantPosition, false);
572   }
573
574   boolean addMigrationRoot(PsiElement element,
575                            PsiType type,
576                            final PsiElement place,
577                            boolean alreadyProcessed,
578                            final boolean isContraVariantPosition,
579                            final boolean userDefinedType) {
580     if (myAllowedRoots != null && !myAllowedRoots.contains(element)) {
581       return false;
582     }
583     if (type.equals(PsiType.NULL)) {
584       return false;
585     }
586     final PsiElement resolved = Util.normalizeElement(element);
587     if (!canBeRoot(resolved, myRules.getSearchScope())) {
588       return false;
589     }
590     final PsiType originalType = getElementType(resolved);
591     LOG.assertTrue(originalType != null);
592     type = userDefinedType ? type : TypeEvaluator.substituteType(type, originalType, isContraVariantPosition);
593
594     if (userDefinedType) {
595       Set<PsiTypeParameter> disappearedTypeParameters = getTypeParameters(originalType);
596       disappearedTypeParameters.removeAll(getTypeParameters(type));
597       myDisappearedTypeParameters.addAll(disappearedTypeParameters);
598     }
599     else if (typeContainsTypeParameters(originalType, getTypeParameters(type))) return false;
600
601     if (type instanceof PsiCapturedWildcardType) {
602       return false;
603     }
604
605     if (resolved instanceof PsiMethod) {
606       final PsiMethod method = ((PsiMethod)resolved);
607
608       final PsiClass containingClass = method.getContainingClass();
609       if (containingClass instanceof PsiAnonymousClass) {
610         final HierarchicalMethodSignature signature = method.getHierarchicalMethodSignature();
611         final List<HierarchicalMethodSignature> superSignatures = signature.getSuperSignatures();
612         if (!superSignatures.isEmpty()) {
613
614           final HierarchicalMethodSignature superSignature = superSignatures.get(0);
615
616           final PsiSubstitutor substitutor = superSignature.getSubstitutor();
617           if (!substitutor.getSubstitutionMap().isEmpty()) {
618             final PsiMethod superMethod = superSignature.getMethod();
619
620             final PsiType superReturnType = superMethod.getReturnType();
621             if (superReturnType instanceof PsiClassType) {
622               final PsiClass resolvedClass = ((PsiClassType)superReturnType).resolve();
623               if (resolvedClass instanceof PsiTypeParameter) {
624                 final PsiType expectedReturnType = substitutor.substitute((PsiTypeParameter)resolvedClass);
625                 if (Comparing.equal(expectedReturnType, method.getReturnType())) {
626                   final PsiClassType baseClassType = ((PsiAnonymousClass)containingClass).getBaseClassType();
627                   final PsiClassType.ClassResolveResult result = baseClassType.resolveGenerics();
628                   final PsiClass anonymousBaseClass = result.getElement();
629
630                   final PsiSubstitutor superHierarchySubstitutor = TypeConversionUtil
631                     .getClassSubstitutor(superMethod.getContainingClass(), anonymousBaseClass, PsiSubstitutor.EMPTY);
632                   final PsiType maybeTypeParameter = superHierarchySubstitutor.substitute((PsiTypeParameter)resolvedClass);
633
634                   if (maybeTypeParameter instanceof PsiClassType &&
635                       ((PsiClassType)maybeTypeParameter).resolve() instanceof PsiTypeParameter) {
636                     final PsiSubstitutor newSubstitutor = result.getSubstitutor().put(
637                       (PsiTypeParameter)((PsiClassType)maybeTypeParameter).resolve(), type);
638                     addRoot(new TypeMigrationUsageInfo(((PsiAnonymousClass)containingClass).getBaseClassReference().getParameterList()),
639                             new PsiImmediateClassType(anonymousBaseClass, newSubstitutor),
640                             place,
641                             alreadyProcessed);
642                   }
643                 }
644               }
645             }
646           }
647         }
648       }
649
650
651       final PsiMethod[] methods = OverridingMethodsSearch.search(method).toArray(PsiMethod.EMPTY_ARRAY);
652       final OverriderUsageInfo[] overriders = new OverriderUsageInfo[methods.length];
653       for (int i = -1; i < methods.length; i++) {
654         final TypeMigrationUsageInfo m;
655         if (i < 0) {
656           final OverriddenUsageInfo overriddenUsageInfo = new OverriddenUsageInfo(method);
657           m = overriddenUsageInfo;
658           final String newMethodName = isMethodNameCanBeChanged(method);
659           if (newMethodName != null) {
660             final MigrateGetterNameSetting migrateGetterNameSetting = myRules.getConversionSettings(MigrateGetterNameSetting.class);
661             migrateGetterNameSetting.askUserIfNeed(overriddenUsageInfo, newMethodName, myTypeEvaluator.getType(myCurrentRoot));
662           }
663         }
664         else {
665           overriders[i] = new OverriderUsageInfo(methods[i], method);
666           m = overriders[i];
667         }
668
669         alreadyProcessed = addRoot(m, type, place, alreadyProcessed);
670       }
671
672       return !alreadyProcessed;
673     }
674     else if (resolved instanceof PsiParameter && ((PsiParameter)resolved).getDeclarationScope() instanceof PsiMethod) {
675       final PsiMethod method = (PsiMethod)((PsiParameter)resolved).getDeclarationScope();
676
677       final int index = method.getParameterList().getParameterIndex(((PsiParameter)resolved));
678       final PsiMethod[] methods = OverridingMethodsSearch.search(method).toArray(PsiMethod.EMPTY_ARRAY);
679
680       final OverriderUsageInfo[] overriders = new OverriderUsageInfo[methods.length];
681       final OverriddenUsageInfo overriddenUsageInfo = new OverriddenUsageInfo(method.getParameterList().getParameters()[index]);
682       for (int i = -1; i < methods.length; i++) {
683         final PsiMethod m = i < 0 ? method : methods[i];
684         final PsiParameter p = m.getParameterList().getParameters()[index];
685         final TypeMigrationUsageInfo paramUsageInfo;
686         if (i < 0) {
687           paramUsageInfo = overriddenUsageInfo;
688         }
689         else {
690           overriders[i] = new OverriderUsageInfo(p, method);
691           paramUsageInfo = overriders[i];
692         }
693         alreadyProcessed = addRoot(paramUsageInfo, type, place, alreadyProcessed);
694       }
695
696       return !alreadyProcessed;
697     }
698     else {
699       return !addRoot(new TypeMigrationUsageInfo(resolved), type, place, alreadyProcessed);
700     }
701   }
702
703   @NotNull
704   private static Set<PsiTypeParameter> getTypeParameters(@NotNull PsiType type) {
705     if (type instanceof PsiClassType) {
706       PsiTypesUtil.TypeParameterSearcher searcher = new PsiTypesUtil.TypeParameterSearcher();
707       type.accept(searcher);
708       return searcher.getTypeParameters();
709     }
710     return Collections.emptySet();
711   }
712
713   @Nullable
714   private String isMethodNameCanBeChanged(PsiMethod method) {
715     if (myCurrentRoot == null) {
716       return null;
717     }
718     final PsiElement root = myCurrentRoot.getElement();
719     if (!(root instanceof PsiField)) {
720       return null;
721     }
722     PsiField field = (PsiField) root;
723     final PsiType migrationType = myTypeEvaluator.getType(root);
724     if (migrationType == null) {
725       return null;
726     }
727     final PsiType sourceType = field.getType();
728     if (TypeConversionUtil.isAssignable(migrationType, sourceType)) {
729       return null;
730     }
731     if (!(migrationType.equals(PsiType.BOOLEAN) || migrationType.equals(PsiType.BOOLEAN.getBoxedType(field))) &&
732         !(sourceType.equals(PsiType.BOOLEAN) || sourceType.equals(PsiType.BOOLEAN.getBoxedType(field)))) {
733       return null;
734     }
735     final PsiMethod[] getters =
736       GetterSetterPrototypeProvider.findGetters(field.getContainingClass(), field.getName(), field.hasModifierProperty(PsiModifier.STATIC));
737     if (getters != null) {
738       for (PsiMethod getter : getters) {
739         if (getter.isEquivalentTo(method)) {
740           final String suggestedName = GenerateMembersUtil.suggestGetterName(field.getName(), migrationType, method.getProject());
741           if (!suggestedName.equals(method.getName())) {
742             if (getter.getContainingClass().findMethodsByName(suggestedName, true).length != 0) {
743               return null;
744             }
745             return suggestedName;
746           }
747           return null;
748         }
749       }
750     }
751     return null;
752   }
753
754   private boolean typeContainsTypeParameters(@Nullable PsiType type, @NotNull Set<PsiTypeParameter> excluded) {
755     if (!(type instanceof PsiClassType)) return false;
756     PsiTypesUtil.TypeParameterSearcher searcher = new PsiTypesUtil.TypeParameterSearcher();
757     type.accept(searcher);
758     for (PsiTypeParameter parameter : searcher.getTypeParameters()) {
759       if (!excluded.contains(parameter) && !myDisappearedTypeParameters.contains(parameter)) {
760         return true;
761       }
762     }
763     return false;
764   }
765
766
767   @Nullable
768   public static PsiType getElementType(final PsiElement resolved) {
769     if (resolved instanceof PsiVariable) {
770       return ((PsiVariable)resolved).getType();
771     }
772     else {
773       if (resolved instanceof PsiMethod) {
774         return (((PsiMethod)resolved).getReturnType());
775       }
776       else if (resolved instanceof PsiExpression){
777         return (((PsiExpression)resolved).getType());
778       } else if (resolved instanceof PsiReferenceParameterList) {
779         PsiElement parent = resolved.getParent();
780         while (parent != null) {
781           LOG.assertTrue(parent instanceof PsiJavaCodeReferenceElement);
782           final PsiClass psiClass = (PsiClass)((PsiJavaCodeReferenceElement)parent).resolve();
783           final PsiClass containingClass = PsiTreeUtil.getParentOfType(parent, PsiClass.class);
784           if (psiClass != null && containingClass != null) {
785            final PsiSubstitutor classSubstitutor = TypeConversionUtil.getClassSubstitutor(psiClass, containingClass, PsiSubstitutor.EMPTY);
786            if (classSubstitutor != null) {
787              return JavaPsiFacade.getElementFactory(parent.getProject()).createType(psiClass, classSubstitutor);
788            }
789           }
790           parent = PsiTreeUtil.getParentOfType(parent, PsiJavaCodeReferenceElement.class, true);
791         }
792       } else if (resolved instanceof PsiClass) {
793         return JavaPsiFacade.getElementFactory(resolved.getProject()).createType((PsiClass)resolved, PsiSubstitutor.EMPTY);
794       }
795     }
796     return null;
797   }
798
799   public void clearStopException() {
800     myException = null;
801   }
802   
803   boolean addRoot(final TypeMigrationUsageInfo usageInfo, final PsiType type, final PsiElement place, boolean alreadyProcessed) {
804     if (myShowWarning && myMigrationRoots.size() > 10 && !ApplicationManager.getApplication().isUnitTestMode()) {
805       myShowWarning = false;
806       myDialogSemaphore.down();
807       try {
808         final Runnable checkTimeToStopRunnable = () -> {
809           if (Messages.showYesNoCancelDialog("Found more than 10 roots to migrate. Do you want to preview?", "Type Migration",
810                                              Messages.getWarningIcon()) == Messages.YES) {
811             myException = new MigrateException();
812           }
813           myDialogSemaphore.up();
814         };
815         SwingUtilities.invokeLater(checkTimeToStopRunnable);
816       }
817       catch (Exception e) {
818         //do nothing
819       }
820     }
821     checkInterrupted();
822     rememberRootTrace(usageInfo, type, place, alreadyProcessed);
823     if (!alreadyProcessed && !(usageInfo.getElement() instanceof PsiExpression) && !getTypeEvaluator().setType(usageInfo, type)) {
824       alreadyProcessed = true;
825     }
826
827     if (!alreadyProcessed) myMigrationRoots.addFirst(Pair.create(usageInfo, type));
828     return alreadyProcessed;
829   }
830
831   private void checkInterrupted() {
832     if (myException != null) throw myException;
833   }
834
835   private void rememberRootTrace(final TypeMigrationUsageInfo usageInfo, final PsiType type, final PsiElement place, final boolean alreadyProcessed) {
836     if (myCurrentRoot != null) {
837       if (!alreadyProcessed) {
838         myProcessedRoots.add(usageInfo);
839       }
840
841       if (myProcessedRoots.contains(usageInfo)) {
842         HashSet<Pair<TypeMigrationUsageInfo, PsiType>> infos = myRootsTree.get(myCurrentRoot);
843         if (infos == null) {
844           infos = new HashSet<>();
845           myRootsTree.put(myCurrentRoot, infos);
846         }
847         infos.add(Pair.create(usageInfo, type));
848       }
849       if (!(usageInfo instanceof OverriderUsageInfo)) { //hide the same usage for all overriders
850         setTypeUsage(usageInfo, place);
851       }
852     }
853   }
854
855   private void setTypeUsage(final TypeMigrationUsageInfo usageInfo, final PsiElement place) {
856     if (place != null) {
857       final Pair<TypeMigrationUsageInfo, TypeMigrationUsageInfo> rooted = Pair.create(usageInfo, myCurrentRoot);
858       Set<PsiElement> usages = myRootUsagesTree.get(rooted);
859       if (usages == null) {
860         usages = new HashSet<>();
861         myRootUsagesTree.put(rooted, usages);
862       }
863       usages.add(place);
864     }
865   }
866   
867   public void setTypeUsage(final PsiElement element, final PsiElement place) {
868     setTypeUsage(new TypeMigrationUsageInfo(element), place);
869   }
870
871   void markFailedConversion(final Pair<PsiType, PsiType> typePair, final PsiExpression expression) {
872     LOG.assertTrue(typePair.getSecond() != null);
873     final Pair<SmartPsiElementPointer<PsiExpression>, PsiType> key =
874       Pair.create(SmartPointerManager.getInstance(expression.getProject()).createSmartPsiElementPointer(expression), typePair.getSecond());
875     if (!myFailedConversions.containsKey(key)) {
876       myFailedConversions.put(key, getCurrentRoot());
877     }
878   }
879
880   void setConversionMapping(final PsiExpression expression, final Object obj) {
881     if (myConversions.get(expression) != null) {
882       return;
883     }
884
885     if (obj instanceof TypeConversionDescriptorBase) {
886       ((TypeConversionDescriptorBase)obj).setRoot(myCurrentRoot);
887     }
888     myConversions.put(expression, obj);
889   }
890
891   public PsiReference[] markRootUsages(final PsiElement element, final PsiType migrationType) {
892     return markRootUsages(element, migrationType, ReferencesSearch.search(element, myRules.getSearchScope(), false).toArray(PsiReference.EMPTY_ARRAY));
893   }
894
895   PsiReference[] markRootUsages(final PsiElement element, final PsiType migrationType, final PsiReference[] refs) {
896     final List<PsiReference> validReferences = new ArrayList<>();
897     for (PsiReference ref1 : refs) {
898       final PsiElement ref = ref1.getElement();
899
900       if (element instanceof PsiMethod) {
901         final PsiElement parent = Util.getEssentialParent(ref);
902
903         if (!(parent instanceof PsiMethodCallExpression)) {
904           continue;
905         }
906
907         getTypeEvaluator().setType(new TypeMigrationUsageInfo(parent), migrationType);
908       }
909       else if (element instanceof PsiVariable) {
910         if (ref instanceof PsiReferenceExpression) {
911           getTypeEvaluator().setType(new TypeMigrationUsageInfo(ref), PsiUtil.captureToplevelWildcards(migrationType, ref));
912         }
913       }
914       else {
915         LOG.error("Method call expression or reference expression expected but found " + element.getClass().getName());
916         continue;
917       }
918       validReferences.add(ref1);
919     }
920
921     Collections.sort(validReferences, Comparator.comparingInt(o -> o.getElement().getTextOffset()));
922
923     return validReferences.toArray(PsiReference.EMPTY_ARRAY);
924   }
925
926   public void setRootAndMigrate(final TypeMigrationUsageInfo newRootUsageInfo, final PsiType migrationType, final PsiReference[] usages) {
927     final TypeMigrationUsageInfo oldRoot = getCurrentRoot();
928     myCurrentRoot = newRootUsageInfo;
929     PsiElement root = newRootUsageInfo.getElement();
930     if (root instanceof PsiMethod) {
931       migrateMethodReturnExpression(migrationType, (PsiMethod)root);
932     }
933     else if (root instanceof PsiParameter && ((PsiParameter)root).getDeclarationScope() instanceof PsiMethod) {
934       migrateMethodCallExpressions(migrationType, (PsiParameter)root, null);
935     }
936     else if (root instanceof PsiVariable || root instanceof PsiExpression) {
937       final PsiElement element = getContainingStatement(root);
938       if (root instanceof PsiExpression) {
939         migrateExpressionType((PsiExpression)root, migrationType, element, false, true);
940         myTypeEvaluator.setType(newRootUsageInfo, migrationType);
941       }
942       element.accept(new TypeMigrationStatementProcessor(element, this));
943     }
944     else if (root instanceof PsiReferenceParameterList) {
945       final TypeMigrationUsageInfo info = new TypeMigrationUsageInfo(root);
946       info.setOwnerRoot(oldRoot);
947       myClassTypeArgumentsChange.put(info, (PsiClassType)migrationType);
948       new ClassTypeArgumentMigrationProcessor(this).migrateClassTypeParameter((PsiReferenceParameterList)root, (PsiClassType)migrationType);
949     }
950
951     final Set<PsiElement> processed = new HashSet<>();
952     for (PsiReference usage : usages) {
953       migrateRootUsageExpression(usage, processed);
954     }
955   }
956
957   private static PsiElement getContainingStatement(final PsiElement root) {
958     final PsiStatement statement = PsiTreeUtil.getParentOfType(root, PsiStatement.class);
959     PsiExpression condition = getContainingCondition(root, statement);
960     if (condition != null) return condition;
961     final PsiField field = PsiTreeUtil.getParentOfType(root, PsiField.class);
962     return statement != null ? statement : field != null ? field : root;
963   }
964
965   private static PsiExpression getContainingCondition(PsiElement root, PsiStatement statement) {
966     PsiExpression condition = null;
967     if (statement instanceof PsiWhileStatement) {
968       condition = ((PsiWhileStatement)statement).getCondition();
969     }
970     else if (statement instanceof PsiDoWhileStatement) {
971       condition = ((PsiDoWhileStatement)statement).getCondition();
972     }
973     else if (statement instanceof PsiForStatement) {
974       condition = ((PsiForStatement)statement).getCondition();
975     }
976     else if (statement instanceof PsiIfStatement) {
977       condition = ((PsiIfStatement)statement).getCondition();
978     }
979     return PsiTreeUtil.isAncestor(condition, root, false) ? condition : null;
980   }
981
982   void migrateRootUsageExpression(final PsiReference usage, final Set<? super PsiElement> processed) {
983     final PsiElement ref = usage.getElement();
984     if (ref.getLanguage() == JavaLanguage.INSTANCE) {
985       final PsiElement element = getContainingStatement(ref);
986       if (element != null && !processed.contains(element)) {
987         processed.add(element);
988         element.accept(new TypeMigrationStatementProcessor(ref, this));
989       }
990     }
991   }
992
993   void migrateMethodCallExpressions(final PsiType migrationType, final PsiParameter param, final PsiClass psiClass) {
994     boolean checkNumberOfArguments = false;
995     if (param.getType() instanceof PsiEllipsisType && !(migrationType instanceof PsiEllipsisType)) {
996       checkNumberOfArguments = true;
997     }
998     final PsiType strippedType =
999                   migrationType instanceof PsiEllipsisType ? ((PsiEllipsisType)migrationType).getComponentType() : migrationType;
1000     final PsiMethod method = (PsiMethod)param.getDeclarationScope();
1001     final PsiParameterList parameterList = method.getParameterList();
1002     final int parametersCount = parameterList.getParametersCount();
1003     final int index = parameterList.getParameterIndex(param);
1004     final List<PsiReference> refs = filterReferences(psiClass, ReferencesSearch.search(method, method.getUseScope().intersectWith(myRules.getSearchScope()), false));
1005     for (PsiReference ref1 : refs) {
1006       final PsiElement ref = ref1.getElement();
1007       final PsiElement parent = Util.getEssentialParent(ref);
1008       if (parent instanceof PsiCallExpression) {
1009         final PsiExpressionList argumentList = ((PsiCallExpression)parent).getArgumentList();
1010         if (argumentList != null) {
1011           final PsiExpression[] expressions = argumentList.getExpressions();
1012           if (checkNumberOfArguments && parametersCount != expressions.length) {
1013             markFailedConversion(Pair.create(param.getType(), migrationType), (PsiCallExpression)parent);
1014           }
1015           if (index > -1 && index < expressions.length) {
1016             for (int idx = index; idx < (param.isVarArgs() ? expressions.length : index + 1); idx++) {
1017               final PsiExpression actual = expressions[idx];
1018               final PsiType type = getTypeEvaluator().evaluateType(actual);
1019               if (type != null) {
1020                 migrateExpressionType(actual, strippedType, parent, TypeConversionUtil.isAssignable(strippedType, type), true);
1021               }
1022             }
1023           }
1024         }
1025       } else if (ref instanceof PsiDocTagValue) {
1026         myConversions.put(ref, method);
1027       }
1028     }
1029   }
1030
1031   private void migrateMethodReturnExpression(final PsiType migrationType, final PsiMethod method) {
1032     final PsiCodeBlock block = method.getBody();
1033     if (block != null) {
1034       block.accept(new JavaRecursiveElementWalkingVisitor() {
1035         @Override
1036         public void visitReturnStatement(PsiReturnStatement statement) {
1037           final PsiExpression value = statement.getReturnValue();
1038           if (value != null) {
1039             final PsiType type = getTypeEvaluator().evaluateType(value);
1040             if (type != null && !type.equals(migrationType)) {
1041               migrateExpressionType(value, migrationType, statement, TypeConversionUtil.isAssignable(migrationType, type), true);
1042             }
1043           }
1044         }
1045
1046         @Override
1047         public void visitClass(PsiClass aClass) {}
1048
1049         @Override
1050         public void visitLambdaExpression(PsiLambdaExpression expression) {}
1051       });
1052     }
1053   }
1054
1055   private void iterate() {
1056     final List<Pair<TypeMigrationUsageInfo, PsiType>> roots = new ArrayList<>(myMigrationRoots);
1057
1058     myMigrationRoots = new LinkedList<>();
1059
1060     final PsiReference[][] cachedUsages = new PsiReference[roots.size()][];
1061     int j = 0;
1062
1063     for (final Pair<TypeMigrationUsageInfo, PsiType> p : roots) {
1064       cachedUsages[j++] = markRootUsages(p.getFirst().getElement(), p.getSecond());
1065     }
1066
1067     j = 0;
1068
1069     for (final Pair<TypeMigrationUsageInfo, PsiType> root : roots) {
1070       setRootAndMigrate(root.getFirst(), root.getSecond(), cachedUsages[j++]);
1071     }
1072   }
1073
1074   private void migrate(boolean autoMigrate, final PsiElement... victims) {
1075
1076     myMigrationRoots = new LinkedList<>();
1077     myTypeEvaluator = new TypeEvaluator(myMigrationRoots, this, myProject);
1078
1079     SmartTypePointerManager smartTypePointerManager = SmartTypePointerManager.getInstance(myProject);
1080     for (PsiElement victim : victims) {
1081       // use deeply immediate types
1082       PsiType migrationType = smartTypePointerManager.createSmartTypePointer(myMigrationRootTypeFunction.fun(victim)).getType();
1083       addMigrationRoot(victim, migrationType, null, false, true, true);
1084     }
1085
1086     if (autoMigrate) {
1087       while (!myMigrationRoots.isEmpty()) {
1088         iterate();
1089       }
1090     }
1091
1092     myDialogSemaphore.waitFor();
1093     checkInterrupted();
1094   }
1095
1096   @NotNull
1097   private PsiReference[] findReferences(PsiElement element) {
1098     return ReferencesSearch.search(element, myRules.getSearchScope(), false).toArray(PsiReference.EMPTY_ARRAY);
1099   }
1100
1101   public TypeEvaluator getTypeEvaluator() {
1102     return myTypeEvaluator;
1103   }
1104
1105   public Map<TypeMigrationUsageInfo, HashSet<Pair<TypeMigrationUsageInfo, PsiType>>> getRootsTree() {
1106     return myRootsTree;
1107   }
1108
1109   TypeMigrationUsageInfo getCurrentRoot() {
1110     return myCurrentRoot;
1111   }
1112
1113   public LinkedList<Pair<TypeMigrationUsageInfo, PsiType>> getMigrationRoots() {
1114     return myMigrationRoots;
1115   }
1116
1117   public static List<PsiReference> filterReferences(final PsiClass psiClass, final Query<? extends PsiReference> memberReferences) {
1118     final List<PsiReference> refs = new ArrayList<>();
1119     for (PsiReference memberReference : memberReferences) {
1120       if (psiClass == null) {
1121         refs.add(memberReference);
1122       } else {
1123         final PsiElement referencedElement = memberReference.getElement();
1124         if (referencedElement instanceof PsiReferenceExpression) {
1125           final PsiExpression qualifierExpression = ((PsiReferenceExpression)referencedElement).getQualifierExpression();
1126           if (qualifierExpression != null) {
1127             final PsiType qualifierType = qualifierExpression.getType();
1128             if (qualifierType instanceof PsiClassType && psiClass == ((PsiClassType)qualifierType).resolve()) {
1129               refs.add(memberReference);
1130             }
1131           } else {
1132             if (psiClass == PsiTreeUtil.getParentOfType(referencedElement, PsiClass.class)) {
1133               refs.add(memberReference);
1134             }
1135           }
1136         }
1137       }
1138     }
1139     return refs;
1140   }
1141
1142   private static boolean canBeRoot(@Nullable PsiElement element, @NotNull SearchScope migrationScope) {
1143     if (element == null) return false;
1144     return element.isValid() &&
1145            element.isPhysical() &&
1146            PsiSearchScopeUtil.isInScope(migrationScope, element);
1147   }
1148
1149   @TestOnly
1150   public String getMigrationReport() {
1151     final StringBuilder buffer = new StringBuilder();
1152
1153     buffer.append("Types:\n").append(getTypeEvaluator().getReport()).append("\n");
1154
1155     buffer.append("Conversions:\n");
1156
1157     final String[] conversions = new String[myConversions.size()];
1158     int k = 0;
1159
1160     for (final PsiElement expr : myConversions.keySet()) {
1161       final Object conversion = myConversions.get(expr);
1162
1163       if (conversion instanceof Pair && ((Pair)conversion).first == null) {
1164         conversions[k++] = (expr.getText() + " -> " + ((Pair)conversion).second + "\n");
1165       } else {
1166         conversions[k++] = (expr.getText() + " -> " + conversion + "\n");
1167       }
1168     }
1169
1170     Arrays.sort(conversions);
1171
1172     for (String conversion : conversions) {
1173       buffer.append(conversion);
1174     }
1175
1176     buffer.append("\nNew expression type changes:\n");
1177
1178     final String[] newChanges = new String[myNewExpressionTypeChange.size()];
1179     k = 0;
1180
1181     for (final Map.Entry<TypeMigrationUsageInfo, PsiType> entry : myNewExpressionTypeChange.entrySet()) {
1182       final PsiElement element = entry.getKey().getElement();
1183       newChanges[k++] = (element != null ? element.getText() : entry.getKey()) + " -> " + entry.getValue().getCanonicalText() + "\n";
1184     }
1185
1186     Arrays.sort(newChanges);
1187
1188     for (String change : newChanges) {
1189       buffer.append(change);
1190     }
1191
1192     buffer.append("Fails:\n");
1193
1194     final ArrayList<Pair<SmartPsiElementPointer<PsiExpression>, PsiType>>
1195       failsList = new ArrayList<>(myFailedConversions.keySet());
1196     Collections.sort(failsList, (o1, o2) -> {
1197       final PsiElement element1 = o1.getFirst().getElement();
1198       final PsiElement element2 = o2.getFirst().getElement();
1199       if (element1 == null || element2 == null) return 0;
1200       return element1.getText().compareTo(element2.getText());
1201     });
1202
1203     for (final Pair<SmartPsiElementPointer<PsiExpression>, PsiType> p : failsList) {
1204       final PsiElement element = p.getFirst().getElement();
1205       if (element != null) {
1206         buffer.append(element.getText()).append("->").append(p.getSecond().getCanonicalText()).append("\n");
1207       }
1208     }
1209
1210     return buffer.toString();
1211   }
1212
1213   public static class MigrateException extends RuntimeException { }
1214 }