constructor reference: don't ignore constructor parameters during method reference...
[idea/community.git] / java / java-impl / src / com / intellij / refactoring / changeSignature / JavaChangeSignatureUsageProcessor.java
1 /*
2  * Copyright 2000-2017 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.refactoring.changeSignature;
17
18 import com.intellij.codeInsight.AnnotationUtil;
19 import com.intellij.codeInsight.ExceptionUtil;
20 import com.intellij.codeInsight.daemon.impl.analysis.JavaHighlightUtil;
21 import com.intellij.codeInsight.daemon.impl.quickfix.RemoveUnusedVariableUtil;
22 import com.intellij.codeInsight.generation.surroundWith.SurroundWithUtil;
23 import com.intellij.codeInspection.dataFlow.JavaMethodContractUtil;
24 import com.intellij.lang.StdLanguages;
25 import com.intellij.lang.java.JavaLanguage;
26 import com.intellij.openapi.application.ApplicationManager;
27 import com.intellij.openapi.application.ReadAction;
28 import com.intellij.openapi.application.ReadActionProcessor;
29 import com.intellij.openapi.diagnostic.Logger;
30 import com.intellij.openapi.project.Project;
31 import com.intellij.openapi.util.Ref;
32 import com.intellij.openapi.util.text.StringUtil;
33 import com.intellij.psi.*;
34 import com.intellij.psi.codeStyle.CodeStyleManager;
35 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
36 import com.intellij.psi.codeStyle.JavaCodeStyleSettings;
37 import com.intellij.psi.codeStyle.VariableKind;
38 import com.intellij.psi.impl.source.resolve.JavaResolveUtil;
39 import com.intellij.psi.javadoc.PsiDocComment;
40 import com.intellij.psi.javadoc.PsiDocTag;
41 import com.intellij.psi.scope.processor.VariablesProcessor;
42 import com.intellij.psi.scope.util.PsiScopesUtil;
43 import com.intellij.psi.search.LocalSearchScope;
44 import com.intellij.psi.search.searches.OverridingMethodsSearch;
45 import com.intellij.psi.search.searches.ReferencesSearch;
46 import com.intellij.psi.search.searches.SuperMethodsSearch;
47 import com.intellij.psi.util.*;
48 import com.intellij.refactoring.RefactoringBundle;
49 import com.intellij.refactoring.rename.RenameUtil;
50 import com.intellij.refactoring.rename.ResolveSnapshotProvider;
51 import com.intellij.refactoring.rename.UnresolvableCollisionUsageInfo;
52 import com.intellij.refactoring.util.*;
53 import com.intellij.refactoring.util.usageInfo.DefaultConstructorImplicitUsageInfo;
54 import com.intellij.refactoring.util.usageInfo.NoConstructorClassUsageInfo;
55 import com.intellij.usageView.UsageInfo;
56 import com.intellij.util.ArrayUtil;
57 import com.intellij.util.IncorrectOperationException;
58 import com.intellij.util.VisibilityUtil;
59 import com.intellij.util.containers.ContainerUtil;
60 import com.intellij.util.containers.MultiMap;
61 import com.siyeh.IntentionPowerPackBundle;
62 import org.jetbrains.annotations.NotNull;
63 import org.jetbrains.annotations.Nullable;
64
65 import java.util.*;
66
67 /**
68  * @author Maxim.Medvedev
69  */
70 public class JavaChangeSignatureUsageProcessor implements ChangeSignatureUsageProcessor {
71   private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.changeSignature.JavaChangeSignatureUsageProcessor");
72
73   private static boolean isJavaUsage(UsageInfo info) {
74     final PsiElement element = info.getElement();
75     if (element == null) return false;
76     return element.getLanguage() == StdLanguages.JAVA;
77   }
78
79   @Override
80   public UsageInfo[] findUsages(ChangeInfo info) {
81     if (info instanceof JavaChangeInfo) {
82       return new JavaChangeSignatureUsageSearcher((JavaChangeInfo)info).findUsages();
83     }
84     else {
85       return UsageInfo.EMPTY_ARRAY;
86     }
87   }
88
89   @Override
90   public MultiMap<PsiElement, String> findConflicts(ChangeInfo info, Ref<UsageInfo[]> refUsages) {
91     if (info instanceof JavaChangeInfo) {
92       return new ConflictSearcher((JavaChangeInfo)info).findConflicts(refUsages);
93     }
94     else {
95       return new MultiMap<>();
96     }
97   }
98
99   @Override
100   public boolean processUsage(ChangeInfo changeInfo, UsageInfo usage, boolean beforeMethodChange, UsageInfo[] usages) {
101     if (!isJavaUsage(usage)) return false;
102     if (!(changeInfo instanceof JavaChangeInfo)) return false;
103
104
105     if (beforeMethodChange) {
106       if (usage instanceof CallerUsageInfo) {
107         final CallerUsageInfo callerUsageInfo = (CallerUsageInfo)usage;
108         processCallerMethod((JavaChangeInfo)changeInfo, callerUsageInfo.getMethod(), null, callerUsageInfo.isToInsertParameter(),
109                             callerUsageInfo.isToInsertException());
110         return true;
111       }
112       else if (usage instanceof OverriderUsageInfo) {
113         OverriderUsageInfo info = (OverriderUsageInfo)usage;
114         final PsiMethod method = info.getOverridingMethod();
115         final PsiMethod baseMethod = info.getBaseMethod();
116         if (info.isOriginalOverrider()) {
117           processPrimaryMethod((JavaChangeInfo)changeInfo, method, baseMethod, false);
118         }
119         else {
120           processCallerMethod((JavaChangeInfo)changeInfo, method, baseMethod, info.isToInsertArgs(), info.isToCatchExceptions());
121         }
122         return true;
123       }
124       else if (usage instanceof MethodReferenceUsageInfo && MethodReferenceUsageInfo.needToExpand((JavaChangeInfo)changeInfo)) {
125         final PsiElement element = usage.getElement();
126         if (element instanceof PsiMethodReferenceExpression ) {
127           final PsiExpression expression = LambdaRefactoringUtil.convertToMethodCallInLambdaBody((PsiMethodReferenceExpression)element);
128           if (expression instanceof PsiCallExpression) {
129             ((MethodReferenceUsageInfo)usage).setCallExpression((PsiCallExpression)expression);
130             return true;
131           }
132         }
133       }
134       else if (usage instanceof FunctionalInterfaceChangedUsageInfo) {
135         final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(usage.getProject());
136         final PsiElement element = usage.getElement();
137         final PsiMethod interfaceMethod = ((FunctionalInterfaceChangedUsageInfo)usage).getMethod();
138         if (element instanceof PsiLambdaExpression) {
139           processMethodParams((JavaChangeInfo)changeInfo, interfaceMethod,
140                               elementFactory, PsiSubstitutor.EMPTY, ((PsiLambdaExpression)element).getParameterList(), ((PsiLambdaExpression)element).getBody());
141         }
142         else if (element instanceof PsiMethodReferenceExpression) {
143           final PsiLambdaExpression lambdaExpression =
144             LambdaRefactoringUtil.convertMethodReferenceToLambda((PsiMethodReferenceExpression)element, false, true);
145           if (lambdaExpression != null) {
146             processMethodParams(((JavaChangeInfo)changeInfo), interfaceMethod, elementFactory, PsiSubstitutor.EMPTY, 
147                                 lambdaExpression.getParameterList(), lambdaExpression.getBody());
148           }
149         }
150         return true;
151       }
152
153     }
154     else {
155       PsiElement element = usage.getElement();
156       LOG.assertTrue(element != null);
157
158       if (usage instanceof DefaultConstructorImplicitUsageInfo) {
159         final DefaultConstructorImplicitUsageInfo defConstructorUsage = (DefaultConstructorImplicitUsageInfo)usage;
160         PsiMethod constructor = defConstructorUsage.getConstructor();
161         if (!constructor.isPhysical()) {
162           final boolean toPropagate =
163             changeInfo instanceof JavaChangeInfoImpl && ((JavaChangeInfoImpl)changeInfo).propagateParametersMethods.remove(constructor);
164           final PsiClass containingClass = defConstructorUsage.getContainingClass();
165           constructor = (PsiMethod)containingClass.add(constructor);
166           PsiUtil.setModifierProperty(constructor, VisibilityUtil.getVisibilityModifier(containingClass.getModifierList()), true);
167           if (toPropagate) {
168             ((JavaChangeInfoImpl)changeInfo).propagateParametersMethods.add(constructor);
169           }
170         }
171         addSuperCall((JavaChangeInfo)changeInfo, constructor, defConstructorUsage.getBaseConstructor(), usages);
172         return true;
173       }
174       else if (usage instanceof NoConstructorClassUsageInfo) {
175         addDefaultConstructor(((JavaChangeInfo)changeInfo), ((NoConstructorClassUsageInfo)usage).getPsiClass(), usages);
176         return true;
177       }
178       else if (usage instanceof MethodReferenceUsageInfo && MethodReferenceUsageInfo.needToExpand((JavaChangeInfo)changeInfo)) {
179         final MethodCallUsageInfo methodCallInfo = ((MethodReferenceUsageInfo)usage).createMethodCallInfo();
180         if (methodCallInfo != null) {
181           processMethodUsage(methodCallInfo.getElement(), (JavaChangeInfo)changeInfo, methodCallInfo.isToChangeArguments(),
182                              methodCallInfo.isToCatchExceptions(), methodCallInfo.getReferencedMethod(), methodCallInfo.getSubstitutor(), usages);
183           return true;
184         }
185       }
186       else if (usage instanceof MethodCallUsageInfo) {
187         final MethodCallUsageInfo methodCallInfo = (MethodCallUsageInfo)usage;
188         processMethodUsage(methodCallInfo.getElement(), (JavaChangeInfo)changeInfo, methodCallInfo.isToChangeArguments(),
189                            methodCallInfo.isToCatchExceptions(), methodCallInfo.getReferencedMethod(), methodCallInfo.getSubstitutor(), usages);
190         return true;
191       }
192       else if (usage instanceof ChangeSignatureParameterUsageInfo) {
193         String newName = ((ChangeSignatureParameterUsageInfo)usage).newParameterName;
194         String oldName = ((ChangeSignatureParameterUsageInfo)usage).oldParameterName;
195         processParameterUsage((PsiReferenceExpression)element, oldName, newName);
196         return true;
197       }
198       else if (usage instanceof CallReferenceUsageInfo) {
199         ((CallReferenceUsageInfo)usage).getReference().handleChangeSignature(changeInfo);
200         return true;
201       }
202       else if (element instanceof PsiEnumConstant) {
203         fixActualArgumentsList(((PsiEnumConstant)element).getArgumentList(), (JavaChangeInfo)changeInfo, true, PsiSubstitutor.EMPTY);
204         return true;
205       }
206       else if (!(usage instanceof OverriderUsageInfo) && !(usage instanceof UnresolvableCollisionUsageInfo)) {
207         PsiReference reference = usage instanceof MoveRenameUsageInfo ? usage.getReference() : element.getReference();
208         if (reference != null) {
209           PsiElement target = changeInfo.getMethod();
210           if (target != null) {
211             reference.bindToElement(target);
212           }
213         }
214       }
215     }
216     return false;
217   }
218
219   private static void processParameterUsage(PsiReferenceExpression ref, String oldName, String newName)
220     throws IncorrectOperationException {
221
222     PsiElement last = ref.getReferenceNameElement();
223     if (last instanceof PsiIdentifier && last.getText().equals(oldName)) {
224       PsiElementFactory factory = JavaPsiFacade.getElementFactory(ref.getProject());
225       PsiIdentifier newNameIdentifier = factory.createIdentifier(newName);
226       last.replace(newNameIdentifier);
227     }
228   }
229
230
231   private static void addDefaultConstructor(JavaChangeInfo changeInfo, PsiClass aClass, final UsageInfo[] usages)
232     throws IncorrectOperationException {
233     if (!(aClass instanceof PsiAnonymousClass)) {
234       PsiElementFactory factory = JavaPsiFacade.getElementFactory(aClass.getProject());
235       PsiMethod defaultConstructor = factory.createMethodFromText(aClass.getName() + "(){}", aClass);
236       defaultConstructor = (PsiMethod)CodeStyleManager.getInstance(aClass.getProject()).reformat(defaultConstructor);
237       defaultConstructor = (PsiMethod)aClass.add(defaultConstructor);
238       PsiUtil.setModifierProperty(defaultConstructor, VisibilityUtil.getVisibilityModifier(aClass.getModifierList()), true);
239       addSuperCall(changeInfo, defaultConstructor, null, usages);
240     }
241     else {
242       final PsiElement parent = aClass.getParent();
243       if (parent instanceof PsiNewExpression) {
244         final PsiExpressionList argumentList = ((PsiNewExpression)parent).getArgumentList();
245         final PsiClass baseClass = changeInfo.getMethod().getContainingClass();
246         final PsiSubstitutor substitutor = TypeConversionUtil.getSuperClassSubstitutor(baseClass, aClass, PsiSubstitutor.EMPTY);
247         fixActualArgumentsList(argumentList, changeInfo, true, substitutor);
248       }
249     }
250   }
251
252   private static void addSuperCall(JavaChangeInfo changeInfo, PsiMethod constructor, PsiMethod callee, final UsageInfo[] usages)
253     throws IncorrectOperationException {
254     final PsiElementFactory factory = JavaPsiFacade.getElementFactory(constructor.getProject());
255     PsiExpressionStatement superCall = (PsiExpressionStatement)factory.createStatementFromText("super();", constructor);
256     PsiCodeBlock body = constructor.getBody();
257     assert body != null;
258     PsiStatement[] statements = body.getStatements();
259     if (statements.length > 0) {
260       superCall = (PsiExpressionStatement)body.addBefore(superCall, statements[0]);
261     }
262     else {
263       superCall = (PsiExpressionStatement)body.add(superCall);
264     }
265     PsiMethodCallExpression callExpression = (PsiMethodCallExpression)superCall.getExpression();
266     final PsiClass aClass = constructor.getContainingClass();
267     final PsiClass baseClass = changeInfo.getMethod().getContainingClass();
268     final PsiSubstitutor substitutor = TypeConversionUtil.getSuperClassSubstitutor(baseClass, aClass, PsiSubstitutor.EMPTY);
269     processMethodUsage(callExpression.getMethodExpression(), changeInfo, true, false, callee, substitutor, usages);
270   }
271
272   private static void processMethodUsage(PsiElement ref,
273                                   JavaChangeInfo changeInfo,
274                                   boolean toChangeArguments,
275                                   boolean toCatchExceptions,
276                                   PsiMethod callee, PsiSubstitutor substitutor, final UsageInfo[] usages) throws IncorrectOperationException {
277     if (changeInfo.isNameChanged()) {
278       if (ref instanceof PsiJavaCodeReferenceElement) {
279         PsiElement last = ((PsiJavaCodeReferenceElement)ref).getReferenceNameElement();
280         if (last instanceof PsiIdentifier && last.getText().equals(changeInfo.getOldName())) {
281           last.replace(changeInfo.getNewNameIdentifier());
282         }
283       }
284     }
285
286     final PsiMethod caller = PsiTreeUtil.getParentOfType(ref, PsiMethod.class);
287     if (toChangeArguments) {
288       final PsiExpressionList list = RefactoringUtil.getArgumentListByMethodReference(ref);
289       LOG.assertTrue(list != null);
290       boolean toInsertDefaultValue = needDefaultValue(changeInfo, caller);
291       if (toInsertDefaultValue && ref instanceof PsiReferenceExpression) {
292         final PsiExpression qualifierExpression = ((PsiReferenceExpression)ref).getQualifierExpression();
293         if (qualifierExpression instanceof PsiSuperExpression && caller != null && callerSignatureIsAboutToChangeToo(caller, usages)) {
294           toInsertDefaultValue = false;
295         }
296       }
297
298       fixActualArgumentsList(list, changeInfo, toInsertDefaultValue, substitutor);
299     }
300
301     if (toCatchExceptions) {
302       PsiStatement statement;
303       if (!(ref instanceof PsiReferenceExpression &&
304             (statement = PsiTreeUtil.getParentOfType(ref, PsiStatement.class)) != null &&
305             JavaHighlightUtil.isSuperOrThisCall(statement, true, false))) {
306         if (needToCatchExceptions(changeInfo, caller)) {
307           PsiClassType[] newExceptions =
308             callee != null ? getCalleeChangedExceptionInfo(callee) : getPrimaryChangedExceptionInfo(changeInfo);
309           fixExceptions(ref, newExceptions);
310         }
311       }
312     }
313   }
314
315   private static boolean callerSignatureIsAboutToChangeToo(@NotNull final PsiMethod caller, final UsageInfo[] usages) {
316     for (UsageInfo usage : usages) {
317       if (usage instanceof MethodCallUsageInfo &&
318           MethodSignatureUtil.isSuperMethod(((MethodCallUsageInfo)usage).getReferencedMethod(), caller)) {
319         return true;
320       }
321     }
322     return false;
323   }
324
325   private static PsiClassType[] getCalleeChangedExceptionInfo(final PsiMethod callee) {
326     return callee.getThrowsList().getReferencedTypes(); //Callee method's throws list is already modified!
327   }
328
329   private static void fixExceptions(PsiElement ref, PsiClassType[] newExceptions) throws IncorrectOperationException {
330     //methods' throws lists are already modified, may use ExceptionUtil.collectUnhandledExceptions
331     newExceptions = filterCheckedExceptions(newExceptions);
332
333     PsiElement context = PsiTreeUtil.getParentOfType(ref, PsiTryStatement.class, PsiMethod.class, PsiLambdaExpression.class);
334     if (context instanceof PsiTryStatement) {
335       PsiTryStatement tryStatement = (PsiTryStatement)context;
336       PsiCodeBlock tryBlock = tryStatement.getTryBlock();
337
338       //Remove unused catches
339       Collection<PsiClassType> classes = ExceptionUtil.collectUnhandledExceptions(tryBlock, tryBlock);
340       PsiParameter[] catchParameters = tryStatement.getCatchBlockParameters();
341       for (PsiParameter parameter : catchParameters) {
342         final PsiType caughtType = parameter.getType();
343
344         if (!(caughtType instanceof PsiClassType)) continue;
345         if (ExceptionUtil.isUncheckedExceptionOrSuperclass((PsiClassType)caughtType)) continue;
346
347         if (!isCatchParameterRedundant((PsiClassType)caughtType, classes)) continue;
348         parameter.getParent().delete(); //delete catch section
349       }
350
351       PsiClassType[] exceptionsToAdd = filterUnhandledExceptions(newExceptions, tryBlock);
352       addExceptions(exceptionsToAdd, tryStatement);
353
354       adjustPossibleEmptyTryStatement(tryStatement);
355     }
356     else {
357       newExceptions = filterUnhandledExceptions(newExceptions, ref);
358       if (newExceptions.length > 0) {
359         //Add new try statement
360         PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(ref.getProject());
361         PsiTryStatement tryStatement = (PsiTryStatement)elementFactory.createStatementFromText("try {} catch (Exception e) {}", null);
362         PsiStatement anchor;
363         if (context instanceof PsiLambdaExpression) {
364           PsiCodeBlock codeBlock = RefactoringUtil.expandExpressionLambdaToCodeBlock((PsiLambdaExpression)context);
365           LOG.assertTrue(codeBlock != null);
366           anchor = codeBlock.getStatements()[0];
367         }
368         else {
369           anchor = PsiTreeUtil.getParentOfType(ref, PsiStatement.class);
370         }
371         LOG.assertTrue(anchor != null);
372         PsiElement container = anchor.getParent();
373         PsiElement[] elements = SurroundWithUtil.moveDeclarationsOut(container, new PsiElement[]{anchor}, true);
374         tryStatement = (PsiTryStatement)container.addAfter(tryStatement, elements[elements.length - 1]);
375         PsiCodeBlock tryBlock = tryStatement.getTryBlock();
376         LOG.assertTrue(tryBlock != null);
377         tryBlock.addRange(elements[0], elements[elements.length - 1]);
378
379         addExceptions(newExceptions, tryStatement);
380         container.deleteChildRange(elements[0], elements[elements.length - 1]);
381         tryStatement.getCatchSections()[0].delete(); //Delete dummy catch section
382       }
383     }
384   }
385
386   public static boolean hasNewCheckedExceptions(JavaChangeInfo changeInfo) {
387     return filterCheckedExceptions(getPrimaryChangedExceptionInfo(changeInfo)).length > 0;
388   }
389
390   private static PsiClassType[] filterCheckedExceptions(PsiClassType[] exceptions) {
391     List<PsiClassType> result = new ArrayList<>();
392     for (PsiClassType exceptionType : exceptions) {
393       if (!ExceptionUtil.isUncheckedException(exceptionType)) result.add(exceptionType);
394     }
395     return result.toArray(PsiClassType.EMPTY_ARRAY);
396   }
397
398   private static void adjustPossibleEmptyTryStatement(PsiTryStatement tryStatement) throws IncorrectOperationException {
399     PsiCodeBlock tryBlock = tryStatement.getTryBlock();
400     if (tryBlock != null) {
401       if (tryStatement.getCatchSections().length == 0 &&
402           tryStatement.getFinallyBlock() == null &&
403           tryStatement.getResourceList() == null) {
404         PsiElement firstBodyElement = tryBlock.getFirstBodyElement();
405         if (firstBodyElement != null) {
406           tryStatement.getParent().addRangeAfter(firstBodyElement, tryBlock.getLastBodyElement(), tryStatement);
407         }
408         tryStatement.delete();
409       }
410     }
411   }
412
413   private static void addExceptions(PsiClassType[] exceptionsToAdd, PsiTryStatement tryStatement) throws IncorrectOperationException {
414     for (PsiClassType type : exceptionsToAdd) {
415       final JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(tryStatement.getProject());
416       String name = styleManager.suggestVariableName(VariableKind.PARAMETER, null, null, type).names[0];
417       name = styleManager.suggestUniqueVariableName(name, tryStatement, false);
418
419       PsiCatchSection catchSection =
420         JavaPsiFacade.getElementFactory(tryStatement.getProject()).createCatchSection(type, name, tryStatement);
421       tryStatement.add(catchSection);
422     }
423   }
424
425   private static PsiClassType[] filterUnhandledExceptions(PsiClassType[] exceptions, PsiElement place) {
426     List<PsiClassType> result = new ArrayList<>();
427     for (PsiClassType exception : exceptions) {
428       if (!ExceptionUtil.isHandled(exception, place)) result.add(exception);
429     }
430     return result.toArray(PsiClassType.EMPTY_ARRAY);
431   }
432
433   private static boolean isCatchParameterRedundant(PsiClassType catchParamType, Collection<? extends PsiClassType> thrownTypes) {
434     for (PsiType exceptionType : thrownTypes) {
435       if (exceptionType.isConvertibleFrom(catchParamType)) return false;
436     }
437     return true;
438   }
439
440   //This methods works equally well for primary usages as well as for propagated callers' usages
441   private static void fixActualArgumentsList(PsiExpressionList list,
442                                              JavaChangeInfo changeInfo,
443                                              boolean toInsertDefaultValue,
444                                              PsiSubstitutor substitutor) throws IncorrectOperationException {
445     final PsiElementFactory factory = JavaPsiFacade.getElementFactory(list.getProject());
446     if (changeInfo.isParameterSetOrOrderChanged()) {
447       if (changeInfo instanceof JavaChangeInfoImpl && ((JavaChangeInfoImpl)changeInfo).isPropagationEnabled) {
448         final ParameterInfoImpl[] createdParmsInfo = ((JavaChangeInfoImpl)changeInfo).getCreatedParmsInfoWithoutVarargs();
449         for (ParameterInfoImpl info : createdParmsInfo) {
450           PsiExpression newArg;
451           if (toInsertDefaultValue) {
452             newArg = createDefaultValue(changeInfo, factory, info, list, substitutor);
453           }
454           else {
455             newArg = factory.createExpressionFromText(info.getName(), list);
456           }
457           if (newArg != null) JavaCodeStyleManager.getInstance(list.getProject()).shortenClassReferences(list.add(newArg));
458         }
459       }
460       else {
461         final PsiExpression[] args = list.getExpressions();
462         final int nonVarargCount = getNonVarargCount(changeInfo, args);
463         final int varargCount = args.length - nonVarargCount;
464         if (varargCount<0) return;
465         PsiExpression[] newVarargInitializers = null;
466
467         final int newArgsLength;
468         final int newNonVarargCount;
469         final JavaParameterInfo[] newParms = changeInfo.getNewParameters();
470         if (changeInfo.isArrayToVarargs()) {
471           newNonVarargCount = newParms.length - 1;
472           final JavaParameterInfo lastNewParm = newParms[newParms.length - 1];
473           final PsiExpression arrayToConvert = args[lastNewParm.getOldIndex()];
474           if (arrayToConvert instanceof PsiNewExpression) {
475             final PsiNewExpression expression = (PsiNewExpression)arrayToConvert;
476             final PsiArrayInitializerExpression arrayInitializer = expression.getArrayInitializer();
477             if (arrayInitializer != null) {
478               newVarargInitializers = arrayInitializer.getInitializers();
479             }
480           }
481           newArgsLength = newVarargInitializers == null ? newParms.length : newNonVarargCount + newVarargInitializers.length;
482         }
483         else if (changeInfo.isRetainsVarargs()) {
484           newNonVarargCount = newParms.length - 1;
485           newArgsLength = newNonVarargCount + varargCount;
486         }
487         else if (changeInfo.isObtainsVarags()) {
488           newNonVarargCount = newParms.length - 1;
489           newArgsLength = newNonVarargCount;
490         }
491         else {
492           newNonVarargCount = newParms.length;
493           newArgsLength = newParms.length;
494         }
495
496         String[] oldVarargs = null;
497         if (changeInfo.wasVararg() && !changeInfo.isRetainsVarargs()) {
498           oldVarargs = new String[varargCount];
499           for (int i = nonVarargCount; i < args.length; i++) {
500             oldVarargs[i - nonVarargCount] = args[i].getText();
501           }
502         }
503
504         final PsiExpression[] newArgs = new PsiExpression[newArgsLength];
505         for (int i = 0; i < newNonVarargCount; i++) {
506           if (newParms[i].getOldIndex() == nonVarargCount && oldVarargs != null) {
507             PsiType type = newParms[i].createType(changeInfo.getMethod(), list.getManager());
508             if (type instanceof PsiArrayType) {
509               type = substitutor.substitute(type);
510               type = TypeConversionUtil.erasure(type);
511               String typeText = type.getCanonicalText();
512               if (type instanceof PsiEllipsisType) {
513                 typeText = typeText.replace("...", "[]");
514               }
515               String text = "new " + typeText + "{" + StringUtil.join(oldVarargs, ",") + "}";
516               newArgs[i] = factory.createExpressionFromText(text, changeInfo.getMethod());
517               continue;
518             }
519           }
520           newArgs[i] = createActualArgument(changeInfo, list, newParms[i], toInsertDefaultValue, args, substitutor);
521         }
522         if (changeInfo.isArrayToVarargs()) {
523           if (newVarargInitializers == null) {
524             newArgs[newNonVarargCount] =
525               createActualArgument(changeInfo, list, newParms[newNonVarargCount], toInsertDefaultValue, args, substitutor);
526           }
527           else {
528             System.arraycopy(newVarargInitializers, 0, newArgs, newNonVarargCount, newVarargInitializers.length);
529           }
530         }
531         else {
532           final int newVarargCount = newArgsLength - newNonVarargCount;
533           LOG.assertTrue(newVarargCount == 0 || newVarargCount == varargCount);
534           for (int i = newNonVarargCount; i < newArgsLength; i++){
535             final int oldIndex = newParms[newNonVarargCount].getOldIndex();
536             if (oldIndex >= 0 && oldIndex != nonVarargCount) {
537               newArgs[i] = createActualArgument(changeInfo, list, newParms[newNonVarargCount], toInsertDefaultValue, args, substitutor);
538             } else {
539               System.arraycopy(args, nonVarargCount, newArgs, newNonVarargCount, newVarargCount);
540               break;
541             }
542           }
543         }
544         ChangeSignatureUtil.synchronizeList(list, Arrays.asList(newArgs), ExpressionList.INSTANCE, changeInfo.toRemoveParm());
545       }
546     }
547   }
548
549   private static int getNonVarargCount(JavaChangeInfo changeInfo, PsiExpression[] args) {
550     if (!changeInfo.wasVararg()) return args.length;
551     return changeInfo.getOldParameterTypes().length - 1;
552   }
553
554
555   @Nullable
556   private static PsiExpression createActualArgument(JavaChangeInfo changeInfo,
557                                                     final PsiExpressionList list,
558                                                     final JavaParameterInfo info,
559                                                     final boolean toInsertDefaultValue,
560                                                     final PsiExpression[] args,
561                                                     PsiSubstitutor substitutor) throws IncorrectOperationException {
562     final PsiElementFactory factory = JavaPsiFacade.getElementFactory(list.getProject());
563     final int index = info.getOldIndex();
564     if (index >= 0 && index < args.length) {
565       return args[index];
566     }
567     else {
568       if (toInsertDefaultValue) {
569         return createDefaultValue(changeInfo, factory, info, list, substitutor);
570       }
571       else {
572         return factory.createExpressionFromText(info.getName(), list);
573       }
574     }
575   }
576
577   @Nullable
578   private static PsiExpression createDefaultValue(JavaChangeInfo changeInfo,
579                                                   final PsiElementFactory factory,
580                                                   final JavaParameterInfo info,
581                                                   final PsiExpressionList list, PsiSubstitutor substitutor)
582     throws IncorrectOperationException {
583     if (info.isUseAnySingleVariable()) {
584       final PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(list.getProject()).getResolveHelper();
585       final PsiType type = info.getTypeWrapper().getType(changeInfo.getMethod(), list.getManager());
586       final VariablesProcessor processor = new VariablesProcessor(false) {
587         @Override
588         protected boolean check(PsiVariable var, ResolveState state) {
589           if (var instanceof PsiField && !resolveHelper.isAccessible((PsiField)var, list, null)) return false;
590           if (var instanceof PsiLocalVariable && list.getTextRange().getStartOffset() <= var.getTextRange().getStartOffset()) return false;
591           if (PsiTreeUtil.isAncestor(var, list, false)) return false;
592           final PsiType varType = state.get(PsiSubstitutor.KEY).substitute(var.getType());
593           return type.isAssignableFrom(varType);
594         }
595
596         @Override
597         public boolean execute(@NotNull PsiElement pe, @NotNull ResolveState state) {
598           super.execute(pe, state);
599           return size() < 2;
600         }
601       };
602       PsiScopesUtil.treeWalkUp(processor, list, null);
603       if (processor.size() == 1) {
604         final PsiVariable result = processor.getResult(0);
605         return factory.createExpressionFromText(result.getName(), list);
606       }
607       if (processor.size() == 0) {
608         final PsiClass parentClass = PsiTreeUtil.getParentOfType(list, PsiClass.class);
609         if (parentClass != null) {
610           PsiClass containingClass = parentClass;
611           final Set<PsiClass> containingClasses = new HashSet<>();
612           while (containingClass != null) {
613             if (type.isAssignableFrom(factory.createType(containingClass, PsiSubstitutor.EMPTY))) {
614               containingClasses.add(containingClass);
615             }
616             containingClass = PsiTreeUtil.getParentOfType(containingClass, PsiClass.class);
617           }
618           if (containingClasses.size() == 1) {
619             return RefactoringChangeUtil.createThisExpression(parentClass.getManager(), containingClasses.contains(parentClass) ? null
620                                                                                                                                 : containingClasses
621                                                                                           .iterator().next());
622           }
623         }
624       }
625     }
626     final PsiCallExpression callExpression = PsiTreeUtil.getParentOfType(list, PsiCallExpression.class);
627     final String defaultValue = info.getDefaultValue();
628     return callExpression != null ? (PsiExpression)info.getActualValue(callExpression, substitutor)
629                                   : !StringUtil.isEmpty(defaultValue) ? factory.createExpressionFromText(defaultValue, list) : null;
630   }
631
632
633   @Override
634   public boolean processPrimaryMethod(ChangeInfo changeInfo) {
635     if (!StdLanguages.JAVA.equals(changeInfo.getLanguage()) || !(changeInfo instanceof JavaChangeInfo)) return false;
636     final PsiElement element = changeInfo.getMethod();
637     LOG.assertTrue(element instanceof PsiMethod);
638     if (!JavaLanguage.INSTANCE.equals(element.getLanguage())) return false;
639     if (changeInfo.isGenerateDelegate()) {
640       generateDelegate((JavaChangeInfo)changeInfo);
641     }
642     processPrimaryMethod((JavaChangeInfo)changeInfo, (PsiMethod)element, null, true);
643     return true;
644   }
645
646   @Override
647   public boolean shouldPreviewUsages(ChangeInfo changeInfo, UsageInfo[] usages) {
648     return false;
649   }
650
651   @Override
652   public boolean setupDefaultValues(ChangeInfo changeInfo, Ref<UsageInfo[]> refUsages, Project project) {
653     if (!(changeInfo instanceof JavaChangeInfo)) return true;
654     for (UsageInfo usageInfo : refUsages.get()) {
655       if (usageInfo instanceof  MethodCallUsageInfo) {
656         MethodCallUsageInfo methodCallUsageInfo = (MethodCallUsageInfo)usageInfo;
657         if (methodCallUsageInfo.isToChangeArguments()){
658           final PsiElement element = methodCallUsageInfo.getElement();
659           if (element == null) continue;
660           final PsiMethod caller = PsiTreeUtil.getParentOfType(element, PsiMethod.class);
661           final boolean needDefaultValue = needDefaultValue(changeInfo, caller);
662           if (needDefaultValue && (caller == null || !MethodSignatureUtil.isSuperMethod(methodCallUsageInfo.getReferencedMethod(), caller))) {
663             final ParameterInfo[] parameters = changeInfo.getNewParameters();
664             for (ParameterInfo parameter : parameters) {
665               final String defaultValue = parameter.getDefaultValue();
666               if (defaultValue == null && parameter.getOldIndex() == -1) {
667                 ((ParameterInfoImpl)parameter).setDefaultValue("");
668                 if (!ApplicationManager.getApplication().isUnitTestMode()) {
669                   final PsiType type = ((ParameterInfoImpl)parameter).getTypeWrapper().getType(element);
670                   final DefaultValueChooser chooser =
671                     new DefaultValueChooser(project, parameter.getName(), PsiTypesUtil.getDefaultValueOfType(type));
672                   if (chooser.showAndGet()) {
673                     if (chooser.feelLucky()) {
674                       parameter.setUseAnySingleVariable(true);
675                     }
676                     else {
677                       ((ParameterInfoImpl)parameter).setDefaultValue(chooser.getDefaultValue());
678                     }
679                   }
680                   else {
681                     return false;
682                   }
683                 }
684               }
685             }
686           }
687         }
688       }
689     }
690     return true;
691   }
692
693   @Override
694   public void registerConflictResolvers(List<ResolveSnapshotProvider.ResolveSnapshot> snapshots,
695                                         @NotNull ResolveSnapshotProvider resolveSnapshotProvider,
696                                         UsageInfo[] usages, ChangeInfo changeInfo) {
697     snapshots.add(resolveSnapshotProvider.createSnapshot(changeInfo.getMethod()));
698     for (UsageInfo usage : usages) {
699       if (usage instanceof OverriderUsageInfo) {
700         snapshots.add(resolveSnapshotProvider.createSnapshot(((OverriderUsageInfo)usage).getOverridingMethod()));
701       }
702     }
703   }
704
705   private static boolean needDefaultValue(ChangeInfo changeInfo, @Nullable PsiMethod method) {
706     if (!(changeInfo instanceof JavaChangeInfoImpl)) {
707       return true;
708     }
709     if (method != null) {
710       final Set<PsiMethod> parametersMethods = ((JavaChangeInfoImpl)changeInfo).propagateParametersMethods;
711       if (parametersMethods.contains(method)) {
712         return false;
713       }
714       for (PsiMethod superMethod : method.findDeepestSuperMethods()) {
715         if (parametersMethods.contains(superMethod)) {
716           return false;
717         }
718       }
719     }
720     return true;
721   }
722
723   public static void generateDelegate(JavaChangeInfo changeInfo) throws IncorrectOperationException {
724     final PsiMethod delegate = generateDelegatePrototype(changeInfo);
725     PsiClass targetClass = changeInfo.getMethod().getContainingClass();
726     LOG.assertTrue(targetClass != null);
727     targetClass.addBefore(delegate, changeInfo.getMethod());
728   }
729
730   public static PsiMethod generateDelegatePrototype(JavaChangeInfo changeInfo) {
731     final PsiMethod delegate = (PsiMethod)changeInfo.getMethod().copy();
732     PsiClass targetClass = changeInfo.getMethod().getContainingClass();
733     LOG.assertTrue(targetClass != null);
734     if (targetClass.isInterface() && delegate.getBody() == null) {
735       delegate.getModifierList().setModifierProperty(PsiModifier.DEFAULT, true);
736     }
737     PsiElementFactory factory = JavaPsiFacade.getElementFactory(targetClass.getProject());
738     ChangeSignatureProcessor.makeEmptyBody(factory, delegate);
739     final PsiCallExpression callExpression = ChangeSignatureProcessor.addDelegatingCallTemplate(delegate, changeInfo.getNewName());
740     addDelegateArguments(changeInfo, factory, callExpression);
741     return delegate;
742   }
743
744
745   private static void addDelegateArguments(JavaChangeInfo changeInfo, PsiElementFactory factory, final PsiCallExpression callExpression) throws IncorrectOperationException {
746     final JavaParameterInfo[] newParms = changeInfo.getNewParameters();
747     final String[] oldParameterNames = changeInfo.getOldParameterNames();
748     for (JavaParameterInfo newParm : newParms) {
749       final PsiExpression actualArg;
750       if (newParm.getOldIndex() >= 0) {
751         actualArg = factory.createExpressionFromText(oldParameterNames[newParm.getOldIndex()], callExpression);
752       }
753       else {
754         actualArg = (PsiExpression)newParm.getActualValue(callExpression, PsiSubstitutor.EMPTY);
755       }
756       final PsiExpressionList argumentList = callExpression.getArgumentList();
757       if (actualArg != null && argumentList != null) {
758         JavaCodeStyleManager.getInstance(callExpression.getProject()).shortenClassReferences(argumentList.add(actualArg));
759       }
760     }
761   }
762
763   private static void processPrimaryMethod(JavaChangeInfo changeInfo, PsiMethod method,
764                                            PsiMethod baseMethod,
765                                            boolean isOriginal) throws IncorrectOperationException {
766     PsiElementFactory factory = JavaPsiFacade.getElementFactory(method.getProject());
767
768     if (changeInfo.isVisibilityChanged()) {
769       PsiModifierList modifierList = method.getModifierList();
770       final String highestVisibility = isOriginal
771                                        ? changeInfo.getNewVisibility()
772                                        : VisibilityUtil.getHighestVisibility(changeInfo.getNewVisibility(),
773                                                                              VisibilityUtil.getVisibilityModifier(modifierList));
774       VisibilityUtil.setVisibility(modifierList, highestVisibility);
775     }
776
777     if (changeInfo.isNameChanged()) {
778       String newName = baseMethod == null ? changeInfo.getNewName() :
779                        RefactoringUtil.suggestNewOverriderName(method.getName(), baseMethod.getName(), changeInfo.getNewName());
780
781       if (newName != null && !newName.equals(method.getName())) {
782         final PsiIdentifier nameId = method.getNameIdentifier();
783         assert nameId != null : method;
784         nameId.replace(JavaPsiFacade.getElementFactory(method.getProject()).createIdentifier(newName));
785       }
786     }
787
788     final PsiSubstitutor substitutor =
789       baseMethod == null ? PsiSubstitutor.EMPTY : ChangeSignatureProcessor.calculateSubstitutor(method, baseMethod);
790
791     final JavaCodeStyleManager javaCodeStyleManager = JavaCodeStyleManager.getInstance(method.getProject());
792     if (changeInfo.isReturnTypeChanged()) {
793       PsiType newTypeElement = changeInfo.getNewReturnType().getType(changeInfo.getMethod().getParameterList(), method.getManager());
794       final PsiType returnType = substitutor.substitute(newTypeElement);
795       // don't modify return type for non-Java overriders (EJB)
796       if (method.getName().equals(changeInfo.getNewName())) {
797         final PsiTypeElement typeElement = method.getReturnTypeElement();
798         if (typeElement != null) {
799           javaCodeStyleManager.shortenClassReferences(typeElement.replace(factory.createTypeElement(returnType)));
800         }
801       }
802     }
803
804     PsiParameterList list = method.getParameterList();
805     int newParamsLength = processMethodParams(changeInfo, baseMethod, factory, substitutor, list, method.getBody());
806     fixJavadocsForChangedMethod(method, changeInfo, newParamsLength);
807     if (changeInfo.isExceptionSetOrOrderChanged()) {
808       final PsiClassType[] newExceptions = getPrimaryChangedExceptionInfo(changeInfo);
809       fixPrimaryThrowsLists(method, newExceptions);
810     }
811
812     tryUpdateContracts(method, changeInfo);
813
814     if (baseMethod == null && method.findSuperMethods().length == 0) {
815       final PsiAnnotation annotation = AnnotationUtil.findAnnotation(method, true, Override.class.getName());
816       if (annotation != null) {
817         annotation.delete();
818       }
819     }
820   }
821
822   private static int processMethodParams(JavaChangeInfo changeInfo,
823                                          PsiMethod baseMethod,
824                                          PsiElementFactory factory,
825                                          PsiSubstitutor substitutor,
826                                          PsiParameterList list,
827                                          PsiElement methodBody) {
828     PsiParameter[] parameters = list.getParameters();
829
830     final JavaParameterInfo[] parameterInfos = changeInfo.getNewParameters();
831     final int delta = baseMethod != null ? baseMethod.getParameterList().getParametersCount() - list.getParametersCount() : 0;
832     PsiParameter[] newParms = new PsiParameter[Math.max(parameterInfos.length - delta, 0)];
833     final String[] oldParameterNames = changeInfo.getOldParameterNames();
834     final String[] oldParameterTypes = changeInfo.getOldParameterTypes();
835     for (int i = 0; i < newParms.length; i++) {
836       JavaParameterInfo info = parameterInfos[i];
837       int index = info.getOldIndex();
838       if (index >= 0) {
839         PsiParameter parameter = parameters[index];
840         newParms[i] = parameter;
841
842         String oldName = oldParameterNames[index];
843         if (!oldName.equals(info.getName()) && oldName.equals(parameter.getName())) {
844           PsiIdentifier newIdentifier = factory.createIdentifier(info.getName());
845           parameter.getNameIdentifier().replace(newIdentifier);
846         }
847
848         PsiTypeElement typeElement = parameter.getTypeElement();
849         if (typeElement != null) {
850           parameter.normalizeDeclaration();
851           typeElement = parameter.getTypeElement();
852           String oldType = oldParameterTypes[index];
853           if (!oldType.equals(info.getTypeText())) {
854             PsiType newType =
855               substitutor.substitute(info.createType(changeInfo.getMethod().getParameterList(), changeInfo.getMethod().getManager()));
856             typeElement.replace(factory.createTypeElement(newType));
857           }
858         }
859       }
860       else {
861         PsiElement parent = list.getParent();
862         if (parent instanceof PsiLambdaExpression && !((PsiLambdaExpression)parent).hasFormalParameterTypes()) {
863           PsiExpression dummyLambdaParam = factory.createExpressionFromText(info.getName() + "-> {}", list);
864           newParms[i] = ((PsiLambdaExpression)dummyLambdaParam).getParameterList().getParameters()[0];
865         }
866         else {
867           newParms[i] = createNewParameter(changeInfo, info, substitutor);
868         }
869       }
870     }
871
872
873     resolveParameterVsFieldsConflicts(newParms, list, changeInfo.toRemoveParm(), methodBody);
874     return newParms.length;
875   }
876
877   private static PsiClassType[] getPrimaryChangedExceptionInfo(JavaChangeInfo changeInfo) throws IncorrectOperationException {
878     final ThrownExceptionInfo[] newExceptionInfos = changeInfo.getNewExceptions();
879     PsiClassType[] newExceptions = new PsiClassType[newExceptionInfos.length];
880     final PsiMethod method = changeInfo.getMethod();
881     for (int i = 0; i < newExceptions.length; i++) {
882       newExceptions[i] =
883         (PsiClassType)newExceptionInfos[i].createType(method, method.getManager()); //context really does not matter here
884     }
885     return newExceptions;
886   }
887
888
889   private static void processCallerMethod(JavaChangeInfo changeInfo, PsiMethod caller,
890                                           PsiMethod baseMethod,
891                                           boolean toInsertParams,
892                                           boolean toInsertThrows) throws IncorrectOperationException {
893     LOG.assertTrue(toInsertParams || toInsertThrows);
894     if (toInsertParams) {
895       List<PsiParameter> newParameters = new ArrayList<>();
896       ContainerUtil.addAll(newParameters, caller.getParameterList().getParameters());
897       final JavaParameterInfo[] primaryNewParms = changeInfo.getNewParameters();
898       PsiSubstitutor substitutor =
899         baseMethod == null ? PsiSubstitutor.EMPTY : ChangeSignatureProcessor.calculateSubstitutor(caller, baseMethod);
900       final PsiClass aClass = changeInfo.getMethod().getContainingClass();
901       final PsiClass callerContainingClass = caller.getContainingClass();
902       final PsiSubstitutor psiSubstitutor = aClass != null && callerContainingClass != null && callerContainingClass.isInheritor(aClass, true)
903                                             ? TypeConversionUtil.getSuperClassSubstitutor(aClass, callerContainingClass, substitutor)
904                                             : PsiSubstitutor.EMPTY;
905       for (JavaParameterInfo info : primaryNewParms) {
906         if (info.getOldIndex() < 0) newParameters.add(createNewParameter(changeInfo, info, psiSubstitutor, substitutor));
907       }
908       PsiParameter[] arrayed = newParameters.toArray(PsiParameter.EMPTY_ARRAY);
909       boolean[] toRemoveParm = new boolean[arrayed.length];
910       Arrays.fill(toRemoveParm, false);
911       resolveParameterVsFieldsConflicts(arrayed, caller.getParameterList(), toRemoveParm, caller.getBody());
912     }
913
914     if (toInsertThrows) {
915       List<PsiJavaCodeReferenceElement> newThrowns = new ArrayList<>();
916       final PsiReferenceList throwsList = caller.getThrowsList();
917       ContainerUtil.addAll(newThrowns, throwsList.getReferenceElements());
918       final ThrownExceptionInfo[] primaryNewExns = changeInfo.getNewExceptions();
919       for (ThrownExceptionInfo thrownExceptionInfo : primaryNewExns) {
920         if (thrownExceptionInfo.getOldIndex() < 0) {
921           final PsiClassType type = (PsiClassType)thrownExceptionInfo.createType(caller, caller.getManager());
922           final PsiJavaCodeReferenceElement ref =
923             JavaPsiFacade.getElementFactory(caller.getProject()).createReferenceElementByType(type);
924           newThrowns.add(ref);
925         }
926       }
927       PsiJavaCodeReferenceElement[] arrayed = newThrowns.toArray(PsiJavaCodeReferenceElement.EMPTY_ARRAY);
928       boolean[] toRemoveParm = new boolean[arrayed.length];
929       Arrays.fill(toRemoveParm, false);
930       ChangeSignatureUtil.synchronizeList(throwsList, Arrays.asList(arrayed), ThrowsList.INSTANCE, toRemoveParm);
931     }
932   }
933
934   private static void fixPrimaryThrowsLists(PsiMethod method, PsiClassType[] newExceptions) throws IncorrectOperationException {
935     PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(method.getProject());
936     PsiJavaCodeReferenceElement[] refs = new PsiJavaCodeReferenceElement[newExceptions.length];
937     for (int i = 0; i < refs.length; i++) {
938       refs[i] = elementFactory.createReferenceElementByType(newExceptions[i]);
939     }
940     PsiReferenceList throwsList = elementFactory.createReferenceList(refs);
941
942     PsiReferenceList methodThrowsList = (PsiReferenceList)method.getThrowsList().replace(throwsList);
943     methodThrowsList = (PsiReferenceList)JavaCodeStyleManager.getInstance(method.getProject()).shortenClassReferences(methodThrowsList);
944     CodeStyleManager.getInstance(method.getManager().getProject())
945         .reformatRange(method, method.getParameterList().getTextRange().getEndOffset(),
946                        methodThrowsList.getTextRange().getEndOffset());
947   }
948
949   private static void fixJavadocsForChangedMethod(final PsiMethod method, final JavaChangeInfo changeInfo, int newParamsLength) throws IncorrectOperationException {
950     final PsiParameter[] parameters = method.getParameterList().getParameters();
951     final JavaParameterInfo[] newParms = changeInfo.getNewParameters();
952     LOG.assertTrue(parameters.length <= newParamsLength);
953     final Set<PsiParameter> newParameters = new HashSet<>();
954     final String[] oldParameterNames = changeInfo.getOldParameterNames();
955     for (int i = 0; i < newParamsLength; i++) {
956       JavaParameterInfo newParm = newParms[i];
957       if (newParm.getOldIndex() < 0 ||
958           newParm.getOldIndex() == i && !(newParm.getName().equals(oldParameterNames[newParm.getOldIndex()]) && newParm.getTypeText().equals(changeInfo.getOldParameterTypes()[newParm.getOldIndex()]))) {
959         newParameters.add(parameters[i]);
960       }
961     }
962     RefactoringUtil.fixJavadocsForParams(method, newParameters, pair -> {
963       final PsiParameter parameter = pair.first;
964       final String oldParamName = pair.second;
965       final int oldIdx = ArrayUtil.find(oldParameterNames, oldParamName);
966       int newIndex = method.getParameterList().getParameterIndex(parameter);
967       return oldIdx >= 0 && newIndex >= 0 && changeInfo.getNewParameters()[newIndex].getOldIndex() == oldIdx;
968     }, paramName -> ArrayUtil.find(oldParameterNames, paramName) >= 0);
969
970     if (changeInfo.isReturnTypeChanged()) {
971       PsiDocComment docComment = method.getDocComment();
972       if (docComment != null) {
973         CanonicalTypes.Type type = changeInfo.getNewReturnType();
974         PsiDocTag aReturn = docComment.findTagByName("return");
975         if (PsiType.VOID.equalsToText(type.getTypeText())) {
976           if (aReturn != null) {
977             aReturn.delete();
978           }
979         }
980         else if (aReturn == null) {
981           docComment.add(JavaPsiFacade.getElementFactory(method.getProject()).createDocTagFromText("@return"));
982         }
983       }
984     }
985   }
986
987   private static PsiParameter createNewParameter(JavaChangeInfo changeInfo, JavaParameterInfo newParm,
988                                                  PsiSubstitutor... substitutor) throws IncorrectOperationException {
989     final PsiParameterList list = changeInfo.getMethod().getParameterList();
990     final Project project = list.getProject();
991     final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
992     PsiType type = newParm.createType(list);
993     for (PsiSubstitutor psiSubstitutor : substitutor) {
994       type = psiSubstitutor.substitute(type);
995     }
996     PsiParameter parameter = factory.createParameter(newParm.getName(), type, list);
997     if (JavaCodeStyleSettings.getInstance(list.getContainingFile()).GENERATE_FINAL_PARAMETERS) {
998       PsiUtil.setModifierProperty(parameter, PsiModifier.FINAL, true);
999     }
1000     return parameter;
1001   }
1002
1003   private static void resolveParameterVsFieldsConflicts(final PsiParameter[] newParms,
1004                                                         final PsiParameterList list,
1005                                                         boolean[] toRemoveParm, 
1006                                                         final PsiElement methodBody) throws IncorrectOperationException {
1007     List<FieldConflictsResolver> conflictResolvers = new ArrayList<>();
1008     for (PsiParameter parameter : newParms) {
1009       conflictResolvers.add(new FieldConflictsResolver(parameter.getName(), methodBody));
1010     }
1011     ChangeSignatureUtil.synchronizeList(list, Arrays.asList(newParms), ParameterList.INSTANCE, toRemoveParm);
1012     JavaCodeStyleManager.getInstance(list.getProject()).shortenClassReferences(list);
1013     for (FieldConflictsResolver fieldConflictsResolver : conflictResolvers) {
1014       fieldConflictsResolver.fix();
1015     }
1016   }
1017
1018   private static boolean needToCatchExceptions(JavaChangeInfo changeInfo, PsiMethod caller) {
1019     return changeInfo.isExceptionSetOrOrderChanged() &&
1020            !(changeInfo instanceof JavaChangeInfoImpl && ((JavaChangeInfoImpl)changeInfo).propagateExceptionsMethods.contains(caller));
1021   }
1022
1023   private static class ParameterList implements ChangeSignatureUtil.ChildrenGenerator<PsiParameterList, PsiParameter> {
1024     public static final ParameterList INSTANCE = new ParameterList();
1025
1026     @Override
1027     public List<PsiParameter> getChildren(PsiParameterList psiParameterList) {
1028       return Arrays.asList(psiParameterList.getParameters());
1029     }
1030   }
1031
1032   private static class ThrowsList implements ChangeSignatureUtil.ChildrenGenerator<PsiReferenceList, PsiJavaCodeReferenceElement> {
1033     public static final ThrowsList INSTANCE = new ThrowsList();
1034
1035     @Override
1036     public List<PsiJavaCodeReferenceElement> getChildren(PsiReferenceList throwsList) {
1037       return Arrays.asList(throwsList.getReferenceElements());
1038     }
1039   }
1040
1041   public static class ConflictSearcher {
1042     private final JavaChangeInfo myChangeInfo;
1043
1044     private ConflictSearcher(@NotNull JavaChangeInfo changeInfo) {
1045       this.myChangeInfo = changeInfo;
1046     }
1047
1048     public MultiMap<PsiElement, String> findConflicts(Ref<UsageInfo[]> refUsages) {
1049       MultiMap<PsiElement, String> conflictDescriptions = new MultiMap<>();
1050       final PsiMethod prototype = addMethodConflicts(conflictDescriptions);
1051       Set<UsageInfo> usagesSet = new HashSet<>(Arrays.asList(refUsages.get()));
1052       RenameUtil.removeConflictUsages(usagesSet);
1053       if (myChangeInfo.isVisibilityChanged()) {
1054         try {
1055           addInaccessibilityDescriptions(usagesSet, conflictDescriptions);
1056         }
1057         catch (IncorrectOperationException e) {
1058           LOG.error(e);
1059         }
1060       }
1061
1062       final boolean[] toRemove = myChangeInfo.toRemoveParm();
1063       //introduce parameter object deletes parameters but replaces their usages with generated code
1064       final boolean checkUnusedParameter = myChangeInfo.checkUnusedParameter();
1065       if (checkUnusedParameter) {
1066         checkParametersToDelete(myChangeInfo.getMethod(), toRemove, conflictDescriptions);
1067       }
1068       checkContract(conflictDescriptions, myChangeInfo.getMethod(), false);
1069
1070       for (UsageInfo usageInfo : usagesSet) {
1071         final PsiElement element = usageInfo.getElement();
1072         if (usageInfo instanceof OverriderUsageInfo) {
1073           final PsiMethod method = ((OverriderUsageInfo)usageInfo).getOverridingMethod();
1074           final PsiMethod baseMethod = ((OverriderUsageInfo)usageInfo).getBaseMethod();
1075           final int delta = baseMethod.getParameterList().getParametersCount() - method.getParameterList().getParametersCount();
1076           if (delta > 0) {
1077             if (toRemove.length > 0 && toRemove[toRemove.length - 1]) { //todo check if implicit parameter is not the last one
1078               conflictDescriptions.putValue(baseMethod, "Implicit last parameter should not be deleted");
1079             }
1080           }
1081           else if (prototype != null && baseMethod == myChangeInfo.getMethod()) {
1082             ConflictsUtil.checkMethodConflicts(method.getContainingClass(), method, prototype, conflictDescriptions);
1083             if (checkUnusedParameter) {
1084               checkParametersToDelete(method, toRemove, conflictDescriptions);
1085             }
1086           }
1087
1088           checkContract(conflictDescriptions, method, true);
1089         }
1090         else if (element instanceof PsiMethodReferenceExpression && MethodReferenceUsageInfo.needToExpand(myChangeInfo)) {
1091           conflictDescriptions.putValue(element, RefactoringBundle.message("expand.method.reference.warning"));
1092         }
1093         else if (element instanceof PsiJavaCodeReferenceElement) {
1094           final PsiElement parent = element.getParent();
1095           if (parent instanceof PsiCallExpression) {
1096             final PsiExpressionList argumentList = ((PsiCallExpression)parent).getArgumentList();
1097             if (argumentList != null) {
1098               final PsiExpression[] args = argumentList.getExpressions();
1099               for (int i = 0; i < toRemove.length; i++) {
1100                 if (toRemove[i] && i < args.length) {
1101                   if (RemoveUnusedVariableUtil.checkSideEffects(args[i], null, new ArrayList<>())) {
1102                     conflictDescriptions.putValue(args[i], "Parameter '" + myChangeInfo.getOldParameterNames()[i] + "' has usage that is not safe to delete");
1103                   }
1104                 }
1105               }
1106             }
1107           }
1108         }
1109       }
1110
1111       return conflictDescriptions;
1112     }
1113
1114     private static void checkParametersToDelete(PsiMethod method, boolean[] toRemove, MultiMap<PsiElement, String> conflictDescriptions) {
1115       final PsiParameter[] parameters = method.getParameterList().getParameters();
1116       final PsiCodeBlock body = method.getBody();
1117       if (body != null) {
1118         final LocalSearchScope searchScope = new LocalSearchScope(body);
1119         for (int i = 0; i < toRemove.length; i++) {
1120           if (toRemove[i] && ReferencesSearch.search(parameters[i], searchScope).findFirst() != null) {
1121             conflictDescriptions.putValue(parameters[i], StringUtil.capitalize(RefactoringUIUtil.getDescription(parameters[i], true)) + " is used in method body");
1122           }
1123         }
1124       }
1125     }
1126
1127     private void checkContract(MultiMap<PsiElement, String> conflictDescriptions, PsiMethod method, boolean override) {
1128       try {
1129         ContractConverter.convertContract(method, myChangeInfo);
1130       }
1131       catch (ContractConverter.ContractInheritedException e) {
1132         if (!override) {
1133           conflictDescriptions.putValue(method, "@Contract annotation cannot be updated automatically: " + e.getMessage());
1134         }
1135       }
1136       catch (ContractConverter.ContractConversionException e) {
1137         conflictDescriptions.putValue(method, "@Contract annotation cannot be updated automatically: " + e.getMessage());
1138       }
1139     }
1140
1141     private boolean needToChangeCalls() {
1142       return myChangeInfo.isNameChanged() || myChangeInfo.isParameterSetOrOrderChanged() || myChangeInfo.isExceptionSetOrOrderChanged();
1143     }
1144
1145
1146     private void addInaccessibilityDescriptions(Set<UsageInfo> usages, MultiMap<PsiElement, String> conflictDescriptions)
1147       throws IncorrectOperationException {
1148       PsiMethod method = myChangeInfo.getMethod();
1149       PsiModifierList modifierList = (PsiModifierList)method.getModifierList().copy();
1150       String visibility = myChangeInfo.getNewVisibility();
1151       VisibilityUtil.setVisibility(modifierList, visibility);
1152
1153       searchForHierarchyConflicts(method, conflictDescriptions, visibility);
1154
1155       for (Iterator<UsageInfo> iterator = usages.iterator(); iterator.hasNext();) {
1156         UsageInfo usageInfo = iterator.next();
1157         PsiElement element = usageInfo.getElement();
1158         if (element != null) {
1159           if (element instanceof PsiQualifiedReference) {
1160             PsiClass accessObjectClass = null;
1161             PsiElement qualifier = ((PsiQualifiedReference)element).getQualifier();
1162             if (qualifier instanceof PsiExpression) {
1163               accessObjectClass = (PsiClass)PsiUtil.getAccessObjectClass((PsiExpression)qualifier).getElement();
1164             }
1165
1166             if (!JavaPsiFacade.getInstance(element.getProject()).getResolveHelper()
1167               .isAccessible(method, modifierList, element, accessObjectClass, null)) {
1168               String message =
1169                 RefactoringBundle.message("0.with.1.visibility.is.not.accessible.from.2",
1170                                           RefactoringUIUtil.getDescription(method, true),
1171                                           VisibilityUtil.toPresentableText(visibility),
1172                                           RefactoringUIUtil.getDescription(ConflictsUtil.getContainer(element), true));
1173               conflictDescriptions.putValue(method, message);
1174               if (!needToChangeCalls()) {
1175                 iterator.remove();
1176               }
1177             }
1178           }
1179         }
1180       }
1181     }
1182
1183     public static void searchForHierarchyConflicts(PsiMethod method, MultiMap<PsiElement, String> conflicts, final String modifier) {
1184       SuperMethodsSearch.search(method, ReadAction.compute(method::getContainingClass), true, false).forEach(
1185         new ReadActionProcessor<MethodSignatureBackedByPsiMethod>() {
1186           @Override
1187           public boolean processInReadAction(MethodSignatureBackedByPsiMethod methodSignature) {
1188             final PsiMethod superMethod = methodSignature.getMethod();
1189             if (!hasCompatibleVisibility(superMethod, true, modifier)) {
1190               conflicts.putValue(superMethod, IntentionPowerPackBundle.message(
1191                 "0.will.have.incompatible.access.privileges.with.super.1",
1192                 RefactoringUIUtil.getDescription(method, false),
1193                 RefactoringUIUtil.getDescription(superMethod, true)));
1194             }
1195             return true;
1196           }
1197         });
1198       OverridingMethodsSearch.search(method).forEach(new ReadActionProcessor<PsiMethod>() {
1199         @Override
1200         public boolean processInReadAction(PsiMethod overridingMethod) {
1201           if (!isVisibleFromOverridingMethod(method, overridingMethod, modifier)) {
1202             conflicts.putValue(overridingMethod, IntentionPowerPackBundle.message(
1203               "0.will.no.longer.be.visible.from.overriding.1",
1204               RefactoringUIUtil.getDescription(method, false),
1205               RefactoringUIUtil.getDescription(overridingMethod, true)));
1206           }
1207           else if (!hasCompatibleVisibility(overridingMethod, false, modifier)) {
1208             conflicts.putValue(overridingMethod, IntentionPowerPackBundle.message(
1209               "0.will.have.incompatible.access.privileges.with.overriding.1",
1210               RefactoringUIUtil.getDescription(method, false),
1211               RefactoringUIUtil.getDescription(overridingMethod, true)));
1212           }
1213           return true;
1214         }
1215       });
1216     }
1217
1218     private static boolean hasCompatibleVisibility(PsiMethod method, boolean isSuper, final String modifier) {
1219       if (modifier.equals(PsiModifier.PRIVATE)) {
1220         return false;
1221       }
1222       else if (modifier.equals(PsiModifier.PACKAGE_LOCAL)) {
1223         if (isSuper) {
1224           return !(method.hasModifierProperty(PsiModifier.PUBLIC) || method.hasModifierProperty(PsiModifier.PROTECTED));
1225         }
1226         return true;
1227       }
1228       else if (modifier.equals(PsiModifier.PROTECTED)) {
1229         if (isSuper) {
1230           return !method.hasModifierProperty(PsiModifier.PUBLIC);
1231         }
1232         else {
1233           return method.hasModifierProperty(PsiModifier.PROTECTED) || method.hasModifierProperty(PsiModifier.PUBLIC);
1234         }
1235       }
1236       else if (modifier.equals(PsiModifier.PUBLIC)) {
1237         if (!isSuper) {
1238           return method.hasModifierProperty(PsiModifier.PUBLIC);
1239         }
1240         return true;
1241       }
1242       throw new AssertionError();
1243     }
1244
1245     private static boolean isVisibleFromOverridingMethod(PsiMethod method, PsiMethod overridingMethod, final String modifier) {
1246       final PsiModifierList modifierListCopy = (PsiModifierList)method.getModifierList().copy();
1247       modifierListCopy.setModifierProperty(modifier, true);
1248       return JavaResolveUtil.isAccessible(method, method.getContainingClass(), modifierListCopy, overridingMethod, null, null);
1249     }
1250
1251
1252     private PsiMethod addMethodConflicts(MultiMap<PsiElement, String> conflicts) {
1253       String newMethodName = myChangeInfo.getNewName();
1254       try {
1255         final PsiMethod method = myChangeInfo.getMethod();
1256         if (!StdLanguages.JAVA.equals(method.getLanguage())) return null;
1257         PsiManager manager = method.getManager();
1258         PsiElementFactory factory = JavaPsiFacade.getElementFactory(manager.getProject());
1259         final CanonicalTypes.Type returnType = myChangeInfo.getNewReturnType();
1260         PsiMethod prototype;
1261         if (returnType != null) {
1262           prototype = factory.createMethod(newMethodName, returnType.getType(method, manager));
1263         }
1264         else {
1265           prototype = factory.createConstructor();
1266           prototype.setName(newMethodName);
1267         }
1268         JavaParameterInfo[] parameters = myChangeInfo.getNewParameters();
1269
1270
1271         for (JavaParameterInfo info : parameters) {
1272           PsiType parameterType = info.createType(method, manager);
1273           if (parameterType == null) {
1274             parameterType =
1275               JavaPsiFacade.getElementFactory(method.getProject()).createTypeFromText(CommonClassNames.JAVA_LANG_OBJECT, method);
1276           }
1277           PsiParameter param = factory.createParameter(info.getName(), parameterType, method);
1278           if (JavaCodeStyleSettings.getInstance(method.getContainingFile()).GENERATE_FINAL_PARAMETERS) {
1279             PsiUtil.setModifierProperty(param, PsiModifier.FINAL, true);
1280           }
1281           prototype.getParameterList().add(param);
1282         }
1283
1284         ConflictsUtil.checkMethodConflicts(method.getContainingClass(), myChangeInfo.isGenerateDelegate() ? null : method, prototype, conflicts);
1285         return prototype;
1286       }
1287       catch (IncorrectOperationException e) {
1288         LOG.error(e);
1289       }
1290       return null;
1291     }
1292   }
1293
1294   private static class ExpressionList implements ChangeSignatureUtil.ChildrenGenerator<PsiExpressionList, PsiExpression> {
1295     public static final ExpressionList INSTANCE = new ExpressionList();
1296
1297     @Override
1298     public List<PsiExpression> getChildren(PsiExpressionList psiExpressionList) {
1299       return Arrays.asList(psiExpressionList.getExpressions());
1300     }
1301   }
1302
1303   private static void tryUpdateContracts(PsiMethod method, JavaChangeInfo info) {
1304     PsiAnnotation annotation = JavaMethodContractUtil.findContractAnnotation(method);
1305     if (annotation == null) return;
1306     try {
1307       PsiAnnotation newAnnotation = ContractConverter.convertContract(method, info);
1308       if (newAnnotation != null && !newAnnotation.isPhysical()) {
1309         annotation.replace(newAnnotation);
1310       }
1311     }
1312     catch (ContractConverter.ContractConversionException ignored) {
1313     }
1314   }
1315 }